Eliminate GPUI View, ViewContext, and WindowContext types (#22632)

Nathan Sobo , Antonio Scandurra , Cole Miller , Mikayla , Joseph , max , Michael Sloan , Mikayla Maki , Mikayla , and joão created

There's still a bit more work to do on this, but this PR is compiling
(with warnings) after eliminating the key types. When the tasks below
are complete, this will be the new narrative for GPUI:

- `Entity<T>` - This replaces `View<T>`/`Model<T>`. It represents a unit
of state, and if `T` implements `Render`, then `Entity<T>` implements
`Element`.
- `&mut App` This replaces `AppContext` and represents the app.
- `&mut Context<T>` This replaces `ModelContext` and derefs to `App`. It
is provided by the framework when updating an entity.
- `&mut Window` Broken out of `&mut WindowContext` which no longer
exists. Every method that once took `&mut WindowContext` now takes `&mut
Window, &mut App` and every method that took `&mut ViewContext<T>` now
takes `&mut Window, &mut Context<T>`

Not pictured here are the two other failed attempts. It's been quite a
month!

Tasks:

- [x] Remove `View`, `ViewContext`, `WindowContext` and thread through
`Window`
- [x] [@cole-miller @mikayla-maki] Redraw window when entities change
- [x] [@cole-miller @mikayla-maki] Get examples and Zed running
- [x] [@cole-miller @mikayla-maki] Fix Zed rendering
- [x] [@mikayla-maki] Fix todo! macros and comments
- [x] Fix a bug where the editor would not be redrawn because of view
caching
- [x] remove publicness window.notify() and replace with
`AppContext::notify`
- [x] remove `observe_new_window_models`, replace with
`observe_new_models` with an optional window
- [x] Fix a bug where the project panel would not be redrawn because of
the wrong refresh() call being used
- [x] Fix the tests
- [x] Fix warnings by eliminating `Window` params or using `_`
- [x] Fix conflicts
- [x] Simplify generic code where possible
- [x] Rename types
- [ ] Update docs

### issues post merge

- [x] Issues switching between normal and insert mode
- [x] Assistant re-rendering failure
- [x] Vim test failures
- [x] Mac build issue



Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Cole Miller <cole@zed.dev>
Co-authored-by: Mikayla <mikayla@zed.dev>
Co-authored-by: Joseph <joseph@zed.dev>
Co-authored-by: max <max@zed.dev>
Co-authored-by: Michael Sloan <michael@zed.dev>
Co-authored-by: Mikayla Maki <mikaylamaki@Mikaylas-MacBook-Pro.local>
Co-authored-by: Mikayla <mikayla.c.maki@gmail.com>
Co-authored-by: joão <joao@zed.dev>

Change summary

crates/activity_indicator/src/activity_indicator.rs               |  125 
crates/anthropic/src/anthropic.rs                                 |    2 
crates/assets/src/assets.rs                                       |    6 
crates/assistant/src/assistant.rs                                 |   14 
crates/assistant/src/assistant_configuration.rs                   |   42 
crates/assistant/src/assistant_panel.rs                           |  386 
crates/assistant/src/inline_assistant.rs                          |  392 
crates/assistant/src/slash_command_settings.rs                    |    4 
crates/assistant/src/terminal_inline_assistant.rs                 |  297 
crates/assistant2/src/active_thread.rs                            |   67 
crates/assistant2/src/assistant.rs                                |    6 
crates/assistant2/src/assistant_configuration.rs                  |   36 
crates/assistant2/src/assistant_model_selector.rs                 |   22 
crates/assistant2/src/assistant_panel.rs                          |  328 
crates/assistant2/src/buffer_codegen.rs                           |  126 
crates/assistant2/src/context.rs                                  |   18 
crates/assistant2/src/context_picker.rs                           |  111 
crates/assistant2/src/context_picker/directory_context_picker.rs  |   66 
crates/assistant2/src/context_picker/fetch_context_picker.rs      |   67 
crates/assistant2/src/context_picker/file_context_picker.rs       |  111 
crates/assistant2/src/context_picker/thread_context_picker.rs     |   68 
crates/assistant2/src/context_store.rs                            |  121 
crates/assistant2/src/context_strip.rs                            |  137 
crates/assistant2/src/inline_assistant.rs                         |  421 
crates/assistant2/src/inline_prompt_editor.rs                     |  301 
crates/assistant2/src/message_editor.rs                           |  160 
crates/assistant2/src/terminal_codegen.rs                         |   24 
crates/assistant2/src/terminal_inline_assistant.rs                |  109 
crates/assistant2/src/thread.rs                                   |   20 
crates/assistant2/src/thread_history.rs                           |   82 
crates/assistant2/src/thread_store.rs                             |   46 
crates/assistant2/src/ui/context_pill.rs                          |   26 
crates/assistant_context_editor/src/assistant_context_editor.rs   |    4 
crates/assistant_context_editor/src/context.rs                    |  146 
crates/assistant_context_editor/src/context/context_tests.rs      |   84 
crates/assistant_context_editor/src/context_editor.rs             |  390 
crates/assistant_context_editor/src/context_history.rs            |   73 
crates/assistant_context_editor/src/context_store.rs              |  104 
crates/assistant_context_editor/src/patch.rs                      |   38 
crates/assistant_context_editor/src/slash_command.rs              |   90 
crates/assistant_context_editor/src/slash_command_picker.rs       |   57 
crates/assistant_settings/src/assistant_settings.rs               |    6 
crates/assistant_slash_command/src/assistant_slash_command.rs     |   17 
crates/assistant_slash_command/src/extension_slash_command.rs     |   14 
crates/assistant_slash_command/src/slash_command_registry.rs      |    6 
crates/assistant_slash_command/src/slash_command_working_set.rs   |    8 
crates/assistant_slash_commands/src/assistant_slash_commands.rs   |    8 
crates/assistant_slash_commands/src/auto_command.rs               |   18 
crates/assistant_slash_commands/src/cargo_workspace_command.rs    |   16 
crates/assistant_slash_commands/src/context_server_command.rs     |   18 
crates/assistant_slash_commands/src/default_command.rs            |   12 
crates/assistant_slash_commands/src/delta_command.rs              |   13 
crates/assistant_slash_commands/src/diagnostics_command.rs        |   24 
crates/assistant_slash_commands/src/docs_command.rs               |   18 
crates/assistant_slash_commands/src/fetch_command.rs              |   12 
crates/assistant_slash_commands/src/file_command.rs               |   20 
crates/assistant_slash_commands/src/now_command.rs                |   12 
crates/assistant_slash_commands/src/project_command.rs            |   16 
crates/assistant_slash_commands/src/prompt_command.rs             |   14 
crates/assistant_slash_commands/src/search_command.rs             |   16 
crates/assistant_slash_commands/src/selection_command.rs          |   17 
crates/assistant_slash_commands/src/streaming_example_command.rs  |   12 
crates/assistant_slash_commands/src/symbols_command.rs            |   14 
crates/assistant_slash_commands/src/tab_command.rs                |   30 
crates/assistant_slash_commands/src/terminal_command.rs           |   20 
crates/assistant_tool/src/assistant_tool.rs                       |    9 
crates/assistant_tool/src/tool_registry.rs                        |    6 
crates/assistant_tool/src/tool_working_set.rs                     |    6 
crates/assistant_tools/src/assistant_tools.rs                     |    4 
crates/assistant_tools/src/now_tool.rs                            |    7 
crates/audio/src/assets.rs                                        |    6 
crates/audio/src/audio.rs                                         |    8 
crates/auto_update/src/auto_update.rs                             |   53 
crates/auto_update_ui/src/auto_update_ui.rs                       |   55 
crates/auto_update_ui/src/update_notification.rs                  |   22 
crates/breadcrumbs/src/breadcrumbs.rs                             |   31 
crates/call/src/call_settings.rs                                  |    4 
crates/call/src/cross_platform/mod.rs                             |   72 
crates/call/src/cross_platform/participant.rs                     |    4 
crates/call/src/cross_platform/room.rs                            |  114 
crates/call/src/macos/mod.rs                                      |   72 
crates/call/src/macos/participant.rs                              |    4 
crates/call/src/macos/room.rs                                     |  112 
crates/channel/src/channel.rs                                     |    4 
crates/channel/src/channel_buffer.rs                              |   48 
crates/channel/src/channel_chat.rs                                |   56 
crates/channel/src/channel_store.rs                               |   87 
crates/channel/src/channel_store_tests.rs                         |   18 
crates/cli/src/main.rs                                            |    4 
crates/client/src/client.rs                                       |   52 
crates/client/src/telemetry.rs                                    |    6 
crates/client/src/test.rs                                         |    6 
crates/client/src/user.rs                                         |   55 
crates/client/src/zed_urls.rs                                     |    6 
crates/collab/src/auth.rs                                         |    2 
crates/collab/src/main.rs                                         |    1 
crates/collab/src/seed.rs                                         |    2 
crates/collab/src/stripe_billing.rs                               |    2 
crates/collab/src/tests.rs                                        |    6 
crates/collab/src/tests/channel_buffer_tests.rs                   |   75 
crates/collab/src/tests/channel_guest_tests.rs                    |    4 
crates/collab/src/tests/channel_message_tests.rs                  |   24 
crates/collab/src/tests/channel_tests.rs                          |    8 
crates/collab/src/tests/editor_tests.rs                           |  284 
crates/collab/src/tests/following_tests.rs                        |  408 
crates/collab/src/tests/integration_tests.rs                      |   53 
crates/collab/src/tests/notification_tests.rs                     |    4 
crates/collab/src/tests/random_project_collaboration_tests.rs     |   18 
crates/collab/src/tests/remote_editing_collaboration_tests.rs     |   10 
crates/collab/src/tests/test_server.rs                            |   94 
crates/collab/src/user_backfiller.rs                              |    2 
crates/collab_ui/src/channel_view.rs                              |  275 
crates/collab_ui/src/chat_panel.rs                                |  227 
crates/collab_ui/src/chat_panel/message_editor.rs                 |   80 
crates/collab_ui/src/collab_panel.rs                              |  425 
crates/collab_ui/src/collab_panel/channel_modal.rs                |  185 
crates/collab_ui/src/collab_panel/contact_finder.rs               |   51 
crates/collab_ui/src/collab_ui.rs                                 |    6 
crates/collab_ui/src/notification_panel.rs                        |  222 
crates/collab_ui/src/notifications.rs                             |    4 
crates/collab_ui/src/notifications/collab_notification.rs         |    2 
crates/collab_ui/src/notifications/incoming_call_notification.rs  |   24 
crates/collab_ui/src/notifications/project_shared_notification.rs |   34 
crates/collab_ui/src/notifications/stories/collab_notification.rs |    2 
crates/collab_ui/src/panel_settings.rs                            |    8 
crates/command_palette/src/command_palette.rs                     |  146 
crates/command_palette_hooks/src/command_palette_hooks.rs         |   28 
crates/context_server/src/client.rs                               |    2 
crates/context_server/src/context_server.rs                       |    4 
crates/context_server/src/context_server_tool.rs                  |   11 
crates/context_server/src/extension_context_server.rs             |   13 
crates/context_server/src/manager.rs                              |   18 
crates/context_server/src/registry.rs                             |   15 
crates/context_server_settings/src/context_server_settings.rs     |    6 
crates/copilot/src/copilot.rs                                     |   91 
crates/copilot/src/copilot_chat.rs                                |   12 
crates/copilot/src/copilot_completion_provider.rs                 |  200 
crates/copilot/src/sign_in.rs                                     |   62 
crates/db/src/db.rs                                               |    6 
crates/diagnostics/src/diagnostics.rs                             |  276 
crates/diagnostics/src/diagnostics_tests.rs                       |  154 
crates/diagnostics/src/items.rs                                   |   74 
crates/diagnostics/src/project_diagnostics_settings.rs            |    4 
crates/diagnostics/src/toolbar_controls.rs                        |   23 
crates/docs_preprocessor/src/main.rs                              |    2 
crates/editor/src/blame_entry_tooltip.rs                          |   35 
crates/editor/src/blink_manager.rs                                |   16 
crates/editor/src/clangd_ext.rs                                   |   15 
crates/editor/src/code_context_menus.rs                           |  101 
crates/editor/src/display_map.rs                                  |  141 
crates/editor/src/display_map/block_map.rs                        |   41 
crates/editor/src/display_map/crease_map.rs                       |   50 
crates/editor/src/display_map/fold_map.rs                         |   28 
crates/editor/src/display_map/inlay_map.rs                        |   10 
crates/editor/src/display_map/tab_map.rs                          |   10 
crates/editor/src/display_map/wrap_map.rs                         |   22 
crates/editor/src/editor.rs                                       |  430 
crates/editor/src/editor_settings.rs                              |    9 
crates/editor/src/editor_settings_controls.rs                     |   78 
crates/editor/src/editor_tests.rs                                 |  414 
crates/editor/src/element.rs                                      |  631 
crates/editor/src/git/blame.rs                                    |   41 
crates/editor/src/git/project_diff.rs                             |  194 
crates/editor/src/highlight_matching_bracket.rs                   |   13 
crates/editor/src/hover_links.rs                                  |  120 
crates/editor/src/hover_popover.rs                                |  222 
crates/editor/src/hunk_diff.rs                                    | 1586 +
crates/editor/src/indent_guides.rs                                |   29 
crates/editor/src/inlay_hint_cache.rs                             |  263 
crates/editor/src/inline_completion_tests.rs                      |   68 
crates/editor/src/items.rs                                        |  308 
crates/editor/src/linked_editing_ranges.rs                        |   10 
crates/editor/src/lsp_ext.rs                                      |    6 
crates/editor/src/mouse_context_menu.rs                           |   81 
crates/editor/src/movement.rs                                     |   47 
crates/editor/src/proposed_changes_editor.rs                      |  153 
crates/editor/src/rust_analyzer_ext.rs                            |   31 
crates/editor/src/scroll.rs                                       |  123 
crates/editor/src/scroll/actions.rs                               |   68 
crates/editor/src/scroll/autoscroll.rs                            |   27 
crates/editor/src/selections_collection.rs                        |   57 
crates/editor/src/signature_help.rs                               |   23 
crates/editor/src/signature_help/popover.rs                       |   12 
crates/editor/src/tasks.rs                                        |   15 
crates/editor/src/test.rs                                         |   36 
crates/editor/src/test/editor_lsp_test_context.rs                 |   30 
crates/editor/src/test/editor_test_context.rs                     |  104 
crates/evals/src/eval.rs                                          |   14 
crates/extension/src/extension.rs                                 |    4 
crates/extension/src/extension_host_proxy.rs                      |   14 
crates/extension/src/extension_manifest.rs                        |    2 
crates/extension_host/src/extension_host.rs                       |   72 
crates/extension_host/src/extension_settings.rs                   |    4 
crates/extension_host/src/extension_store_test.rs                 |    8 
crates/extension_host/src/headless_host.rs                        |   20 
crates/extension_host/src/wasm_host.rs                            |    4 
crates/extension_host/src/wasm_host/wit.rs                        |    2 
crates/extensions_ui/src/components/extension_card.rs             |    2 
crates/extensions_ui/src/components/feature_upsell.rs             |    4 
crates/extensions_ui/src/extension_suggest.rs                     |   21 
crates/extensions_ui/src/extension_version_selector.rs            |   55 
crates/extensions_ui/src/extensions_ui.rs                         |  237 
crates/feature_flags/src/feature_flags.rs                         |   30 
crates/feedback/src/feedback.rs                                   |   42 
crates/feedback/src/feedback_modal.rs                             |  110 
crates/feedback/src/system_specs.rs                               |    6 
crates/file_finder/src/file_finder.rs                             |  276 
crates/file_finder/src/file_finder_settings.rs                    |    2 
crates/file_finder/src/file_finder_tests.rs                       |  205 
crates/file_finder/src/new_path_prompt.rs                         |   79 
crates/file_finder/src/open_path_prompt.rs                        |   42 
crates/file_icons/src/file_icons.rs                               |   16 
crates/fs/src/fs.rs                                               |    8 
crates/git/src/blame.rs                                           |    2 
crates/git/src/git.rs                                             |    2 
crates/git/src/hosting_provider.rs                                |   10 
crates/git/src/repository.rs                                      |    2 
crates/git_hosting_providers/src/git_hosting_providers.rs         |    4 
crates/git_ui/src/git_panel.rs                                    |  376 
crates/git_ui/src/git_panel_settings.rs                           |    2 
crates/git_ui/src/git_ui.rs                                       |    8 
crates/git_ui/src/repository_selector.rs                          |   85 
crates/go_to_line/src/cursor_position.rs                          |   53 
crates/go_to_line/src/go_to_line.rs                               |  161 
crates/gpui/docs/contexts.md                                      |    2 
crates/gpui/docs/key_dispatch.md                                  |    8 
crates/gpui/examples/animation.rs                                 |   14 
crates/gpui/examples/gif_viewer.rs                                |    8 
crates/gpui/examples/gradient.rs                                  |   18 
crates/gpui/examples/hello_world.rs                               |   10 
crates/gpui/examples/image/image.rs                               |   18 
crates/gpui/examples/image_loading.rs                             |   38 
crates/gpui/examples/input.rs                                     |  165 
crates/gpui/examples/opacity.rs                                   |   20 
crates/gpui/examples/ownership_post.rs                            |    8 
crates/gpui/examples/painting.rs                                  |   28 
crates/gpui/examples/set_menus.rs                                 |   14 
crates/gpui/examples/shadow.rs                                    |   10 
crates/gpui/examples/svg/svg.rs                                   |   12 
crates/gpui/examples/text_wrapper.rs                              |    9 
crates/gpui/examples/uniform_list.rs                              |   47 
crates/gpui/examples/window.rs                                    |   65 
crates/gpui/examples/window_positioning.rs                        |   46 
crates/gpui/examples/window_shadow.rs                             |   60 
crates/gpui/src/app.rs                                            |  486 
crates/gpui/src/app/async_context.rs                              |  164 
crates/gpui/src/app/entity_map.rs                                 |  286 
crates/gpui/src/app/model_context.rs                              |  567 
crates/gpui/src/app/test_context.rs                               |  263 
crates/gpui/src/asset_cache.rs                                    |    6 
crates/gpui/src/element.rs                                        |  178 
crates/gpui/src/elements/anchored.rs                              |   27 
crates/gpui/src/elements/animation.rs                             |   21 
crates/gpui/src/elements/canvas.rs                                |   29 
crates/gpui/src/elements/deferred.rs                              |   17 
crates/gpui/src/elements/div.rs                                   |  378 
crates/gpui/src/elements/img.rs                                   |  114 
crates/gpui/src/elements/list.rs                                  |  123 
crates/gpui/src/elements/surface.rs                               |   17 
crates/gpui/src/elements/svg.rs                                   |   48 
crates/gpui/src/elements/text.rs                                  |  183 
crates/gpui/src/elements/uniform_list.rs                          |  122 
crates/gpui/src/executor.rs                                       |    4 
crates/gpui/src/geometry.rs                                       |    6 
crates/gpui/src/global.rs                                         |    6 
crates/gpui/src/gpui.rs                                           |   82 
crates/gpui/src/input.rs                                          |   70 
crates/gpui/src/interactive.rs                                    |   32 
crates/gpui/src/key_dispatch.rs                                   |   18 
crates/gpui/src/platform.rs                                       |   71 
crates/gpui/src/platform/app_menu.rs                              |    4 
crates/gpui/src/platform/linux/text_system.rs                     |    2 
crates/gpui/src/platform/linux/x11/window.rs                      |    2 
crates/gpui/src/platform/windows/dispatcher.rs                    |    2 
crates/gpui/src/platform/windows/events.rs                        |    2 
crates/gpui/src/platform/windows/platform.rs                      |    2 
crates/gpui/src/platform/windows/window.rs                        |    2 
crates/gpui/src/prelude.rs                                        |    2 
crates/gpui/src/style.rs                                          |   41 
crates/gpui/src/taffy.rs                                          |   16 
crates/gpui/src/test.rs                                           |    2 
crates/gpui/src/text_system/line.rs                               |   42 
crates/gpui/src/view.rs                                           |  389 
crates/gpui/src/window.rs                                         |  544 
crates/gpui/src/window/prompts.rs                                 |   73 
crates/gpui_macros/src/derive_render.rs                           |    2 
crates/gpui_macros/src/test.rs                                    |    2 
crates/html_to_markdown/src/html_to_markdown.rs                   |    2 
crates/image_viewer/src/image_viewer.rs                           |   91 
crates/indexed_docs/src/extension_indexed_docs_provider.rs        |    4 
crates/indexed_docs/src/indexed_docs.rs                           |    4 
crates/indexed_docs/src/registry.rs                               |    6 
crates/indexed_docs/src/store.rs                                  |    4 
crates/inline_completion/src/inline_completion.rs                 |   74 
crates/inline_completion_button/src/inline_completion_button.rs   |  280 
crates/journal/src/journal.rs                                     |  103 
crates/language/src/buffer.rs                                     |  174 
crates/language/src/buffer_tests.rs                               |  190 
crates/language/src/language.rs                                   |   10 
crates/language/src/language_registry.rs                          |    6 
crates/language/src/language_settings.rs                          |   16 
crates/language/src/syntax_map/syntax_map_tests.rs                |   50 
crates/language/src/task_context.rs                               |    6 
crates/language_extension/src/extension_lsp_adapter.rs            |    2 
crates/language_model/src/fake_provider.rs                        |   20 
crates/language_model/src/language_model.rs                       |   30 
crates/language_model/src/registry.rs                             |   38 
crates/language_model/src/request.rs                              |    4 
crates/language_model_selector/src/language_model_selector.rs     |   91 
crates/language_models/src/language_models.rs                     |   13 
crates/language_models/src/logging.rs                             |    2 
crates/language_models/src/provider/anthropic.rs                  |   70 
crates/language_models/src/provider/cloud.rs                      |   78 
crates/language_models/src/provider/copilot_chat.rs               |   38 
crates/language_models/src/provider/google.rs                     |   68 
crates/language_models/src/provider/lmstudio.rs                   |   54 
crates/language_models/src/provider/ollama.rs                     |   55 
crates/language_models/src/provider/open_ai.rs                    |   70 
crates/language_models/src/settings.rs                            |    6 
crates/language_selector/src/active_buffer_language.rs            |   26 
crates/language_selector/src/language_selector.rs                 |   92 
crates/language_tools/src/key_context_view.rs                     |   75 
crates/language_tools/src/language_tools.rs                       |    4 
crates/language_tools/src/lsp_log.rs                              |  657 
crates/language_tools/src/lsp_log_tests.rs                        |    7 
crates/language_tools/src/syntax_tree_view.rs                     |  143 
crates/languages/src/c.rs                                         |    4 
crates/languages/src/css.rs                                       |    5 
crates/languages/src/go.rs                                        |    8 
crates/languages/src/json.rs                                      |    4 
crates/languages/src/lib.rs                                       |    6 
crates/languages/src/python.rs                                    |   14 
crates/languages/src/rust.rs                                      |   12 
crates/languages/src/typescript.rs                                |    5 
crates/livekit_client/examples/test_app.rs                        |   51 
crates/livekit_client/src/remote_video_track_view.rs              |   23 
crates/livekit_client/src/test.rs                                 |    2 
crates/livekit_client_macos/examples/test_app_macos.rs            |    4 
crates/livekit_client_macos/src/prod.rs                           |    2 
crates/livekit_client_macos/src/test.rs                           |    2 
crates/lmstudio/src/lmstudio.rs                                   |    2 
crates/lsp/src/lsp.rs                                             |    8 
crates/markdown/examples/markdown.rs                              |   20 
crates/markdown/examples/markdown_as_child.rs                     |   23 
crates/markdown/src/markdown.rs                                   |  180 
crates/markdown_preview/src/markdown_preview.rs                   |   11 
crates/markdown_preview/src/markdown_preview_view.rs              |  194 
crates/markdown_preview/src/markdown_renderer.rs                  |   75 
crates/multi_buffer/src/multi_buffer.rs                           |  246 
crates/multi_buffer/src/multi_buffer_tests.rs                     |  175 
crates/notifications/src/notification_store.rs                    |   42 
crates/ollama/src/ollama.rs                                       |    2 
crates/open_ai/src/open_ai.rs                                     |    2 
crates/outline/src/outline.rs                                     |  143 
crates/outline_panel/src/outline_panel.rs                         |  403 
crates/outline_panel/src/outline_panel_settings.rs                |    2 
crates/picker/src/head.rs                                         |   36 
crates/picker/src/highlighted_match_with_paths.rs                 |    4 
crates/picker/src/picker.rs                                       |  341 
crates/prettier/src/prettier.rs                                   |    6 
crates/project/src/buffer_store.rs                                |  287 
crates/project/src/connection_manager.rs                          |   22 
crates/project/src/debounced_delay.rs                             |    6 
crates/project/src/environment.rs                                 |   16 
crates/project/src/git.rs                                         |   29 
crates/project/src/image_store.rs                                 |  121 
crates/project/src/lsp_command.rs                                 |  278 
crates/project/src/lsp_ext_command.rs                             |   52 
crates/project/src/lsp_store.rs                                   |  305 
crates/project/src/prettier_store.rs                              |   42 
crates/project/src/project.rs                                     |  326 
crates/project/src/project_settings.rs                            |   43 
crates/project/src/project_tests.rs                               |   13 
crates/project/src/search.rs                                      |   14 
crates/project/src/task_inventory.rs                              |   32 
crates/project/src/task_store.rs                                  |   57 
crates/project/src/terminals.rs                                   |   36 
crates/project/src/toolchain_store.rs                             |   55 
crates/project/src/worktree_store.rs                              |   98 
crates/project/src/yarn.rs                                        |    8 
crates/project_panel/src/project_panel.rs                         |  373 
crates/project_panel/src/project_panel_settings.rs                |    2 
crates/project_symbols/src/project_symbols.rs                     |  109 
crates/prompt_library/src/prompt_library.rs                       |  446 
crates/prompt_library/src/prompt_store.rs                         |    6 
crates/prompt_library/src/prompts.rs                              |    6 
crates/recent_projects/src/disconnected_overlay.rs                |  103 
crates/recent_projects/src/recent_projects.rs                     |  189 
crates/recent_projects/src/remote_servers.rs                      |  392 
crates/recent_projects/src/ssh_connections.rs                     |  116 
crates/release_channel/src/lib.rs                                 |   14 
crates/remote/src/ssh_session.rs                                  |   56 
crates/remote_server/src/headless_project.rs                      |   62 
crates/remote_server/src/remote_editing_tests.rs                  |   12 
crates/remote_server/src/unix.rs                                  |   22 
crates/repl/src/components/kernel_list_item.rs                    |    2 
crates/repl/src/components/kernel_options.rs                      |   38 
crates/repl/src/jupyter_settings.rs                               |    6 
crates/repl/src/kernels/mod.rs                                    |   10 
crates/repl/src/kernels/native_kernel.rs                          |   19 
crates/repl/src/kernels/remote_kernels.rs                         |   17 
crates/repl/src/notebook/cell.rs                                  |  119 
crates/repl/src/notebook/notebook_ui.rs                           |  308 
crates/repl/src/outputs.rs                                        |  168 
crates/repl/src/outputs/image.rs                                  |   10 
crates/repl/src/outputs/markdown.rs                               |   18 
crates/repl/src/outputs/plain.rs                                  |   65 
crates/repl/src/outputs/table.rs                                  |   29 
crates/repl/src/outputs/user_error.rs                             |    8 
crates/repl/src/repl.rs                                           |    6 
crates/repl/src/repl_editor.rs                                    |   88 
crates/repl/src/repl_sessions_ui.rs                               |  177 
crates/repl/src/repl_store.rs                                     |   40 
crates/repl/src/session.rs                                        |  118 
crates/reqwest_client/examples/client.rs                          |    2 
crates/rich_text/src/rich_text.rs                                 |   18 
crates/rpc/src/auth.rs                                            |    2 
crates/rpc/src/peer.rs                                            |    2 
crates/rpc/src/proto_client.rs                                    |   18 
crates/search/src/buffer_search.rs                                |  456 
crates/search/src/buffer_search/registrar.rs                      |   41 
crates/search/src/project_search.rs                               |  443 
crates/search/src/search.rs                                       |   16 
crates/search/src/search_bar.rs                                   |    4 
crates/semantic_index/examples/index.rs                           |    4 
crates/semantic_index/src/embedding/cloud.rs                      |    2 
crates/semantic_index/src/embedding_index.rs                      |   31 
crates/semantic_index/src/project_index.rs                        |   40 
crates/semantic_index/src/project_index_debug_view.rs             |   91 
crates/semantic_index/src/semantic_index.rs                       |   31 
crates/semantic_index/src/summary_index.rs                        |   30 
crates/semantic_index/src/worktree_index.rs                       |   26 
crates/session/src/session.rs                                     |    6 
crates/settings/src/editable_setting_control.rs                   |    8 
crates/settings/src/keymap_file.rs                                |   16 
crates/settings/src/settings.rs                                   |    4 
crates/settings/src/settings_file.rs                              |   12 
crates/settings/src/settings_store.rs                             |   76 
crates/settings_ui/src/appearance_settings_controls.rs            |   70 
crates/settings_ui/src/settings_ui.rs                             |   55 
crates/snippet/src/snippet.rs                                     |    2 
crates/snippet_provider/src/extension_snippet.rs                  |    4 
crates/snippet_provider/src/lib.rs                                |   28 
crates/snippet_provider/src/registry.rs                           |    8 
crates/snippets_ui/src/snippets_ui.rs                             |   79 
crates/sqlez/src/bindable.rs                                      |    2 
crates/sqlez/src/migrations.rs                                    |    2 
crates/sqlez/src/thread_safe_connection.rs                        |    2 
crates/sqlez/src/typed_statements.rs                              |    2 
crates/story/src/story.rs                                         |    8 
crates/storybook/src/stories/auto_height_editor.rs                |   16 
crates/storybook/src/stories/cursor.rs                            |    2 
crates/storybook/src/stories/default_colors.rs                    |   10 
crates/storybook/src/stories/focus.rs                             |   44 
crates/storybook/src/stories/indent_guides.rs                     |   11 
crates/storybook/src/stories/kitchen_sink.rs                      |   10 
crates/storybook/src/stories/overflow_scroll.rs                   |    2 
crates/storybook/src/stories/picker.rs                            |   36 
crates/storybook/src/stories/scroll.rs                            |   12 
crates/storybook/src/stories/text.rs                              |   16 
crates/storybook/src/stories/viewport_units.rs                    |   10 
crates/storybook/src/stories/with_rem_size.rs                     |    4 
crates/storybook/src/story_selector.rs                            |   68 
crates/storybook/src/storybook.rs                                 |   21 
crates/supermaven/src/supermaven.rs                               |   28 
crates/supermaven/src/supermaven_completion_provider.rs           |   24 
crates/supermaven_api/src/supermaven_api.rs                       |    2 
crates/tab_switcher/src/tab_switcher.rs                           |  146 
crates/tab_switcher/src/tab_switcher_tests.rs                     |   37 
crates/task/src/static_source.rs                                  |    6 
crates/tasks_ui/Cargo.toml                                        |    3 
crates/tasks_ui/src/modal.rs                                      |  212 
crates/tasks_ui/src/settings.rs                                   |    5 
crates/tasks_ui/src/tasks_ui.rs                                   |   92 
crates/terminal/src/terminal.rs                                   |   37 
crates/terminal/src/terminal_settings.rs                          |    9 
crates/terminal_view/src/persistence.rs                           |   77 
crates/terminal_view/src/terminal_element.rs                      |  199 
crates/terminal_view/src/terminal_panel.rs                        |  419 
crates/terminal_view/src/terminal_tab_tooltip.rs                  |    8 
crates/terminal_view/src/terminal_view.rs                         |  397 
crates/theme/src/font_family_cache.rs                             |    8 
crates/theme/src/registry.rs                                      |   10 
crates/theme/src/scale.rs                                         |    6 
crates/theme/src/settings.rs                                      |   23 
crates/theme/src/styles/colors.rs                                 |    4 
crates/theme/src/theme.rs                                         |    8 
crates/theme_extension/src/theme_extension.rs                     |    4 
crates/theme_importer/src/main.rs                                 |    2 
crates/theme_selector/src/theme_selector.rs                       |   81 
crates/title_bar/src/application_menu.rs                          |   48 
crates/title_bar/src/collab.rs                                    |  116 
crates/title_bar/src/platforms/platform_linux.rs                  |    6 
crates/title_bar/src/platforms/platform_windows.rs                |    8 
crates/title_bar/src/stories/application_menu.rs                  |   10 
crates/title_bar/src/title_bar.rs                                 |  261 
crates/title_bar/src/window_controls.rs                           |   21 
crates/toolchain_selector/src/active_toolchain.rs                 |   60 
crates/toolchain_selector/src/toolchain_selector.rs               |  108 
crates/ui/src/components/avatar/avatar.rs                         |    4 
crates/ui/src/components/avatar/avatar_audio_status_indicator.rs  |   10 
crates/ui/src/components/avatar/avatar_availability_indicator.rs  |    4 
crates/ui/src/components/button/button.rs                         |   12 
crates/ui/src/components/button/button_icon.rs                    |    2 
crates/ui/src/components/button/button_like.rs                    |   37 
crates/ui/src/components/button/icon_button.rs                    |    8 
crates/ui/src/components/button/toggle_button.rs                  |    6 
crates/ui/src/components/content_group.rs                         |    4 
crates/ui/src/components/context_menu.rs                          |  146 
crates/ui/src/components/disclosure.rs                            |   10 
crates/ui/src/components/divider.rs                               |    8 
crates/ui/src/components/dropdown_menu.rs                         |   22 
crates/ui/src/components/facepile.rs                              |    4 
crates/ui/src/components/icon.rs                                  |   16 
crates/ui/src/components/icon/decorated_icon.rs                   |    4 
crates/ui/src/components/icon/icon_decoration.rs                  |    6 
crates/ui/src/components/image.rs                                 |    6 
crates/ui/src/components/indent_guides.rs                         |   85 
crates/ui/src/components/indicator.rs                             |    4 
crates/ui/src/components/keybinding.rs                            |   28 
crates/ui/src/components/label/highlighted_label.rs               |    4 
crates/ui/src/components/label/label.rs                           |    4 
crates/ui/src/components/label/label_like.rs                      |    9 
crates/ui/src/components/list/list.rs                             |    2 
crates/ui/src/components/list/list_header.rs                      |   10 
crates/ui/src/components/list/list_item.rs                        |   25 
crates/ui/src/components/list/list_separator.rs                   |    2 
crates/ui/src/components/list/list_sub_header.rs                  |    2 
crates/ui/src/components/modal.rs                                 |   20 
crates/ui/src/components/navigable.rs                             |   27 
crates/ui/src/components/numeric_stepper.rs                       |   16 
crates/ui/src/components/popover.rs                               |    4 
crates/ui/src/components/popover_menu.rs                          |  130 
crates/ui/src/components/radio.rs                                 |   10 
crates/ui/src/components/right_click_menu.rs                      |  247 
crates/ui/src/components/scrollbar.rs                             |   48 
crates/ui/src/components/settings_container.rs                    |    2 
crates/ui/src/components/settings_group.rs                        |    2 
crates/ui/src/components/stories/avatar.rs                        |    2 
crates/ui/src/components/stories/button.rs                        |    2 
crates/ui/src/components/stories/context_menu.rs                  |   32 
crates/ui/src/components/stories/disclosure.rs                    |    2 
crates/ui/src/components/stories/icon.rs                          |    2 
crates/ui/src/components/stories/icon_button.rs                   |   18 
crates/ui/src/components/stories/keybinding.rs                    |    2 
crates/ui/src/components/stories/label.rs                         |    2 
crates/ui/src/components/stories/list.rs                          |    2 
crates/ui/src/components/stories/list_header.rs                   |    2 
crates/ui/src/components/stories/list_item.rs                     |   16 
crates/ui/src/components/stories/tab.rs                           |    2 
crates/ui/src/components/stories/tab_bar.rs                       |    2 
crates/ui/src/components/stories/toggle_button.rs                 |    2 
crates/ui/src/components/stories/tool_strip.rs                    |    8 
crates/ui/src/components/tab.rs                                   |    6 
crates/ui/src/components/tab_bar.rs                               |    2 
crates/ui/src/components/table.rs                                 |    6 
crates/ui/src/components/toggle.rs                                |   62 
crates/ui/src/components/tool_strip.rs                            |    2 
crates/ui/src/components/tooltip.rs                               |   65 
crates/ui/src/prelude.rs                                          |    5 
crates/ui/src/styles/appearance.rs                                |    6 
crates/ui/src/styles/color.rs                                     |    4 
crates/ui/src/styles/elevation.rs                                 |    8 
crates/ui/src/styles/spacing.rs                                   |    4 
crates/ui/src/styles/typography.rs                                |   23 
crates/ui/src/styles/units.rs                                     |   10 
crates/ui/src/traits/clickable.rs                                 |    4 
crates/ui/src/traits/component_preview.rs                         |   19 
crates/ui/src/traits/styled_ext.rs                                |   22 
crates/ui/src/utils.rs                                            |    4 
crates/ui/src/utils/with_rem_size.rs                              |   26 
crates/ui_input/src/ui_input.rs                                   |   27 
crates/ui_macros/src/dynamic_spacing.rs                           |    6 
crates/vcs_menu/src/lib.rs                                        |   95 
crates/vim/src/change_list.rs                                     |   27 
crates/vim/src/command.rs                                         |  236 
crates/vim/src/digraph.rs                                         |   75 
crates/vim/src/helix.rs                                           |   68 
crates/vim/src/indent.rs                                          |   82 
crates/vim/src/insert.rs                                          |   34 
crates/vim/src/mode_indicator.rs                                  |   36 
crates/vim/src/motion.rs                                          |  237 
crates/vim/src/normal.rs                                          |  265 
crates/vim/src/normal/case.rs                                     |   80 
crates/vim/src/normal/change.rs                                   |   43 
crates/vim/src/normal/delete.rs                                   |   39 
crates/vim/src/normal/increment.rs                                |   30 
crates/vim/src/normal/mark.rs                                     |   46 
crates/vim/src/normal/paste.rs                                    |   16 
crates/vim/src/normal/repeat.rs                                   |  110 
crates/vim/src/normal/scroll.rs                                   |   92 
crates/vim/src/normal/search.rs                                   |  180 
crates/vim/src/normal/substitute.rs                               |   29 
crates/vim/src/normal/toggle_comments.rs                          |   30 
crates/vim/src/normal/yank.rs                                     |   42 
crates/vim/src/object.rs                                          |   91 
crates/vim/src/replace.rs                                         |   42 
crates/vim/src/rewrap.rs                                          |   41 
crates/vim/src/state.rs                                           |   22 
crates/vim/src/surrounds.rs                                       |   53 
crates/vim/src/test.rs                                            |   50 
crates/vim/src/test/neovim_backed_test_context.rs                 |   14 
crates/vim/src/test/vim_test_context.rs                           |   49 
crates/vim/src/vim.rs                                             |  403 
crates/vim/src/visual.rs                                          |  249 
crates/vim_mode_setting/src/vim_mode_setting.rs                   |    6 
crates/welcome/src/base_keymap_picker.rs                          |   63 
crates/welcome/src/base_keymap_setting.rs                         |    2 
crates/welcome/src/multibuffer_hint.rs                            |   26 
crates/welcome/src/welcome.rs                                     |  124 
crates/workspace/src/dock.rs                                      |  452 
crates/workspace/src/item.rs                                      |  497 
crates/workspace/src/modal_layer.rs                               |   68 
crates/workspace/src/notifications.rs                             |  183 
crates/workspace/src/pane.rs                                      |  389 
crates/workspace/src/pane_group.rs                                |  170 
crates/workspace/src/persistence/model.rs                         |   39 
crates/workspace/src/searchable.rs                                |  208 
crates/workspace/src/shared_screen/cross_platform.rs              |   41 
crates/workspace/src/shared_screen/macos.rs                       |   38 
crates/workspace/src/status_bar.rs                                |   79 
crates/workspace/src/tasks.rs                                     |    6 
crates/workspace/src/theme_preview.rs                             |  131 
crates/workspace/src/toolbar.rs                                   |   54 
crates/workspace/src/workspace.rs                                 |  431 
crates/workspace/src/workspace_settings.rs                        |    6 
crates/worktree/src/worktree.rs                                   |  123 
crates/worktree/src/worktree_settings.rs                          |    9 
crates/worktree/src/worktree_tests.rs                             |    8 
crates/zed/RELEASE_CHANNEL                                        |    2 
crates/zed/src/main.rs                                            |   80 
crates/zed/src/reliability.rs                                     |   12 
crates/zed/src/zed.rs                                             |  442 
crates/zed/src/zed/inline_completion_registry.rs                  |   93 
crates/zed/src/zed/linux_prompts.rs                               |   49 
crates/zed/src/zed/open_listener.rs                               |   32 
crates/zed/src/zed/quick_action_bar.rs                            |   80 
crates/zed/src/zed/quick_action_bar/markdown_preview.rs           |   17 
crates/zed/src/zed/quick_action_bar/repl_menu.rs                  |   78 
crates/zed_actions/src/lib.rs                                     |    4 
crates/zed_predict_tos/src/zed_predict_tos.rs                     |   47 
crates/zeta/src/completion_diff_element.rs                        |   25 
crates/zeta/src/rate_completion_modal.rs                          |  148 
crates/zeta/src/zeta.rs                                           |   90 
refactor.sh                                                       |  111 
resolve-their-hunks.py                                            |   68 
tooling/xtask/src/tasks/package_conformity.rs                     |    2 
tooling/xtask/src/workspace.rs                                    |    2 
648 files changed, 26,027 insertions(+), 19,350 deletions(-)

Detailed changes

crates/activity_indicator/src/activity_indicator.rs 🔗

@@ -3,9 +3,9 @@ use editor::Editor;
 use extension_host::ExtensionStore;
 use futures::StreamExt;
 use gpui::{
-    actions, percentage, Animation, AnimationExt as _, AppContext, CursorStyle, EventEmitter,
-    InteractiveElement as _, Model, ParentElement as _, Render, SharedString,
-    StatefulInteractiveElement, Styled, Transformation, View, ViewContext, VisualContext as _,
+    actions, percentage, Animation, AnimationExt as _, App, Context, CursorStyle, Entity,
+    EventEmitter, InteractiveElement as _, ParentElement as _, Render, SharedString,
+    StatefulInteractiveElement, Styled, Transformation, Window,
 };
 use language::{LanguageRegistry, LanguageServerBinaryStatus, LanguageServerId};
 use lsp::LanguageServerName;
@@ -27,8 +27,8 @@ pub enum Event {
 
 pub struct ActivityIndicator {
     statuses: Vec<LspStatus>,
-    project: Model<Project>,
-    auto_updater: Option<Model<AutoUpdater>>,
+    project: Entity<Project>,
+    auto_updater: Option<Entity<AutoUpdater>>,
     context_menu_handle: PopoverMenuHandle<ContextMenu>,
 }
 
@@ -46,22 +46,24 @@ struct PendingWork<'a> {
 struct Content {
     icon: Option<gpui::AnyElement>,
     message: String,
-    on_click: Option<Arc<dyn Fn(&mut ActivityIndicator, &mut ViewContext<ActivityIndicator>)>>,
+    on_click:
+        Option<Arc<dyn Fn(&mut ActivityIndicator, &mut Window, &mut Context<ActivityIndicator>)>>,
 }
 
 impl ActivityIndicator {
     pub fn new(
         workspace: &mut Workspace,
         languages: Arc<LanguageRegistry>,
-        cx: &mut ViewContext<Workspace>,
-    ) -> View<ActivityIndicator> {
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Entity<ActivityIndicator> {
         let project = workspace.project().clone();
         let auto_updater = AutoUpdater::get(cx);
-        let this = cx.new_view(|cx: &mut ViewContext<Self>| {
+        let this = cx.new(|cx| {
             let mut status_events = languages.language_server_binary_statuses();
             cx.spawn(|this, mut cx| async move {
                 while let Some((name, status)) = status_events.next().await {
-                    this.update(&mut cx, |this, cx| {
+                    this.update(&mut cx, |this: &mut ActivityIndicator, cx| {
                         this.statuses.retain(|s| s.name != name);
                         this.statuses.push(LspStatus { name, status });
                         cx.notify();
@@ -70,6 +72,7 @@ impl ActivityIndicator {
                 anyhow::Ok(())
             })
             .detach();
+
             cx.observe(&project, |_, _, cx| cx.notify()).detach();
 
             if let Some(auto_updater) = auto_updater.as_ref() {
@@ -84,13 +87,13 @@ impl ActivityIndicator {
             }
         });
 
-        cx.subscribe(&this, move |_, _, event, cx| match event {
+        cx.subscribe_in(&this, window, move |_, _, event, window, cx| match event {
             Event::ShowError { lsp_name, error } => {
                 let create_buffer = project.update(cx, |project, cx| project.create_buffer(cx));
                 let project = project.clone();
                 let error = error.clone();
                 let lsp_name = lsp_name.clone();
-                cx.spawn(|workspace, mut cx| async move {
+                cx.spawn_in(window, |workspace, mut cx| async move {
                     let buffer = create_buffer.await?;
                     buffer.update(&mut cx, |buffer, cx| {
                         buffer.edit(
@@ -103,13 +106,14 @@ impl ActivityIndicator {
                         );
                         buffer.set_capability(language::Capability::ReadOnly, cx);
                     })?;
-                    workspace.update(&mut cx, |workspace, cx| {
+                    workspace.update_in(&mut cx, |workspace, window, cx| {
                         workspace.add_item_to_active_pane(
-                            Box::new(cx.new_view(|cx| {
-                                Editor::for_buffer(buffer, Some(project.clone()), cx)
+                            Box::new(cx.new(|cx| {
+                                Editor::for_buffer(buffer, Some(project.clone()), window, cx)
                             })),
                             None,
                             true,
+                            window,
                             cx,
                         );
                     })?;
@@ -123,7 +127,7 @@ impl ActivityIndicator {
         this
     }
 
-    fn show_error_message(&mut self, _: &ShowErrorMessage, cx: &mut ViewContext<Self>) {
+    fn show_error_message(&mut self, _: &ShowErrorMessage, _: &mut Window, cx: &mut Context<Self>) {
         self.statuses.retain(|status| {
             if let LanguageServerBinaryStatus::Failed { error } = &status.status {
                 cx.emit(Event::ShowError {
@@ -139,7 +143,12 @@ impl ActivityIndicator {
         cx.notify();
     }
 
-    fn dismiss_error_message(&mut self, _: &DismissErrorMessage, cx: &mut ViewContext<Self>) {
+    fn dismiss_error_message(
+        &mut self,
+        _: &DismissErrorMessage,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(updater) = &self.auto_updater {
             updater.update(cx, |updater, cx| {
                 updater.dismiss_error(cx);
@@ -150,7 +159,7 @@ impl ActivityIndicator {
 
     fn pending_language_server_work<'a>(
         &self,
-        cx: &'a AppContext,
+        cx: &'a App,
     ) -> impl Iterator<Item = PendingWork<'a>> {
         self.project
             .read(cx)
@@ -178,12 +187,12 @@ impl ActivityIndicator {
 
     fn pending_environment_errors<'a>(
         &'a self,
-        cx: &'a AppContext,
+        cx: &'a App,
     ) -> impl Iterator<Item = (&'a WorktreeId, &'a EnvironmentErrorMessage)> {
         self.project.read(cx).shell_environment_errors(cx)
     }
 
-    fn content_to_render(&mut self, cx: &mut ViewContext<Self>) -> Option<Content> {
+    fn content_to_render(&mut self, cx: &mut Context<Self>) -> Option<Content> {
         // Show if any direnv calls failed
         if let Some((&worktree_id, error)) = self.pending_environment_errors(cx).next() {
             return Some(Content {
@@ -193,11 +202,11 @@ impl ActivityIndicator {
                         .into_any_element(),
                 ),
                 message: error.0.clone(),
-                on_click: Some(Arc::new(move |this, cx| {
+                on_click: Some(Arc::new(move |this, window, cx| {
                     this.project.update(cx, |project, cx| {
                         project.remove_environment_error(cx, worktree_id);
                     });
-                    cx.dispatch_action(Box::new(workspace::OpenLog));
+                    window.dispatch_action(Box::new(workspace::OpenLog), cx);
                 })),
             });
         }
@@ -280,10 +289,10 @@ impl ActivityIndicator {
                         }
                     )
                 ),
-                on_click: Some(Arc::new(move |this, cx| {
+                on_click: Some(Arc::new(move |this, window, cx| {
                     this.statuses
                         .retain(|status| !downloading.contains(&status.name));
-                    this.dismiss_error_message(&DismissErrorMessage, cx)
+                    this.dismiss_error_message(&DismissErrorMessage, window, cx)
                 })),
             });
         }
@@ -308,10 +317,10 @@ impl ActivityIndicator {
                         }
                     ),
                 ),
-                on_click: Some(Arc::new(move |this, cx| {
+                on_click: Some(Arc::new(move |this, window, cx| {
                     this.statuses
                         .retain(|status| !checking_for_update.contains(&status.name));
-                    this.dismiss_error_message(&DismissErrorMessage, cx)
+                    this.dismiss_error_message(&DismissErrorMessage, window, cx)
                 })),
             });
         }
@@ -336,8 +345,8 @@ impl ActivityIndicator {
                             acc
                         }),
                 ),
-                on_click: Some(Arc::new(|this, cx| {
-                    this.show_error_message(&Default::default(), cx)
+                on_click: Some(Arc::new(|this, window, cx| {
+                    this.show_error_message(&Default::default(), window, cx)
                 })),
             });
         }
@@ -351,11 +360,11 @@ impl ActivityIndicator {
                         .into_any_element(),
                 ),
                 message: format!("Formatting failed: {}. Click to see logs.", failure),
-                on_click: Some(Arc::new(|indicator, cx| {
+                on_click: Some(Arc::new(|indicator, window, cx| {
                     indicator.project.update(cx, |project, cx| {
                         project.reset_last_formatting_failure(cx);
                     });
-                    cx.dispatch_action(Box::new(workspace::OpenLog));
+                    window.dispatch_action(Box::new(workspace::OpenLog), cx);
                 })),
             });
         }
@@ -370,8 +379,8 @@ impl ActivityIndicator {
                             .into_any_element(),
                     ),
                     message: "Checking for Zed updates…".to_string(),
-                    on_click: Some(Arc::new(|this, cx| {
-                        this.dismiss_error_message(&DismissErrorMessage, cx)
+                    on_click: Some(Arc::new(|this, window, cx| {
+                        this.dismiss_error_message(&DismissErrorMessage, window, cx)
                     })),
                 }),
                 AutoUpdateStatus::Downloading => Some(Content {
@@ -381,8 +390,8 @@ impl ActivityIndicator {
                             .into_any_element(),
                     ),
                     message: "Downloading Zed update…".to_string(),
-                    on_click: Some(Arc::new(|this, cx| {
-                        this.dismiss_error_message(&DismissErrorMessage, cx)
+                    on_click: Some(Arc::new(|this, window, cx| {
+                        this.dismiss_error_message(&DismissErrorMessage, window, cx)
                     })),
                 }),
                 AutoUpdateStatus::Installing => Some(Content {
@@ -392,8 +401,8 @@ impl ActivityIndicator {
                             .into_any_element(),
                     ),
                     message: "Installing Zed update…".to_string(),
-                    on_click: Some(Arc::new(|this, cx| {
-                        this.dismiss_error_message(&DismissErrorMessage, cx)
+                    on_click: Some(Arc::new(|this, window, cx| {
+                        this.dismiss_error_message(&DismissErrorMessage, window, cx)
                     })),
                 }),
                 AutoUpdateStatus::Updated { binary_path } => Some(Content {
@@ -403,7 +412,7 @@ impl ActivityIndicator {
                         let reload = workspace::Reload {
                             binary_path: Some(binary_path.clone()),
                         };
-                        move |_, cx| workspace::reload(&reload, cx)
+                        move |_, _, cx| workspace::reload(&reload, cx)
                     })),
                 }),
                 AutoUpdateStatus::Errored => Some(Content {
@@ -413,8 +422,8 @@ impl ActivityIndicator {
                             .into_any_element(),
                     ),
                     message: "Auto update failed".to_string(),
-                    on_click: Some(Arc::new(|this, cx| {
-                        this.dismiss_error_message(&DismissErrorMessage, cx)
+                    on_click: Some(Arc::new(|this, window, cx| {
+                        this.dismiss_error_message(&DismissErrorMessage, window, cx)
                     })),
                 }),
                 AutoUpdateStatus::Idle => None,
@@ -432,8 +441,8 @@ impl ActivityIndicator {
                             .into_any_element(),
                     ),
                     message: format!("Updating {extension_id} extension…"),
-                    on_click: Some(Arc::new(|this, cx| {
-                        this.dismiss_error_message(&DismissErrorMessage, cx)
+                    on_click: Some(Arc::new(|this, window, cx| {
+                        this.dismiss_error_message(&DismissErrorMessage, window, cx)
                     })),
                 });
             }
@@ -442,8 +451,12 @@ impl ActivityIndicator {
         None
     }
 
-    fn toggle_language_server_work_context_menu(&mut self, cx: &mut ViewContext<Self>) {
-        self.context_menu_handle.toggle(cx);
+    fn toggle_language_server_work_context_menu(
+        &mut self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.context_menu_handle.toggle(window, cx);
     }
 }
 
@@ -452,7 +465,7 @@ impl EventEmitter<Event> for ActivityIndicator {}
 const MAX_MESSAGE_LEN: usize = 50;
 
 impl Render for ActivityIndicator {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let result = h_flex()
             .id("activity-indicator")
             .on_action(cx.listener(Self::show_error_message))
@@ -460,7 +473,7 @@ impl Render for ActivityIndicator {
         let Some(content) = self.content_to_render(cx) else {
             return result;
         };
-        let this = cx.view().downgrade();
+        let this = cx.model().downgrade();
         let truncate_content = content.message.len() > MAX_MESSAGE_LEN;
         result.gap_2().child(
             PopoverMenu::new("activity-indicator-popover")
@@ -480,24 +493,24 @@ impl Render for ActivityIndicator {
                                             ))
                                             .size(LabelSize::Small),
                                         )
-                                        .tooltip(move |cx| Tooltip::text(&content.message, cx))
+                                        .tooltip(Tooltip::text(content.message))
                                 } else {
                                     button.child(Label::new(content.message).size(LabelSize::Small))
                                 }
                             })
                             .when_some(content.on_click, |this, handler| {
-                                this.on_click(cx.listener(move |this, _, cx| {
-                                    handler(this, cx);
+                                this.on_click(cx.listener(move |this, _, window, cx| {
+                                    handler(this, window, cx);
                                 }))
                                 .cursor(CursorStyle::PointingHand)
                             }),
                     ),
                 )
                 .anchor(gpui::Corner::BottomLeft)
-                .menu(move |cx| {
+                .menu(move |window, cx| {
                     let strong_this = this.upgrade()?;
                     let mut has_work = false;
-                    let menu = ContextMenu::build(cx, |mut menu, cx| {
+                    let menu = ContextMenu::build(window, cx, |mut menu, _, cx| {
                         for work in strong_this.read(cx).pending_language_server_work(cx) {
                             has_work = true;
                             let this = this.clone();
@@ -513,7 +526,7 @@ impl Render for ActivityIndicator {
                                 let token = work.progress_token.to_string();
                                 let title = SharedString::from(title);
                                 menu = menu.custom_entry(
-                                    move |_| {
+                                    move |_, _| {
                                         h_flex()
                                             .w_full()
                                             .justify_between()
@@ -521,7 +534,7 @@ impl Render for ActivityIndicator {
                                             .child(Icon::new(IconName::XCircle))
                                             .into_any_element()
                                     },
-                                    move |cx| {
+                                    move |_, cx| {
                                         this.update(cx, |this, cx| {
                                             this.project.update(cx, |project, cx| {
                                                 project.cancel_language_server_work(
@@ -554,5 +567,11 @@ impl Render for ActivityIndicator {
 }
 
 impl StatusItemView for ActivityIndicator {
-    fn set_active_pane_item(&mut self, _: Option<&dyn ItemHandle>, _: &mut ViewContext<Self>) {}
+    fn set_active_pane_item(
+        &mut self,
+        _: Option<&dyn ItemHandle>,
+        _window: &mut Window,
+        _: &mut Context<Self>,
+    ) {
+    }
 }

crates/anthropic/src/anthropic.rs 🔗

@@ -2,7 +2,7 @@ mod supported_countries;
 
 use std::{pin::Pin, str::FromStr};
 
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use chrono::{DateTime, Utc};
 use futures::{io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, Stream, StreamExt};
 use http_client::http::{HeaderMap, HeaderValue};

crates/assets/src/assets.rs 🔗

@@ -1,7 +1,7 @@
 // This crate was essentially pulled out verbatim from main `zed` crate to avoid having to run RustEmbed macro whenever zed has to be rebuilt. It saves a second or two on an incremental build.
 use anyhow::anyhow;
 
-use gpui::{AppContext, AssetSource, Result, SharedString};
+use gpui::{App, AssetSource, Result, SharedString};
 use rust_embed::RustEmbed;
 
 #[derive(RustEmbed)]
@@ -39,7 +39,7 @@ impl AssetSource for Assets {
 
 impl Assets {
     /// Populate the [`TextSystem`] of the given [`AppContext`] with all `.ttf` fonts in the `fonts` directory.
-    pub fn load_fonts(&self, cx: &AppContext) -> gpui::Result<()> {
+    pub fn load_fonts(&self, cx: &App) -> gpui::Result<()> {
         let font_paths = self.list("fonts")?;
         let mut embedded_fonts = Vec::new();
         for font_path in font_paths {
@@ -55,7 +55,7 @@ impl Assets {
         cx.text_system().add_fonts(embedded_fonts)
     }
 
-    pub fn load_test_fonts(&self, cx: &AppContext) {
+    pub fn load_test_fonts(&self, cx: &App) {
         cx.text_system()
             .add_fonts(vec![self
                 .load("fonts/plex-mono/ZedPlexMono-Regular.ttf")

crates/assistant/src/assistant.rs 🔗

@@ -15,7 +15,7 @@ use client::Client;
 use command_palette_hooks::CommandPaletteFilter;
 use feature_flags::FeatureFlagAppExt;
 use fs::Fs;
-use gpui::{actions, AppContext, Global, UpdateGlobal};
+use gpui::{actions, App, Global, UpdateGlobal};
 use language_model::{
     LanguageModelId, LanguageModelProviderId, LanguageModelRegistry, LanguageModelResponseMessage,
 };
@@ -67,7 +67,7 @@ impl Global for Assistant {}
 impl Assistant {
     const NAMESPACE: &'static str = "assistant";
 
-    fn set_enabled(&mut self, enabled: bool, cx: &mut AppContext) {
+    fn set_enabled(&mut self, enabled: bool, cx: &mut App) {
         if self.enabled == enabled {
             return;
         }
@@ -92,7 +92,7 @@ pub fn init(
     fs: Arc<dyn Fs>,
     client: Arc<Client>,
     prompt_builder: Arc<PromptBuilder>,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) {
     cx.set_global(Assistant::default());
     AssistantSettings::register(cx);
@@ -165,7 +165,7 @@ pub fn init(
     .detach();
 }
 
-fn init_language_model_settings(cx: &mut AppContext) {
+fn init_language_model_settings(cx: &mut App) {
     update_active_language_model_from_settings(cx);
 
     cx.observe_global::<SettingsStore>(update_active_language_model_from_settings)
@@ -184,7 +184,7 @@ fn init_language_model_settings(cx: &mut AppContext) {
     .detach();
 }
 
-fn update_active_language_model_from_settings(cx: &mut AppContext) {
+fn update_active_language_model_from_settings(cx: &mut App) {
     let settings = AssistantSettings::get_global(cx);
     let provider_name = LanguageModelProviderId::from(settings.default_model.provider.clone());
     let model_id = LanguageModelId::from(settings.default_model.model.clone());
@@ -204,7 +204,7 @@ fn update_active_language_model_from_settings(cx: &mut AppContext) {
     });
 }
 
-fn register_slash_commands(prompt_builder: Option<Arc<PromptBuilder>>, cx: &mut AppContext) {
+fn register_slash_commands(prompt_builder: Option<Arc<PromptBuilder>>, cx: &mut App) {
     let slash_command_registry = SlashCommandRegistry::global(cx);
 
     slash_command_registry.register_command(assistant_slash_commands::FileSlashCommand, true);
@@ -278,7 +278,7 @@ fn register_slash_commands(prompt_builder: Option<Arc<PromptBuilder>>, cx: &mut
     .detach();
 }
 
-fn update_slash_commands_from_settings(cx: &mut AppContext) {
+fn update_slash_commands_from_settings(cx: &mut App) {
     let slash_command_registry = SlashCommandRegistry::global(cx);
     let settings = SlashCommandSettings::get_global(cx);
 

crates/assistant/src/assistant_configuration.rs 🔗

@@ -1,7 +1,7 @@
 use std::sync::Arc;
 
 use collections::HashMap;
-use gpui::{canvas, AnyView, AppContext, EventEmitter, FocusHandle, FocusableView, Subscription};
+use gpui::{canvas, AnyView, App, EventEmitter, FocusHandle, Focusable, Subscription};
 use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
 use ui::{prelude::*, ElevationIndex};
 use workspace::Item;
@@ -13,16 +13,17 @@ pub struct ConfigurationView {
 }
 
 impl ConfigurationView {
-    pub fn new(cx: &mut ViewContext<Self>) -> Self {
+    pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
         let focus_handle = cx.focus_handle();
 
-        let registry_subscription = cx.subscribe(
+        let registry_subscription = cx.subscribe_in(
             &LanguageModelRegistry::global(cx),
-            |this, _, event: &language_model::Event, cx| match event {
+            window,
+            |this, _, event: &language_model::Event, window, cx| match event {
                 language_model::Event::AddedProvider(provider_id) => {
                     let provider = LanguageModelRegistry::read_global(cx).provider(provider_id);
                     if let Some(provider) = provider {
-                        this.add_configuration_view(&provider, cx);
+                        this.add_configuration_view(&provider, window, cx);
                     }
                 }
                 language_model::Event::RemovedProvider(provider_id) => {
@@ -37,14 +38,14 @@ impl ConfigurationView {
             configuration_views: HashMap::default(),
             _registry_subscription: registry_subscription,
         };
-        this.build_configuration_views(cx);
+        this.build_configuration_views(window, cx);
         this
     }
 
-    fn build_configuration_views(&mut self, cx: &mut ViewContext<Self>) {
+    fn build_configuration_views(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let providers = LanguageModelRegistry::read_global(cx).providers();
         for provider in providers {
-            self.add_configuration_view(&provider, cx);
+            self.add_configuration_view(&provider, window, cx);
         }
     }
 
@@ -55,9 +56,10 @@ impl ConfigurationView {
     fn add_configuration_view(
         &mut self,
         provider: &Arc<dyn LanguageModelProvider>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
-        let configuration_view = provider.configuration_view(cx);
+        let configuration_view = provider.configuration_view(window, cx);
         self.configuration_views
             .insert(provider.id(), configuration_view);
     }
@@ -65,7 +67,7 @@ impl ConfigurationView {
     fn render_provider_view(
         &mut self,
         provider: &Arc<dyn LanguageModelProvider>,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Div {
         let provider_id = provider.id().0.clone();
         let provider_name = provider.name().0.clone();
@@ -73,7 +75,7 @@ impl ConfigurationView {
 
         let open_new_context = cx.listener({
             let provider = provider.clone();
-            move |_, _, cx| {
+            move |_, _, _window, cx| {
                 cx.emit(ConfigurationViewEvent::NewProviderContextEditor(
                     provider.clone(),
                 ))
@@ -123,7 +125,7 @@ impl ConfigurationView {
 }
 
 impl Render for ConfigurationView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let providers = LanguageModelRegistry::read_global(cx).providers();
         let provider_views = providers
             .into_iter()
@@ -163,12 +165,12 @@ impl Render for ConfigurationView {
         // We use a canvas here to get scrolling to work in the ConfigurationView. It's a workaround
         // because we couldn't the element to take up the size of the parent.
         canvas(
-            move |bounds, cx| {
-                element.prepaint_as_root(bounds.origin, bounds.size.into(), cx);
+            move |bounds, window, cx| {
+                element.prepaint_as_root(bounds.origin, bounds.size.into(), window, cx);
                 element
             },
-            |_, mut element, cx| {
-                element.paint(cx);
+            |_, mut element, window, cx| {
+                element.paint(window, cx);
             },
         )
         .flex_1()
@@ -182,8 +184,8 @@ pub enum ConfigurationViewEvent {
 
 impl EventEmitter<ConfigurationViewEvent> for ConfigurationView {}
 
-impl FocusableView for ConfigurationView {
-    fn focus_handle(&self, _: &AppContext) -> FocusHandle {
+impl Focusable for ConfigurationView {
+    fn focus_handle(&self, _: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -191,7 +193,7 @@ impl FocusableView for ConfigurationView {
 impl Item for ConfigurationView {
     type Event = ConfigurationViewEvent;
 
-    fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
+    fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option<SharedString> {
         Some("Configuration".into())
     }
 }

crates/assistant/src/assistant_panel.rs 🔗

@@ -4,7 +4,7 @@ use crate::{
 };
 use anyhow::{anyhow, Result};
 use assistant_context_editor::{
-    make_lsp_adapter_delegate, AssistantPanelDelegate, Context, ContextEditor,
+    make_lsp_adapter_delegate, AssistantContext, AssistantPanelDelegate, ContextEditor,
     ContextEditorToolbarItem, ContextEditorToolbarItemEvent, ContextHistory, ContextId,
     ContextStore, ContextStoreEvent, InsertDraggedFiles, SlashCommandCompletionProvider,
     ToggleModelSelector, DEFAULT_TAB_TITLE,
@@ -16,9 +16,9 @@ use client::{proto, Client, Status};
 use editor::{Editor, EditorEvent};
 use fs::Fs;
 use gpui::{
-    prelude::*, Action, AppContext, AsyncWindowContext, EventEmitter, ExternalPaths, FocusHandle,
-    FocusableView, InteractiveElement, IntoElement, Model, ParentElement, Pixels, Render, Styled,
-    Subscription, Task, UpdateGlobal, View, WeakView,
+    prelude::*, Action, App, AsyncWindowContext, Entity, EventEmitter, ExternalPaths, FocusHandle,
+    Focusable, InteractiveElement, IntoElement, ParentElement, Pixels, Render, Styled,
+    Subscription, Task, UpdateGlobal, WeakEntity,
 };
 use language::LanguageRegistry;
 use language_model::{LanguageModelProviderId, LanguageModelRegistry, ZED_CLOUD_PROVIDER_ID};
@@ -39,10 +39,10 @@ use workspace::{
 };
 use zed_actions::assistant::{DeployPromptLibrary, InlineAssist, ToggleFocus};
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     workspace::FollowableViewRegistry::register::<ContextEditor>(cx);
-    cx.observe_new_views(
-        |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
+    cx.observe_new(
+        |workspace: &mut Workspace, _window, _cx: &mut Context<Workspace>| {
             workspace
                 .register_action(ContextEditor::quote_selection)
                 .register_action(ContextEditor::insert_selection)
@@ -55,8 +55,8 @@ pub fn init(cx: &mut AppContext) {
     )
     .detach();
 
-    cx.observe_new_views(
-        |terminal_panel: &mut TerminalPanel, cx: &mut ViewContext<TerminalPanel>| {
+    cx.observe_new(
+        |terminal_panel: &mut TerminalPanel, _, cx: &mut Context<TerminalPanel>| {
             let settings = AssistantSettings::get_global(cx);
             terminal_panel.set_assistant_enabled(settings.enabled, cx);
         },
@@ -69,17 +69,17 @@ pub enum AssistantPanelEvent {
 }
 
 pub struct AssistantPanel {
-    pane: View<Pane>,
-    workspace: WeakView<Workspace>,
+    pane: Entity<Pane>,
+    workspace: WeakEntity<Workspace>,
     width: Option<Pixels>,
     height: Option<Pixels>,
-    project: Model<Project>,
-    context_store: Model<ContextStore>,
+    project: Entity<Project>,
+    context_store: Entity<ContextStore>,
     languages: Arc<LanguageRegistry>,
     fs: Arc<dyn Fs>,
     subscriptions: Vec<Subscription>,
     model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
-    model_summary_editor: View<Editor>,
+    model_summary_editor: Entity<Editor>,
     authenticate_provider_task: Option<(LanguageModelProviderId, Task<()>)>,
     configuration_subscription: Option<Subscription>,
     client_status: Option<client::Status>,
@@ -88,16 +88,16 @@ pub struct AssistantPanel {
 }
 
 enum InlineAssistTarget {
-    Editor(View<Editor>, bool),
-    Terminal(View<TerminalView>),
+    Editor(Entity<Editor>, bool),
+    Terminal(Entity<TerminalView>),
 }
 
 impl AssistantPanel {
     pub fn load(
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         prompt_builder: Arc<PromptBuilder>,
         cx: AsyncWindowContext,
-    ) -> Task<Result<View<Self>>> {
+    ) -> Task<Result<Entity<Self>>> {
         cx.spawn(|mut cx| async move {
             let slash_commands = Arc::new(SlashCommandWorkingSet::default());
             let tools = Arc::new(ToolWorkingSet::default());
@@ -108,41 +108,44 @@ impl AssistantPanel {
                 })?
                 .await?;
 
-            workspace.update(&mut cx, |workspace, cx| {
+            workspace.update_in(&mut cx, |workspace, window, cx| {
                 // TODO: deserialize state.
-                cx.new_view(|cx| Self::new(workspace, context_store, cx))
+                cx.new(|cx| Self::new(workspace, context_store, window, cx))
             })
         })
     }
 
     fn new(
         workspace: &Workspace,
-        context_store: Model<ContextStore>,
-        cx: &mut ViewContext<Self>,
+        context_store: Entity<ContextStore>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let model_selector_menu_handle = PopoverMenuHandle::default();
-        let model_summary_editor = cx.new_view(Editor::single_line);
-        let context_editor_toolbar = cx.new_view(|cx| {
+        let model_summary_editor = cx.new(|cx| Editor::single_line(window, cx));
+        let context_editor_toolbar = cx.new(|cx| {
             ContextEditorToolbarItem::new(
                 workspace,
                 model_selector_menu_handle.clone(),
                 model_summary_editor.clone(),
+                window,
                 cx,
             )
         });
 
-        let pane = cx.new_view(|cx| {
+        let pane = cx.new(|cx| {
             let mut pane = Pane::new(
                 workspace.weak_handle(),
                 workspace.project().clone(),
                 Default::default(),
                 None,
                 NewContext.boxed_clone(),
+                window,
                 cx,
             );
 
             let project = workspace.project().clone();
-            pane.set_custom_drop_handle(cx, move |_, dropped_item, cx| {
+            pane.set_custom_drop_handle(cx, move |_, dropped_item, window, cx| {
                 let action = maybe!({
                     if project.read(cx).is_local() {
                         if let Some(paths) = dropped_item.downcast_ref::<ExternalPaths>() {
@@ -152,7 +155,7 @@ impl AssistantPanel {
 
                     let project_paths = if let Some(tab) = dropped_item.downcast_ref::<DraggedTab>()
                     {
-                        if &tab.pane == cx.view() {
+                        if tab.pane == cx.model() {
                             return None;
                         }
                         let item = tab.pane.read(cx).item_for_index(tab.ix);
@@ -192,7 +195,7 @@ impl AssistantPanel {
                 });
 
                 if let Some(action) = action {
-                    cx.dispatch_action(action.boxed_clone());
+                    window.dispatch_action(action.boxed_clone(), cx);
                 }
 
                 ControlFlow::Break(())
@@ -200,25 +203,26 @@ impl AssistantPanel {
 
             pane.set_can_navigate(true, cx);
             pane.display_nav_history_buttons(None);
-            pane.set_should_display_tab_bar(|_| true);
-            pane.set_render_tab_bar_buttons(cx, move |pane, cx| {
+            pane.set_should_display_tab_bar(|_, _| true);
+            pane.set_render_tab_bar_buttons(cx, move |pane, _window, cx| {
                 let focus_handle = pane.focus_handle(cx);
                 let left_children = IconButton::new("history", IconName::HistoryRerun)
                     .icon_size(IconSize::Small)
                     .on_click(cx.listener({
                         let focus_handle = focus_handle.clone();
-                        move |_, _, cx| {
-                            focus_handle.focus(cx);
-                            cx.dispatch_action(DeployHistory.boxed_clone())
+                        move |_, _, window, cx| {
+                            focus_handle.focus(window);
+                            window.dispatch_action(DeployHistory.boxed_clone(), cx)
                         }
                     }))
                     .tooltip({
                         let focus_handle = focus_handle.clone();
-                        move |cx| {
+                        move |window, cx| {
                             Tooltip::for_action_in(
                                 "Open History",
                                 &DeployHistory,
                                 &focus_handle,
+                                window,
                                 cx,
                             )
                         }
@@ -227,19 +231,23 @@ impl AssistantPanel {
                         pane.active_item()
                             .map_or(false, |item| item.downcast::<ContextHistory>().is_some()),
                     );
-                let _pane = cx.view().clone();
+                let _pane = cx.model().clone();
                 let right_children = h_flex()
                     .gap(DynamicSpacing::Base02.rems(cx))
                     .child(
                         IconButton::new("new-chat", IconName::Plus)
                             .icon_size(IconSize::Small)
-                            .on_click(
-                                cx.listener(|_, _, cx| {
-                                    cx.dispatch_action(NewContext.boxed_clone())
-                                }),
-                            )
-                            .tooltip(move |cx| {
-                                Tooltip::for_action_in("New Chat", &NewContext, &focus_handle, cx)
+                            .on_click(cx.listener(|_, _, window, cx| {
+                                window.dispatch_action(NewContext.boxed_clone(), cx)
+                            }))
+                            .tooltip(move |window, cx| {
+                                Tooltip::for_action_in(
+                                    "New Chat",
+                                    &NewContext,
+                                    &focus_handle,
+                                    window,
+                                    cx,
+                                )
                             }),
                     )
                     .child(
@@ -247,16 +255,16 @@ impl AssistantPanel {
                             .trigger(
                                 IconButton::new("menu", IconName::EllipsisVertical)
                                     .icon_size(IconSize::Small)
-                                    .tooltip(|cx| Tooltip::text("Toggle Assistant Menu", cx)),
+                                    .tooltip(Tooltip::text("Toggle Assistant Menu")),
                             )
-                            .menu(move |cx| {
+                            .menu(move |window, cx| {
                                 let zoom_label = if _pane.read(cx).is_zoomed() {
                                     "Zoom Out"
                                 } else {
                                     "Zoom In"
                                 };
                                 let focus_handle = _pane.focus_handle(cx);
-                                Some(ContextMenu::build(cx, move |menu, _| {
+                                Some(ContextMenu::build(window, cx, move |menu, _, _| {
                                     menu.context(focus_handle.clone())
                                         .action("New Chat", Box::new(NewContext))
                                         .action("History", Box::new(DeployHistory))
@@ -272,37 +280,38 @@ impl AssistantPanel {
                 (Some(left_children.into_any_element()), right_children)
             });
             pane.toolbar().update(cx, |toolbar, cx| {
-                toolbar.add_item(context_editor_toolbar.clone(), cx);
-                toolbar.add_item(cx.new_view(BufferSearchBar::new), cx)
+                toolbar.add_item(context_editor_toolbar.clone(), window, cx);
+                toolbar.add_item(cx.new(|cx| BufferSearchBar::new(window, cx)), window, cx)
             });
             pane
         });
 
         let subscriptions = vec![
             cx.observe(&pane, |_, _, cx| cx.notify()),
-            cx.subscribe(&pane, Self::handle_pane_event),
+            cx.subscribe_in(&pane, window, Self::handle_pane_event),
             cx.subscribe(&context_editor_toolbar, Self::handle_toolbar_event),
             cx.subscribe(&model_summary_editor, Self::handle_summary_editor_event),
-            cx.subscribe(&context_store, Self::handle_context_store_event),
-            cx.subscribe(
+            cx.subscribe_in(&context_store, window, Self::handle_context_store_event),
+            cx.subscribe_in(
                 &LanguageModelRegistry::global(cx),
-                |this, _, event: &language_model::Event, cx| match event {
+                window,
+                |this, _, event: &language_model::Event, window, cx| match event {
                     language_model::Event::ActiveModelChanged => {
-                        this.completion_provider_changed(cx);
+                        this.completion_provider_changed(window, cx);
                     }
                     language_model::Event::ProviderStateChanged => {
-                        this.ensure_authenticated(cx);
+                        this.ensure_authenticated(window, cx);
                         cx.notify()
                     }
                     language_model::Event::AddedProvider(_)
                     | language_model::Event::RemovedProvider(_) => {
-                        this.ensure_authenticated(cx);
+                        this.ensure_authenticated(window, cx);
                     }
                 },
             ),
         ];
 
-        let watch_client_status = Self::watch_client_status(workspace.client().clone(), cx);
+        let watch_client_status = Self::watch_client_status(workspace.client().clone(), window, cx);
 
         let mut this = Self {
             pane,
@@ -322,27 +331,32 @@ impl AssistantPanel {
             watch_client_status: Some(watch_client_status),
             show_zed_ai_notice: false,
         };
-        this.new_context(cx);
+        this.new_context(window, cx);
         this
     }
 
     pub fn toggle_focus(
         workspace: &mut Workspace,
         _: &ToggleFocus,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
         let settings = AssistantSettings::get_global(cx);
         if !settings.enabled {
             return;
         }
 
-        workspace.toggle_panel_focus::<Self>(cx);
+        workspace.toggle_panel_focus::<Self>(window, cx);
     }
 
-    fn watch_client_status(client: Arc<Client>, cx: &mut ViewContext<Self>) -> Task<()> {
+    fn watch_client_status(
+        client: Arc<Client>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Task<()> {
         let mut status_rx = client.status();
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             while let Some(status) = status_rx.next().await {
                 this.update(&mut cx, |this, cx| {
                     if this.client_status.is_none()
@@ -363,9 +377,10 @@ impl AssistantPanel {
 
     fn handle_pane_event(
         &mut self,
-        pane: View<Pane>,
+        pane: &Entity<Pane>,
         event: &pane::Event,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let update_model_summary = match event {
             pane::Event::Remove { .. } => {
@@ -384,7 +399,7 @@ impl AssistantPanel {
             pane::Event::AddItem { item } => {
                 self.workspace
                     .update(cx, |workspace, cx| {
-                        item.added_to_pane(workspace, self.pane.clone(), cx)
+                        item.added_to_pane(workspace, self.pane.clone(), window, cx)
                     })
                     .ok();
                 true
@@ -394,7 +409,7 @@ impl AssistantPanel {
                 if *local {
                     self.workspace
                         .update(cx, |workspace, cx| {
-                            workspace.unfollow_in_pane(&pane, cx);
+                            workspace.unfollow_in_pane(&pane, window, cx);
                         })
                         .ok();
                 }
@@ -422,16 +437,16 @@ impl AssistantPanel {
 
         if update_model_summary {
             if let Some(editor) = self.active_context_editor(cx) {
-                self.show_updated_summary(&editor, cx)
+                self.show_updated_summary(&editor, window, cx)
             }
         }
     }
 
     fn handle_summary_editor_event(
         &mut self,
-        model_summary_editor: View<Editor>,
+        model_summary_editor: Entity<Editor>,
         event: &EditorEvent,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if matches!(event, EditorEvent::Edited { .. }) {
             if let Some(context_editor) = self.active_context_editor(cx) {
@@ -450,11 +465,7 @@ impl AssistantPanel {
         }
     }
 
-    fn update_zed_ai_notice_visibility(
-        &mut self,
-        client_status: Status,
-        cx: &mut ViewContext<Self>,
-    ) {
+    fn update_zed_ai_notice_visibility(&mut self, client_status: Status, cx: &mut Context<Self>) {
         let active_provider = LanguageModelRegistry::read_global(cx).active_provider();
 
         // If we're signed out and don't have a provider configured, or we're signed-out AND Zed.dev is
@@ -468,9 +479,9 @@ impl AssistantPanel {
 
     fn handle_toolbar_event(
         &mut self,
-        _: View<ContextEditorToolbarItem>,
+        _: Entity<ContextEditorToolbarItem>,
         _: &ContextEditorToolbarItemEvent,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if let Some(context_editor) = self.active_context_editor(cx) {
             context_editor.update(cx, |context_editor, cx| {
@@ -483,9 +494,10 @@ impl AssistantPanel {
 
     fn handle_context_store_event(
         &mut self,
-        _context_store: Model<ContextStore>,
+        _context_store: &Entity<ContextStore>,
         event: &ContextStoreEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let ContextStoreEvent::ContextCreated(context_id) = event;
         let Some(context) = self
@@ -500,23 +512,24 @@ impl AssistantPanel {
             .log_err()
             .flatten();
 
-        let editor = cx.new_view(|cx| {
+        let editor = cx.new(|cx| {
             let mut editor = ContextEditor::for_context(
                 context,
                 self.fs.clone(),
                 self.workspace.clone(),
                 self.project.clone(),
                 lsp_adapter_delegate,
+                window,
                 cx,
             );
-            editor.insert_default_prompt(cx);
+            editor.insert_default_prompt(window, cx);
             editor
         });
 
-        self.show_context(editor.clone(), cx);
+        self.show_context(editor.clone(), window, cx);
     }
 
-    fn completion_provider_changed(&mut self, cx: &mut ViewContext<Self>) {
+    fn completion_provider_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(editor) = self.active_context_editor(cx) {
             editor.update(cx, |active_context, cx| {
                 active_context
@@ -540,7 +553,7 @@ impl AssistantPanel {
             })
         {
             self.authenticate_provider_task = None;
-            self.ensure_authenticated(cx);
+            self.ensure_authenticated(window, cx);
         }
 
         if let Some(status) = self.client_status {
@@ -548,7 +561,7 @@ impl AssistantPanel {
         }
     }
 
-    fn ensure_authenticated(&mut self, cx: &mut ViewContext<Self>) {
+    fn ensure_authenticated(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if self.is_authenticated(cx) {
             return;
         }
@@ -562,7 +575,7 @@ impl AssistantPanel {
         if self.authenticate_provider_task.is_none() {
             self.authenticate_provider_task = Some((
                 provider.id(),
-                cx.spawn(|this, mut cx| async move {
+                cx.spawn_in(window, |this, mut cx| async move {
                     if let Some(future) = load_credentials {
                         let _ = future.await;
                     }
@@ -578,7 +591,8 @@ impl AssistantPanel {
     pub fn inline_assist(
         workspace: &mut Workspace,
         action: &InlineAssist,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
         let settings = AssistantSettings::get_global(cx);
         if !settings.enabled {
@@ -590,7 +604,7 @@ impl AssistantPanel {
         };
 
         let Some(inline_assist_target) =
-            Self::resolve_inline_assist_target(workspace, &assistant_panel, cx)
+            Self::resolve_inline_assist_target(workspace, &assistant_panel, window, cx)
         else {
             return;
         };
@@ -603,9 +617,10 @@ impl AssistantPanel {
                     InlineAssistant::update_global(cx, |assistant, cx| {
                         assistant.assist(
                             &active_editor,
-                            Some(cx.view().downgrade()),
+                            Some(cx.model().downgrade()),
                             include_context.then_some(&assistant_panel),
                             initial_prompt,
+                            window,
                             cx,
                         )
                     })
@@ -614,9 +629,10 @@ impl AssistantPanel {
                     TerminalInlineAssistant::update_global(cx, |assistant, cx| {
                         assistant.assist(
                             &active_terminal,
-                            Some(cx.view().downgrade()),
+                            Some(cx.model().downgrade()),
                             Some(&assistant_panel),
                             initial_prompt,
+                            window,
                             cx,
                         )
                     })
@@ -624,7 +640,7 @@ impl AssistantPanel {
             }
         } else {
             let assistant_panel = assistant_panel.downgrade();
-            cx.spawn(|workspace, mut cx| async move {
+            cx.spawn_in(window, |workspace, mut cx| async move {
                 let Some(task) =
                     assistant_panel.update(&mut cx, |assistant, cx| assistant.authenticate(cx))?
                 else {
@@ -639,15 +655,17 @@ impl AssistantPanel {
                         .ok();
                     if let Some(answer) = answer {
                         if answer == 0 {
-                            cx.update(|cx| cx.dispatch_action(Box::new(ShowConfiguration)))
-                                .ok();
+                            cx.update(|window, cx| {
+                                window.dispatch_action(Box::new(ShowConfiguration), cx)
+                            })
+                            .ok();
                         }
                     }
                     return Ok(());
                 };
                 task.await?;
                 if assistant_panel.update(&mut cx, |panel, cx| panel.is_authenticated(cx))? {
-                    cx.update(|cx| match inline_assist_target {
+                    cx.update(|window, cx| match inline_assist_target {
                         InlineAssistTarget::Editor(active_editor, include_context) => {
                             let assistant_panel = if include_context {
                                 assistant_panel.upgrade()
@@ -660,6 +678,7 @@ impl AssistantPanel {
                                     Some(workspace),
                                     assistant_panel.as_ref(),
                                     initial_prompt,
+                                    window,
                                     cx,
                                 )
                             })
@@ -671,14 +690,15 @@ impl AssistantPanel {
                                     Some(workspace),
                                     assistant_panel.upgrade().as_ref(),
                                     initial_prompt,
+                                    window,
                                     cx,
                                 )
                             })
                         }
                     })?
                 } else {
-                    workspace.update(&mut cx, |workspace, cx| {
-                        workspace.focus_panel::<AssistantPanel>(cx)
+                    workspace.update_in(&mut cx, |workspace, window, cx| {
+                        workspace.focus_panel::<AssistantPanel>(window, cx)
                     })?;
                 }
 
@@ -690,14 +710,15 @@ impl AssistantPanel {
 
     fn resolve_inline_assist_target(
         workspace: &mut Workspace,
-        assistant_panel: &View<AssistantPanel>,
-        cx: &mut WindowContext,
+        assistant_panel: &Entity<AssistantPanel>,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<InlineAssistTarget> {
         if let Some(terminal_panel) = workspace.panel::<TerminalPanel>(cx) {
             if terminal_panel
                 .read(cx)
                 .focus_handle(cx)
-                .contains_focused(cx)
+                .contains_focused(window, cx)
             {
                 if let Some(terminal_view) = terminal_panel.read(cx).pane().and_then(|pane| {
                     pane.read(cx)
@@ -714,7 +735,7 @@ impl AssistantPanel {
                 .active_context_editor(cx)
                 .and_then(|editor| {
                     let editor = &editor.read(cx).editor().clone();
-                    if editor.read(cx).is_focused(cx) {
+                    if editor.read(cx).is_focused(window) {
                         Some(editor.clone())
                     } else {
                         None
@@ -741,33 +762,38 @@ impl AssistantPanel {
     pub fn create_new_context(
         workspace: &mut Workspace,
         _: &NewContext,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
         if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
             let did_create_context = panel
                 .update(cx, |panel, cx| {
-                    panel.new_context(cx)?;
+                    panel.new_context(window, cx)?;
 
                     Some(())
                 })
                 .is_some();
             if did_create_context {
-                ContextEditor::quote_selection(workspace, &Default::default(), cx);
+                ContextEditor::quote_selection(workspace, &Default::default(), window, cx);
             }
         }
     }
 
-    pub fn new_context(&mut self, cx: &mut ViewContext<Self>) -> Option<View<ContextEditor>> {
+    pub fn new_context(
+        &mut self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Entity<ContextEditor>> {
         let project = self.project.read(cx);
         if project.is_via_collab() {
             let task = self
                 .context_store
                 .update(cx, |store, cx| store.create_remote_context(cx));
 
-            cx.spawn(|this, mut cx| async move {
+            cx.spawn_in(window, |this, mut cx| async move {
                 let context = task.await?;
 
-                this.update(&mut cx, |this, cx| {
+                this.update_in(&mut cx, |this, window, cx| {
                     let workspace = this.workspace.clone();
                     let project = this.project.clone();
                     let lsp_adapter_delegate =
@@ -776,18 +802,19 @@ impl AssistantPanel {
                     let fs = this.fs.clone();
                     let project = this.project.clone();
 
-                    let editor = cx.new_view(|cx| {
+                    let editor = cx.new(|cx| {
                         ContextEditor::for_context(
                             context,
                             fs,
                             workspace,
                             project,
                             lsp_adapter_delegate,
+                            window,
                             cx,
                         )
                     });
 
-                    this.show_context(editor, cx);
+                    this.show_context(editor, window, cx);
 
                     anyhow::Ok(())
                 })??;
@@ -803,25 +830,26 @@ impl AssistantPanel {
                 .log_err()
                 .flatten();
 
-            let editor = cx.new_view(|cx| {
+            let editor = cx.new(|cx| {
                 let mut editor = ContextEditor::for_context(
                     context,
                     self.fs.clone(),
                     self.workspace.clone(),
                     self.project.clone(),
                     lsp_adapter_delegate,
+                    window,
                     cx,
                 );
-                editor.insert_default_prompt(cx);
+                editor.insert_default_prompt(window, cx);
                 editor
             });
 
-            self.show_context(editor.clone(), cx);
+            self.show_context(editor.clone(), window, cx);
             let workspace = self.workspace.clone();
-            cx.spawn(move |_, mut cx| async move {
+            cx.spawn_in(window, move |_, mut cx| async move {
                 workspace
-                    .update(&mut cx, |workspace, cx| {
-                        workspace.focus_panel::<AssistantPanel>(cx);
+                    .update_in(&mut cx, |workspace, window, cx| {
+                        workspace.focus_panel::<AssistantPanel>(window, cx);
                     })
                     .ok();
             })
@@ -830,19 +858,34 @@ impl AssistantPanel {
         }
     }
 
-    fn show_context(&mut self, context_editor: View<ContextEditor>, cx: &mut ViewContext<Self>) {
-        let focus = self.focus_handle(cx).contains_focused(cx);
+    fn show_context(
+        &mut self,
+        context_editor: Entity<ContextEditor>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        let focus = self.focus_handle(cx).contains_focused(window, cx);
         let prev_len = self.pane.read(cx).items_len();
         self.pane.update(cx, |pane, cx| {
-            pane.add_item(Box::new(context_editor.clone()), focus, focus, None, cx)
+            pane.add_item(
+                Box::new(context_editor.clone()),
+                focus,
+                focus,
+                None,
+                window,
+                cx,
+            )
         });
 
         if prev_len != self.pane.read(cx).items_len() {
-            self.subscriptions
-                .push(cx.subscribe(&context_editor, Self::handle_context_editor_event));
+            self.subscriptions.push(cx.subscribe_in(
+                &context_editor,
+                window,
+                Self::handle_context_editor_event,
+            ));
         }
 
-        self.show_updated_summary(&context_editor, cx);
+        self.show_updated_summary(&context_editor, window, cx);
 
         cx.emit(AssistantPanelEvent::ContextEdited);
         cx.notify();
@@ -850,14 +893,15 @@ impl AssistantPanel {
 
     fn show_updated_summary(
         &self,
-        context_editor: &View<ContextEditor>,
-        cx: &mut ViewContext<Self>,
+        context_editor: &Entity<ContextEditor>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         context_editor.update(cx, |context_editor, cx| {
             let new_summary = context_editor.title(cx).to_string();
             self.model_summary_editor.update(cx, |summary_editor, cx| {
                 if summary_editor.text(cx) != new_summary {
-                    summary_editor.set_text(new_summary, cx);
+                    summary_editor.set_text(new_summary, window, cx);
                 }
             });
         });
@@ -865,13 +909,14 @@ impl AssistantPanel {
 
     fn handle_context_editor_event(
         &mut self,
-        context_editor: View<ContextEditor>,
+        context_editor: &Entity<ContextEditor>,
         event: &EditorEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match event {
             EditorEvent::TitleChanged => {
-                self.show_updated_summary(&context_editor, cx);
+                self.show_updated_summary(&context_editor, window, cx);
                 cx.notify()
             }
             EditorEvent::Edited { .. } => {
@@ -896,22 +941,23 @@ impl AssistantPanel {
     fn show_configuration(
         workspace: &mut Workspace,
         _: &ShowConfiguration,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
         let Some(panel) = workspace.panel::<AssistantPanel>(cx) else {
             return;
         };
 
-        if !panel.focus_handle(cx).contains_focused(cx) {
-            workspace.toggle_panel_focus::<AssistantPanel>(cx);
+        if !panel.focus_handle(cx).contains_focused(window, cx) {
+            workspace.toggle_panel_focus::<AssistantPanel>(window, cx);
         }
 
         panel.update(cx, |this, cx| {
-            this.show_configuration_tab(cx);
+            this.show_configuration_tab(window, cx);
         })
     }
 
-    fn show_configuration_tab(&mut self, cx: &mut ViewContext<Self>) {
+    fn show_configuration_tab(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let configuration_item_ix = self
             .pane
             .read(cx)
@@ -920,13 +966,14 @@ impl AssistantPanel {
 
         if let Some(configuration_item_ix) = configuration_item_ix {
             self.pane.update(cx, |pane, cx| {
-                pane.activate_item(configuration_item_ix, true, true, cx);
+                pane.activate_item(configuration_item_ix, true, true, window, cx);
             });
         } else {
-            let configuration = cx.new_view(ConfigurationView::new);
-            self.configuration_subscription = Some(cx.subscribe(
+            let configuration = cx.new(|cx| ConfigurationView::new(window, cx));
+            self.configuration_subscription = Some(cx.subscribe_in(
                 &configuration,
-                |this, _, event: &ConfigurationViewEvent, cx| match event {
+                window,
+                |this, _, event: &ConfigurationViewEvent, window, cx| match event {
                     ConfigurationViewEvent::NewProviderContextEditor(provider) => {
                         if LanguageModelRegistry::read_global(cx)
                             .active_provider()
@@ -941,17 +988,17 @@ impl AssistantPanel {
                             }
                         }
 
-                        this.new_context(cx);
+                        this.new_context(window, cx);
                     }
                 },
             ));
             self.pane.update(cx, |pane, cx| {
-                pane.add_item(Box::new(configuration), true, true, None, cx);
+                pane.add_item(Box::new(configuration), true, true, None, window, cx);
             });
         }
     }
 
-    fn deploy_history(&mut self, _: &DeployHistory, cx: &mut ViewContext<Self>) {
+    fn deploy_history(&mut self, _: &DeployHistory, window: &mut Window, cx: &mut Context<Self>) {
         let history_item_ix = self
             .pane
             .read(cx)
@@ -960,24 +1007,30 @@ impl AssistantPanel {
 
         if let Some(history_item_ix) = history_item_ix {
             self.pane.update(cx, |pane, cx| {
-                pane.activate_item(history_item_ix, true, true, cx);
+                pane.activate_item(history_item_ix, true, true, window, cx);
             });
         } else {
-            let history = cx.new_view(|cx| {
+            let history = cx.new(|cx| {
                 ContextHistory::new(
                     self.project.clone(),
                     self.context_store.clone(),
                     self.workspace.clone(),
+                    window,
                     cx,
                 )
             });
             self.pane.update(cx, |pane, cx| {
-                pane.add_item(Box::new(history), true, true, None, cx);
+                pane.add_item(Box::new(history), true, true, None, window, cx);
             });
         }
     }
 
-    fn deploy_prompt_library(&mut self, _: &DeployPromptLibrary, cx: &mut ViewContext<Self>) {
+    fn deploy_prompt_library(
+        &mut self,
+        _: &DeployPromptLibrary,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         open_prompt_library(
             self.languages.clone(),
             Box::new(PromptLibraryInlineAssist),
@@ -993,33 +1046,41 @@ impl AssistantPanel {
         .detach_and_log_err(cx);
     }
 
-    fn toggle_model_selector(&mut self, _: &ToggleModelSelector, cx: &mut ViewContext<Self>) {
-        self.model_selector_menu_handle.toggle(cx);
+    fn toggle_model_selector(
+        &mut self,
+        _: &ToggleModelSelector,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.model_selector_menu_handle.toggle(window, cx);
     }
 
-    pub(crate) fn active_context_editor(&self, cx: &AppContext) -> Option<View<ContextEditor>> {
+    pub(crate) fn active_context_editor(&self, cx: &App) -> Option<Entity<ContextEditor>> {
         self.pane
             .read(cx)
             .active_item()?
             .downcast::<ContextEditor>()
     }
 
-    pub fn active_context(&self, cx: &AppContext) -> Option<Model<Context>> {
+    pub fn active_context(&self, cx: &App) -> Option<Entity<AssistantContext>> {
         Some(self.active_context_editor(cx)?.read(cx).context().clone())
     }
 
     pub fn open_saved_context(
         &mut self,
         path: PathBuf,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let existing_context = self.pane.read(cx).items().find_map(|item| {
             item.downcast::<ContextEditor>()
                 .filter(|editor| editor.read(cx).context().read(cx).path() == Some(&path))
         });
         if let Some(existing_context) = existing_context {
-            return cx.spawn(|this, mut cx| async move {
-                this.update(&mut cx, |this, cx| this.show_context(existing_context, cx))
+            return cx.spawn_in(window, |this, mut cx| async move {
+                this.update_in(&mut cx, |this, window, cx| {
+                    this.show_context(existing_context, window, cx)
+                })
             });
         }
 
@@ -1032,20 +1093,21 @@ impl AssistantPanel {
 
         let lsp_adapter_delegate = make_lsp_adapter_delegate(&project, cx).log_err().flatten();
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let context = context.await?;
-            this.update(&mut cx, |this, cx| {
-                let editor = cx.new_view(|cx| {
+            this.update_in(&mut cx, |this, window, cx| {
+                let editor = cx.new(|cx| {
                     ContextEditor::for_context(
                         context,
                         fs,
                         workspace,
                         project,
                         lsp_adapter_delegate,
+                        window,
                         cx,
                     )
                 });
-                this.show_context(editor, cx);
+                this.show_context(editor, window, cx);
                 anyhow::Ok(())
             })??;
             Ok(())
@@ -1055,16 +1117,17 @@ impl AssistantPanel {
     pub fn open_remote_context(
         &mut self,
         id: ContextId,
-        cx: &mut ViewContext<Self>,
-    ) -> Task<Result<View<ContextEditor>>> {
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<ContextEditor>>> {
         let existing_context = self.pane.read(cx).items().find_map(|item| {
             item.downcast::<ContextEditor>()
                 .filter(|editor| *editor.read(cx).context().read(cx).id() == id)
         });
         if let Some(existing_context) = existing_context {
-            return cx.spawn(|this, mut cx| async move {
-                this.update(&mut cx, |this, cx| {
-                    this.show_context(existing_context.clone(), cx)
+            return cx.spawn_in(window, |this, mut cx| async move {
+                this.update_in(&mut cx, |this, window, cx| {
+                    this.show_context(existing_context.clone(), window, cx)
                 })?;
                 Ok(existing_context)
             });
@@ -1079,32 +1142,33 @@ impl AssistantPanel {
             .log_err()
             .flatten();
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let context = context.await?;
-            this.update(&mut cx, |this, cx| {
-                let editor = cx.new_view(|cx| {
+            this.update_in(&mut cx, |this, window, cx| {
+                let editor = cx.new(|cx| {
                     ContextEditor::for_context(
                         context,
                         fs,
                         workspace,
                         this.project.clone(),
                         lsp_adapter_delegate,
+                        window,
                         cx,
                     )
                 });
-                this.show_context(editor.clone(), cx);
+                this.show_context(editor.clone(), window, cx);
                 anyhow::Ok(editor)
             })?
         })
     }
 
-    fn is_authenticated(&mut self, cx: &mut ViewContext<Self>) -> bool {
+    fn is_authenticated(&mut self, cx: &mut Context<Self>) -> bool {
         LanguageModelRegistry::read_global(cx)
             .active_provider()
             .map_or(false, |provider| provider.is_authenticated(cx))
     }
 
-    fn authenticate(&mut self, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
+    fn authenticate(&mut self, cx: &mut Context<Self>) -> Option<Task<Result<()>>> {
         LanguageModelRegistry::read_global(cx)
             .active_provider()
             .map_or(None, |provider| Some(provider.authenticate(cx)))

crates/assistant/src/inline_assistant.rs 🔗

@@ -26,9 +26,9 @@ use futures::{
     join, SinkExt, Stream, StreamExt,
 };
 use gpui::{
-    anchored, deferred, point, AnyElement, AppContext, ClickEvent, CursorStyle, EventEmitter,
-    FocusHandle, FocusableView, FontWeight, Global, HighlightStyle, Model, ModelContext,
-    Subscription, Task, TextStyle, UpdateGlobal, View, ViewContext, WeakView, WindowContext,
+    anchored, deferred, point, AnyElement, App, ClickEvent, Context, CursorStyle, Entity,
+    EventEmitter, FocusHandle, Focusable, FontWeight, Global, HighlightStyle, Subscription, Task,
+    TextStyle, UpdateGlobal, WeakEntity, Window,
 };
 use language::{Buffer, IndentKind, Point, Selection, TransactionId};
 use language_model::{
@@ -70,17 +70,20 @@ pub fn init(
     fs: Arc<dyn Fs>,
     prompt_builder: Arc<PromptBuilder>,
     telemetry: Arc<Telemetry>,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) {
     cx.set_global(InlineAssistant::new(fs, prompt_builder, telemetry));
-    cx.observe_new_views(|_, cx| {
-        let workspace = cx.view().clone();
+    cx.observe_new(|_, window, cx| {
+        let Some(window) = window else {
+            return;
+        };
+        let workspace = cx.model().clone();
         InlineAssistant::update_global(cx, |inline_assistant, cx| {
-            inline_assistant.register_workspace(&workspace, cx)
+            inline_assistant.register_workspace(&workspace, window, cx)
         });
 
-        cx.observe_flag::<Assistant2FeatureFlag, _>({
-            |is_assistant2_enabled, _view, cx| {
+        cx.observe_flag::<Assistant2FeatureFlag, _>(window, {
+            |is_assistant2_enabled, _workspace, _window, cx| {
                 InlineAssistant::update_global(cx, |inline_assistant, _cx| {
                     inline_assistant.is_assistant2_enabled = is_assistant2_enabled;
                 });
@@ -97,9 +100,9 @@ pub struct InlineAssistant {
     next_assist_id: InlineAssistId,
     next_assist_group_id: InlineAssistGroupId,
     assists: HashMap<InlineAssistId, InlineAssist>,
-    assists_by_editor: HashMap<WeakView<Editor>, EditorInlineAssists>,
+    assists_by_editor: HashMap<WeakEntity<Editor>, EditorInlineAssists>,
     assist_groups: HashMap<InlineAssistGroupId, InlineAssistGroup>,
-    confirmed_assists: HashMap<InlineAssistId, Model<CodegenAlternative>>,
+    confirmed_assists: HashMap<InlineAssistId, Entity<CodegenAlternative>>,
     prompt_history: VecDeque<String>,
     prompt_builder: Arc<PromptBuilder>,
     telemetry: Arc<Telemetry>,
@@ -130,13 +133,19 @@ impl InlineAssistant {
         }
     }
 
-    pub fn register_workspace(&mut self, workspace: &View<Workspace>, cx: &mut WindowContext) {
-        cx.subscribe(workspace, |workspace, event, cx| {
-            Self::update_global(cx, |this, cx| {
-                this.handle_workspace_event(workspace, event, cx)
-            });
-        })
-        .detach();
+    pub fn register_workspace(
+        &mut self,
+        workspace: &Entity<Workspace>,
+        window: &mut Window,
+        cx: &mut App,
+    ) {
+        window
+            .subscribe(workspace, cx, |workspace, event, window, cx| {
+                Self::update_global(cx, |this, cx| {
+                    this.handle_workspace_event(workspace, event, window, cx)
+                });
+            })
+            .detach();
 
         let workspace = workspace.downgrade();
         cx.observe_global::<SettingsStore>(move |cx| {
@@ -156,9 +165,10 @@ impl InlineAssistant {
 
     fn handle_workspace_event(
         &mut self,
-        workspace: View<Workspace>,
+        workspace: Entity<Workspace>,
         event: &workspace::Event,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         match event {
             workspace::Event::UserSavedItem { item, .. } => {
@@ -168,14 +178,14 @@ impl InlineAssistant {
                         for assist_id in editor_assists.assist_ids.clone() {
                             let assist = &self.assists[&assist_id];
                             if let CodegenStatus::Done = assist.codegen.read(cx).status(cx) {
-                                self.finish_assist(assist_id, false, cx)
+                                self.finish_assist(assist_id, false, window, cx)
                             }
                         }
                     }
                 }
             }
             workspace::Event::ItemAdded { item } => {
-                self.register_workspace_item(&workspace, item.as_ref(), cx);
+                self.register_workspace_item(&workspace, item.as_ref(), window, cx);
             }
             _ => (),
         }
@@ -183,23 +193,28 @@ impl InlineAssistant {
 
     fn register_workspace_item(
         &mut self,
-        workspace: &View<Workspace>,
+        workspace: &Entity<Workspace>,
         item: &dyn ItemHandle,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let is_assistant2_enabled = self.is_assistant2_enabled;
 
         if let Some(editor) = item.act_as::<Editor>(cx) {
             editor.update(cx, |editor, cx| {
                 if is_assistant2_enabled {
-                    editor
-                        .remove_code_action_provider(ASSISTANT_CODE_ACTION_PROVIDER_ID.into(), cx);
+                    editor.remove_code_action_provider(
+                        ASSISTANT_CODE_ACTION_PROVIDER_ID.into(),
+                        window,
+                        cx,
+                    );
                 } else {
                     editor.add_code_action_provider(
                         Rc::new(AssistantCodeActionProvider {
-                            editor: cx.view().downgrade(),
+                            editor: cx.model().downgrade(),
                             workspace: workspace.downgrade(),
                         }),
+                        window,
                         cx,
                     );
                 }
@@ -209,11 +224,12 @@ impl InlineAssistant {
 
     pub fn assist(
         &mut self,
-        editor: &View<Editor>,
-        workspace: Option<WeakView<Workspace>>,
-        assistant_panel: Option<&View<AssistantPanel>>,
+        editor: &Entity<Editor>,
+        workspace: Option<WeakEntity<Workspace>>,
+        assistant_panel: Option<&Entity<AssistantPanel>>,
         initial_prompt: Option<String>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let (snapshot, initial_selections) = editor.update(cx, |editor, cx| {
             (
@@ -280,15 +296,14 @@ impl InlineAssistant {
         }
 
         let assist_group_id = self.next_assist_group_id.post_inc();
-        let prompt_buffer =
-            cx.new_model(|cx| Buffer::local(initial_prompt.unwrap_or_default(), cx));
-        let prompt_buffer = cx.new_model(|cx| MultiBuffer::singleton(prompt_buffer, cx));
+        let prompt_buffer = cx.new(|cx| Buffer::local(initial_prompt.unwrap_or_default(), cx));
+        let prompt_buffer = cx.new(|cx| MultiBuffer::singleton(prompt_buffer, cx));
 
         let mut assists = Vec::new();
         let mut assist_to_focus = None;
         for range in codegen_ranges {
             let assist_id = self.next_assist_id.post_inc();
-            let codegen = cx.new_model(|cx| {
+            let codegen = cx.new(|cx| {
                 Codegen::new(
                     editor.read(cx).buffer().clone(),
                     range.clone(),
@@ -300,7 +315,7 @@ impl InlineAssistant {
             });
 
             let gutter_dimensions = Arc::new(Mutex::new(GutterDimensions::default()));
-            let prompt_editor = cx.new_view(|cx| {
+            let prompt_editor = cx.new(|cx| {
                 PromptEditor::new(
                     assist_id,
                     gutter_dimensions.clone(),
@@ -311,6 +326,7 @@ impl InlineAssistant {
                     assistant_panel,
                     workspace.clone(),
                     self.fs.clone(),
+                    window,
                     cx,
                 )
             });
@@ -341,7 +357,7 @@ impl InlineAssistant {
         let editor_assists = self
             .assists_by_editor
             .entry(editor.downgrade())
-            .or_insert_with(|| EditorInlineAssists::new(&editor, cx));
+            .or_insert_with(|| EditorInlineAssists::new(&editor, window, cx));
         let mut assist_group = InlineAssistGroup::new();
         for (assist_id, range, prompt_editor, prompt_block_id, end_block_id) in assists {
             self.assists.insert(
@@ -357,6 +373,7 @@ impl InlineAssistant {
                     range,
                     prompt_editor.read(cx).codegen.clone(),
                     workspace.clone(),
+                    window,
                     cx,
                 ),
             );
@@ -366,25 +383,26 @@ impl InlineAssistant {
         self.assist_groups.insert(assist_group_id, assist_group);
 
         if let Some(assist_id) = assist_to_focus {
-            self.focus_assist(assist_id, cx);
+            self.focus_assist(assist_id, window, cx);
         }
     }
 
     #[allow(clippy::too_many_arguments)]
     pub fn suggest_assist(
         &mut self,
-        editor: &View<Editor>,
+        editor: &Entity<Editor>,
         mut range: Range<Anchor>,
         initial_prompt: String,
         initial_transaction_id: Option<TransactionId>,
         focus: bool,
-        workspace: Option<WeakView<Workspace>>,
-        assistant_panel: Option<&View<AssistantPanel>>,
-        cx: &mut WindowContext,
+        workspace: Option<WeakEntity<Workspace>>,
+        assistant_panel: Option<&Entity<AssistantPanel>>,
+        window: &mut Window,
+        cx: &mut App,
     ) -> InlineAssistId {
         let assist_group_id = self.next_assist_group_id.post_inc();
-        let prompt_buffer = cx.new_model(|cx| Buffer::local(&initial_prompt, cx));
-        let prompt_buffer = cx.new_model(|cx| MultiBuffer::singleton(prompt_buffer, cx));
+        let prompt_buffer = cx.new(|cx| Buffer::local(&initial_prompt, cx));
+        let prompt_buffer = cx.new(|cx| MultiBuffer::singleton(prompt_buffer, cx));
 
         let assist_id = self.next_assist_id.post_inc();
 
@@ -395,7 +413,7 @@ impl InlineAssistant {
             range.end = range.end.bias_right(&snapshot);
         }
 
-        let codegen = cx.new_model(|cx| {
+        let codegen = cx.new(|cx| {
             Codegen::new(
                 editor.read(cx).buffer().clone(),
                 range.clone(),
@@ -407,7 +425,7 @@ impl InlineAssistant {
         });
 
         let gutter_dimensions = Arc::new(Mutex::new(GutterDimensions::default()));
-        let prompt_editor = cx.new_view(|cx| {
+        let prompt_editor = cx.new(|cx| {
             PromptEditor::new(
                 assist_id,
                 gutter_dimensions.clone(),
@@ -418,6 +436,7 @@ impl InlineAssistant {
                 assistant_panel,
                 workspace.clone(),
                 self.fs.clone(),
+                window,
                 cx,
             )
         });
@@ -428,7 +447,7 @@ impl InlineAssistant {
         let editor_assists = self
             .assists_by_editor
             .entry(editor.downgrade())
-            .or_insert_with(|| EditorInlineAssists::new(&editor, cx));
+            .or_insert_with(|| EditorInlineAssists::new(&editor, window, cx));
 
         let mut assist_group = InlineAssistGroup::new();
         self.assists.insert(
@@ -444,6 +463,7 @@ impl InlineAssistant {
                 range,
                 prompt_editor.read(cx).codegen.clone(),
                 workspace.clone(),
+                window,
                 cx,
             ),
         );
@@ -452,7 +472,7 @@ impl InlineAssistant {
         self.assist_groups.insert(assist_group_id, assist_group);
 
         if focus {
-            self.focus_assist(assist_id, cx);
+            self.focus_assist(assist_id, window, cx);
         }
 
         assist_id
@@ -460,10 +480,10 @@ impl InlineAssistant {
 
     fn insert_assist_blocks(
         &self,
-        editor: &View<Editor>,
+        editor: &Entity<Editor>,
         range: &Range<Anchor>,
-        prompt_editor: &View<PromptEditor>,
-        cx: &mut WindowContext,
+        prompt_editor: &Entity<PromptEditor>,
+        cx: &mut App,
     ) -> [CustomBlockId; 2] {
         let prompt_editor_height = prompt_editor.update(cx, |prompt_editor, cx| {
             prompt_editor
@@ -500,7 +520,7 @@ impl InlineAssistant {
         })
     }
 
-    fn handle_prompt_editor_focus_in(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) {
+    fn handle_prompt_editor_focus_in(&mut self, assist_id: InlineAssistId, cx: &mut App) {
         let assist = &self.assists[&assist_id];
         let Some(decorations) = assist.decorations.as_ref() else {
             return;
@@ -541,11 +561,7 @@ impl InlineAssistant {
             .ok();
     }
 
-    fn handle_prompt_editor_focus_out(
-        &mut self,
-        assist_id: InlineAssistId,
-        cx: &mut WindowContext,
-    ) {
+    fn handle_prompt_editor_focus_out(&mut self, assist_id: InlineAssistId, cx: &mut App) {
         let assist = &self.assists[&assist_id];
         let assist_group = self.assist_groups.get_mut(&assist.group_id).unwrap();
         if assist_group.active_assist_id == Some(assist_id) {
@@ -564,31 +580,32 @@ impl InlineAssistant {
 
     fn handle_prompt_editor_event(
         &mut self,
-        prompt_editor: View<PromptEditor>,
+        prompt_editor: Entity<PromptEditor>,
         event: &PromptEditorEvent,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let assist_id = prompt_editor.read(cx).id;
         match event {
             PromptEditorEvent::StartRequested => {
-                self.start_assist(assist_id, cx);
+                self.start_assist(assist_id, window, cx);
             }
             PromptEditorEvent::StopRequested => {
                 self.stop_assist(assist_id, cx);
             }
             PromptEditorEvent::ConfirmRequested => {
-                self.finish_assist(assist_id, false, cx);
+                self.finish_assist(assist_id, false, window, cx);
             }
             PromptEditorEvent::CancelRequested => {
-                self.finish_assist(assist_id, true, cx);
+                self.finish_assist(assist_id, true, window, cx);
             }
             PromptEditorEvent::DismissRequested => {
-                self.dismiss_assist(assist_id, cx);
+                self.dismiss_assist(assist_id, window, cx);
             }
         }
     }
 
-    fn handle_editor_newline(&mut self, editor: View<Editor>, cx: &mut WindowContext) {
+    fn handle_editor_newline(&mut self, editor: Entity<Editor>, window: &mut Window, cx: &mut App) {
         let Some(editor_assists) = self.assists_by_editor.get(&editor.downgrade()) else {
             return;
         };
@@ -606,9 +623,9 @@ impl InlineAssistant {
                 if assist_range.contains(&selection.start) && assist_range.contains(&selection.end)
                 {
                     if matches!(assist.codegen.read(cx).status(cx), CodegenStatus::Pending) {
-                        self.dismiss_assist(*assist_id, cx);
+                        self.dismiss_assist(*assist_id, window, cx);
                     } else {
-                        self.finish_assist(*assist_id, false, cx);
+                        self.finish_assist(*assist_id, false, window, cx);
                     }
 
                     return;
@@ -619,7 +636,7 @@ impl InlineAssistant {
         cx.propagate();
     }
 
-    fn handle_editor_cancel(&mut self, editor: View<Editor>, cx: &mut WindowContext) {
+    fn handle_editor_cancel(&mut self, editor: Entity<Editor>, window: &mut Window, cx: &mut App) {
         let Some(editor_assists) = self.assists_by_editor.get(&editor.downgrade()) else {
             return;
         };
@@ -639,7 +656,7 @@ impl InlineAssistant {
                     if assist_range.contains(&selection.start)
                         && assist_range.contains(&selection.end)
                     {
-                        self.focus_assist(*assist_id, cx);
+                        self.focus_assist(*assist_id, window, cx);
                         return;
                     } else {
                         let distance_from_selection = assist_range
@@ -666,22 +683,27 @@ impl InlineAssistant {
             }
 
             if let Some((&assist_id, _)) = closest_assist_fallback {
-                self.focus_assist(assist_id, cx);
+                self.focus_assist(assist_id, window, cx);
             }
         }
 
         cx.propagate();
     }
 
-    fn handle_editor_release(&mut self, editor: WeakView<Editor>, cx: &mut WindowContext) {
+    fn handle_editor_release(
+        &mut self,
+        editor: WeakEntity<Editor>,
+        window: &mut Window,
+        cx: &mut App,
+    ) {
         if let Some(editor_assists) = self.assists_by_editor.get_mut(&editor) {
             for assist_id in editor_assists.assist_ids.clone() {
-                self.finish_assist(assist_id, true, cx);
+                self.finish_assist(assist_id, true, window, cx);
             }
         }
     }
 
-    fn handle_editor_change(&mut self, editor: View<Editor>, cx: &mut WindowContext) {
+    fn handle_editor_change(&mut self, editor: Entity<Editor>, window: &mut Window, cx: &mut App) {
         let Some(editor_assists) = self.assists_by_editor.get(&editor.downgrade()) else {
             return;
         };
@@ -701,16 +723,17 @@ impl InlineAssistant {
                 .0 as f32
                 - scroll_lock.distance_from_top;
             if target_scroll_top != scroll_position.y {
-                editor.set_scroll_position(point(scroll_position.x, target_scroll_top), cx);
+                editor.set_scroll_position(point(scroll_position.x, target_scroll_top), window, cx);
             }
         });
     }
 
     fn handle_editor_event(
         &mut self,
-        editor: View<Editor>,
+        editor: Entity<Editor>,
         event: &EditorEvent,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let Some(editor_assists) = self.assists_by_editor.get_mut(&editor.downgrade()) else {
             return;
@@ -734,7 +757,7 @@ impl InlineAssistant {
                             .iter()
                             .any(|range| range.overlaps(&assist_range))
                         {
-                            self.finish_assist(assist_id, false, cx);
+                            self.finish_assist(assist_id, false, window, cx);
                         }
                     }
                 }
@@ -762,7 +785,11 @@ impl InlineAssistant {
                 for assist_id in editor_assists.assist_ids.clone() {
                     let assist = &self.assists[&assist_id];
                     if let Some(decorations) = assist.decorations.as_ref() {
-                        if decorations.prompt_editor.focus_handle(cx).is_focused(cx) {
+                        if decorations
+                            .prompt_editor
+                            .focus_handle(cx)
+                            .is_focused(window)
+                        {
                             return;
                         }
                     }
@@ -774,18 +801,24 @@ impl InlineAssistant {
         }
     }
 
-    pub fn finish_assist(&mut self, assist_id: InlineAssistId, undo: bool, cx: &mut WindowContext) {
+    pub fn finish_assist(
+        &mut self,
+        assist_id: InlineAssistId,
+        undo: bool,
+        window: &mut Window,
+        cx: &mut App,
+    ) {
         if let Some(assist) = self.assists.get(&assist_id) {
             let assist_group_id = assist.group_id;
             if self.assist_groups[&assist_group_id].linked {
-                for assist_id in self.unlink_assist_group(assist_group_id, cx) {
-                    self.finish_assist(assist_id, undo, cx);
+                for assist_id in self.unlink_assist_group(assist_group_id, window, cx) {
+                    self.finish_assist(assist_id, undo, window, cx);
                 }
                 return;
             }
         }
 
-        self.dismiss_assist(assist_id, cx);
+        self.dismiss_assist(assist_id, window, cx);
 
         if let Some(assist) = self.assists.remove(&assist_id) {
             if let hash_map::Entry::Occupied(mut entry) = self.assist_groups.entry(assist.group_id)
@@ -854,7 +887,12 @@ impl InlineAssistant {
         }
     }
 
-    fn dismiss_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) -> bool {
+    fn dismiss_assist(
+        &mut self,
+        assist_id: InlineAssistId,
+        window: &mut Window,
+        cx: &mut App,
+    ) -> bool {
         let Some(assist) = self.assists.get_mut(&assist_id) else {
             return false;
         };
@@ -875,9 +913,9 @@ impl InlineAssistant {
         if decorations
             .prompt_editor
             .focus_handle(cx)
-            .contains_focused(cx)
+            .contains_focused(window, cx)
         {
-            self.focus_next_assist(assist_id, cx);
+            self.focus_next_assist(assist_id, window, cx);
         }
 
         if let Some(editor_assists) = self.assists_by_editor.get_mut(&editor.downgrade()) {
@@ -894,7 +932,7 @@ impl InlineAssistant {
         true
     }
 
-    fn focus_next_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) {
+    fn focus_next_assist(&mut self, assist_id: InlineAssistId, window: &mut Window, cx: &mut App) {
         let Some(assist) = self.assists.get(&assist_id) else {
             return;
         };
@@ -914,15 +952,18 @@ impl InlineAssistant {
         for assist_id in assist_ids {
             let assist = &self.assists[assist_id];
             if assist.decorations.is_some() {
-                self.focus_assist(*assist_id, cx);
+                self.focus_assist(*assist_id, window, cx);
                 return;
             }
         }
 
-        assist.editor.update(cx, |editor, cx| editor.focus(cx)).ok();
+        assist
+            .editor
+            .update(cx, |editor, cx| window.focus(&editor.focus_handle(cx)))
+            .ok();
     }
 
-    fn focus_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) {
+    fn focus_assist(&mut self, assist_id: InlineAssistId, window: &mut Window, cx: &mut App) {
         let Some(assist) = self.assists.get(&assist_id) else {
             return;
         };
@@ -930,16 +971,21 @@ impl InlineAssistant {
         if let Some(decorations) = assist.decorations.as_ref() {
             decorations.prompt_editor.update(cx, |prompt_editor, cx| {
                 prompt_editor.editor.update(cx, |editor, cx| {
-                    editor.focus(cx);
-                    editor.select_all(&SelectAll, cx);
+                    window.focus(&editor.focus_handle(cx));
+                    editor.select_all(&SelectAll, window, cx);
                 })
             });
         }
 
-        self.scroll_to_assist(assist_id, cx);
+        self.scroll_to_assist(assist_id, window, cx);
     }
 
-    pub fn scroll_to_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) {
+    pub fn scroll_to_assist(
+        &mut self,
+        assist_id: InlineAssistId,
+        window: &mut Window,
+        cx: &mut App,
+    ) {
         let Some(assist) = self.assists.get(&assist_id) else {
             return;
         };
@@ -949,7 +995,7 @@ impl InlineAssistant {
 
         let position = assist.range.start;
         editor.update(cx, |editor, cx| {
-            editor.change_selections(None, cx, |selections| {
+            editor.change_selections(None, window, cx, |selections| {
                 selections.select_anchor_ranges([position..position])
             });
 
@@ -965,7 +1011,7 @@ impl InlineAssistant {
                     .unwrap()
                     .0 as f32;
             } else {
-                let snapshot = editor.snapshot(cx);
+                let snapshot = editor.snapshot(window, cx);
                 let start_row = assist
                     .range
                     .start
@@ -982,13 +1028,16 @@ impl InlineAssistant {
             let scroll_bottom = scroll_top + height_in_lines;
 
             if scroll_target_top < scroll_top {
-                editor.set_scroll_position(point(0., scroll_target_top), cx);
+                editor.set_scroll_position(point(0., scroll_target_top), window, cx);
             } else if scroll_target_bottom > scroll_bottom {
                 if (scroll_target_bottom - scroll_target_top) <= height_in_lines {
-                    editor
-                        .set_scroll_position(point(0., scroll_target_bottom - height_in_lines), cx);
+                    editor.set_scroll_position(
+                        point(0., scroll_target_bottom - height_in_lines),
+                        window,
+                        cx,
+                    );
                 } else {
-                    editor.set_scroll_position(point(0., scroll_target_top), cx);
+                    editor.set_scroll_position(point(0., scroll_target_top), window, cx);
                 }
             }
         });
@@ -997,7 +1046,8 @@ impl InlineAssistant {
     fn unlink_assist_group(
         &mut self,
         assist_group_id: InlineAssistGroupId,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Vec<InlineAssistId> {
         let assist_group = self.assist_groups.get_mut(&assist_group_id).unwrap();
         assist_group.linked = false;
@@ -1006,13 +1056,13 @@ impl InlineAssistant {
             if let Some(editor_decorations) = assist.decorations.as_ref() {
                 editor_decorations
                     .prompt_editor
-                    .update(cx, |prompt_editor, cx| prompt_editor.unlink(cx));
+                    .update(cx, |prompt_editor, cx| prompt_editor.unlink(window, cx));
             }
         }
         assist_group.assist_ids.clone()
     }
 
-    pub fn start_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) {
+    pub fn start_assist(&mut self, assist_id: InlineAssistId, window: &mut Window, cx: &mut App) {
         let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
             assist
         } else {
@@ -1021,8 +1071,8 @@ impl InlineAssistant {
 
         let assist_group_id = assist.group_id;
         if self.assist_groups[&assist_group_id].linked {
-            for assist_id in self.unlink_assist_group(assist_group_id, cx) {
-                self.start_assist(assist_id, cx);
+            for assist_id in self.unlink_assist_group(assist_group_id, window, cx) {
+                self.start_assist(assist_id, window, cx);
             }
             return;
         }
@@ -1047,7 +1097,7 @@ impl InlineAssistant {
             .log_err();
     }
 
-    pub fn stop_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) {
+    pub fn stop_assist(&mut self, assist_id: InlineAssistId, cx: &mut App) {
         let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
             assist
         } else {
@@ -1057,7 +1107,7 @@ impl InlineAssistant {
         assist.codegen.update(cx, |codegen, cx| codegen.stop(cx));
     }
 
-    fn update_editor_highlights(&self, editor: &View<Editor>, cx: &mut WindowContext) {
+    fn update_editor_highlights(&self, editor: &Entity<Editor>, cx: &mut App) {
         let mut gutter_pending_ranges = Vec::new();
         let mut gutter_transformed_ranges = Vec::new();
         let mut foreground_ranges = Vec::new();
@@ -1150,9 +1200,10 @@ impl InlineAssistant {
 
     fn update_editor_blocks(
         &mut self,
-        editor: &View<Editor>,
+        editor: &Entity<Editor>,
         assist_id: InlineAssistId,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let Some(assist) = self.assists.get_mut(&assist_id) else {
             return;
@@ -1182,10 +1233,9 @@ impl InlineAssistant {
                     ))
                     .unwrap();
 
-                let deleted_lines_editor = cx.new_view(|cx| {
-                    let multi_buffer = cx.new_model(|_| {
-                        MultiBuffer::without_headers(language::Capability::ReadOnly)
-                    });
+                let deleted_lines_editor = cx.new(|cx| {
+                    let multi_buffer =
+                        cx.new(|_| MultiBuffer::without_headers(language::Capability::ReadOnly));
                     multi_buffer.update(cx, |multi_buffer, cx| {
                         multi_buffer.push_excerpts(
                             old_buffer.clone(),
@@ -1198,14 +1248,14 @@ impl InlineAssistant {
                     });
 
                     enum DeletedLines {}
-                    let mut editor = Editor::for_multibuffer(multi_buffer, None, true, cx);
+                    let mut editor = Editor::for_multibuffer(multi_buffer, None, true, window, cx);
                     editor.set_soft_wrap_mode(language::language_settings::SoftWrap::None, cx);
                     editor.set_show_wrap_guides(false, cx);
                     editor.set_show_gutter(false, cx);
                     editor.scroll_manager.set_forbid_vertical_scroll(true);
                     editor.set_show_scrollbars(false, cx);
                     editor.set_read_only(true);
-                    editor.set_show_inline_completions(Some(false), cx);
+                    editor.set_show_inline_completions(Some(false), window, cx);
                     editor.highlight_rows::<DeletedLines>(
                         Anchor::min()..Anchor::max(),
                         cx.theme().status().deleted_background,
@@ -1226,7 +1276,7 @@ impl InlineAssistant {
                             .block_mouse_down()
                             .bg(cx.theme().status().deleted_background)
                             .size_full()
-                            .h(height as f32 * cx.line_height())
+                            .h(height as f32 * cx.window.line_height())
                             .pl(cx.gutter_dimensions.full_width())
                             .child(deleted_lines_editor.clone())
                             .into_any_element()
@@ -1257,14 +1307,13 @@ struct InlineAssistScrollLock {
 }
 
 impl EditorInlineAssists {
-    #[allow(clippy::too_many_arguments)]
-    fn new(editor: &View<Editor>, cx: &mut WindowContext) -> Self {
+    fn new(editor: &Entity<Editor>, window: &mut Window, cx: &mut App) -> Self {
         let (highlight_updates_tx, mut highlight_updates_rx) = async_watch::channel(());
         Self {
             assist_ids: Vec::new(),
             scroll_lock: None,
             highlight_updates: highlight_updates_tx,
-            _update_highlights: cx.spawn(|mut cx| {
+            _update_highlights: cx.spawn(|cx| {
                 let editor = editor.downgrade();
                 async move {
                     while let Ok(()) = highlight_updates_rx.changed().await {
@@ -1277,47 +1326,43 @@ impl EditorInlineAssists {
                 }
             }),
             _subscriptions: vec![
-                cx.observe_release(editor, {
+                cx.observe_release_in(editor, window, {
                     let editor = editor.downgrade();
-                    |_, cx| {
+                    |_, window, cx| {
                         InlineAssistant::update_global(cx, |this, cx| {
-                            this.handle_editor_release(editor, cx);
+                            this.handle_editor_release(editor, window, cx);
                         })
                     }
                 }),
-                cx.observe(editor, move |editor, cx| {
+                window.observe(editor, cx, move |editor, window, cx| {
                     InlineAssistant::update_global(cx, |this, cx| {
-                        this.handle_editor_change(editor, cx)
+                        this.handle_editor_change(editor, window, cx)
                     })
                 }),
-                cx.subscribe(editor, move |editor, event, cx| {
+                window.subscribe(editor, cx, move |editor, event, window, cx| {
                     InlineAssistant::update_global(cx, |this, cx| {
-                        this.handle_editor_event(editor, event, cx)
+                        this.handle_editor_event(editor, event, window, cx)
                     })
                 }),
                 editor.update(cx, |editor, cx| {
-                    let editor_handle = cx.view().downgrade();
-                    editor.register_action(
-                        move |_: &editor::actions::Newline, cx: &mut WindowContext| {
-                            InlineAssistant::update_global(cx, |this, cx| {
-                                if let Some(editor) = editor_handle.upgrade() {
-                                    this.handle_editor_newline(editor, cx)
-                                }
-                            })
-                        },
-                    )
+                    let editor_handle = cx.model().downgrade();
+                    editor.register_action(move |_: &editor::actions::Newline, window, cx| {
+                        InlineAssistant::update_global(cx, |this, cx| {
+                            if let Some(editor) = editor_handle.upgrade() {
+                                this.handle_editor_newline(editor, window, cx)
+                            }
+                        })
+                    })
                 }),
                 editor.update(cx, |editor, cx| {
-                    let editor_handle = cx.view().downgrade();
-                    editor.register_action(
-                        move |_: &editor::actions::Cancel, cx: &mut WindowContext| {
-                            InlineAssistant::update_global(cx, |this, cx| {
-                                if let Some(editor) = editor_handle.upgrade() {
-                                    this.handle_editor_cancel(editor, cx)
-                                }
-                            })
-                        },
-                    )
+                    let editor_handle = cx.model().downgrade();
+                    editor.register_action(move |_: &editor::actions::Cancel, window, cx| {
+                        InlineAssistant::update_global(cx, |this, cx| {
+                            if let Some(editor) = editor_handle.upgrade() {
+                                this.handle_editor_cancel(editor, window, cx)
+                            }
+                        })
+                    })
                 }),
             ],
         }
@@ -1340,7 +1385,7 @@ impl InlineAssistGroup {
     }
 }
 
-fn build_assist_editor_renderer(editor: &View<PromptEditor>) -> RenderBlock {
+fn build_assist_editor_renderer(editor: &Entity<PromptEditor>) -> RenderBlock {
     let editor = editor.clone();
     Arc::new(move |cx: &mut BlockContext| {
         *editor.read(cx).gutter_dimensions.lock() = *cx.gutter_dimensions;
@@ -1380,20 +1425,20 @@ enum PromptEditorEvent {
 
 struct PromptEditor {
     id: InlineAssistId,
-    editor: View<Editor>,
-    language_model_selector: View<LanguageModelSelector>,
+    editor: Entity<Editor>,
+    language_model_selector: Entity<LanguageModelSelector>,
     edited_since_done: bool,
     gutter_dimensions: Arc<Mutex<GutterDimensions>>,
     prompt_history: VecDeque<String>,
     prompt_history_ix: Option<usize>,
     pending_prompt: String,
-    codegen: Model<Codegen>,
+    codegen: Entity<Codegen>,
     _codegen_subscription: Subscription,
     editor_subscriptions: Vec<Subscription>,
     pending_token_count: Task<Result<()>>,
     token_counts: Option<TokenCounts>,
     _token_count_subscriptions: Vec<Subscription>,
-    workspace: Option<WeakView<Workspace>>,
+    workspace: Option<WeakEntity<Workspace>>,
     show_rate_limit_notice: bool,
 }
 
@@ -1406,7 +1451,7 @@ pub struct TokenCounts {
 impl EventEmitter<PromptEditorEvent> for PromptEditor {}
 
 impl Render for PromptEditor {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let gutter_dimensions = *self.gutter_dimensions.lock();
         let codegen = self.codegen.read(cx);
 
@@ -1422,17 +1467,21 @@ impl Render for PromptEditor {
                     IconButton::new("cancel", IconName::Close)
                         .icon_color(Color::Muted)
                         .shape(IconButtonShape::Square)
-                        .tooltip(|cx| Tooltip::for_action("Cancel Assist", &menu::Cancel, cx))
+                        .tooltip(|window, cx| {
+                            Tooltip::for_action("Cancel Assist", &menu::Cancel, window, cx)
+                        })
                         .on_click(
-                            cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::CancelRequested)),
+                            cx.listener(|_, _, _, cx| cx.emit(PromptEditorEvent::CancelRequested)),
                         )
                         .into_any_element(),
                     IconButton::new("start", IconName::SparkleAlt)
                         .icon_color(Color::Muted)
                         .shape(IconButtonShape::Square)
-                        .tooltip(|cx| Tooltip::for_action("Transform", &menu::Confirm, cx))
+                        .tooltip(|window, cx| {
+                            Tooltip::for_action("Transform", &menu::Confirm, window, cx)
+                        })
                         .on_click(
-                            cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::StartRequested)),
+                            cx.listener(|_, _, _, cx| cx.emit(PromptEditorEvent::StartRequested)),
                         )
                         .into_any_element(),
                 ]
@@ -1442,23 +1491,26 @@ impl Render for PromptEditor {
                     IconButton::new("cancel", IconName::Close)
                         .icon_color(Color::Muted)
                         .shape(IconButtonShape::Square)
-                        .tooltip(|cx| Tooltip::text("Cancel Assist", cx))
+                        .tooltip(Tooltip::text("Cancel Assist"))
                         .on_click(
-                            cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::CancelRequested)),
+                            cx.listener(|_, _, _, cx| cx.emit(PromptEditorEvent::CancelRequested)),
                         )
                         .into_any_element(),
                     IconButton::new("stop", IconName::Stop)
                         .icon_color(Color::Error)
                         .shape(IconButtonShape::Square)
-                        .tooltip(|cx| {
+                        .tooltip(|window, cx| {
                             Tooltip::with_meta(
                                 "Interrupt Transformation",
                                 Some(&menu::Cancel),
                                 "Changes won't be discarded",
+                                window,
                                 cx,
                             )
                         })
-                        .on_click(cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::StopRequested)))
+                        .on_click(
+                            cx.listener(|_, _, _, cx| cx.emit(PromptEditorEvent::StopRequested)),
+                        )
                         .into_any_element(),
                 ]
             }
@@ -1476,23 +1528,26 @@ impl Render for PromptEditor {
                     IconButton::new("cancel", IconName::Close)
                         .icon_color(Color::Muted)
                         .shape(IconButtonShape::Square)
-                        .tooltip(|cx| Tooltip::for_action("Cancel Assist", &menu::Cancel, cx))
+                        .tooltip(|window, cx| {
+                            Tooltip::for_action("Cancel Assist", &menu::Cancel, window, cx)
+                        })
                         .on_click(
-                            cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::CancelRequested)),
+                            cx.listener(|_, _, _, cx| cx.emit(PromptEditorEvent::CancelRequested)),
                         )
                         .into_any_element(),
                     IconButton::new("restart", IconName::RotateCw)
                         .icon_color(Color::Muted)
                         .shape(IconButtonShape::Square)
-                        .tooltip(|cx| {
+                        .tooltip(|window, cx| {
                             Tooltip::with_meta(
                                 "Regenerate Transformation",
                                 Some(restart_key),
                                 "Current change will be discarded",
+                                window,
                                 cx,
                             )
                         })
-                        .on_click(cx.listener(|_, _, cx| {
+                        .on_click(cx.listener(|_, _, _, cx| {
                             cx.emit(PromptEditorEvent::StartRequested);
                         }))
                         .into_any_element(),
@@ -1500,8 +1555,10 @@ impl Render for PromptEditor {
                         IconButton::new("confirm", IconName::Check)
                             .icon_color(Color::Info)
                             .shape(IconButtonShape::Square)
-                            .tooltip(|cx| Tooltip::for_action("Confirm Assist", &menu::Confirm, cx))
-                            .on_click(cx.listener(|_, _, cx| {
+                            .tooltip(|window, cx| {
+                                Tooltip::for_action("Confirm Assist", &menu::Confirm, window, cx)
+                            })
+                            .on_click(cx.listener(|_, _, _, cx| {
                                 cx.emit(PromptEditorEvent::ConfirmRequested);
                             }))
                             .into_any_element()
@@ -1520,7 +1577,7 @@ impl Render for PromptEditor {
             .border_y_1()
             .border_color(cx.theme().status().info_border)
             .size_full()
-            .py(cx.line_height() / 2.5)
+            .py(window.line_height() / 2.5)
             .on_action(cx.listener(Self::confirm))
             .on_action(cx.listener(Self::cancel))
             .on_action(cx.listener(Self::restart))
@@ -1539,7 +1596,7 @@ impl Render for PromptEditor {
                             .shape(IconButtonShape::Square)
                             .icon_size(IconSize::Small)
                             .icon_color(Color::Muted)
-                            .tooltip(move |cx| {
+                            .tooltip(move |window, cx| {
                                 Tooltip::with_meta(
                                     format!(
                                         "Using {}",
@@ -1550,6 +1607,7 @@ impl Render for PromptEditor {
                                     ),
                                     None,
                                     "Change Model",
+                                    window,
                                     cx,
                                 )
                             }),
@@ -1586,7 +1644,7 @@ impl Render for PromptEditor {
                             el.child(
                                 div()
                                     .id("error")
-                                    .tooltip(move |cx| Tooltip::text(error_message.clone(), cx))
+                                    .tooltip(Tooltip::text(error_message))
                                     .child(
                                         Icon::new(IconName::XCircle)
                                             .size(IconSize::Small)

crates/assistant/src/slash_command_settings.rs 🔗

@@ -1,5 +1,5 @@
 use anyhow::Result;
-use gpui::AppContext;
+use gpui::App;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsSources};
@@ -36,7 +36,7 @@ impl Settings for SlashCommandSettings {
 
     type FileContent = Self;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _cx: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _cx: &mut App) -> Result<Self> {
         SettingsSources::<Self::FileContent>::json_merge_with(
             [sources.default]
                 .into_iter()

crates/assistant/src/terminal_inline_assistant.rs 🔗

@@ -11,8 +11,8 @@ use editor::{
 use fs::Fs;
 use futures::{channel::mpsc, SinkExt, StreamExt};
 use gpui::{
-    AppContext, Context, EventEmitter, FocusHandle, FocusableView, Global, Model, ModelContext,
-    Subscription, Task, TextStyle, UpdateGlobal, View, WeakView,
+    App, Context, Entity, EventEmitter, FocusHandle, Focusable, Global, Subscription, Task,
+    TextStyle, UpdateGlobal, WeakEntity,
 };
 use language::Buffer;
 use language_model::{
@@ -39,7 +39,7 @@ pub fn init(
     fs: Arc<dyn Fs>,
     prompt_builder: Arc<PromptBuilder>,
     telemetry: Arc<Telemetry>,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) {
     cx.set_global(TerminalInlineAssistant::new(fs, prompt_builder, telemetry));
 }
@@ -86,20 +86,20 @@ impl TerminalInlineAssistant {
 
     pub fn assist(
         &mut self,
-        terminal_view: &View<TerminalView>,
-        workspace: Option<WeakView<Workspace>>,
-        assistant_panel: Option<&View<AssistantPanel>>,
+        terminal_view: &Entity<TerminalView>,
+        workspace: Option<WeakEntity<Workspace>>,
+        assistant_panel: Option<&Entity<AssistantPanel>>,
         initial_prompt: Option<String>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let terminal = terminal_view.read(cx).terminal().clone();
         let assist_id = self.next_assist_id.post_inc();
-        let prompt_buffer =
-            cx.new_model(|cx| Buffer::local(initial_prompt.unwrap_or_default(), cx));
-        let prompt_buffer = cx.new_model(|cx| MultiBuffer::singleton(prompt_buffer, cx));
-        let codegen = cx.new_model(|_| Codegen::new(terminal, self.telemetry.clone()));
+        let prompt_buffer = cx.new(|cx| Buffer::local(initial_prompt.unwrap_or_default(), cx));
+        let prompt_buffer = cx.new(|cx| MultiBuffer::singleton(prompt_buffer, cx));
+        let codegen = cx.new(|_| Codegen::new(terminal, self.telemetry.clone()));
 
-        let prompt_editor = cx.new_view(|cx| {
+        let prompt_editor = cx.new(|cx| {
             PromptEditor::new(
                 assist_id,
                 self.prompt_history.clone(),
@@ -108,6 +108,7 @@ impl TerminalInlineAssistant {
                 assistant_panel,
                 workspace.clone(),
                 self.fs.clone(),
+                window,
                 cx,
             )
         });
@@ -117,7 +118,7 @@ impl TerminalInlineAssistant {
             render: Box::new(move |_| prompt_editor_render.clone().into_any_element()),
         };
         terminal_view.update(cx, |terminal_view, cx| {
-            terminal_view.set_block_below_cursor(block, cx);
+            terminal_view.set_block_below_cursor(block, window, cx);
         });
 
         let terminal_assistant = TerminalInlineAssist::new(
@@ -126,21 +127,27 @@ impl TerminalInlineAssistant {
             assistant_panel.is_some(),
             prompt_editor,
             workspace.clone(),
+            window,
             cx,
         );
 
         self.assists.insert(assist_id, terminal_assistant);
 
-        self.focus_assist(assist_id, cx);
+        self.focus_assist(assist_id, window, cx);
     }
 
-    fn focus_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut WindowContext) {
+    fn focus_assist(
+        &mut self,
+        assist_id: TerminalInlineAssistId,
+        window: &mut Window,
+        cx: &mut App,
+    ) {
         let assist = &self.assists[&assist_id];
         if let Some(prompt_editor) = assist.prompt_editor.as_ref() {
             prompt_editor.update(cx, |this, cx| {
                 this.editor.update(cx, |editor, cx| {
-                    editor.focus(cx);
-                    editor.select_all(&SelectAll, cx);
+                    window.focus(&editor.focus_handle(cx));
+                    editor.select_all(&SelectAll, window, cx);
                 });
             });
         }
@@ -148,9 +155,10 @@ impl TerminalInlineAssistant {
 
     fn handle_prompt_editor_event(
         &mut self,
-        prompt_editor: View<PromptEditor>,
+        prompt_editor: Entity<PromptEditor>,
         event: &PromptEditorEvent,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let assist_id = prompt_editor.read(cx).id;
         match event {
@@ -161,21 +169,21 @@ impl TerminalInlineAssistant {
                 self.stop_assist(assist_id, cx);
             }
             PromptEditorEvent::ConfirmRequested { execute } => {
-                self.finish_assist(assist_id, false, *execute, cx);
+                self.finish_assist(assist_id, false, *execute, window, cx);
             }
             PromptEditorEvent::CancelRequested => {
-                self.finish_assist(assist_id, true, false, cx);
+                self.finish_assist(assist_id, true, false, window, cx);
             }
             PromptEditorEvent::DismissRequested => {
-                self.dismiss_assist(assist_id, cx);
+                self.dismiss_assist(assist_id, window, cx);
             }
             PromptEditorEvent::Resized { height_in_lines } => {
-                self.insert_prompt_editor_into_terminal(assist_id, *height_in_lines, cx);
+                self.insert_prompt_editor_into_terminal(assist_id, *height_in_lines, window, cx);
             }
         }
     }
 
-    fn start_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut WindowContext) {
+    fn start_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut App) {
         let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
             assist
         } else {
@@ -213,7 +221,7 @@ impl TerminalInlineAssistant {
         codegen.update(cx, |codegen, cx| codegen.start(request, cx));
     }
 
-    fn stop_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut WindowContext) {
+    fn stop_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut App) {
         let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
             assist
         } else {
@@ -226,7 +234,7 @@ impl TerminalInlineAssistant {
     fn request_for_inline_assist(
         &self,
         assist_id: TerminalInlineAssistId,
-        cx: &mut WindowContext,
+        cx: &mut App,
     ) -> Result<LanguageModelRequest> {
         let assist = self.assists.get(&assist_id).context("invalid assist")?;
 
@@ -296,16 +304,17 @@ impl TerminalInlineAssistant {
         assist_id: TerminalInlineAssistId,
         undo: bool,
         execute: bool,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        self.dismiss_assist(assist_id, cx);
+        self.dismiss_assist(assist_id, window, cx);
 
         if let Some(assist) = self.assists.remove(&assist_id) {
             assist
                 .terminal
                 .update(cx, |this, cx| {
                     this.clear_block_below_cursor(cx);
-                    this.focus_handle(cx).focus(cx);
+                    this.focus_handle(cx).focus(window);
                 })
                 .log_err();
 
@@ -348,7 +357,8 @@ impl TerminalInlineAssistant {
     fn dismiss_assist(
         &mut self,
         assist_id: TerminalInlineAssistId,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> bool {
         let Some(assist) = self.assists.get_mut(&assist_id) else {
             return false;
@@ -361,7 +371,7 @@ impl TerminalInlineAssistant {
             .terminal
             .update(cx, |this, cx| {
                 this.clear_block_below_cursor(cx);
-                this.focus_handle(cx).focus(cx);
+                this.focus_handle(cx).focus(window);
             })
             .is_ok()
     }
@@ -370,7 +380,8 @@ impl TerminalInlineAssistant {
         &mut self,
         assist_id: TerminalInlineAssistId,
         height: u8,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         if let Some(assist) = self.assists.get_mut(&assist_id) {
             if let Some(prompt_editor) = assist.prompt_editor.as_ref().cloned() {
@@ -382,7 +393,7 @@ impl TerminalInlineAssistant {
                             height,
                             render: Box::new(move |_| prompt_editor.clone().into_any_element()),
                         };
-                        terminal.set_block_below_cursor(block, cx);
+                        terminal.set_block_below_cursor(block, window, cx);
                     })
                     .log_err();
             }
@@ -391,10 +402,10 @@ impl TerminalInlineAssistant {
 }
 
 struct TerminalInlineAssist {
-    terminal: WeakView<TerminalView>,
-    prompt_editor: Option<View<PromptEditor>>,
-    codegen: Model<Codegen>,
-    workspace: Option<WeakView<Workspace>>,
+    terminal: WeakEntity<TerminalView>,
+    prompt_editor: Option<Entity<PromptEditor>>,
+    codegen: Entity<Codegen>,
+    workspace: Option<WeakEntity<Workspace>>,
     include_context: bool,
     _subscriptions: Vec<Subscription>,
 }
@@ -402,11 +413,12 @@ struct TerminalInlineAssist {
 impl TerminalInlineAssist {
     pub fn new(
         assist_id: TerminalInlineAssistId,
-        terminal: &View<TerminalView>,
+        terminal: &Entity<TerminalView>,
         include_context: bool,
-        prompt_editor: View<PromptEditor>,
-        workspace: Option<WeakView<Workspace>>,
-        cx: &mut WindowContext,
+        prompt_editor: Entity<PromptEditor>,
+        workspace: Option<WeakEntity<Workspace>>,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Self {
         let codegen = prompt_editor.read(cx).codegen.clone();
         Self {
@@ -416,12 +428,12 @@ impl TerminalInlineAssist {
             workspace: workspace.clone(),
             include_context,
             _subscriptions: vec![
-                cx.subscribe(&prompt_editor, |prompt_editor, event, cx| {
+                window.subscribe(&prompt_editor, cx, |prompt_editor, event, window, cx| {
                     TerminalInlineAssistant::update_global(cx, |this, cx| {
-                        this.handle_prompt_editor_event(prompt_editor, event, cx)
+                        this.handle_prompt_editor_event(prompt_editor, event, window, cx)
                     })
                 }),
-                cx.subscribe(&codegen, move |codegen, event, cx| {
+                window.subscribe(&codegen, cx, move |codegen, event, window, cx| {
                     TerminalInlineAssistant::update_global(cx, |this, cx| match event {
                         CodegenEvent::Finished => {
                             let assist = if let Some(assist) = this.assists.get(&assist_id) {
@@ -454,7 +466,7 @@ impl TerminalInlineAssist {
                             }
 
                             if assist.prompt_editor.is_none() {
-                                this.finish_assist(assist_id, false, false, cx);
+                                this.finish_assist(assist_id, false, false, window, cx);
                             }
                         }
                     })
@@ -476,25 +488,25 @@ enum PromptEditorEvent {
 struct PromptEditor {
     id: TerminalInlineAssistId,
     height_in_lines: u8,
-    editor: View<Editor>,
-    language_model_selector: View<LanguageModelSelector>,
+    editor: Entity<Editor>,
+    language_model_selector: Entity<LanguageModelSelector>,
     edited_since_done: bool,
     prompt_history: VecDeque<String>,
     prompt_history_ix: Option<usize>,
     pending_prompt: String,
-    codegen: Model<Codegen>,
+    codegen: Entity<Codegen>,
     _codegen_subscription: Subscription,
     editor_subscriptions: Vec<Subscription>,
     pending_token_count: Task<Result<()>>,
     token_count: Option<usize>,
     _token_count_subscriptions: Vec<Subscription>,
-    workspace: Option<WeakView<Workspace>>,
+    workspace: Option<WeakEntity<Workspace>>,
 }
 
 impl EventEmitter<PromptEditorEvent> for PromptEditor {}
 
 impl Render for PromptEditor {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let status = &self.codegen.read(cx).status;
         let buttons = match status {
             CodegenStatus::Idle => {
@@ -502,16 +514,20 @@ impl Render for PromptEditor {
                     IconButton::new("cancel", IconName::Close)
                         .icon_color(Color::Muted)
                         .shape(IconButtonShape::Square)
-                        .tooltip(|cx| Tooltip::for_action("Cancel Assist", &menu::Cancel, cx))
+                        .tooltip(|window, cx| {
+                            Tooltip::for_action("Cancel Assist", &menu::Cancel, window, cx)
+                        })
                         .on_click(
-                            cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::CancelRequested)),
+                            cx.listener(|_, _, _, cx| cx.emit(PromptEditorEvent::CancelRequested)),
                         ),
                     IconButton::new("start", IconName::SparkleAlt)
                         .icon_color(Color::Muted)
                         .shape(IconButtonShape::Square)
-                        .tooltip(|cx| Tooltip::for_action("Generate", &menu::Confirm, cx))
+                        .tooltip(|window, cx| {
+                            Tooltip::for_action("Generate", &menu::Confirm, window, cx)
+                        })
                         .on_click(
-                            cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::StartRequested)),
+                            cx.listener(|_, _, _, cx| cx.emit(PromptEditorEvent::StartRequested)),
                         ),
                 ]
             }
@@ -520,23 +536,24 @@ impl Render for PromptEditor {
                     IconButton::new("cancel", IconName::Close)
                         .icon_color(Color::Muted)
                         .shape(IconButtonShape::Square)
-                        .tooltip(|cx| Tooltip::text("Cancel Assist", cx))
+                        .tooltip(Tooltip::text("Cancel Assist"))
                         .on_click(
-                            cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::CancelRequested)),
+                            cx.listener(|_, _, _, cx| cx.emit(PromptEditorEvent::CancelRequested)),
                         ),
                     IconButton::new("stop", IconName::Stop)
                         .icon_color(Color::Error)
                         .shape(IconButtonShape::Square)
-                        .tooltip(|cx| {
+                        .tooltip(|window, cx| {
                             Tooltip::with_meta(
                                 "Interrupt Generation",
                                 Some(&menu::Cancel),
                                 "Changes won't be discarded",
+                                window,
                                 cx,
                             )
                         })
                         .on_click(
-                            cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::StopRequested)),
+                            cx.listener(|_, _, _, cx| cx.emit(PromptEditorEvent::StopRequested)),
                         ),
                 ]
             }
@@ -544,8 +561,12 @@ impl Render for PromptEditor {
                 let cancel = IconButton::new("cancel", IconName::Close)
                     .icon_color(Color::Muted)
                     .shape(IconButtonShape::Square)
-                    .tooltip(|cx| Tooltip::for_action("Cancel Assist", &menu::Cancel, cx))
-                    .on_click(cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::CancelRequested)));
+                    .tooltip(|window, cx| {
+                        Tooltip::for_action("Cancel Assist", &menu::Cancel, window, cx)
+                    })
+                    .on_click(
+                        cx.listener(|_, _, _, cx| cx.emit(PromptEditorEvent::CancelRequested)),
+                    );
 
                 let has_error = matches!(status, CodegenStatus::Error(_));
                 if has_error || self.edited_since_done {
@@ -554,15 +575,16 @@ impl Render for PromptEditor {
                         IconButton::new("restart", IconName::RotateCw)
                             .icon_color(Color::Info)
                             .shape(IconButtonShape::Square)
-                            .tooltip(|cx| {
+                            .tooltip(|window, cx| {
                                 Tooltip::with_meta(
                                     "Restart Generation",
                                     Some(&menu::Confirm),
                                     "Changes will be discarded",
+                                    window,
                                     cx,
                                 )
                             })
-                            .on_click(cx.listener(|_, _, cx| {
+                            .on_click(cx.listener(|_, _, _, cx| {
                                 cx.emit(PromptEditorEvent::StartRequested);
                             })),
                     ]
@@ -572,23 +594,29 @@ impl Render for PromptEditor {
                         IconButton::new("accept", IconName::Check)
                             .icon_color(Color::Info)
                             .shape(IconButtonShape::Square)
-                            .tooltip(|cx| {
-                                Tooltip::for_action("Accept Generated Command", &menu::Confirm, cx)
+                            .tooltip(|window, cx| {
+                                Tooltip::for_action(
+                                    "Accept Generated Command",
+                                    &menu::Confirm,
+                                    window,
+                                    cx,
+                                )
                             })
-                            .on_click(cx.listener(|_, _, cx| {
+                            .on_click(cx.listener(|_, _, _, cx| {
                                 cx.emit(PromptEditorEvent::ConfirmRequested { execute: false });
                             })),
                         IconButton::new("confirm", IconName::Play)
                             .icon_color(Color::Info)
                             .shape(IconButtonShape::Square)
-                            .tooltip(|cx| {
+                            .tooltip(|window, cx| {
                                 Tooltip::for_action(
                                     "Execute Generated Command",
                                     &menu::SecondaryConfirm,
+                                    window,
                                     cx,
                                 )
                             })
-                            .on_click(cx.listener(|_, _, cx| {
+                            .on_click(cx.listener(|_, _, _, cx| {
                                 cx.emit(PromptEditorEvent::ConfirmRequested { execute: true });
                             })),
                     ]
@@ -619,7 +647,7 @@ impl Render for PromptEditor {
                             .shape(IconButtonShape::Square)
                             .icon_size(IconSize::Small)
                             .icon_color(Color::Muted)
-                            .tooltip(move |cx| {
+                            .tooltip(move |window, cx| {
                                 Tooltip::with_meta(
                                     format!(
                                         "Using {}",
@@ -630,6 +658,7 @@ impl Render for PromptEditor {
                                     ),
                                     None,
                                     "Change Model",
+                                    window,
                                     cx,
                                 )
                             }),
@@ -640,7 +669,7 @@ impl Render for PromptEditor {
                             Some(
                                 div()
                                     .id("error")
-                                    .tooltip(move |cx| Tooltip::text(error_message.clone(), cx))
+                                    .tooltip(Tooltip::text(error_message))
                                     .child(
                                         Icon::new(IconName::XCircle)
                                             .size(IconSize::Small)
@@ -663,8 +692,8 @@ impl Render for PromptEditor {
     }
 }
 
-impl FocusableView for PromptEditor {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for PromptEditor {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.editor.focus_handle(cx)
     }
 }
@@ -676,14 +705,15 @@ impl PromptEditor {
     fn new(
         id: TerminalInlineAssistId,
         prompt_history: VecDeque<String>,
-        prompt_buffer: Model<MultiBuffer>,
-        codegen: Model<Codegen>,
-        assistant_panel: Option<&View<AssistantPanel>>,
-        workspace: Option<WeakView<Workspace>>,
+        prompt_buffer: Entity<MultiBuffer>,
+        codegen: Entity<Codegen>,
+        assistant_panel: Option<&Entity<AssistantPanel>>,
+        workspace: Option<WeakEntity<Workspace>>,
         fs: Arc<dyn Fs>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
-        let prompt_editor = cx.new_view(|cx| {
+        let prompt_editor = cx.new(|cx| {
             let mut editor = Editor::new(
                 EditorMode::AutoHeight {
                     max_lines: Self::MAX_LINES as usize,
@@ -691,24 +721,28 @@ impl PromptEditor {
                 prompt_buffer,
                 None,
                 false,
+                window,
                 cx,
             );
             editor.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
-            editor.set_placeholder_text(Self::placeholder_text(cx), cx);
+            editor.set_placeholder_text(Self::placeholder_text(window), cx);
             editor
         });
 
         let mut token_count_subscriptions = Vec::new();
         if let Some(assistant_panel) = assistant_panel {
-            token_count_subscriptions
-                .push(cx.subscribe(assistant_panel, Self::handle_assistant_panel_event));
+            token_count_subscriptions.push(cx.subscribe_in(
+                assistant_panel,
+                window,
+                Self::handle_assistant_panel_event,
+            ));
         }
 
         let mut this = Self {
             id,
             height_in_lines: 1,
             editor: prompt_editor,
-            language_model_selector: cx.new_view(|cx| {
+            language_model_selector: cx.new(|cx| {
                 let fs = fs.clone();
                 LanguageModelSelector::new(
                     move |model, cx| {
@@ -718,6 +752,7 @@ impl PromptEditor {
                             move |settings, _| settings.set_model(model.clone()),
                         );
                     },
+                    window,
                     cx,
                 )
             }),
@@ -725,7 +760,7 @@ impl PromptEditor {
             prompt_history,
             prompt_history_ix: None,
             pending_prompt: String::new(),
-            _codegen_subscription: cx.observe(&codegen, Self::handle_codegen_changed),
+            _codegen_subscription: cx.observe_in(&codegen, window, Self::handle_codegen_changed),
             editor_subscriptions: Vec::new(),
             codegen,
             pending_token_count: Task::ready(Ok(())),
@@ -739,15 +774,15 @@ impl PromptEditor {
         this
     }
 
-    fn placeholder_text(cx: &WindowContext) -> String {
-        let context_keybinding = text_for_action(&zed_actions::assistant::ToggleFocus, cx)
+    fn placeholder_text(window: &Window) -> String {
+        let context_keybinding = text_for_action(&zed_actions::assistant::ToggleFocus, window)
             .map(|keybinding| format!(" • {keybinding} for context"))
             .unwrap_or_default();
 
         format!("Generate…{context_keybinding} • ↓↑ for history")
     }
 
-    fn subscribe_to_editor(&mut self, cx: &mut ViewContext<Self>) {
+    fn subscribe_to_editor(&mut self, cx: &mut Context<Self>) {
         self.editor_subscriptions.clear();
         self.editor_subscriptions
             .push(cx.observe(&self.editor, Self::handle_prompt_editor_changed));
@@ -755,11 +790,11 @@ impl PromptEditor {
             .push(cx.subscribe(&self.editor, Self::handle_prompt_editor_events));
     }
 
-    fn prompt(&self, cx: &AppContext) -> String {
+    fn prompt(&self, cx: &App) -> String {
         self.editor.read(cx).text(cx)
     }
 
-    fn count_lines(&mut self, cx: &mut ViewContext<Self>) {
+    fn count_lines(&mut self, cx: &mut Context<Self>) {
         let height_in_lines = cmp::max(
             2, // Make the editor at least two lines tall, to account for padding and buttons.
             cmp::min(
@@ -777,15 +812,16 @@ impl PromptEditor {
 
     fn handle_assistant_panel_event(
         &mut self,
-        _: View<AssistantPanel>,
+        _: &Entity<AssistantPanel>,
         event: &AssistantPanelEvent,
-        cx: &mut ViewContext<Self>,
+        _: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let AssistantPanelEvent::ContextEdited { .. } = event;
         self.count_tokens(cx);
     }
 
-    fn count_tokens(&mut self, cx: &mut ViewContext<Self>) {
+    fn count_tokens(&mut self, cx: &mut Context<Self>) {
         let assist_id = self.id;
         let Some(model) = LanguageModelRegistry::read_global(cx).active_model() else {
             return;
@@ -805,15 +841,15 @@ impl PromptEditor {
         })
     }
 
-    fn handle_prompt_editor_changed(&mut self, _: View<Editor>, cx: &mut ViewContext<Self>) {
+    fn handle_prompt_editor_changed(&mut self, _: Entity<Editor>, cx: &mut Context<Self>) {
         self.count_lines(cx);
     }
 
     fn handle_prompt_editor_events(
         &mut self,
-        _: View<Editor>,
+        _: Entity<Editor>,
         event: &EditorEvent,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match event {
             EditorEvent::Edited { .. } => {
@@ -836,7 +872,12 @@ impl PromptEditor {
         }
     }
 
-    fn handle_codegen_changed(&mut self, _: Model<Codegen>, cx: &mut ViewContext<Self>) {
+    fn handle_codegen_changed(
+        &mut self,
+        _: Entity<Codegen>,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         match &self.codegen.read(cx).status {
             CodegenStatus::Idle => {
                 self.editor
@@ -854,7 +895,7 @@ impl PromptEditor {
         }
     }
 
-    fn cancel(&mut self, _: &editor::actions::Cancel, cx: &mut ViewContext<Self>) {
+    fn cancel(&mut self, _: &editor::actions::Cancel, _: &mut Window, cx: &mut Context<Self>) {
         match &self.codegen.read(cx).status {
             CodegenStatus::Idle | CodegenStatus::Done | CodegenStatus::Error(_) => {
                 cx.emit(PromptEditorEvent::CancelRequested);
@@ -865,7 +906,7 @@ impl PromptEditor {
         }
     }
 
-    fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
+    fn confirm(&mut self, _: &menu::Confirm, _: &mut Window, cx: &mut Context<Self>) {
         match &self.codegen.read(cx).status {
             CodegenStatus::Idle => {
                 if !self.editor.read(cx).text(cx).trim().is_empty() {
@@ -888,53 +929,58 @@ impl PromptEditor {
         }
     }
 
-    fn secondary_confirm(&mut self, _: &menu::SecondaryConfirm, cx: &mut ViewContext<Self>) {
+    fn secondary_confirm(
+        &mut self,
+        _: &menu::SecondaryConfirm,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if matches!(self.codegen.read(cx).status, CodegenStatus::Done) {
             cx.emit(PromptEditorEvent::ConfirmRequested { execute: true });
         }
     }
 
-    fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
+    fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(ix) = self.prompt_history_ix {
             if ix > 0 {
                 self.prompt_history_ix = Some(ix - 1);
                 let prompt = self.prompt_history[ix - 1].as_str();
                 self.editor.update(cx, |editor, cx| {
-                    editor.set_text(prompt, cx);
-                    editor.move_to_beginning(&Default::default(), cx);
+                    editor.set_text(prompt, window, cx);
+                    editor.move_to_beginning(&Default::default(), window, cx);
                 });
             }
         } else if !self.prompt_history.is_empty() {
             self.prompt_history_ix = Some(self.prompt_history.len() - 1);
             let prompt = self.prompt_history[self.prompt_history.len() - 1].as_str();
             self.editor.update(cx, |editor, cx| {
-                editor.set_text(prompt, cx);
-                editor.move_to_beginning(&Default::default(), cx);
+                editor.set_text(prompt, window, cx);
+                editor.move_to_beginning(&Default::default(), window, cx);
             });
         }
     }
 
-    fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
+    fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(ix) = self.prompt_history_ix {
             if ix < self.prompt_history.len() - 1 {
                 self.prompt_history_ix = Some(ix + 1);
                 let prompt = self.prompt_history[ix + 1].as_str();
                 self.editor.update(cx, |editor, cx| {
-                    editor.set_text(prompt, cx);
-                    editor.move_to_end(&Default::default(), cx)
+                    editor.set_text(prompt, window, cx);
+                    editor.move_to_end(&Default::default(), window, cx)
                 });
             } else {
                 self.prompt_history_ix = None;
                 let prompt = self.pending_prompt.as_str();
                 self.editor.update(cx, |editor, cx| {
-                    editor.set_text(prompt, cx);
-                    editor.move_to_end(&Default::default(), cx)
+                    editor.set_text(prompt, window, cx);
+                    editor.move_to_end(&Default::default(), window, cx)
                 });
             }
         }
     }
 
-    fn render_token_count(&self, cx: &mut ViewContext<Self>) -> Option<impl IntoElement> {
+    fn render_token_count(&self, cx: &mut Context<Self>) -> Option<impl IntoElement> {
         let model = LanguageModelRegistry::read_global(cx).active_model()?;
         let token_count = self.token_count?;
         let max_token_count = model.max_token_count();
@@ -964,34 +1010,35 @@ impl PromptEditor {
             );
         if let Some(workspace) = self.workspace.clone() {
             token_count = token_count
-                .tooltip(|cx| {
+                .tooltip(|window, cx| {
                     Tooltip::with_meta(
                         "Tokens Used by Inline Assistant",
                         None,
                         "Click to Open Assistant Panel",
+                        window,
                         cx,
                     )
                 })
                 .cursor_pointer()
-                .on_mouse_down(gpui::MouseButton::Left, |_, cx| cx.stop_propagation())
-                .on_click(move |_, cx| {
+                .on_mouse_down(gpui::MouseButton::Left, |_, _, cx| cx.stop_propagation())
+                .on_click(move |_, window, cx| {
                     cx.stop_propagation();
                     workspace
                         .update(cx, |workspace, cx| {
-                            workspace.focus_panel::<AssistantPanel>(cx)
+                            workspace.focus_panel::<AssistantPanel>(window, cx)
                         })
                         .ok();
                 });
         } else {
             token_count = token_count
                 .cursor_default()
-                .tooltip(|cx| Tooltip::text("Tokens Used by Inline Assistant", cx));
+                .tooltip(Tooltip::text("Tokens Used by Inline Assistant"));
         }
 
         Some(token_count)
     }
 
-    fn render_prompt_editor(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
         let settings = ThemeSettings::get_global(cx);
         let text_style = TextStyle {
             color: if self.editor.read(cx).read_only(cx) {
@@ -1029,27 +1076,27 @@ const CLEAR_INPUT: &str = "\x15";
 const CARRIAGE_RETURN: &str = "\x0d";
 
 struct TerminalTransaction {
-    terminal: Model<Terminal>,
+    terminal: Entity<Terminal>,
 }
 
 impl TerminalTransaction {
-    pub fn start(terminal: Model<Terminal>) -> Self {
+    pub fn start(terminal: Entity<Terminal>) -> Self {
         Self { terminal }
     }
 
-    pub fn push(&mut self, hunk: String, cx: &mut AppContext) {
+    pub fn push(&mut self, hunk: String, cx: &mut App) {
         // Ensure that the assistant cannot accidentally execute commands that are streamed into the terminal
         let input = Self::sanitize_input(hunk);
         self.terminal
             .update(cx, |terminal, _| terminal.input(input));
     }
 
-    pub fn undo(&self, cx: &mut AppContext) {
+    pub fn undo(&self, cx: &mut App) {
         self.terminal
             .update(cx, |terminal, _| terminal.input(CLEAR_INPUT.to_string()));
     }
 
-    pub fn complete(&self, cx: &mut AppContext) {
+    pub fn complete(&self, cx: &mut App) {
         self.terminal.update(cx, |terminal, _| {
             terminal.input(CARRIAGE_RETURN.to_string())
         });
@@ -1063,14 +1110,14 @@ impl TerminalTransaction {
 pub struct Codegen {
     status: CodegenStatus,
     telemetry: Option<Arc<Telemetry>>,
-    terminal: Model<Terminal>,
+    terminal: Entity<Terminal>,
     generation: Task<()>,
     message_id: Option<String>,
     transaction: Option<TerminalTransaction>,
 }
 
 impl Codegen {
-    pub fn new(terminal: Model<Terminal>, telemetry: Option<Arc<Telemetry>>) -> Self {
+    pub fn new(terminal: Entity<Terminal>, telemetry: Option<Arc<Telemetry>>) -> Self {
         Self {
             terminal,
             telemetry,
@@ -1081,7 +1128,7 @@ impl Codegen {
         }
     }
 
-    pub fn start(&mut self, prompt: LanguageModelRequest, cx: &mut ModelContext<Self>) {
+    pub fn start(&mut self, prompt: LanguageModelRequest, cx: &mut Context<Self>) {
         let Some(model) = LanguageModelRegistry::read_global(cx).active_model() else {
             return;
         };
@@ -1181,20 +1228,20 @@ impl Codegen {
         cx.notify();
     }
 
-    pub fn stop(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn stop(&mut self, cx: &mut Context<Self>) {
         self.status = CodegenStatus::Done;
         self.generation = Task::ready(());
         cx.emit(CodegenEvent::Finished);
         cx.notify();
     }
 
-    pub fn complete(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn complete(&mut self, cx: &mut Context<Self>) {
         if let Some(transaction) = self.transaction.take() {
             transaction.complete(cx);
         }
     }
 
-    pub fn undo(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn undo(&mut self, cx: &mut Context<Self>) {
         if let Some(transaction) = self.transaction.take() {
             transaction.undo(cx);
         }

crates/assistant2/src/active_thread.rs 🔗

@@ -3,9 +3,9 @@ use std::sync::Arc;
 use assistant_tool::ToolWorkingSet;
 use collections::HashMap;
 use gpui::{
-    list, AbsoluteLength, AnyElement, AppContext, DefiniteLength, EdgesRefinement, Empty, Length,
-    ListAlignment, ListOffset, ListState, Model, StyleRefinement, Subscription,
-    TextStyleRefinement, UnderlineStyle, View, WeakView,
+    list, AbsoluteLength, AnyElement, App, DefiniteLength, EdgesRefinement, Empty, Entity, Length,
+    ListAlignment, ListOffset, ListState, StyleRefinement, Subscription, TextStyleRefinement,
+    UnderlineStyle, WeakEntity,
 };
 use language::LanguageRegistry;
 use language_model::Role;
@@ -20,30 +20,31 @@ use crate::thread_store::ThreadStore;
 use crate::ui::ContextPill;
 
 pub struct ActiveThread {
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     language_registry: Arc<LanguageRegistry>,
     tools: Arc<ToolWorkingSet>,
-    thread_store: Model<ThreadStore>,
-    thread: Model<Thread>,
+    thread_store: Entity<ThreadStore>,
+    thread: Entity<Thread>,
     messages: Vec<MessageId>,
     list_state: ListState,
-    rendered_messages_by_id: HashMap<MessageId, View<Markdown>>,
+    rendered_messages_by_id: HashMap<MessageId, Entity<Markdown>>,
     last_error: Option<ThreadError>,
     _subscriptions: Vec<Subscription>,
 }
 
 impl ActiveThread {
     pub fn new(
-        thread: Model<Thread>,
-        thread_store: Model<ThreadStore>,
-        workspace: WeakView<Workspace>,
+        thread: Entity<Thread>,
+        thread_store: Entity<ThreadStore>,
+        workspace: WeakEntity<Workspace>,
         language_registry: Arc<LanguageRegistry>,
         tools: Arc<ToolWorkingSet>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let subscriptions = vec![
             cx.observe(&thread, |_, _, cx| cx.notify()),
-            cx.subscribe(&thread, Self::handle_thread_event),
+            cx.subscribe_in(&thread, window, Self::handle_thread_event),
         ];
 
         let mut this = Self {
@@ -55,8 +56,8 @@ impl ActiveThread {
             messages: Vec::new(),
             rendered_messages_by_id: HashMap::default(),
             list_state: ListState::new(0, ListAlignment::Bottom, px(1024.), {
-                let this = cx.view().downgrade();
-                move |ix, cx: &mut WindowContext| {
+                let this = cx.model().downgrade();
+                move |ix, _: &mut Window, cx: &mut App| {
                     this.update(cx, |this, cx| this.render_message(ix, cx))
                         .unwrap()
                 }
@@ -66,13 +67,13 @@ impl ActiveThread {
         };
 
         for message in thread.read(cx).messages().cloned().collect::<Vec<_>>() {
-            this.push_message(&message.id, message.text.clone(), cx);
+            this.push_message(&message.id, message.text.clone(), window, cx);
         }
 
         this
     }
 
-    pub fn thread(&self) -> &Model<Thread> {
+    pub fn thread(&self) -> &Entity<Thread> {
         &self.thread
     }
 
@@ -80,15 +81,15 @@ impl ActiveThread {
         self.messages.is_empty()
     }
 
-    pub fn summary(&self, cx: &AppContext) -> Option<SharedString> {
+    pub fn summary(&self, cx: &App) -> Option<SharedString> {
         self.thread.read(cx).summary()
     }
 
-    pub fn summary_or_default(&self, cx: &AppContext) -> SharedString {
+    pub fn summary_or_default(&self, cx: &App) -> SharedString {
         self.thread.read(cx).summary_or_default()
     }
 
-    pub fn cancel_last_completion(&mut self, cx: &mut AppContext) -> bool {
+    pub fn cancel_last_completion(&mut self, cx: &mut App) -> bool {
         self.last_error.take();
         self.thread
             .update(cx, |thread, _cx| thread.cancel_last_completion())
@@ -102,7 +103,13 @@ impl ActiveThread {
         self.last_error.take();
     }
 
-    fn push_message(&mut self, id: &MessageId, text: String, cx: &mut ViewContext<Self>) {
+    fn push_message(
+        &mut self,
+        id: &MessageId,
+        text: String,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let old_len = self.messages.len();
         self.messages.push(*id);
         self.list_state.splice(old_len..old_len, 1);
@@ -111,7 +118,7 @@ impl ActiveThread {
         let colors = cx.theme().colors();
         let ui_font_size = TextSize::Default.rems(cx);
         let buffer_font_size = TextSize::Small.rems(cx);
-        let mut text_style = cx.text_style();
+        let mut text_style = window.text_style();
 
         text_style.refine(&TextStyleRefinement {
             font_family: Some(theme_settings.ui_font.family.clone()),
@@ -170,12 +177,13 @@ impl ActiveThread {
             ..Default::default()
         };
 
-        let markdown = cx.new_view(|cx| {
+        let markdown = cx.new(|cx| {
             Markdown::new(
                 text,
                 markdown_style,
                 Some(self.language_registry.clone()),
                 None,
+                window,
                 cx,
             )
         });
@@ -188,9 +196,10 @@ impl ActiveThread {
 
     fn handle_thread_event(
         &mut self,
-        _: Model<Thread>,
+        _: &Entity<Thread>,
         event: &ThreadEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match event {
             ThreadEvent::ShowError(error) => {
@@ -206,7 +215,7 @@ impl ActiveThread {
             ThreadEvent::StreamedAssistantText(message_id, text) => {
                 if let Some(markdown) = self.rendered_messages_by_id.get_mut(&message_id) {
                     markdown.update(cx, |markdown, cx| {
-                        markdown.append(text, cx);
+                        markdown.append(text, window, cx);
                     });
                 }
             }
@@ -217,7 +226,7 @@ impl ActiveThread {
                     .message(*message_id)
                     .map(|message| message.text.clone())
                 {
-                    self.push_message(message_id, message_text, cx);
+                    self.push_message(message_id, message_text, window, cx);
                 }
 
                 self.thread_store
@@ -240,7 +249,7 @@ impl ActiveThread {
 
                 for tool_use in pending_tool_uses {
                     if let Some(tool) = self.tools.tool(&tool_use.name, cx) {
-                        let task = tool.run(tool_use.input, self.workspace.clone(), cx);
+                        let task = tool.run(tool_use.input, self.workspace.clone(), window, cx);
 
                         self.thread.update(cx, |thread, cx| {
                             thread.insert_tool_output(
@@ -257,7 +266,7 @@ impl ActiveThread {
         }
     }
 
-    fn render_message(&self, ix: usize, cx: &mut ViewContext<Self>) -> AnyElement {
+    fn render_message(&self, ix: usize, cx: &mut Context<Self>) -> AnyElement {
         let message_id = self.messages[ix];
         let Some(message) = self.thread.read(cx).message(message_id) else {
             return Empty.into_any();
@@ -338,7 +347,7 @@ impl ActiveThread {
 }
 
 impl Render for ActiveThread {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         v_flex()
             .size_full()
             .pt_1p5()

crates/assistant2/src/assistant.rs 🔗

@@ -24,7 +24,7 @@ use client::Client;
 use command_palette_hooks::CommandPaletteFilter;
 use feature_flags::{Assistant2FeatureFlag, FeatureFlagAppExt};
 use fs::Fs;
-use gpui::{actions, AppContext};
+use gpui::{actions, App};
 use prompt_library::PromptBuilder;
 use settings::Settings as _;
 
@@ -63,7 +63,7 @@ pub fn init(
     fs: Arc<dyn Fs>,
     client: Arc<Client>,
     prompt_builder: Arc<PromptBuilder>,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) {
     AssistantSettings::register(cx);
     assistant_panel::init(cx);
@@ -84,7 +84,7 @@ pub fn init(
     feature_gate_assistant2_actions(cx);
 }
 
-fn feature_gate_assistant2_actions(cx: &mut AppContext) {
+fn feature_gate_assistant2_actions(cx: &mut App) {
     CommandPaletteFilter::update_global(cx, |filter, _cx| {
         filter.hide_namespace(NAMESPACE);
     });

crates/assistant2/src/assistant_configuration.rs 🔗

@@ -1,7 +1,7 @@
 use std::sync::Arc;
 
 use collections::HashMap;
-use gpui::{Action, AnyView, AppContext, EventEmitter, FocusHandle, FocusableView, Subscription};
+use gpui::{AnyView, App, EventEmitter, FocusHandle, Focusable, Subscription};
 use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
 use ui::{prelude::*, ElevationIndex};
 use zed_actions::assistant::DeployPromptLibrary;
@@ -13,16 +13,17 @@ pub struct AssistantConfiguration {
 }
 
 impl AssistantConfiguration {
-    pub fn new(cx: &mut ViewContext<Self>) -> Self {
+    pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
         let focus_handle = cx.focus_handle();
 
-        let registry_subscription = cx.subscribe(
+        let registry_subscription = cx.subscribe_in(
             &LanguageModelRegistry::global(cx),
-            |this, _, event: &language_model::Event, cx| match event {
+            window,
+            |this, _, event: &language_model::Event, window, cx| match event {
                 language_model::Event::AddedProvider(provider_id) => {
                     let provider = LanguageModelRegistry::read_global(cx).provider(provider_id);
                     if let Some(provider) = provider {
-                        this.add_provider_configuration_view(&provider, cx);
+                        this.add_provider_configuration_view(&provider, window, cx);
                     }
                 }
                 language_model::Event::RemovedProvider(provider_id) => {
@@ -37,14 +38,14 @@ impl AssistantConfiguration {
             configuration_views_by_provider: HashMap::default(),
             _registry_subscription: registry_subscription,
         };
-        this.build_provider_configuration_views(cx);
+        this.build_provider_configuration_views(window, cx);
         this
     }
 
-    fn build_provider_configuration_views(&mut self, cx: &mut ViewContext<Self>) {
+    fn build_provider_configuration_views(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let providers = LanguageModelRegistry::read_global(cx).providers();
         for provider in providers {
-            self.add_provider_configuration_view(&provider, cx);
+            self.add_provider_configuration_view(&provider, window, cx);
         }
     }
 
@@ -55,16 +56,17 @@ impl AssistantConfiguration {
     fn add_provider_configuration_view(
         &mut self,
         provider: &Arc<dyn LanguageModelProvider>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
-        let configuration_view = provider.configuration_view(cx);
+        let configuration_view = provider.configuration_view(window, cx);
         self.configuration_views_by_provider
             .insert(provider.id(), configuration_view);
     }
 }
 
-impl FocusableView for AssistantConfiguration {
-    fn focus_handle(&self, _: &AppContext) -> FocusHandle {
+impl Focusable for AssistantConfiguration {
+    fn focus_handle(&self, _: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -79,7 +81,7 @@ impl AssistantConfiguration {
     fn render_provider_configuration(
         &mut self,
         provider: &Arc<dyn LanguageModelProvider>,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> impl IntoElement {
         let provider_id = provider.id().0.clone();
         let provider_name = provider.name().0.clone();
@@ -107,7 +109,7 @@ impl AssistantConfiguration {
                                 .layer(ElevationIndex::ModalSurface)
                                 .on_click(cx.listener({
                                     let provider = provider.clone();
-                                    move |_this, _event, cx| {
+                                    move |_this, _event, _window, cx| {
                                         cx.emit(AssistantConfigurationEvent::NewThread(
                                             provider.clone(),
                                         ))
@@ -135,7 +137,7 @@ impl AssistantConfiguration {
 }
 
 impl Render for AssistantConfiguration {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let providers = LanguageModelRegistry::read_global(cx).providers();
 
         v_flex()
@@ -152,9 +154,7 @@ impl Render for AssistantConfiguration {
                         .icon(IconName::Book)
                         .icon_size(IconSize::Small)
                         .icon_position(IconPosition::Start)
-                        .on_click(|_event, cx| {
-                            cx.dispatch_action(DeployPromptLibrary.boxed_clone())
-                        }),
+                        .on_click(|_event, _window, cx| cx.dispatch_action(&DeployPromptLibrary)),
                 ),
             )
             .child(

crates/assistant2/src/assistant_model_selector.rs 🔗

@@ -1,6 +1,6 @@
 use assistant_settings::AssistantSettings;
 use fs::Fs;
-use gpui::{FocusHandle, View};
+use gpui::{Entity, FocusHandle};
 use language_model::LanguageModelRegistry;
 use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
 use settings::update_settings_file;
@@ -10,7 +10,7 @@ use ui::{prelude::*, ButtonLike, PopoverMenuHandle, Tooltip};
 use crate::ToggleModelSelector;
 
 pub struct AssistantModelSelector {
-    selector: View<LanguageModelSelector>,
+    selector: Entity<LanguageModelSelector>,
     menu_handle: PopoverMenuHandle<LanguageModelSelector>,
     focus_handle: FocusHandle,
 }
@@ -20,10 +20,11 @@ impl AssistantModelSelector {
         fs: Arc<dyn Fs>,
         menu_handle: PopoverMenuHandle<LanguageModelSelector>,
         focus_handle: FocusHandle,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Self {
         Self {
-            selector: cx.new_view(|cx| {
+            selector: cx.new(|cx| {
                 let fs = fs.clone();
                 LanguageModelSelector::new(
                     move |model, cx| {
@@ -33,6 +34,7 @@ impl AssistantModelSelector {
                             move |settings, _cx| settings.set_model(model.clone()),
                         );
                     },
+                    window,
                     cx,
                 )
             }),
@@ -43,7 +45,7 @@ impl AssistantModelSelector {
 }
 
 impl Render for AssistantModelSelector {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let active_model = LanguageModelRegistry::read_global(cx).active_model();
         let focus_handle = self.focus_handle.clone();
 
@@ -79,8 +81,14 @@ impl Render for AssistantModelSelector {
                                 .size(IconSize::XSmall),
                         ),
                 )
-                .tooltip(move |cx| {
-                    Tooltip::for_action_in("Change Model", &ToggleModelSelector, &focus_handle, cx)
+                .tooltip(move |window, cx| {
+                    Tooltip::for_action_in(
+                        "Change Model",
+                        &ToggleModelSelector,
+                        &focus_handle,
+                        window,
+                        cx,
+                    )
                 }),
         )
         .with_handle(self.menu_handle.clone())

crates/assistant2/src/assistant_panel.rs 🔗

@@ -14,9 +14,8 @@ use client::zed_urls;
 use editor::Editor;
 use fs::Fs;
 use gpui::{
-    prelude::*, px, svg, Action, AnyElement, AppContext, AsyncWindowContext, Corner, EventEmitter,
-    FocusHandle, FocusableView, FontWeight, Model, Pixels, Subscription, Task, UpdateGlobal, View,
-    ViewContext, WeakView, WindowContext,
+    prelude::*, px, svg, Action, AnyElement, App, AsyncWindowContext, Corner, Entity, EventEmitter,
+    FocusHandle, Focusable, FontWeight, Pixels, Subscription, Task, UpdateGlobal, WeakEntity,
 };
 use language::LanguageRegistry;
 use language_model::{LanguageModelProviderTosView, LanguageModelRegistry};
@@ -41,38 +40,38 @@ use crate::{
     OpenPromptEditorHistory,
 };
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(
-        |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
+pub fn init(cx: &mut App) {
+    cx.observe_new(
+        |workspace: &mut Workspace, _window, _cx: &mut Context<Workspace>| {
             workspace
-                .register_action(|workspace, _: &NewThread, cx| {
+                .register_action(|workspace, _: &NewThread, window, cx| {
                     if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
-                        panel.update(cx, |panel, cx| panel.new_thread(cx));
-                        workspace.focus_panel::<AssistantPanel>(cx);
+                        panel.update(cx, |panel, cx| panel.new_thread(window, cx));
+                        workspace.focus_panel::<AssistantPanel>(window, cx);
                     }
                 })
-                .register_action(|workspace, _: &OpenHistory, cx| {
+                .register_action(|workspace, _: &OpenHistory, window, cx| {
                     if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
-                        workspace.focus_panel::<AssistantPanel>(cx);
-                        panel.update(cx, |panel, cx| panel.open_history(cx));
+                        workspace.focus_panel::<AssistantPanel>(window, cx);
+                        panel.update(cx, |panel, cx| panel.open_history(window, cx));
                     }
                 })
-                .register_action(|workspace, _: &NewPromptEditor, cx| {
+                .register_action(|workspace, _: &NewPromptEditor, window, cx| {
                     if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
-                        workspace.focus_panel::<AssistantPanel>(cx);
-                        panel.update(cx, |panel, cx| panel.new_prompt_editor(cx));
+                        workspace.focus_panel::<AssistantPanel>(window, cx);
+                        panel.update(cx, |panel, cx| panel.new_prompt_editor(window, cx));
                     }
                 })
-                .register_action(|workspace, _: &OpenPromptEditorHistory, cx| {
+                .register_action(|workspace, _: &OpenPromptEditorHistory, window, cx| {
                     if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
-                        workspace.focus_panel::<AssistantPanel>(cx);
-                        panel.update(cx, |panel, cx| panel.open_prompt_editor_history(cx));
+                        workspace.focus_panel::<AssistantPanel>(window, cx);
+                        panel.update(cx, |panel, cx| panel.open_prompt_editor_history(window, cx));
                     }
                 })
-                .register_action(|workspace, _: &OpenConfiguration, cx| {
+                .register_action(|workspace, _: &OpenConfiguration, window, cx| {
                     if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
-                        workspace.focus_panel::<AssistantPanel>(cx);
-                        panel.update(cx, |panel, cx| panel.open_configuration(cx));
+                        workspace.focus_panel::<AssistantPanel>(window, cx);
+                        panel.update(cx, |panel, cx| panel.open_configuration(window, cx));
                     }
                 });
         },
@@ -89,22 +88,22 @@ enum ActiveView {
 }
 
 pub struct AssistantPanel {
-    workspace: WeakView<Workspace>,
-    project: Model<Project>,
+    workspace: WeakEntity<Workspace>,
+    project: Entity<Project>,
     fs: Arc<dyn Fs>,
     language_registry: Arc<LanguageRegistry>,
-    thread_store: Model<ThreadStore>,
-    thread: View<ActiveThread>,
-    message_editor: View<MessageEditor>,
-    context_store: Model<assistant_context_editor::ContextStore>,
-    context_editor: Option<View<ContextEditor>>,
-    context_history: Option<View<ContextHistory>>,
-    configuration: Option<View<AssistantConfiguration>>,
+    thread_store: Entity<ThreadStore>,
+    thread: Entity<ActiveThread>,
+    message_editor: Entity<MessageEditor>,
+    context_store: Entity<assistant_context_editor::ContextStore>,
+    context_editor: Option<Entity<ContextEditor>>,
+    context_history: Option<Entity<ContextHistory>>,
+    configuration: Option<Entity<AssistantConfiguration>>,
     configuration_subscription: Option<Subscription>,
     tools: Arc<ToolWorkingSet>,
     local_timezone: UtcOffset,
     active_view: ActiveView,
-    history: View<ThreadHistory>,
+    history: Entity<ThreadHistory>,
     new_item_context_menu_handle: PopoverMenuHandle<ContextMenu>,
     open_history_context_menu_handle: PopoverMenuHandle<ContextMenu>,
     width: Option<Pixels>,
@@ -113,10 +112,10 @@ pub struct AssistantPanel {
 
 impl AssistantPanel {
     pub fn load(
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         prompt_builder: Arc<PromptBuilder>,
         cx: AsyncWindowContext,
-    ) -> Task<Result<View<Self>>> {
+    ) -> Task<Result<Entity<Self>>> {
         cx.spawn(|mut cx| async move {
             let tools = Arc::new(ToolWorkingSet::default());
             let thread_store = workspace
@@ -140,32 +139,34 @@ impl AssistantPanel {
                 })?
                 .await?;
 
-            workspace.update(&mut cx, |workspace, cx| {
-                cx.new_view(|cx| Self::new(workspace, thread_store, context_store, tools, cx))
+            workspace.update_in(&mut cx, |workspace, window, cx| {
+                cx.new(|cx| Self::new(workspace, thread_store, context_store, tools, window, cx))
             })
         })
     }
 
     fn new(
         workspace: &Workspace,
-        thread_store: Model<ThreadStore>,
-        context_store: Model<assistant_context_editor::ContextStore>,
+        thread_store: Entity<ThreadStore>,
+        context_store: Entity<assistant_context_editor::ContextStore>,
         tools: Arc<ToolWorkingSet>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let thread = thread_store.update(cx, |this, cx| this.create_thread(cx));
         let fs = workspace.app_state().fs.clone();
         let project = workspace.project().clone();
         let language_registry = project.read(cx).languages().clone();
         let workspace = workspace.weak_handle();
-        let weak_self = cx.view().downgrade();
+        let weak_self = cx.model().downgrade();
 
-        let message_editor = cx.new_view(|cx| {
+        let message_editor = cx.new(|cx| {
             MessageEditor::new(
                 fs.clone(),
                 workspace.clone(),
                 thread_store.downgrade(),
                 thread.clone(),
+                window,
                 cx,
             )
         });
@@ -177,13 +178,14 @@ impl AssistantPanel {
             fs: fs.clone(),
             language_registry: language_registry.clone(),
             thread_store: thread_store.clone(),
-            thread: cx.new_view(|cx| {
+            thread: cx.new(|cx| {
                 ActiveThread::new(
                     thread.clone(),
                     thread_store.clone(),
                     workspace,
                     language_registry,
                     tools.clone(),
+                    window,
                     cx,
                 )
             }),
@@ -198,7 +200,7 @@ impl AssistantPanel {
                 chrono::Local::now().offset().local_minus_utc(),
             )
             .unwrap(),
-            history: cx.new_view(|cx| ThreadHistory::new(weak_self, thread_store, cx)),
+            history: cx.new(|cx| ThreadHistory::new(weak_self, thread_store, cx)),
             new_item_context_menu_handle: PopoverMenuHandle::default(),
             open_history_context_menu_handle: PopoverMenuHandle::default(),
             width: None,
@@ -209,58 +211,66 @@ impl AssistantPanel {
     pub fn toggle_focus(
         workspace: &mut Workspace,
         _: &ToggleFocus,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
         let settings = AssistantSettings::get_global(cx);
         if !settings.enabled {
             return;
         }
 
-        workspace.toggle_panel_focus::<Self>(cx);
+        workspace.toggle_panel_focus::<Self>(window, cx);
     }
 
     pub(crate) fn local_timezone(&self) -> UtcOffset {
         self.local_timezone
     }
 
-    pub(crate) fn thread_store(&self) -> &Model<ThreadStore> {
+    pub(crate) fn thread_store(&self) -> &Entity<ThreadStore> {
         &self.thread_store
     }
 
-    fn cancel(&mut self, _: &editor::actions::Cancel, cx: &mut ViewContext<Self>) {
+    fn cancel(
+        &mut self,
+        _: &editor::actions::Cancel,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.thread
             .update(cx, |thread, cx| thread.cancel_last_completion(cx));
     }
 
-    fn new_thread(&mut self, cx: &mut ViewContext<Self>) {
+    fn new_thread(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let thread = self
             .thread_store
             .update(cx, |this, cx| this.create_thread(cx));
 
         self.active_view = ActiveView::Thread;
-        self.thread = cx.new_view(|cx| {
+        self.thread = cx.new(|cx| {
             ActiveThread::new(
                 thread.clone(),
                 self.thread_store.clone(),
                 self.workspace.clone(),
                 self.language_registry.clone(),
                 self.tools.clone(),
+                window,
                 cx,
             )
         });
-        self.message_editor = cx.new_view(|cx| {
+        self.message_editor = cx.new(|cx| {
             MessageEditor::new(
                 self.fs.clone(),
                 self.workspace.clone(),
                 self.thread_store.downgrade(),
                 thread,
+                window,
                 cx,
             )
         });
-        self.message_editor.focus_handle(cx).focus(cx);
+        self.message_editor.focus_handle(cx).focus(window);
     }
 
-    fn new_prompt_editor(&mut self, cx: &mut ViewContext<Self>) {
+    fn new_prompt_editor(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.active_view = ActiveView::PromptEditor;
 
         let context = self
@@ -270,25 +280,31 @@ impl AssistantPanel {
             .log_err()
             .flatten();
 
-        self.context_editor = Some(cx.new_view(|cx| {
+        self.context_editor = Some(cx.new(|cx| {
             let mut editor = ContextEditor::for_context(
                 context,
                 self.fs.clone(),
                 self.workspace.clone(),
                 self.project.clone(),
                 lsp_adapter_delegate,
+                window,
                 cx,
             );
-            editor.insert_default_prompt(cx);
+            editor.insert_default_prompt(window, cx);
             editor
         }));
 
         if let Some(context_editor) = self.context_editor.as_ref() {
-            context_editor.focus_handle(cx).focus(cx);
+            context_editor.focus_handle(cx).focus(window);
         }
     }
 
-    fn deploy_prompt_library(&mut self, _: &DeployPromptLibrary, cx: &mut ViewContext<Self>) {
+    fn deploy_prompt_library(
+        &mut self,
+        _: &DeployPromptLibrary,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         open_prompt_library(
             self.language_registry.clone(),
             Box::new(PromptLibraryInlineAssist::new(self.workspace.clone())),
@@ -304,25 +320,26 @@ impl AssistantPanel {
         .detach_and_log_err(cx);
     }
 
-    fn open_history(&mut self, cx: &mut ViewContext<Self>) {
+    fn open_history(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.active_view = ActiveView::History;
-        self.history.focus_handle(cx).focus(cx);
+        self.history.focus_handle(cx).focus(window);
         cx.notify();
     }
 
-    fn open_prompt_editor_history(&mut self, cx: &mut ViewContext<Self>) {
+    fn open_prompt_editor_history(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.active_view = ActiveView::PromptEditorHistory;
-        self.context_history = Some(cx.new_view(|cx| {
+        self.context_history = Some(cx.new(|cx| {
             ContextHistory::new(
                 self.project.clone(),
                 self.context_store.clone(),
                 self.workspace.clone(),
+                window,
                 cx,
             )
         }));
 
         if let Some(context_history) = self.context_history.as_ref() {
-            context_history.focus_handle(cx).focus(cx);
+            context_history.focus_handle(cx).focus(window);
         }
 
         cx.notify();
@@ -331,7 +348,8 @@ impl AssistantPanel {
     fn open_saved_prompt_editor(
         &mut self,
         path: PathBuf,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let context = self
             .context_store
@@ -342,16 +360,17 @@ impl AssistantPanel {
 
         let lsp_adapter_delegate = make_lsp_adapter_delegate(&project, cx).log_err().flatten();
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let context = context.await?;
-            this.update(&mut cx, |this, cx| {
-                let editor = cx.new_view(|cx| {
+            this.update_in(&mut cx, |this, window, cx| {
+                let editor = cx.new(|cx| {
                     ContextEditor::for_context(
                         context,
                         fs,
                         workspace,
                         project,
                         lsp_adapter_delegate,
+                        window,
                         cx,
                     )
                 });
@@ -367,57 +386,64 @@ impl AssistantPanel {
     pub(crate) fn open_thread(
         &mut self,
         thread_id: &ThreadId,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let open_thread_task = self
             .thread_store
             .update(cx, |this, cx| this.open_thread(thread_id, cx));
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let thread = open_thread_task.await?;
-            this.update(&mut cx, |this, cx| {
+            this.update_in(&mut cx, |this, window, cx| {
                 this.active_view = ActiveView::Thread;
-                this.thread = cx.new_view(|cx| {
+                this.thread = cx.new(|cx| {
                     ActiveThread::new(
                         thread.clone(),
                         this.thread_store.clone(),
                         this.workspace.clone(),
                         this.language_registry.clone(),
                         this.tools.clone(),
+                        window,
                         cx,
                     )
                 });
-                this.message_editor = cx.new_view(|cx| {
+                this.message_editor = cx.new(|cx| {
                     MessageEditor::new(
                         this.fs.clone(),
                         this.workspace.clone(),
                         this.thread_store.downgrade(),
                         thread,
+                        window,
                         cx,
                     )
                 });
-                this.message_editor.focus_handle(cx).focus(cx);
+                this.message_editor.focus_handle(cx).focus(window);
             })
         })
     }
 
-    pub(crate) fn open_configuration(&mut self, cx: &mut ViewContext<Self>) {
+    pub(crate) fn open_configuration(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.active_view = ActiveView::Configuration;
-        self.configuration = Some(cx.new_view(AssistantConfiguration::new));
+        self.configuration = Some(cx.new(|cx| AssistantConfiguration::new(window, cx)));
 
         if let Some(configuration) = self.configuration.as_ref() {
-            self.configuration_subscription =
-                Some(cx.subscribe(configuration, Self::handle_assistant_configuration_event));
+            self.configuration_subscription = Some(cx.subscribe_in(
+                configuration,
+                window,
+                Self::handle_assistant_configuration_event,
+            ));
 
-            configuration.focus_handle(cx).focus(cx);
+            configuration.focus_handle(cx).focus(window);
         }
     }
 
     fn handle_assistant_configuration_event(
         &mut self,
-        _view: View<AssistantConfiguration>,
+        _model: &Entity<AssistantConfiguration>,
         event: &AssistantConfigurationEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match event {
             AssistantConfigurationEvent::NewThread(provider) => {
@@ -436,24 +462,24 @@ impl AssistantPanel {
                     }
                 }
 
-                self.new_thread(cx);
+                self.new_thread(window, cx);
             }
         }
     }
 
-    pub(crate) fn active_thread(&self, cx: &AppContext) -> Model<Thread> {
+    pub(crate) fn active_thread(&self, cx: &App) -> Entity<Thread> {
         self.thread.read(cx).thread().clone()
     }
 
-    pub(crate) fn delete_thread(&mut self, thread_id: &ThreadId, cx: &mut ViewContext<Self>) {
+    pub(crate) fn delete_thread(&mut self, thread_id: &ThreadId, cx: &mut Context<Self>) {
         self.thread_store
             .update(cx, |this, cx| this.delete_thread(thread_id, cx))
             .detach_and_log_err(cx);
     }
 }
 
-impl FocusableView for AssistantPanel {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for AssistantPanel {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         match self.active_view {
             ActiveView::Thread => self.message_editor.focus_handle(cx),
             ActiveView::History => self.history.focus_handle(cx),
@@ -489,7 +515,7 @@ impl Panel for AssistantPanel {
         "AssistantPanel2"
     }
 
-    fn position(&self, cx: &WindowContext) -> DockPosition {
+    fn position(&self, _window: &Window, cx: &App) -> DockPosition {
         match AssistantSettings::get_global(cx).dock {
             AssistantDockPosition::Left => DockPosition::Left,
             AssistantDockPosition::Bottom => DockPosition::Bottom,
@@ -501,7 +527,7 @@ impl Panel for AssistantPanel {
         true
     }
 
-    fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) {
+    fn set_position(&mut self, position: DockPosition, _: &mut Window, cx: &mut Context<Self>) {
         settings::update_settings_file::<AssistantSettings>(
             self.fs.clone(),
             cx,
@@ -516,9 +542,9 @@ impl Panel for AssistantPanel {
         );
     }
 
-    fn size(&self, cx: &WindowContext) -> Pixels {
+    fn size(&self, window: &Window, cx: &App) -> Pixels {
         let settings = AssistantSettings::get_global(cx);
-        match self.position(cx) {
+        match self.position(window, cx) {
             DockPosition::Left | DockPosition::Right => {
                 self.width.unwrap_or(settings.default_width)
             }
@@ -526,21 +552,21 @@ impl Panel for AssistantPanel {
         }
     }
 
-    fn set_size(&mut self, size: Option<Pixels>, cx: &mut ViewContext<Self>) {
-        match self.position(cx) {
+    fn set_size(&mut self, size: Option<Pixels>, window: &mut Window, cx: &mut Context<Self>) {
+        match self.position(window, cx) {
             DockPosition::Left | DockPosition::Right => self.width = size,
             DockPosition::Bottom => self.height = size,
         }
         cx.notify();
     }
 
-    fn set_active(&mut self, _active: bool, _cx: &mut ViewContext<Self>) {}
+    fn set_active(&mut self, _active: bool, _window: &mut Window, _cx: &mut Context<Self>) {}
 
     fn remote_id() -> Option<proto::PanelId> {
         Some(proto::PanelId::AssistantPanel)
     }
 
-    fn icon(&self, cx: &WindowContext) -> Option<IconName> {
+    fn icon(&self, _window: &Window, cx: &App) -> Option<IconName> {
         let settings = AssistantSettings::get_global(cx);
         if !settings.enabled || !settings.button {
             return None;
@@ -549,7 +575,7 @@ impl Panel for AssistantPanel {
         Some(IconName::ZedAssistant)
     }
 
-    fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> {
+    fn icon_tooltip(&self, _window: &Window, _cx: &App) -> Option<&'static str> {
         Some("Assistant Panel")
     }
 
@@ -563,7 +589,7 @@ impl Panel for AssistantPanel {
 }
 
 impl AssistantPanel {
-    fn render_toolbar(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render_toolbar(&self, cx: &mut Context<Self>) -> impl IntoElement {
         let thread = self.thread.read(cx);
 
         let title = match self.active_view {
@@ -612,12 +638,12 @@ impl AssistantPanel {
                                 IconButton::new("new", IconName::Plus)
                                     .icon_size(IconSize::Small)
                                     .style(ButtonStyle::Subtle)
-                                    .tooltip(|cx| Tooltip::text("New…", cx)),
+                                    .tooltip(Tooltip::text("New…")),
                             )
                             .anchor(Corner::TopRight)
                             .with_handle(self.new_item_context_menu_handle.clone())
-                            .menu(move |cx| {
-                                Some(ContextMenu::build(cx, |menu, _| {
+                            .menu(move |window, cx| {
+                                Some(ContextMenu::build(window, cx, |menu, _window, _cx| {
                                     menu.action("New Thread", NewThread.boxed_clone())
                                         .action("New Prompt Editor", NewPromptEditor.boxed_clone())
                                 }))
@@ -629,12 +655,12 @@ impl AssistantPanel {
                                 IconButton::new("open-history", IconName::HistoryRerun)
                                     .icon_size(IconSize::Small)
                                     .style(ButtonStyle::Subtle)
-                                    .tooltip(|cx| Tooltip::text("History…", cx)),
+                                    .tooltip(Tooltip::text("History…")),
                             )
                             .anchor(Corner::TopRight)
                             .with_handle(self.open_history_context_menu_handle.clone())
-                            .menu(move |cx| {
-                                Some(ContextMenu::build(cx, |menu, _| {
+                            .menu(move |window, cx| {
+                                Some(ContextMenu::build(window, cx, |menu, _window, _cx| {
                                     menu.action("Thread History", OpenHistory.boxed_clone())
                                         .action(
                                             "Prompt Editor History",
@@ -647,23 +673,29 @@ impl AssistantPanel {
                         IconButton::new("configure-assistant", IconName::Settings)
                             .icon_size(IconSize::Small)
                             .style(ButtonStyle::Subtle)
-                            .tooltip(move |cx| Tooltip::text("Configure Assistant", cx))
-                            .on_click(move |_event, cx| {
-                                cx.dispatch_action(OpenConfiguration.boxed_clone());
+                            .tooltip(Tooltip::text("Configure Assistant"))
+                            .on_click(move |_event, _window, cx| {
+                                cx.dispatch_action(&OpenConfiguration);
                             }),
                     ),
             )
     }
 
-    fn render_active_thread_or_empty_state(&self, cx: &mut ViewContext<Self>) -> AnyElement {
+    fn render_active_thread_or_empty_state(
+        &self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> AnyElement {
         if self.thread.read(cx).is_empty() {
-            return self.render_thread_empty_state(cx).into_any_element();
+            return self
+                .render_thread_empty_state(window, cx)
+                .into_any_element();
         }
 
-        self.thread.clone().into_any()
+        self.thread.clone().into_any_element()
     }
 
-    fn configuration_error(&self, cx: &AppContext) -> Option<ConfigurationError> {
+    fn configuration_error(&self, cx: &App) -> Option<ConfigurationError> {
         let Some(provider) = LanguageModelRegistry::read_global(cx).active_provider() else {
             return Some(ConfigurationError::NoProvider);
         };
@@ -679,7 +711,11 @@ impl AssistantPanel {
         None
     }
 
-    fn render_thread_empty_state(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render_thread_empty_state(
+        &self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> impl IntoElement {
         let recent_threads = self
             .thread_store
             .update(cx, |this, _cx| this.recent_threads(3));
@@ -729,8 +765,8 @@ impl AssistantPanel {
                                             .icon(Some(IconName::Sliders))
                                             .icon_size(IconSize::Small)
                                             .icon_position(IconPosition::Start)
-                                            .on_click(cx.listener(|this, _, cx| {
-                                                this.open_configuration(cx);
+                                            .on_click(cx.listener(|this, _, window, cx| {
+                                                this.open_configuration(window, cx);
                                             })),
                                     ),
                                 ),
@@ -775,7 +811,7 @@ impl AssistantPanel {
                     .child(v_flex().mx_auto().w_4_5().gap_2().children(
                         recent_threads.into_iter().map(|thread| {
                             // TODO: keyboard navigation
-                            PastThread::new(thread, cx.view().downgrade(), false)
+                            PastThread::new(thread, cx.model().downgrade(), false)
                         }),
                     ))
                     .child(
@@ -786,17 +822,17 @@ impl AssistantPanel {
                                 .key_binding(KeyBinding::for_action_in(
                                     &OpenHistory,
                                     &self.focus_handle(cx),
-                                    cx,
+                                    window,
                                 ))
-                                .on_click(move |_event, cx| {
-                                    cx.dispatch_action(OpenHistory.boxed_clone());
+                                .on_click(move |_event, window, cx| {
+                                    window.dispatch_action(OpenHistory.boxed_clone(), cx);
                                 }),
                         ),
                     )
             })
     }
 
-    fn render_last_error(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
+    fn render_last_error(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
         let last_error = self.thread.read(cx).last_error()?;
 
         Some(
@@ -822,7 +858,7 @@ impl AssistantPanel {
         )
     }
 
-    fn render_payment_required_error(&self, cx: &mut ViewContext<Self>) -> AnyElement {
+    fn render_payment_required_error(&self, cx: &mut Context<Self>) -> AnyElement {
         const ERROR_MESSAGE: &str = "Free tier exceeded. Subscribe and add payment to continue using Zed LLMs. You'll be billed at cost for tokens used.";
 
         v_flex()
@@ -846,7 +882,7 @@ impl AssistantPanel {
                     .justify_end()
                     .mt_1()
                     .child(Button::new("subscribe", "Subscribe").on_click(cx.listener(
-                        |this, _, cx| {
+                        |this, _, _, cx| {
                             this.thread.update(cx, |this, _cx| {
                                 this.clear_last_error();
                             });
@@ -856,7 +892,7 @@ impl AssistantPanel {
                         },
                     )))
                     .child(Button::new("dismiss", "Dismiss").on_click(cx.listener(
-                        |this, _, cx| {
+                        |this, _, _, cx| {
                             this.thread.update(cx, |this, _cx| {
                                 this.clear_last_error();
                             });
@@ -868,7 +904,7 @@ impl AssistantPanel {
             .into_any()
     }
 
-    fn render_max_monthly_spend_reached_error(&self, cx: &mut ViewContext<Self>) -> AnyElement {
+    fn render_max_monthly_spend_reached_error(&self, cx: &mut Context<Self>) -> AnyElement {
         const ERROR_MESSAGE: &str = "You have reached your maximum monthly spend. Increase your spend limit to continue using Zed LLMs.";
 
         v_flex()
@@ -893,7 +929,7 @@ impl AssistantPanel {
                     .mt_1()
                     .child(
                         Button::new("subscribe", "Update Monthly Spend Limit").on_click(
-                            cx.listener(|this, _, cx| {
+                            cx.listener(|this, _, _, cx| {
                                 this.thread.update(cx, |this, _cx| {
                                     this.clear_last_error();
                                 });
@@ -904,7 +940,7 @@ impl AssistantPanel {
                         ),
                     )
                     .child(Button::new("dismiss", "Dismiss").on_click(cx.listener(
-                        |this, _, cx| {
+                        |this, _, _, cx| {
                             this.thread.update(cx, |this, _cx| {
                                 this.clear_last_error();
                             });
@@ -919,7 +955,7 @@ impl AssistantPanel {
     fn render_error_message(
         &self,
         error_message: &SharedString,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> AnyElement {
         v_flex()
             .gap_0p5()
@@ -945,7 +981,7 @@ impl AssistantPanel {
                     .justify_end()
                     .mt_1()
                     .child(Button::new("dismiss", "Dismiss").on_click(cx.listener(
-                        |this, _, cx| {
+                        |this, _, _, cx| {
                             this.thread.update(cx, |this, _cx| {
                                 this.clear_last_error();
                             });
@@ -959,23 +995,23 @@ impl AssistantPanel {
 }
 
 impl Render for AssistantPanel {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         v_flex()
             .key_context("AssistantPanel2")
             .justify_between()
             .size_full()
             .on_action(cx.listener(Self::cancel))
-            .on_action(cx.listener(|this, _: &NewThread, cx| {
-                this.new_thread(cx);
+            .on_action(cx.listener(|this, _: &NewThread, window, cx| {
+                this.new_thread(window, cx);
             }))
-            .on_action(cx.listener(|this, _: &OpenHistory, cx| {
-                this.open_history(cx);
+            .on_action(cx.listener(|this, _: &OpenHistory, window, cx| {
+                this.open_history(window, cx);
             }))
             .on_action(cx.listener(Self::deploy_prompt_library))
             .child(self.render_toolbar(cx))
             .map(|parent| match self.active_view {
                 ActiveView::Thread => parent
-                    .child(self.render_active_thread_or_empty_state(cx))
+                    .child(self.render_active_thread_or_empty_state(window, cx))
                     .child(
                         h_flex()
                             .border_t_1()
@@ -992,11 +1028,11 @@ impl Render for AssistantPanel {
 }
 
 struct PromptLibraryInlineAssist {
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
 }
 
 impl PromptLibraryInlineAssist {
-    pub fn new(workspace: WeakView<Workspace>) -> Self {
+    pub fn new(workspace: WeakEntity<Workspace>) -> Self {
         Self { workspace }
     }
 }
@@ -1004,21 +1040,25 @@ impl PromptLibraryInlineAssist {
 impl prompt_library::InlineAssistDelegate for PromptLibraryInlineAssist {
     fn assist(
         &self,
-        prompt_editor: &View<Editor>,
+        prompt_editor: &Entity<Editor>,
         _initial_prompt: Option<String>,
-        cx: &mut ViewContext<PromptLibrary>,
+        window: &mut Window,
+        cx: &mut Context<PromptLibrary>,
     ) {
         InlineAssistant::update_global(cx, |assistant, cx| {
-            assistant.assist(&prompt_editor, self.workspace.clone(), None, cx)
+            assistant.assist(&prompt_editor, self.workspace.clone(), None, window, cx)
         })
     }
 
     fn focus_assistant_panel(
         &self,
         workspace: &mut Workspace,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) -> bool {
-        workspace.focus_panel::<AssistantPanel>(cx).is_some()
+        workspace
+            .focus_panel::<AssistantPanel>(window, cx)
+            .is_some()
     }
 }
 
@@ -1028,8 +1068,9 @@ impl AssistantPanelDelegate for ConcreteAssistantPanelDelegate {
     fn active_context_editor(
         &self,
         workspace: &mut Workspace,
-        cx: &mut ViewContext<Workspace>,
-    ) -> Option<View<ContextEditor>> {
+        _window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Option<Entity<ContextEditor>> {
         let panel = workspace.panel::<AssistantPanel>(cx)?;
         panel.update(cx, |panel, _cx| panel.context_editor.clone())
     }
@@ -1038,21 +1079,25 @@ impl AssistantPanelDelegate for ConcreteAssistantPanelDelegate {
         &self,
         workspace: &mut Workspace,
         path: std::path::PathBuf,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) -> Task<Result<()>> {
         let Some(panel) = workspace.panel::<AssistantPanel>(cx) else {
             return Task::ready(Err(anyhow!("Assistant panel not found")));
         };
 
-        panel.update(cx, |panel, cx| panel.open_saved_prompt_editor(path, cx))
+        panel.update(cx, |panel, cx| {
+            panel.open_saved_prompt_editor(path, window, cx)
+        })
     }
 
     fn open_remote_context(
         &self,
         _workspace: &mut Workspace,
         _context_id: assistant_context_editor::ContextId,
-        _cx: &mut ViewContext<Workspace>,
-    ) -> Task<Result<View<ContextEditor>>> {
+        _window: &mut Window,
+        _cx: &mut Context<Workspace>,
+    ) -> Task<Result<Entity<ContextEditor>>> {
         Task::ready(Err(anyhow!("opening remote context not implemented")))
     }
 
@@ -1060,7 +1105,8 @@ impl AssistantPanelDelegate for ConcreteAssistantPanelDelegate {
         &self,
         _workspace: &mut Workspace,
         _creases: Vec<(String, String)>,
-        _cx: &mut ViewContext<Workspace>,
+        _window: &mut Window,
+        _cx: &mut Context<Workspace>,
     ) {
     }
 }

crates/assistant2/src/buffer_codegen.rs 🔗

@@ -6,7 +6,7 @@ use client::telemetry::Telemetry;
 use collections::HashSet;
 use editor::{Anchor, AnchorRangeExt, MultiBuffer, MultiBufferSnapshot, ToOffset as _, ToPoint};
 use futures::{channel::mpsc, future::LocalBoxFuture, join, SinkExt, Stream, StreamExt};
-use gpui::{AppContext, Context as _, EventEmitter, Model, ModelContext, Subscription, Task};
+use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Subscription, Task};
 use language::{Buffer, IndentKind, Point, TransactionId};
 use language_model::{
     LanguageModel, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
@@ -32,14 +32,14 @@ use streaming_diff::{CharOperation, LineDiff, LineOperation, StreamingDiff};
 use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
 
 pub struct BufferCodegen {
-    alternatives: Vec<Model<CodegenAlternative>>,
+    alternatives: Vec<Entity<CodegenAlternative>>,
     pub active_alternative: usize,
     seen_alternatives: HashSet<usize>,
     subscriptions: Vec<Subscription>,
-    buffer: Model<MultiBuffer>,
+    buffer: Entity<MultiBuffer>,
     range: Range<Anchor>,
     initial_transaction_id: Option<TransactionId>,
-    context_store: Model<ContextStore>,
+    context_store: Entity<ContextStore>,
     telemetry: Arc<Telemetry>,
     builder: Arc<PromptBuilder>,
     pub is_insertion: bool,
@@ -47,15 +47,15 @@ pub struct BufferCodegen {
 
 impl BufferCodegen {
     pub fn new(
-        buffer: Model<MultiBuffer>,
+        buffer: Entity<MultiBuffer>,
         range: Range<Anchor>,
         initial_transaction_id: Option<TransactionId>,
-        context_store: Model<ContextStore>,
+        context_store: Entity<ContextStore>,
         telemetry: Arc<Telemetry>,
         builder: Arc<PromptBuilder>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Self {
-        let codegen = cx.new_model(|cx| {
+        let codegen = cx.new(|cx| {
             CodegenAlternative::new(
                 buffer.clone(),
                 range.clone(),
@@ -83,7 +83,7 @@ impl BufferCodegen {
         this
     }
 
-    fn subscribe_to_alternative(&mut self, cx: &mut ModelContext<Self>) {
+    fn subscribe_to_alternative(&mut self, cx: &mut Context<Self>) {
         let codegen = self.active_alternative().clone();
         self.subscriptions.clear();
         self.subscriptions
@@ -92,22 +92,22 @@ impl BufferCodegen {
             .push(cx.subscribe(&codegen, |_, _, event, cx| cx.emit(*event)));
     }
 
-    pub fn active_alternative(&self) -> &Model<CodegenAlternative> {
+    pub fn active_alternative(&self) -> &Entity<CodegenAlternative> {
         &self.alternatives[self.active_alternative]
     }
 
-    pub fn status<'a>(&self, cx: &'a AppContext) -> &'a CodegenStatus {
+    pub fn status<'a>(&self, cx: &'a App) -> &'a CodegenStatus {
         &self.active_alternative().read(cx).status
     }
 
-    pub fn alternative_count(&self, cx: &AppContext) -> usize {
+    pub fn alternative_count(&self, cx: &App) -> usize {
         LanguageModelRegistry::read_global(cx)
             .inline_alternative_models()
             .len()
             + 1
     }
 
-    pub fn cycle_prev(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn cycle_prev(&mut self, cx: &mut Context<Self>) {
         let next_active_ix = if self.active_alternative == 0 {
             self.alternatives.len() - 1
         } else {
@@ -116,12 +116,12 @@ impl BufferCodegen {
         self.activate(next_active_ix, cx);
     }
 
-    pub fn cycle_next(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn cycle_next(&mut self, cx: &mut Context<Self>) {
         let next_active_ix = (self.active_alternative + 1) % self.alternatives.len();
         self.activate(next_active_ix, cx);
     }
 
-    fn activate(&mut self, index: usize, cx: &mut ModelContext<Self>) {
+    fn activate(&mut self, index: usize, cx: &mut Context<Self>) {
         self.active_alternative()
             .update(cx, |codegen, cx| codegen.set_active(false, cx));
         self.seen_alternatives.insert(index);
@@ -132,7 +132,7 @@ impl BufferCodegen {
         cx.notify();
     }
 
-    pub fn start(&mut self, user_prompt: String, cx: &mut ModelContext<Self>) -> Result<()> {
+    pub fn start(&mut self, user_prompt: String, cx: &mut Context<Self>) -> Result<()> {
         let alternative_models = LanguageModelRegistry::read_global(cx)
             .inline_alternative_models()
             .to_vec();
@@ -143,7 +143,7 @@ impl BufferCodegen {
         self.alternatives.truncate(1);
 
         for _ in 0..alternative_models.len() {
-            self.alternatives.push(cx.new_model(|cx| {
+            self.alternatives.push(cx.new(|cx| {
                 CodegenAlternative::new(
                     self.buffer.clone(),
                     self.range.clone(),
@@ -172,13 +172,13 @@ impl BufferCodegen {
         Ok(())
     }
 
-    pub fn stop(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn stop(&mut self, cx: &mut Context<Self>) {
         for codegen in &self.alternatives {
             codegen.update(cx, |codegen, cx| codegen.stop(cx));
         }
     }
 
-    pub fn undo(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn undo(&mut self, cx: &mut Context<Self>) {
         self.active_alternative()
             .update(cx, |codegen, cx| codegen.undo(cx));
 
@@ -190,27 +190,27 @@ impl BufferCodegen {
         });
     }
 
-    pub fn buffer(&self, cx: &AppContext) -> Model<MultiBuffer> {
+    pub fn buffer(&self, cx: &App) -> Entity<MultiBuffer> {
         self.active_alternative().read(cx).buffer.clone()
     }
 
-    pub fn old_buffer(&self, cx: &AppContext) -> Model<Buffer> {
+    pub fn old_buffer(&self, cx: &App) -> Entity<Buffer> {
         self.active_alternative().read(cx).old_buffer.clone()
     }
 
-    pub fn snapshot(&self, cx: &AppContext) -> MultiBufferSnapshot {
+    pub fn snapshot(&self, cx: &App) -> MultiBufferSnapshot {
         self.active_alternative().read(cx).snapshot.clone()
     }
 
-    pub fn edit_position(&self, cx: &AppContext) -> Option<Anchor> {
+    pub fn edit_position(&self, cx: &App) -> Option<Anchor> {
         self.active_alternative().read(cx).edit_position
     }
 
-    pub fn diff<'a>(&self, cx: &'a AppContext) -> &'a Diff {
+    pub fn diff<'a>(&self, cx: &'a App) -> &'a Diff {
         &self.active_alternative().read(cx).diff
     }
 
-    pub fn last_equal_ranges<'a>(&self, cx: &'a AppContext) -> &'a [Range<Anchor>] {
+    pub fn last_equal_ranges<'a>(&self, cx: &'a App) -> &'a [Range<Anchor>] {
         self.active_alternative().read(cx).last_equal_ranges()
     }
 }
@@ -218,8 +218,8 @@ impl BufferCodegen {
 impl EventEmitter<CodegenEvent> for BufferCodegen {}
 
 pub struct CodegenAlternative {
-    buffer: Model<MultiBuffer>,
-    old_buffer: Model<Buffer>,
+    buffer: Entity<MultiBuffer>,
+    old_buffer: Entity<Buffer>,
     snapshot: MultiBufferSnapshot,
     edit_position: Option<Anchor>,
     range: Range<Anchor>,
@@ -228,7 +228,7 @@ pub struct CodegenAlternative {
     status: CodegenStatus,
     generation: Task<()>,
     diff: Diff,
-    context_store: Option<Model<ContextStore>>,
+    context_store: Option<Entity<ContextStore>>,
     telemetry: Option<Arc<Telemetry>>,
     _subscription: gpui::Subscription,
     builder: Arc<PromptBuilder>,
@@ -245,13 +245,13 @@ impl EventEmitter<CodegenEvent> for CodegenAlternative {}
 
 impl CodegenAlternative {
     pub fn new(
-        buffer: Model<MultiBuffer>,
+        buffer: Entity<MultiBuffer>,
         range: Range<Anchor>,
         active: bool,
-        context_store: Option<Model<ContextStore>>,
+        context_store: Option<Entity<ContextStore>>,
         telemetry: Option<Arc<Telemetry>>,
         builder: Arc<PromptBuilder>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Self {
         let snapshot = buffer.read(cx).snapshot(cx);
 
@@ -259,7 +259,7 @@ impl CodegenAlternative {
             .range_to_buffer_ranges(range.clone())
             .pop()
             .unwrap();
-        let old_buffer = cx.new_model(|cx| {
+        let old_buffer = cx.new(|cx| {
             let text = old_buffer.as_rope().clone();
             let line_ending = old_buffer.line_ending();
             let language = old_buffer.language().cloned();
@@ -303,7 +303,7 @@ impl CodegenAlternative {
         }
     }
 
-    pub fn set_active(&mut self, active: bool, cx: &mut ModelContext<Self>) {
+    pub fn set_active(&mut self, active: bool, cx: &mut Context<Self>) {
         if active != self.active {
             self.active = active;
 
@@ -327,9 +327,9 @@ impl CodegenAlternative {
 
     fn handle_buffer_event(
         &mut self,
-        _buffer: Model<MultiBuffer>,
+        _buffer: Entity<MultiBuffer>,
         event: &multi_buffer::Event,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if let multi_buffer::Event::TransactionUndone { transaction_id } = event {
             if self.transformation_transaction_id == Some(*transaction_id) {
@@ -348,7 +348,7 @@ impl CodegenAlternative {
         &mut self,
         user_prompt: String,
         model: Arc<dyn LanguageModel>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Result<()> {
         if let Some(transformation_transaction_id) = self.transformation_transaction_id.take() {
             self.buffer.update(cx, |buffer, cx| {
@@ -375,11 +375,7 @@ impl CodegenAlternative {
         Ok(())
     }
 
-    fn build_request(
-        &self,
-        user_prompt: String,
-        cx: &mut AppContext,
-    ) -> Result<LanguageModelRequest> {
+    fn build_request(&self, user_prompt: String, cx: &mut App) -> Result<LanguageModelRequest> {
         let buffer = self.buffer.read(cx).snapshot(cx);
         let language = buffer.language_at(self.range.start);
         let language_name = if let Some(language) = language.as_ref() {
@@ -438,7 +434,7 @@ impl CodegenAlternative {
         model_provider_id: String,
         model_api_key: Option<String>,
         stream: impl 'static + Future<Output = Result<LanguageModelTextStream>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let start_time = Instant::now();
         let snapshot = self.snapshot.clone();
@@ -696,7 +692,7 @@ impl CodegenAlternative {
         cx.notify();
     }
 
-    pub fn stop(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn stop(&mut self, cx: &mut Context<Self>) {
         self.last_equal_ranges.clear();
         if self.diff.is_empty() {
             self.status = CodegenStatus::Idle;
@@ -708,7 +704,7 @@ impl CodegenAlternative {
         cx.notify();
     }
 
-    pub fn undo(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn undo(&mut self, cx: &mut Context<Self>) {
         self.buffer.update(cx, |buffer, cx| {
             if let Some(transaction_id) = self.transformation_transaction_id.take() {
                 buffer.undo_transaction(transaction_id, cx);
@@ -720,7 +716,7 @@ impl CodegenAlternative {
     fn apply_edits(
         &mut self,
         edits: impl IntoIterator<Item = (Range<Anchor>, String)>,
-        cx: &mut ModelContext<CodegenAlternative>,
+        cx: &mut Context<CodegenAlternative>,
     ) {
         let transaction = self.buffer.update(cx, |buffer, cx| {
             // Avoid grouping assistant edits with user edits.
@@ -747,7 +743,7 @@ impl CodegenAlternative {
     fn reapply_line_based_diff(
         &mut self,
         line_operations: impl IntoIterator<Item = LineOperation>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let old_snapshot = self.snapshot.clone();
         let old_range = self.range.to_point(&old_snapshot);
@@ -803,7 +799,7 @@ impl CodegenAlternative {
         }
     }
 
-    fn reapply_batch_diff(&mut self, cx: &mut ModelContext<Self>) -> Task<()> {
+    fn reapply_batch_diff(&mut self, cx: &mut Context<Self>) -> Task<()> {
         let old_snapshot = self.snapshot.clone();
         let old_range = self.range.to_point(&old_snapshot);
         let new_snapshot = self.buffer.read(cx).snapshot(cx);
@@ -1081,15 +1077,14 @@ mod tests {
                 }
             }
         "};
-        let buffer =
-            cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
+        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
         let range = buffer.read_with(cx, |buffer, cx| {
             let snapshot = buffer.snapshot(cx);
             snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(4, 5))
         });
         let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
-        let codegen = cx.new_model(|cx| {
+        let codegen = cx.new(|cx| {
             CodegenAlternative::new(
                 buffer.clone(),
                 range.clone(),
@@ -1146,15 +1141,14 @@ mod tests {
                 le
             }
         "};
-        let buffer =
-            cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
+        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
         let range = buffer.read_with(cx, |buffer, cx| {
             let snapshot = buffer.snapshot(cx);
             snapshot.anchor_before(Point::new(1, 6))..snapshot.anchor_after(Point::new(1, 6))
         });
         let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
-        let codegen = cx.new_model(|cx| {
+        let codegen = cx.new(|cx| {
             CodegenAlternative::new(
                 buffer.clone(),
                 range.clone(),
@@ -1214,15 +1208,14 @@ mod tests {
             "  \n",
             "}\n" //
         );
-        let buffer =
-            cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
+        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
         let range = buffer.read_with(cx, |buffer, cx| {
             let snapshot = buffer.snapshot(cx);
             snapshot.anchor_before(Point::new(1, 2))..snapshot.anchor_after(Point::new(1, 2))
         });
         let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
-        let codegen = cx.new_model(|cx| {
+        let codegen = cx.new(|cx| {
             CodegenAlternative::new(
                 buffer.clone(),
                 range.clone(),
@@ -1282,14 +1275,14 @@ mod tests {
             \t}
             }
         "};
-        let buffer = cx.new_model(|cx| Buffer::local(text, cx));
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
+        let buffer = cx.new(|cx| Buffer::local(text, cx));
+        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
         let range = buffer.read_with(cx, |buffer, cx| {
             let snapshot = buffer.snapshot(cx);
             snapshot.anchor_before(Point::new(0, 0))..snapshot.anchor_after(Point::new(4, 2))
         });
         let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
-        let codegen = cx.new_model(|cx| {
+        let codegen = cx.new(|cx| {
             CodegenAlternative::new(
                 buffer.clone(),
                 range.clone(),
@@ -1337,15 +1330,14 @@ mod tests {
                 let x = 0;
             }
         "};
-        let buffer =
-            cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
+        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
         let range = buffer.read_with(cx, |buffer, cx| {
             let snapshot = buffer.snapshot(cx);
             snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(1, 14))
         });
         let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
-        let codegen = cx.new_model(|cx| {
+        let codegen = cx.new(|cx| {
             CodegenAlternative::new(
                 buffer.clone(),
                 range.clone(),
@@ -1432,7 +1424,7 @@ mod tests {
     }
 
     fn simulate_response_stream(
-        codegen: Model<CodegenAlternative>,
+        codegen: Entity<CodegenAlternative>,
         cx: &mut TestAppContext,
     ) -> mpsc::UnboundedSender<String> {
         let (chunks_tx, chunks_rx) = mpsc::unbounded();

crates/assistant2/src/context.rs 🔗

@@ -2,7 +2,7 @@ use std::path::Path;
 use std::rc::Rc;
 
 use file_icons::FileIcons;
-use gpui::{AppContext, Model, SharedString};
+use gpui::{App, Entity, SharedString};
 use language::Buffer;
 use language_model::{LanguageModelRequestMessage, MessageContent};
 use serde::{Deserialize, Serialize};
@@ -63,14 +63,14 @@ impl ContextKind {
 }
 
 #[derive(Debug)]
-pub enum Context {
+pub enum AssistantContext {
     File(FileContext),
     Directory(DirectoryContext),
     FetchedUrl(FetchedUrlContext),
     Thread(ThreadContext),
 }
 
-impl Context {
+impl AssistantContext {
     pub fn id(&self) -> ContextId {
         match self {
             Self::File(file) => file.id,
@@ -107,7 +107,7 @@ pub struct FetchedUrlContext {
 #[derive(Debug)]
 pub struct ThreadContext {
     pub id: ContextId,
-    pub thread: Model<Thread>,
+    pub thread: Entity<Thread>,
     pub text: SharedString,
 }
 
@@ -117,13 +117,13 @@ pub struct ThreadContext {
 #[derive(Debug, Clone)]
 pub struct ContextBuffer {
     pub id: BufferId,
-    pub buffer: Model<Buffer>,
+    pub buffer: Entity<Buffer>,
     pub version: clock::Global,
     pub text: SharedString,
 }
 
-impl Context {
-    pub fn snapshot(&self, cx: &AppContext) -> Option<ContextSnapshot> {
+impl AssistantContext {
+    pub fn snapshot(&self, cx: &App) -> Option<ContextSnapshot> {
         match &self {
             Self::File(file_context) => file_context.snapshot(cx),
             Self::Directory(directory_context) => Some(directory_context.snapshot()),
@@ -134,7 +134,7 @@ impl Context {
 }
 
 impl FileContext {
-    pub fn snapshot(&self, cx: &AppContext) -> Option<ContextSnapshot> {
+    pub fn snapshot(&self, cx: &App) -> Option<ContextSnapshot> {
         let buffer = self.context_buffer.buffer.read(cx);
         let path = buffer_path_log_err(buffer)?;
         let full_path: SharedString = path.to_string_lossy().into_owned().into();
@@ -221,7 +221,7 @@ impl FetchedUrlContext {
 }
 
 impl ThreadContext {
-    pub fn snapshot(&self, cx: &AppContext) -> ContextSnapshot {
+    pub fn snapshot(&self, cx: &App) -> ContextSnapshot {
         let thread = self.thread.read(cx);
         ContextSnapshot {
             id: self.id,

crates/assistant2/src/context_picker.rs 🔗

@@ -9,10 +9,7 @@ use std::sync::Arc;
 use anyhow::{anyhow, Result};
 use editor::Editor;
 use file_context_picker::render_file_context_entry;
-use gpui::{
-    AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Task, View, WeakModel,
-    WeakView,
-};
+use gpui::{App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Task, WeakEntity};
 use project::ProjectPath;
 use thread_context_picker::{render_thread_context_entry, ThreadContextEntry};
 use ui::{prelude::*, ContextMenu, ContextMenuEntry, ContextMenuItem};
@@ -35,33 +32,38 @@ pub enum ConfirmBehavior {
 
 #[derive(Debug, Clone)]
 enum ContextPickerMode {
-    Default(View<ContextMenu>),
-    File(View<FileContextPicker>),
-    Directory(View<DirectoryContextPicker>),
-    Fetch(View<FetchContextPicker>),
-    Thread(View<ThreadContextPicker>),
+    Default(Entity<ContextMenu>),
+    File(Entity<FileContextPicker>),
+    Directory(Entity<DirectoryContextPicker>),
+    Fetch(Entity<FetchContextPicker>),
+    Thread(Entity<ThreadContextPicker>),
 }
 
 pub(super) struct ContextPicker {
     mode: ContextPickerMode,
-    workspace: WeakView<Workspace>,
-    editor: WeakView<Editor>,
-    context_store: WeakModel<ContextStore>,
-    thread_store: Option<WeakModel<ThreadStore>>,
+    workspace: WeakEntity<Workspace>,
+    editor: WeakEntity<Editor>,
+    context_store: WeakEntity<ContextStore>,
+    thread_store: Option<WeakEntity<ThreadStore>>,
     confirm_behavior: ConfirmBehavior,
 }
 
 impl ContextPicker {
     pub fn new(
-        workspace: WeakView<Workspace>,
-        thread_store: Option<WeakModel<ThreadStore>>,
-        context_store: WeakModel<ContextStore>,
-        editor: WeakView<Editor>,
+        workspace: WeakEntity<Workspace>,
+        thread_store: Option<WeakEntity<ThreadStore>>,
+        context_store: WeakEntity<ContextStore>,
+        editor: WeakEntity<Editor>,
         confirm_behavior: ConfirmBehavior,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         ContextPicker {
-            mode: ContextPickerMode::Default(ContextMenu::build(cx, |menu, _cx| menu)),
+            mode: ContextPickerMode::Default(ContextMenu::build(
+                window,
+                cx,
+                |menu, _window, _cx| menu,
+            )),
             workspace,
             context_store,
             thread_store,
@@ -70,15 +72,15 @@ impl ContextPicker {
         }
     }
 
-    pub fn init(&mut self, cx: &mut ViewContext<Self>) {
-        self.mode = ContextPickerMode::Default(self.build_menu(cx));
+    pub fn init(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        self.mode = ContextPickerMode::Default(self.build_menu(window, cx));
         cx.notify();
     }
 
-    fn build_menu(&mut self, cx: &mut ViewContext<Self>) -> View<ContextMenu> {
-        let context_picker = cx.view().clone();
+    fn build_menu(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Entity<ContextMenu> {
+        let context_picker = cx.model().clone();
 
-        let menu = ContextMenu::build(cx, move |menu, cx| {
+        let menu = ContextMenu::build(window, cx, move |menu, _window, cx| {
             let recent = self.recent_entries(cx);
             let has_recent = !recent.is_empty();
             let recent_entries = recent
@@ -97,7 +99,7 @@ impl ContextPicker {
 
             let menu = menu
                 .when(has_recent, |menu| {
-                    menu.custom_row(|_| {
+                    menu.custom_row(|_, _| {
                         div()
                             .mb_1()
                             .child(
@@ -117,8 +119,8 @@ impl ContextPicker {
                         .icon(kind.icon())
                         .icon_size(IconSize::XSmall)
                         .icon_color(Color::Muted)
-                        .handler(move |cx| {
-                            context_picker.update(cx, |this, cx| this.select_kind(kind, cx))
+                        .handler(move |window, cx| {
+                            context_picker.update(cx, |this, cx| this.select_kind(kind, window, cx))
                         })
                 }));
 
@@ -141,52 +143,56 @@ impl ContextPicker {
         self.thread_store.is_some()
     }
 
-    fn select_kind(&mut self, kind: ContextKind, cx: &mut ViewContext<Self>) {
-        let context_picker = cx.view().downgrade();
+    fn select_kind(&mut self, kind: ContextKind, window: &mut Window, cx: &mut Context<Self>) {
+        let context_picker = cx.model().downgrade();
 
         match kind {
             ContextKind::File => {
-                self.mode = ContextPickerMode::File(cx.new_view(|cx| {
+                self.mode = ContextPickerMode::File(cx.new(|cx| {
                     FileContextPicker::new(
                         context_picker.clone(),
                         self.workspace.clone(),
                         self.editor.clone(),
                         self.context_store.clone(),
                         self.confirm_behavior,
+                        window,
                         cx,
                     )
                 }));
             }
             ContextKind::Directory => {
-                self.mode = ContextPickerMode::Directory(cx.new_view(|cx| {
+                self.mode = ContextPickerMode::Directory(cx.new(|cx| {
                     DirectoryContextPicker::new(
                         context_picker.clone(),
                         self.workspace.clone(),
                         self.context_store.clone(),
                         self.confirm_behavior,
+                        window,
                         cx,
                     )
                 }));
             }
             ContextKind::FetchedUrl => {
-                self.mode = ContextPickerMode::Fetch(cx.new_view(|cx| {
+                self.mode = ContextPickerMode::Fetch(cx.new(|cx| {
                     FetchContextPicker::new(
                         context_picker.clone(),
                         self.workspace.clone(),
                         self.context_store.clone(),
                         self.confirm_behavior,
+                        window,
                         cx,
                     )
                 }));
             }
             ContextKind::Thread => {
                 if let Some(thread_store) = self.thread_store.as_ref() {
-                    self.mode = ContextPickerMode::Thread(cx.new_view(|cx| {
+                    self.mode = ContextPickerMode::Thread(cx.new(|cx| {
                         ThreadContextPicker::new(
                             thread_store.clone(),
                             context_picker.clone(),
                             self.context_store.clone(),
                             self.confirm_behavior,
+                            window,
                             cx,
                         )
                     }));
@@ -195,12 +201,12 @@ impl ContextPicker {
         }
 
         cx.notify();
-        cx.focus_self();
+        cx.focus_self(window);
     }
 
     fn recent_menu_item(
         &self,
-        context_picker: View<ContextPicker>,
+        context_picker: Entity<ContextPicker>,
         ix: usize,
         entry: RecentEntry,
     ) -> ContextMenuItem {
@@ -213,7 +219,7 @@ impl ContextPicker {
                 let path = project_path.path.clone();
 
                 ContextMenuItem::custom_entry(
-                    move |cx| {
+                    move |_window, cx| {
                         render_file_context_entry(
                             ElementId::NamedInteger("ctx-recent".into(), ix),
                             &path,
@@ -223,9 +229,9 @@ impl ContextPicker {
                         )
                         .into_any()
                     },
-                    move |cx| {
+                    move |window, cx| {
                         context_picker.update(cx, |this, cx| {
-                            this.add_recent_file(project_path.clone(), cx);
+                            this.add_recent_file(project_path.clone(), window, cx);
                         })
                     },
                 )
@@ -235,11 +241,11 @@ impl ContextPicker {
                 let view_thread = thread.clone();
 
                 ContextMenuItem::custom_entry(
-                    move |cx| {
+                    move |_window, cx| {
                         render_thread_context_entry(&view_thread, context_store.clone(), cx)
                             .into_any()
                     },
-                    move |cx| {
+                    move |_window, cx| {
                         context_picker.update(cx, |this, cx| {
                             this.add_recent_thread(thread.clone(), cx)
                                 .detach_and_log_err(cx);
@@ -250,7 +256,12 @@ impl ContextPicker {
         }
     }
 
-    fn add_recent_file(&self, project_path: ProjectPath, cx: &mut ViewContext<Self>) {
+    fn add_recent_file(
+        &self,
+        project_path: ProjectPath,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let Some(context_store) = self.context_store.upgrade() else {
             return;
         };
@@ -259,8 +270,10 @@ impl ContextPicker {
             context_store.add_file_from_path(project_path.clone(), cx)
         });
 
-        cx.spawn(|_, mut cx| async move { task.await.notify_async_err(&mut cx) })
-            .detach();
+        cx.spawn_in(window, |_, mut cx| async move {
+            task.await.notify_async_err(&mut cx)
+        })
+        .detach();
 
         cx.notify();
     }
@@ -268,7 +281,7 @@ impl ContextPicker {
     fn add_recent_thread(
         &self,
         thread: ThreadContextEntry,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let Some(context_store) = self.context_store.upgrade() else {
             return Task::ready(Err(anyhow!("context store not available")));
@@ -293,7 +306,7 @@ impl ContextPicker {
         })
     }
 
-    fn recent_entries(&self, cx: &mut WindowContext) -> Vec<RecentEntry> {
+    fn recent_entries(&self, cx: &mut App) -> Vec<RecentEntry> {
         let Some(workspace) = self.workspace.upgrade().map(|w| w.read(cx)) else {
             return vec![];
         };
@@ -363,7 +376,7 @@ impl ContextPicker {
         recent
     }
 
-    fn active_singleton_buffer_path(workspace: &Workspace, cx: &AppContext) -> Option<PathBuf> {
+    fn active_singleton_buffer_path(workspace: &Workspace, cx: &App) -> Option<PathBuf> {
         let active_item = workspace.active_item(cx)?;
 
         let editor = active_item.to_any().downcast::<Editor>().ok()?.read(cx);
@@ -376,8 +389,8 @@ impl ContextPicker {
 
 impl EventEmitter<DismissEvent> for ContextPicker {}
 
-impl FocusableView for ContextPicker {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for ContextPicker {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         match &self.mode {
             ContextPickerMode::Default(menu) => menu.focus_handle(cx),
             ContextPickerMode::File(file_picker) => file_picker.focus_handle(cx),
@@ -389,7 +402,7 @@ impl FocusableView for ContextPicker {
 }
 
 impl Render for ContextPicker {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         v_flex()
             .w(px(400.))
             .min_w(px(400.))

crates/assistant2/src/context_picker/directory_context_picker.rs 🔗

@@ -3,7 +3,7 @@ use std::sync::atomic::AtomicBool;
 use std::sync::Arc;
 
 use fuzzy::PathMatch;
-use gpui::{AppContext, DismissEvent, FocusHandle, FocusableView, Task, View, WeakModel, WeakView};
+use gpui::{App, DismissEvent, Entity, FocusHandle, Focusable, Task, WeakEntity};
 use picker::{Picker, PickerDelegate};
 use project::{PathMatchCandidateSet, ProjectPath, WorktreeId};
 use ui::{prelude::*, ListItem};
@@ -14,16 +14,17 @@ use crate::context_picker::{ConfirmBehavior, ContextPicker};
 use crate::context_store::ContextStore;
 
 pub struct DirectoryContextPicker {
-    picker: View<Picker<DirectoryContextPickerDelegate>>,
+    picker: Entity<Picker<DirectoryContextPickerDelegate>>,
 }
 
 impl DirectoryContextPicker {
     pub fn new(
-        context_picker: WeakView<ContextPicker>,
-        workspace: WeakView<Workspace>,
-        context_store: WeakModel<ContextStore>,
+        context_picker: WeakEntity<ContextPicker>,
+        workspace: WeakEntity<Workspace>,
+        context_store: WeakEntity<ContextStore>,
         confirm_behavior: ConfirmBehavior,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let delegate = DirectoryContextPickerDelegate::new(
             context_picker,
@@ -31,28 +32,28 @@ impl DirectoryContextPicker {
             context_store,
             confirm_behavior,
         );
-        let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
+        let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
 
         Self { picker }
     }
 }
 
-impl FocusableView for DirectoryContextPicker {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for DirectoryContextPicker {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
 
 impl Render for DirectoryContextPicker {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         self.picker.clone()
     }
 }
 
 pub struct DirectoryContextPickerDelegate {
-    context_picker: WeakView<ContextPicker>,
-    workspace: WeakView<Workspace>,
-    context_store: WeakModel<ContextStore>,
+    context_picker: WeakEntity<ContextPicker>,
+    workspace: WeakEntity<Workspace>,
+    context_store: WeakEntity<ContextStore>,
     confirm_behavior: ConfirmBehavior,
     matches: Vec<PathMatch>,
     selected_index: usize,
@@ -60,9 +61,9 @@ pub struct DirectoryContextPickerDelegate {
 
 impl DirectoryContextPickerDelegate {
     pub fn new(
-        context_picker: WeakView<ContextPicker>,
-        workspace: WeakView<Workspace>,
-        context_store: WeakModel<ContextStore>,
+        context_picker: WeakEntity<ContextPicker>,
+        workspace: WeakEntity<Workspace>,
+        context_store: WeakEntity<ContextStore>,
         confirm_behavior: ConfirmBehavior,
     ) -> Self {
         Self {
@@ -79,8 +80,8 @@ impl DirectoryContextPickerDelegate {
         &mut self,
         query: String,
         cancellation_flag: Arc<AtomicBool>,
-        workspace: &View<Workspace>,
-        cx: &mut ViewContext<Picker<Self>>,
+        workspace: &Entity<Workspace>,
+        cx: &mut Context<Picker<Self>>,
     ) -> Task<Vec<PathMatch>> {
         if query.is_empty() {
             let workspace = workspace.read(cx);
@@ -146,15 +147,25 @@ impl PickerDelegate for DirectoryContextPickerDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, _cx: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
+    ) {
         self.selected_index = ix;
     }
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Search folders…".into()
     }
 
-    fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
+    fn update_matches(
+        &mut self,
+        query: String,
+        _window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Task<()> {
         let Some(workspace) = self.workspace.upgrade() else {
             return Task::ready(());
         };
@@ -173,7 +184,7 @@ impl PickerDelegate for DirectoryContextPickerDelegate {
         })
     }
 
-    fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
         let Some(mat) = self.matches.get(self.selected_index) else {
             return;
         };
@@ -194,19 +205,19 @@ impl PickerDelegate for DirectoryContextPickerDelegate {
         };
 
         let confirm_behavior = self.confirm_behavior;
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             match task.await.notify_async_err(&mut cx) {
                 None => anyhow::Ok(()),
-                Some(()) => this.update(&mut cx, |this, cx| match confirm_behavior {
+                Some(()) => this.update_in(&mut cx, |this, window, cx| match confirm_behavior {
                     ConfirmBehavior::KeepOpen => {}
-                    ConfirmBehavior::Close => this.delegate.dismissed(cx),
+                    ConfirmBehavior::Close => this.delegate.dismissed(window, cx),
                 }),
             }
         })
         .detach_and_log_err(cx);
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+    fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.context_picker
             .update(cx, |_, cx| {
                 cx.emit(DismissEvent);
@@ -218,7 +229,8 @@ impl PickerDelegate for DirectoryContextPickerDelegate {
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        _window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let path_match = &self.matches[ix];
         let directory_name = path_match.path.to_string_lossy().to_string();

crates/assistant2/src/context_picker/fetch_context_picker.rs 🔗

@@ -4,27 +4,28 @@ use std::sync::Arc;
 
 use anyhow::{bail, Context as _, Result};
 use futures::AsyncReadExt as _;
-use gpui::{AppContext, DismissEvent, FocusHandle, FocusableView, Task, View, WeakModel, WeakView};
+use gpui::{App, DismissEvent, Entity, FocusHandle, Focusable, Task, WeakEntity};
 use html_to_markdown::{convert_html_to_markdown, markdown, TagHandler};
 use http_client::{AsyncBody, HttpClientWithUrl};
 use picker::{Picker, PickerDelegate};
-use ui::{prelude::*, ListItem, ViewContext};
+use ui::{prelude::*, Context, ListItem, Window};
 use workspace::Workspace;
 
 use crate::context_picker::{ConfirmBehavior, ContextPicker};
 use crate::context_store::ContextStore;
 
 pub struct FetchContextPicker {
-    picker: View<Picker<FetchContextPickerDelegate>>,
+    picker: Entity<Picker<FetchContextPickerDelegate>>,
 }
 
 impl FetchContextPicker {
     pub fn new(
-        context_picker: WeakView<ContextPicker>,
-        workspace: WeakView<Workspace>,
-        context_store: WeakModel<ContextStore>,
+        context_picker: WeakEntity<ContextPicker>,
+        workspace: WeakEntity<Workspace>,
+        context_store: WeakEntity<ContextStore>,
         confirm_behavior: ConfirmBehavior,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let delegate = FetchContextPickerDelegate::new(
             context_picker,
@@ -32,20 +33,20 @@ impl FetchContextPicker {
             context_store,
             confirm_behavior,
         );
-        let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
+        let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
 
         Self { picker }
     }
 }
 
-impl FocusableView for FetchContextPicker {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for FetchContextPicker {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
 
 impl Render for FetchContextPicker {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         self.picker.clone()
     }
 }
@@ -58,18 +59,18 @@ enum ContentType {
 }
 
 pub struct FetchContextPickerDelegate {
-    context_picker: WeakView<ContextPicker>,
-    workspace: WeakView<Workspace>,
-    context_store: WeakModel<ContextStore>,
+    context_picker: WeakEntity<ContextPicker>,
+    workspace: WeakEntity<Workspace>,
+    context_store: WeakEntity<ContextStore>,
     confirm_behavior: ConfirmBehavior,
     url: String,
 }
 
 impl FetchContextPickerDelegate {
     pub fn new(
-        context_picker: WeakView<ContextPicker>,
-        workspace: WeakView<Workspace>,
-        context_store: WeakModel<ContextStore>,
+        context_picker: WeakEntity<ContextPicker>,
+        workspace: WeakEntity<Workspace>,
+        context_store: WeakEntity<ContextStore>,
         confirm_behavior: ConfirmBehavior,
     ) -> Self {
         FetchContextPickerDelegate {
@@ -166,7 +167,7 @@ impl PickerDelegate for FetchContextPickerDelegate {
         }
     }
 
-    fn no_matches_text(&self, _cx: &mut WindowContext) -> SharedString {
+    fn no_matches_text(&self, _window: &mut Window, _cx: &mut App) -> SharedString {
         "Enter the URL that you would like to fetch".into()
     }
 
@@ -174,19 +175,30 @@ impl PickerDelegate for FetchContextPickerDelegate {
         0
     }
 
-    fn set_selected_index(&mut self, _ix: usize, _cx: &mut ViewContext<Picker<Self>>) {}
+    fn set_selected_index(
+        &mut self,
+        _ix: usize,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
+    ) {
+    }
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Enter a URL…".into()
     }
 
-    fn update_matches(&mut self, query: String, _cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
+    fn update_matches(
+        &mut self,
+        query: String,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
+    ) -> Task<()> {
         self.url = query;
 
         Task::ready(())
     }
 
-    fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
         let Some(workspace) = self.workspace.upgrade() else {
             return;
         };
@@ -194,13 +206,13 @@ impl PickerDelegate for FetchContextPickerDelegate {
         let http_client = workspace.read(cx).client().http_client().clone();
         let url = self.url.clone();
         let confirm_behavior = self.confirm_behavior;
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let text = cx
                 .background_executor()
                 .spawn(Self::build_message(http_client, url.clone()))
                 .await?;
 
-            this.update(&mut cx, |this, cx| {
+            this.update_in(&mut cx, |this, window, cx| {
                 this.delegate
                     .context_store
                     .update(cx, |context_store, _cx| {
@@ -209,7 +221,7 @@ impl PickerDelegate for FetchContextPickerDelegate {
 
                 match confirm_behavior {
                     ConfirmBehavior::KeepOpen => {}
-                    ConfirmBehavior::Close => this.delegate.dismissed(cx),
+                    ConfirmBehavior::Close => this.delegate.dismissed(window, cx),
                 }
 
                 anyhow::Ok(())
@@ -220,7 +232,7 @@ impl PickerDelegate for FetchContextPickerDelegate {
         .detach_and_log_err(cx);
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+    fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.context_picker
             .update(cx, |_, cx| {
                 cx.emit(DismissEvent);
@@ -232,7 +244,8 @@ impl PickerDelegate for FetchContextPickerDelegate {
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        _window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let added = self.context_store.upgrade().map_or(false, |context_store| {
             context_store.read(cx).includes_url(&self.url).is_some()

crates/assistant2/src/context_picker/file_context_picker.rs 🔗

@@ -11,8 +11,8 @@ use editor::{Anchor, Editor, FoldPlaceholder, ToPoint};
 use file_icons::FileIcons;
 use fuzzy::PathMatch;
 use gpui::{
-    AnyElement, AppContext, DismissEvent, Empty, FocusHandle, FocusableView, Stateful, Task, View,
-    WeakModel, WeakView,
+    AnyElement, App, DismissEvent, Empty, Entity, FocusHandle, Focusable, Stateful, Task,
+    WeakEntity,
 };
 use multi_buffer::{MultiBufferPoint, MultiBufferRow};
 use picker::{Picker, PickerDelegate};
@@ -27,17 +27,18 @@ use crate::context_picker::{ConfirmBehavior, ContextPicker};
 use crate::context_store::{ContextStore, FileInclusion};
 
 pub struct FileContextPicker {
-    picker: View<Picker<FileContextPickerDelegate>>,
+    picker: Entity<Picker<FileContextPickerDelegate>>,
 }
 
 impl FileContextPicker {
     pub fn new(
-        context_picker: WeakView<ContextPicker>,
-        workspace: WeakView<Workspace>,
-        editor: WeakView<Editor>,
-        context_store: WeakModel<ContextStore>,
+        context_picker: WeakEntity<ContextPicker>,
+        workspace: WeakEntity<Workspace>,
+        editor: WeakEntity<Editor>,
+        context_store: WeakEntity<ContextStore>,
         confirm_behavior: ConfirmBehavior,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let delegate = FileContextPickerDelegate::new(
             context_picker,
@@ -46,29 +47,29 @@ impl FileContextPicker {
             context_store,
             confirm_behavior,
         );
-        let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
+        let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
 
         Self { picker }
     }
 }
 
-impl FocusableView for FileContextPicker {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for FileContextPicker {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
 
 impl Render for FileContextPicker {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         self.picker.clone()
     }
 }
 
 pub struct FileContextPickerDelegate {
-    context_picker: WeakView<ContextPicker>,
-    workspace: WeakView<Workspace>,
-    editor: WeakView<Editor>,
-    context_store: WeakModel<ContextStore>,
+    context_picker: WeakEntity<ContextPicker>,
+    workspace: WeakEntity<Workspace>,
+    editor: WeakEntity<Editor>,
+    context_store: WeakEntity<ContextStore>,
     confirm_behavior: ConfirmBehavior,
     matches: Vec<PathMatch>,
     selected_index: usize,
@@ -76,10 +77,10 @@ pub struct FileContextPickerDelegate {
 
 impl FileContextPickerDelegate {
     pub fn new(
-        context_picker: WeakView<ContextPicker>,
-        workspace: WeakView<Workspace>,
-        editor: WeakView<Editor>,
-        context_store: WeakModel<ContextStore>,
+        context_picker: WeakEntity<ContextPicker>,
+        workspace: WeakEntity<Workspace>,
+        editor: WeakEntity<Editor>,
+        context_store: WeakEntity<ContextStore>,
         confirm_behavior: ConfirmBehavior,
     ) -> Self {
         Self {
@@ -97,8 +98,9 @@ impl FileContextPickerDelegate {
         &mut self,
         query: String,
         cancellation_flag: Arc<AtomicBool>,
-        workspace: &View<Workspace>,
-        cx: &mut ViewContext<Picker<Self>>,
+        workspace: &Entity<Workspace>,
+
+        cx: &mut Context<Picker<Self>>,
     ) -> Task<Vec<PathMatch>> {
         if query.is_empty() {
             let workspace = workspace.read(cx);
@@ -180,22 +182,32 @@ impl PickerDelegate for FileContextPickerDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, _cx: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
+    ) {
         self.selected_index = ix;
     }
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Search files…".into()
     }
 
-    fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
+    fn update_matches(
+        &mut self,
+        query: String,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Task<()> {
         let Some(workspace) = self.workspace.upgrade() else {
             return Task::ready(());
         };
 
         let search_task = self.search(query, Arc::<AtomicBool>::default(), &workspace, cx);
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             // TODO: This should be probably be run in the background.
             let paths = search_task.await;
 
@@ -206,7 +218,7 @@ impl PickerDelegate for FileContextPickerDelegate {
         })
     }
 
-    fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
         let Some(mat) = self.matches.get(self.selected_index) else {
             return;
         };
@@ -231,7 +243,7 @@ impl PickerDelegate for FileContextPickerDelegate {
         };
 
         editor.update(cx, |editor, cx| {
-            editor.transact(cx, |editor, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 // Move empty selections left by 1 column to select the `@`s, so they get overwritten when we insert.
                 {
                     let mut selections = editor.selections.all::<MultiBufferPoint>(cx);
@@ -247,7 +259,9 @@ impl PickerDelegate for FileContextPickerDelegate {
                         }
                     }
 
-                    editor.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
+                    editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                        s.select(selections)
+                    });
                 }
 
                 let start_anchors = {
@@ -260,7 +274,7 @@ impl PickerDelegate for FileContextPickerDelegate {
                         .collect::<Vec<_>>()
                 };
 
-                editor.insert(&full_path, cx);
+                editor.insert(&full_path, window, cx);
 
                 let end_anchors = {
                     let snapshot = editor.buffer().read(cx).snapshot(cx);
@@ -272,14 +286,15 @@ impl PickerDelegate for FileContextPickerDelegate {
                         .collect::<Vec<_>>()
                 };
 
-                editor.insert("\n", cx); // Needed to end the fold
+                editor.insert("\n", window, cx); // Needed to end the fold
 
                 let placeholder = FoldPlaceholder {
                     render: render_fold_icon_button(IconName::File, file_name.into()),
                     ..Default::default()
                 };
 
-                let render_trailer = move |_row, _unfold, _cx: &mut WindowContext| Empty.into_any();
+                let render_trailer =
+                    move |_row, _unfold, _window: &mut Window, _cx: &mut App| Empty.into_any();
 
                 let buffer = editor.buffer().read(cx).snapshot(cx);
                 let mut rows_to_fold = BTreeSet::new();
@@ -300,7 +315,7 @@ impl PickerDelegate for FileContextPickerDelegate {
                 editor.insert_creases(crease_iter, cx);
 
                 for buffer_row in rows_to_fold {
-                    editor.fold_at(&FoldAt { buffer_row }, cx);
+                    editor.fold_at(&FoldAt { buffer_row }, window, cx);
                 }
             });
         });
@@ -316,19 +331,19 @@ impl PickerDelegate for FileContextPickerDelegate {
         };
 
         let confirm_behavior = self.confirm_behavior;
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             match task.await.notify_async_err(&mut cx) {
                 None => anyhow::Ok(()),
-                Some(()) => this.update(&mut cx, |this, cx| match confirm_behavior {
+                Some(()) => this.update_in(&mut cx, |this, window, cx| match confirm_behavior {
                     ConfirmBehavior::KeepOpen => {}
-                    ConfirmBehavior::Close => this.delegate.dismissed(cx),
+                    ConfirmBehavior::Close => this.delegate.dismissed(window, cx),
                 }),
             }
         })
         .detach_and_log_err(cx);
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+    fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.context_picker
             .update(cx, |_, cx| {
                 cx.emit(DismissEvent);
@@ -340,7 +355,8 @@ impl PickerDelegate for FileContextPickerDelegate {
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        _window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let path_match = &self.matches[ix];
 
@@ -363,8 +379,8 @@ pub fn render_file_context_entry(
     id: ElementId,
     path: &Path,
     path_prefix: &Arc<str>,
-    context_store: WeakModel<ContextStore>,
-    cx: &WindowContext,
+    context_store: WeakEntity<ContextStore>,
+    cx: &App,
 ) -> Stateful<Div> {
     let (file_name, directory) = if path == Path::new("") {
         (SharedString::from(path_prefix.clone()), None)
@@ -437,7 +453,7 @@ pub fn render_file_context_entry(
                         )
                         .child(Label::new("Included").size(LabelSize::Small)),
                 )
-                .tooltip(move |cx| Tooltip::text(format!("in {dir_name}"), cx))
+                .tooltip(Tooltip::text(format!("in {dir_name}")))
             }
         })
 }
@@ -445,8 +461,8 @@ pub fn render_file_context_entry(
 fn render_fold_icon_button(
     icon: IconName,
     label: SharedString,
-) -> Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut WindowContext) -> AnyElement> {
-    Arc::new(move |fold_id, _fold_range, _cx| {
+) -> Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut Window, &mut App) -> AnyElement> {
+    Arc::new(move |fold_id, _fold_range, _window, _cx| {
         ButtonLike::new(fold_id)
             .style(ButtonStyle::Filled)
             .layer(ElevationIndex::ElevatedSurface)
@@ -461,13 +477,14 @@ fn fold_toggle(
 ) -> impl Fn(
     MultiBufferRow,
     bool,
-    Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
-    &mut WindowContext,
+    Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
+    &mut Window,
+    &mut App,
 ) -> AnyElement {
-    move |row, is_folded, fold, _cx| {
+    move |row, is_folded, fold, _window, _cx| {
         Disclosure::new((name, row.0 as u64), !is_folded)
             .toggle_state(is_folded)
-            .on_click(move |_e, cx| fold(!is_folded, cx))
+            .on_click(move |_e, window, cx| fold(!is_folded, window, cx))
             .into_any_element()
     }
 }

crates/assistant2/src/context_picker/thread_context_picker.rs 🔗

@@ -1,7 +1,7 @@
 use std::sync::Arc;
 
 use fuzzy::StringMatchCandidate;
-use gpui::{AppContext, DismissEvent, FocusHandle, FocusableView, Task, View, WeakModel, WeakView};
+use gpui::{App, DismissEvent, Entity, FocusHandle, Focusable, Task, WeakEntity};
 use picker::{Picker, PickerDelegate};
 use ui::{prelude::*, ListItem};
 
@@ -11,16 +11,17 @@ use crate::thread::ThreadId;
 use crate::thread_store::ThreadStore;
 
 pub struct ThreadContextPicker {
-    picker: View<Picker<ThreadContextPickerDelegate>>,
+    picker: Entity<Picker<ThreadContextPickerDelegate>>,
 }
 
 impl ThreadContextPicker {
     pub fn new(
-        thread_store: WeakModel<ThreadStore>,
-        context_picker: WeakView<ContextPicker>,
-        context_store: WeakModel<context_store::ContextStore>,
+        thread_store: WeakEntity<ThreadStore>,
+        context_picker: WeakEntity<ContextPicker>,
+        context_store: WeakEntity<context_store::ContextStore>,
         confirm_behavior: ConfirmBehavior,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let delegate = ThreadContextPickerDelegate::new(
             thread_store,
@@ -28,20 +29,20 @@ impl ThreadContextPicker {
             context_store,
             confirm_behavior,
         );
-        let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
+        let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
 
         ThreadContextPicker { picker }
     }
 }
 
-impl FocusableView for ThreadContextPicker {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for ThreadContextPicker {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
 
 impl Render for ThreadContextPicker {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         self.picker.clone()
     }
 }
@@ -53,9 +54,9 @@ pub struct ThreadContextEntry {
 }
 
 pub struct ThreadContextPickerDelegate {
-    thread_store: WeakModel<ThreadStore>,
-    context_picker: WeakView<ContextPicker>,
-    context_store: WeakModel<context_store::ContextStore>,
+    thread_store: WeakEntity<ThreadStore>,
+    context_picker: WeakEntity<ContextPicker>,
+    context_store: WeakEntity<context_store::ContextStore>,
     confirm_behavior: ConfirmBehavior,
     matches: Vec<ThreadContextEntry>,
     selected_index: usize,
@@ -63,9 +64,9 @@ pub struct ThreadContextPickerDelegate {
 
 impl ThreadContextPickerDelegate {
     pub fn new(
-        thread_store: WeakModel<ThreadStore>,
-        context_picker: WeakView<ContextPicker>,
-        context_store: WeakModel<context_store::ContextStore>,
+        thread_store: WeakEntity<ThreadStore>,
+        context_picker: WeakEntity<ContextPicker>,
+        context_store: WeakEntity<context_store::ContextStore>,
         confirm_behavior: ConfirmBehavior,
     ) -> Self {
         ThreadContextPickerDelegate {
@@ -90,15 +91,25 @@ impl PickerDelegate for ThreadContextPickerDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, _cx: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
+    ) {
         self.selected_index = ix;
     }
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Search threads…".into()
     }
 
-    fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
+    fn update_matches(
+        &mut self,
+        query: String,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Task<()> {
         let Ok(threads) = self.thread_store.update(cx, |this, _cx| {
             this.threads()
                 .into_iter()
@@ -138,7 +149,7 @@ impl PickerDelegate for ThreadContextPickerDelegate {
             }
         });
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let matches = search_task.await;
             this.update(&mut cx, |this, cx| {
                 this.delegate.matches = matches;
@@ -149,7 +160,7 @@ impl PickerDelegate for ThreadContextPickerDelegate {
         })
     }
 
-    fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
         let Some(entry) = self.matches.get(self.selected_index) else {
             return;
         };
@@ -160,9 +171,9 @@ impl PickerDelegate for ThreadContextPickerDelegate {
 
         let open_thread_task = thread_store.update(cx, |this, cx| this.open_thread(&entry.id, cx));
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let thread = open_thread_task.await?;
-            this.update(&mut cx, |this, cx| {
+            this.update_in(&mut cx, |this, window, cx| {
                 this.delegate
                     .context_store
                     .update(cx, |context_store, cx| context_store.add_thread(thread, cx))
@@ -170,14 +181,14 @@ impl PickerDelegate for ThreadContextPickerDelegate {
 
                 match this.delegate.confirm_behavior {
                     ConfirmBehavior::KeepOpen => {}
-                    ConfirmBehavior::Close => this.delegate.dismissed(cx),
+                    ConfirmBehavior::Close => this.delegate.dismissed(window, cx),
                 }
             })
         })
         .detach_and_log_err(cx);
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+    fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.context_picker
             .update(cx, |_, cx| {
                 cx.emit(DismissEvent);
@@ -189,7 +200,8 @@ impl PickerDelegate for ThreadContextPickerDelegate {
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        _window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let thread = &self.matches[ix];
 
@@ -201,8 +213,8 @@ impl PickerDelegate for ThreadContextPickerDelegate {
 
 pub fn render_thread_context_entry(
     thread: &ThreadContextEntry,
-    context_store: WeakModel<ContextStore>,
-    cx: &mut WindowContext,
+    context_store: WeakEntity<ContextStore>,
+    cx: &mut App,
 ) -> Div {
     let added = context_store.upgrade().map_or(false, |ctx_store| {
         ctx_store.read(cx).includes_thread(&thread.id).is_some()

crates/assistant2/src/context_store.rs 🔗

@@ -4,7 +4,7 @@ use std::sync::Arc;
 use anyhow::{anyhow, bail, Result};
 use collections::{BTreeMap, HashMap, HashSet};
 use futures::{self, future, Future, FutureExt};
-use gpui::{AppContext, AsyncAppContext, Model, ModelContext, SharedString, Task, WeakView};
+use gpui::{App, AsyncAppContext, Context, Entity, SharedString, Task, WeakEntity};
 use language::Buffer;
 use project::{ProjectPath, Worktree};
 use rope::Rope;
@@ -12,15 +12,15 @@ use text::BufferId;
 use workspace::Workspace;
 
 use crate::context::{
-    Context, ContextBuffer, ContextId, ContextSnapshot, DirectoryContext, FetchedUrlContext,
-    FileContext, ThreadContext,
+    AssistantContext, ContextBuffer, ContextId, ContextSnapshot, DirectoryContext,
+    FetchedUrlContext, FileContext, ThreadContext,
 };
 use crate::context_strip::SuggestedContext;
 use crate::thread::{Thread, ThreadId};
 
 pub struct ContextStore {
-    workspace: WeakView<Workspace>,
-    context: Vec<Context>,
+    workspace: WeakEntity<Workspace>,
+    context: Vec<AssistantContext>,
     // TODO: If an EntityId is used for all context types (like BufferId), can remove ContextId.
     next_context_id: ContextId,
     files: BTreeMap<BufferId, ContextId>,
@@ -30,7 +30,7 @@ pub struct ContextStore {
 }
 
 impl ContextStore {
-    pub fn new(workspace: WeakView<Workspace>) -> Self {
+    pub fn new(workspace: WeakEntity<Workspace>) -> Self {
         Self {
             workspace,
             context: Vec::new(),
@@ -42,16 +42,13 @@ impl ContextStore {
         }
     }
 
-    pub fn snapshot<'a>(
-        &'a self,
-        cx: &'a AppContext,
-    ) -> impl Iterator<Item = ContextSnapshot> + 'a {
+    pub fn snapshot<'a>(&'a self, cx: &'a App) -> impl Iterator<Item = ContextSnapshot> + 'a {
         self.context()
             .iter()
             .flat_map(|context| context.snapshot(cx))
     }
 
-    pub fn context(&self) -> &Vec<Context> {
+    pub fn context(&self) -> &Vec<AssistantContext> {
         &self.context
     }
 
@@ -66,7 +63,7 @@ impl ContextStore {
     pub fn add_file_from_path(
         &mut self,
         project_path: ProjectPath,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let workspace = self.workspace.clone();
 
@@ -122,8 +119,8 @@ impl ContextStore {
 
     pub fn add_file_from_buffer(
         &mut self,
-        buffer_model: Model<Buffer>,
-        cx: &mut ModelContext<Self>,
+        buffer_model: Entity<Buffer>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         cx.spawn(|this, mut cx| async move {
             let (buffer_info, text_task) = this.update(&mut cx, |_, cx| {
@@ -153,13 +150,13 @@ impl ContextStore {
         let id = self.next_context_id.post_inc();
         self.files.insert(context_buffer.id, id);
         self.context
-            .push(Context::File(FileContext { id, context_buffer }));
+            .push(AssistantContext::File(FileContext { id, context_buffer }));
     }
 
     pub fn add_directory(
         &mut self,
         project_path: ProjectPath,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let workspace = self.workspace.clone();
         let Some(project) = workspace
@@ -244,14 +241,15 @@ impl ContextStore {
         let id = self.next_context_id.post_inc();
         self.directories.insert(path.to_path_buf(), id);
 
-        self.context.push(Context::Directory(DirectoryContext::new(
-            id,
-            path,
-            context_buffers,
-        )));
+        self.context
+            .push(AssistantContext::Directory(DirectoryContext::new(
+                id,
+                path,
+                context_buffers,
+            )));
     }
 
-    pub fn add_thread(&mut self, thread: Model<Thread>, cx: &mut ModelContext<Self>) {
+    pub fn add_thread(&mut self, thread: Entity<Thread>, cx: &mut Context<Self>) {
         if let Some(context_id) = self.includes_thread(&thread.read(cx).id()) {
             self.remove_context(context_id);
         } else {
@@ -259,13 +257,13 @@ impl ContextStore {
         }
     }
 
-    fn insert_thread(&mut self, thread: Model<Thread>, cx: &AppContext) {
+    fn insert_thread(&mut self, thread: Entity<Thread>, cx: &App) {
         let id = self.next_context_id.post_inc();
         let text = thread.read(cx).text().into();
 
         self.threads.insert(thread.read(cx).id().clone(), id);
         self.context
-            .push(Context::Thread(ThreadContext { id, thread, text }));
+            .push(AssistantContext::Thread(ThreadContext { id, thread, text }));
     }
 
     pub fn add_fetched_url(&mut self, url: String, text: impl Into<SharedString>) {
@@ -278,17 +276,18 @@ impl ContextStore {
         let id = self.next_context_id.post_inc();
 
         self.fetched_urls.insert(url.clone(), id);
-        self.context.push(Context::FetchedUrl(FetchedUrlContext {
-            id,
-            url: url.into(),
-            text: text.into(),
-        }));
+        self.context
+            .push(AssistantContext::FetchedUrl(FetchedUrlContext {
+                id,
+                url: url.into(),
+                text: text.into(),
+            }));
     }
 
     pub fn accept_suggested_context(
         &mut self,
         suggested: &SuggestedContext,
-        cx: &mut ModelContext<ContextStore>,
+        cx: &mut Context<ContextStore>,
     ) -> Task<Result<()>> {
         match suggested {
             SuggestedContext::File {
@@ -315,16 +314,16 @@ impl ContextStore {
         };
 
         match self.context.remove(ix) {
-            Context::File(_) => {
+            AssistantContext::File(_) => {
                 self.files.retain(|_, context_id| *context_id != id);
             }
-            Context::Directory(_) => {
+            AssistantContext::Directory(_) => {
                 self.directories.retain(|_, context_id| *context_id != id);
             }
-            Context::FetchedUrl(_) => {
+            AssistantContext::FetchedUrl(_) => {
                 self.fetched_urls.retain(|_, context_id| *context_id != id);
             }
-            Context::Thread(_) => {
+            AssistantContext::Thread(_) => {
                 self.threads.retain(|_, context_id| *context_id != id);
             }
         }
@@ -343,10 +342,10 @@ impl ContextStore {
 
     /// Returns whether this file path is already included directly in the context, or if it will be
     /// included in the context via a directory.
-    pub fn will_include_file_path(&self, path: &Path, cx: &AppContext) -> Option<FileInclusion> {
+    pub fn will_include_file_path(&self, path: &Path, cx: &App) -> Option<FileInclusion> {
         if !self.files.is_empty() {
             let found_file_context = self.context.iter().find(|context| match &context {
-                Context::File(file_context) => {
+                AssistantContext::File(file_context) => {
                     let buffer = file_context.context_buffer.buffer.read(cx);
                     if let Some(file_path) = buffer_path_log_err(buffer) {
                         *file_path == *path
@@ -393,7 +392,7 @@ impl ContextStore {
     }
 
     /// Replaces the context that matches the ID of the new context, if any match.
-    fn replace_context(&mut self, new_context: Context) {
+    fn replace_context(&mut self, new_context: AssistantContext) {
         let id = new_context.id();
         for context in self.context.iter_mut() {
             if context.id() == id {
@@ -403,15 +402,17 @@ impl ContextStore {
         }
     }
 
-    pub fn file_paths(&self, cx: &AppContext) -> HashSet<PathBuf> {
+    pub fn file_paths(&self, cx: &App) -> HashSet<PathBuf> {
         self.context
             .iter()
             .filter_map(|context| match context {
-                Context::File(file) => {
+                AssistantContext::File(file) => {
                     let buffer = file.context_buffer.buffer.read(cx);
                     buffer_path_log_err(buffer).map(|p| p.to_path_buf())
                 }
-                Context::Directory(_) | Context::FetchedUrl(_) | Context::Thread(_) => None,
+                AssistantContext::Directory(_)
+                | AssistantContext::FetchedUrl(_)
+                | AssistantContext::Thread(_) => None,
             })
             .collect()
     }
@@ -428,7 +429,7 @@ pub enum FileInclusion {
 
 // ContextBuffer without text.
 struct BufferInfo {
-    buffer_model: Model<Buffer>,
+    buffer_model: Entity<Buffer>,
     id: BufferId,
     version: clock::Global,
 }
@@ -444,7 +445,7 @@ fn make_context_buffer(info: BufferInfo, text: SharedString) -> ContextBuffer {
 
 fn collect_buffer_info_and_text(
     path: Arc<Path>,
-    buffer_model: Model<Buffer>,
+    buffer_model: Entity<Buffer>,
     buffer: &Buffer,
     cx: AsyncAppContext,
 ) -> (BufferInfo, Task<SharedString>) {
@@ -525,32 +526,32 @@ fn collect_files_in_path(worktree: &Worktree, path: &Path) -> Vec<Arc<Path>> {
 }
 
 pub fn refresh_context_store_text(
-    context_store: Model<ContextStore>,
-    cx: &AppContext,
+    context_store: Entity<ContextStore>,
+    cx: &App,
 ) -> impl Future<Output = ()> {
     let mut tasks = Vec::new();
     for context in &context_store.read(cx).context {
         match context {
-            Context::File(file_context) => {
+            AssistantContext::File(file_context) => {
                 let context_store = context_store.clone();
                 if let Some(task) = refresh_file_text(context_store, file_context, cx) {
                     tasks.push(task);
                 }
             }
-            Context::Directory(directory_context) => {
+            AssistantContext::Directory(directory_context) => {
                 let context_store = context_store.clone();
                 if let Some(task) = refresh_directory_text(context_store, directory_context, cx) {
                     tasks.push(task);
                 }
             }
-            Context::Thread(thread_context) => {
+            AssistantContext::Thread(thread_context) => {
                 let context_store = context_store.clone();
                 tasks.push(refresh_thread_text(context_store, thread_context, cx));
             }
             // Intentionally omit refreshing fetched URLs as it doesn't seem all that useful,
             // and doing the caching properly could be tricky (unless it's already handled by
             // the HttpClient?).
-            Context::FetchedUrl(_) => {}
+            AssistantContext::FetchedUrl(_) => {}
         }
     }
 
@@ -558,9 +559,9 @@ pub fn refresh_context_store_text(
 }
 
 fn refresh_file_text(
-    context_store: Model<ContextStore>,
+    context_store: Entity<ContextStore>,
     file_context: &FileContext,
-    cx: &AppContext,
+    cx: &App,
 ) -> Option<Task<()>> {
     let id = file_context.id;
     let task = refresh_context_buffer(&file_context.context_buffer, cx);
@@ -570,7 +571,7 @@ fn refresh_file_text(
             context_store
                 .update(&mut cx, |context_store, _| {
                     let new_file_context = FileContext { id, context_buffer };
-                    context_store.replace_context(Context::File(new_file_context));
+                    context_store.replace_context(AssistantContext::File(new_file_context));
                 })
                 .ok();
         }))
@@ -580,9 +581,9 @@ fn refresh_file_text(
 }
 
 fn refresh_directory_text(
-    context_store: Model<ContextStore>,
+    context_store: Entity<ContextStore>,
     directory_context: &DirectoryContext,
-    cx: &AppContext,
+    cx: &App,
 ) -> Option<Task<()>> {
     let mut stale = false;
     let futures = directory_context
@@ -611,16 +612,16 @@ fn refresh_directory_text(
         context_store
             .update(&mut cx, |context_store, _| {
                 let new_directory_context = DirectoryContext::new(id, &path, context_buffers);
-                context_store.replace_context(Context::Directory(new_directory_context));
+                context_store.replace_context(AssistantContext::Directory(new_directory_context));
             })
             .ok();
     }))
 }
 
 fn refresh_thread_text(
-    context_store: Model<ContextStore>,
+    context_store: Entity<ContextStore>,
     thread_context: &ThreadContext,
-    cx: &AppContext,
+    cx: &App,
 ) -> Task<()> {
     let id = thread_context.id;
     let thread = thread_context.thread.clone();
@@ -628,7 +629,11 @@ fn refresh_thread_text(
         context_store
             .update(&mut cx, |context_store, cx| {
                 let text = thread.read(cx).text().into();
-                context_store.replace_context(Context::Thread(ThreadContext { id, thread, text }));
+                context_store.replace_context(AssistantContext::Thread(ThreadContext {
+                    id,
+                    thread,
+                    text,
+                }));
             })
             .ok();
     })
@@ -636,7 +641,7 @@ fn refresh_thread_text(
 
 fn refresh_context_buffer(
     context_buffer: &ContextBuffer,
-    cx: &AppContext,
+    cx: &App,
 ) -> Option<impl Future<Output = ContextBuffer>> {
     let buffer = context_buffer.buffer.read(cx);
     let path = buffer_path_log_err(buffer)?;

crates/assistant2/src/context_strip.rs 🔗

@@ -4,8 +4,8 @@ use collections::HashSet;
 use editor::Editor;
 use file_icons::FileIcons;
 use gpui::{
-    AppContext, Bounds, DismissEvent, EventEmitter, FocusHandle, FocusableView, Model,
-    Subscription, View, WeakModel, WeakView,
+    App, Bounds, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Subscription,
+    WeakEntity,
 };
 use itertools::Itertools;
 use language::Buffer;
@@ -24,34 +24,37 @@ use crate::{
 };
 
 pub struct ContextStrip {
-    context_store: Model<ContextStore>,
-    pub context_picker: View<ContextPicker>,
+    context_store: Entity<ContextStore>,
+    pub context_picker: Entity<ContextPicker>,
     context_picker_menu_handle: PopoverMenuHandle<ContextPicker>,
     focus_handle: FocusHandle,
     suggest_context_kind: SuggestContextKind,
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     _subscriptions: Vec<Subscription>,
     focused_index: Option<usize>,
     children_bounds: Option<Vec<Bounds<Pixels>>>,
 }
 
 impl ContextStrip {
+    #[allow(clippy::too_many_arguments)]
     pub fn new(
-        context_store: Model<ContextStore>,
-        workspace: WeakView<Workspace>,
-        editor: WeakView<Editor>,
-        thread_store: Option<WeakModel<ThreadStore>>,
+        context_store: Entity<ContextStore>,
+        workspace: WeakEntity<Workspace>,
+        editor: WeakEntity<Editor>,
+        thread_store: Option<WeakEntity<ThreadStore>>,
         context_picker_menu_handle: PopoverMenuHandle<ContextPicker>,
         suggest_context_kind: SuggestContextKind,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
-        let context_picker = cx.new_view(|cx| {
+        let context_picker = cx.new(|cx| {
             ContextPicker::new(
                 workspace.clone(),
                 thread_store.clone(),
                 context_store.downgrade(),
                 editor.clone(),
                 ConfirmBehavior::KeepOpen,
+                window,
                 cx,
             )
         });
@@ -59,9 +62,9 @@ impl ContextStrip {
         let focus_handle = cx.focus_handle();
 
         let subscriptions = vec![
-            cx.subscribe(&context_picker, Self::handle_context_picker_event),
-            cx.on_focus(&focus_handle, Self::handle_focus),
-            cx.on_blur(&focus_handle, Self::handle_blur),
+            cx.subscribe_in(&context_picker, window, Self::handle_context_picker_event),
+            cx.on_focus(&focus_handle, window, Self::handle_focus),
+            cx.on_blur(&focus_handle, window, Self::handle_blur),
         ];
 
         Self {
@@ -77,14 +80,14 @@ impl ContextStrip {
         }
     }
 
-    fn suggested_context(&self, cx: &ViewContext<Self>) -> Option<SuggestedContext> {
+    fn suggested_context(&self, cx: &Context<Self>) -> Option<SuggestedContext> {
         match self.suggest_context_kind {
             SuggestContextKind::File => self.suggested_file(cx),
             SuggestContextKind::Thread => self.suggested_thread(cx),
         }
     }
 
-    fn suggested_file(&self, cx: &ViewContext<Self>) -> Option<SuggestedContext> {
+    fn suggested_file(&self, cx: &Context<Self>) -> Option<SuggestedContext> {
         let workspace = self.workspace.upgrade()?;
         let active_item = workspace.read(cx).active_item(cx)?;
 
@@ -117,7 +120,7 @@ impl ContextStrip {
         })
     }
 
-    fn suggested_thread(&self, cx: &ViewContext<Self>) -> Option<SuggestedContext> {
+    fn suggested_thread(&self, cx: &Context<Self>) -> Option<SuggestedContext> {
         if !self.context_picker.read(cx).allow_threads() {
             return None;
         }
@@ -149,24 +152,25 @@ impl ContextStrip {
 
     fn handle_context_picker_event(
         &mut self,
-        _picker: View<ContextPicker>,
+        _picker: &Entity<ContextPicker>,
         _event: &DismissEvent,
-        cx: &mut ViewContext<Self>,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         cx.emit(ContextStripEvent::PickerDismissed);
     }
 
-    fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
+    fn handle_focus(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
         self.focused_index = self.last_pill_index();
         cx.notify();
     }
 
-    fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
+    fn handle_blur(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
         self.focused_index = None;
         cx.notify();
     }
 
-    fn focus_left(&mut self, _: &FocusLeft, cx: &mut ViewContext<Self>) {
+    fn focus_left(&mut self, _: &FocusLeft, _window: &mut Window, cx: &mut Context<Self>) {
         self.focused_index = match self.focused_index {
             Some(index) if index > 0 => Some(index - 1),
             _ => self.last_pill_index(),
@@ -175,7 +179,7 @@ impl ContextStrip {
         cx.notify();
     }
 
-    fn focus_right(&mut self, _: &FocusRight, cx: &mut ViewContext<Self>) {
+    fn focus_right(&mut self, _: &FocusRight, _window: &mut Window, cx: &mut Context<Self>) {
         let Some(last_index) = self.last_pill_index() else {
             return;
         };
@@ -188,7 +192,7 @@ impl ContextStrip {
         cx.notify();
     }
 
-    fn focus_up(&mut self, _: &FocusUp, cx: &mut ViewContext<Self>) {
+    fn focus_up(&mut self, _: &FocusUp, _window: &mut Window, cx: &mut Context<Self>) {
         let Some(focused_index) = self.focused_index else {
             return;
         };
@@ -206,7 +210,7 @@ impl ContextStrip {
         cx.notify();
     }
 
-    fn focus_down(&mut self, _: &FocusDown, cx: &mut ViewContext<Self>) {
+    fn focus_down(&mut self, _: &FocusDown, _window: &mut Window, cx: &mut Context<Self>) {
         let Some(focused_index) = self.focused_index else {
             return;
         };
@@ -276,7 +280,12 @@ impl ContextStrip {
         best.map(|(index, _, _)| index)
     }
 
-    fn remove_focused_context(&mut self, _: &RemoveFocusedContext, cx: &mut ViewContext<Self>) {
+    fn remove_focused_context(
+        &mut self,
+        _: &RemoveFocusedContext,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(index) = self.focused_index {
             let mut is_empty = false;
 
@@ -302,22 +311,32 @@ impl ContextStrip {
         self.focused_index == Some(context.len())
     }
 
-    fn accept_suggested_context(&mut self, _: &AcceptSuggestedContext, cx: &mut ViewContext<Self>) {
+    fn accept_suggested_context(
+        &mut self,
+        _: &AcceptSuggestedContext,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(suggested) = self.suggested_context(cx) {
             let context_store = self.context_store.read(cx);
 
             if self.is_suggested_focused(context_store.context()) {
-                self.add_suggested_context(&suggested, cx);
+                self.add_suggested_context(&suggested, window, cx);
             }
         }
     }
 
-    fn add_suggested_context(&mut self, suggested: &SuggestedContext, cx: &mut ViewContext<Self>) {
+    fn add_suggested_context(
+        &mut self,
+        suggested: &SuggestedContext,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let task = self.context_store.update(cx, |context_store, cx| {
             context_store.accept_suggested_context(&suggested, cx)
         });
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             match task.await.notify_async_err(&mut cx) {
                 None => {}
                 Some(()) => {
@@ -334,14 +353,14 @@ impl ContextStrip {
     }
 }
 
-impl FocusableView for ContextStrip {
-    fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
+impl Focusable for ContextStrip {
+    fn focus_handle(&self, _cx: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
 
 impl Render for ContextStrip {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let context_store = self.context_store.read(cx);
         let context = context_store
             .context()
@@ -374,19 +393,20 @@ impl Render for ContextStrip {
             .on_action(cx.listener(Self::remove_focused_context))
             .on_action(cx.listener(Self::accept_suggested_context))
             .on_children_prepainted({
-                let view = cx.view().downgrade();
-                move |children_bounds, cx| {
-                    view.update(cx, |this, _| {
-                        this.children_bounds = Some(children_bounds);
-                    })
-                    .ok();
+                let model = cx.model().downgrade();
+                move |children_bounds, _window, cx| {
+                    model
+                        .update(cx, |this, _| {
+                            this.children_bounds = Some(children_bounds);
+                        })
+                        .ok();
                 }
             })
             .child(
                 PopoverMenu::new("context-picker")
-                    .menu(move |cx| {
+                    .menu(move |window, cx| {
                         context_picker.update(cx, |this, cx| {
-                            this.init(cx);
+                            this.init(window, cx);
                         });
 
                         Some(context_picker.clone())
@@ -397,12 +417,12 @@ impl Render for ContextStrip {
                             .style(ui::ButtonStyle::Filled)
                             .tooltip({
                                 let focus_handle = focus_handle.clone();
-
-                                move |cx| {
+                                move |window, cx| {
                                     Tooltip::for_action_in(
                                         "Add Context",
                                         &ToggleContextPicker,
                                         &focus_handle,
+                                        window,
                                         cx,
                                     )
                                 }
@@ -429,8 +449,12 @@ impl Render for ContextStrip {
                             )
                             .opacity(0.5)
                             .children(
-                                KeyBinding::for_action_in(&ToggleContextPicker, &focus_handle, cx)
-                                    .map(|binding| binding.into_any_element()),
+                                KeyBinding::for_action_in(
+                                    &ToggleContextPicker,
+                                    &focus_handle,
+                                    window,
+                                )
+                                .map(|binding| binding.into_any_element()),
                             ),
                     )
                 }
@@ -443,7 +467,7 @@ impl Render for ContextStrip {
                     Some({
                         let id = context.id;
                         let context_store = self.context_store.clone();
-                        Rc::new(cx.listener(move |_this, _event, cx| {
+                        Rc::new(cx.listener(move |_this, _event, _window, cx| {
                             context_store.update(cx, |this, _cx| {
                                 this.remove_context(id);
                             });
@@ -451,7 +475,7 @@ impl Render for ContextStrip {
                         }))
                     }),
                 )
-                .on_click(Rc::new(cx.listener(move |this, _, cx| {
+                .on_click(Rc::new(cx.listener(move |this, _, _window, cx| {
                     this.focused_index = Some(i);
                     cx.notify();
                 })))
@@ -464,9 +488,11 @@ impl Render for ContextStrip {
                         suggested.kind(),
                         self.is_suggested_focused(&context),
                     )
-                    .on_click(Rc::new(cx.listener(move |this, _event, cx| {
-                        this.add_suggested_context(&suggested, cx);
-                    }))),
+                    .on_click(Rc::new(cx.listener(
+                        move |this, _event, window, cx| {
+                            this.add_suggested_context(&suggested, window, cx);
+                        },
+                    ))),
                 )
             })
             .when(!context.is_empty(), {
@@ -476,19 +502,20 @@ impl Render for ContextStrip {
                             .icon_size(IconSize::Small)
                             .tooltip({
                                 let focus_handle = focus_handle.clone();
-                                move |cx| {
+                                move |window, cx| {
                                     Tooltip::for_action_in(
                                         "Remove All Context",
                                         &RemoveAllContext,
                                         &focus_handle,
+                                        window,
                                         cx,
                                     )
                                 }
                             })
                             .on_click(cx.listener({
                                 let focus_handle = focus_handle.clone();
-                                move |_this, _event, cx| {
-                                    focus_handle.dispatch_action(&RemoveAllContext, cx);
+                                move |_this, _event, window, cx| {
+                                    focus_handle.dispatch_action(&RemoveAllContext, window, cx);
                                 }
                             })),
                     )
@@ -516,11 +543,11 @@ pub enum SuggestedContext {
     File {
         name: SharedString,
         icon_path: Option<SharedString>,
-        buffer: WeakModel<Buffer>,
+        buffer: WeakEntity<Buffer>,
     },
     Thread {
         name: SharedString,
-        thread: WeakModel<Thread>,
+        thread: WeakEntity<Thread>,
     },
 }
 

crates/assistant2/src/inline_assistant.rs 🔗

@@ -20,8 +20,8 @@ use editor::{
 use feature_flags::{Assistant2FeatureFlag, FeatureFlagViewExt as _};
 use fs::Fs;
 use gpui::{
-    point, AppContext, FocusableView, Global, HighlightStyle, Model, Subscription, Task,
-    UpdateGlobal, View, ViewContext, WeakModel, WeakView, WindowContext,
+    point, App, Context, Entity, Focusable, Global, HighlightStyle, Subscription, Task,
+    UpdateGlobal, WeakEntity, Window,
 };
 use language::{Buffer, Point, Selection, TransactionId};
 use language_model::LanguageModelRegistry;
@@ -51,17 +51,20 @@ pub fn init(
     fs: Arc<dyn Fs>,
     prompt_builder: Arc<PromptBuilder>,
     telemetry: Arc<Telemetry>,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) {
     cx.set_global(InlineAssistant::new(fs, prompt_builder, telemetry));
-    cx.observe_new_views(|_workspace: &mut Workspace, cx| {
-        let workspace = cx.view().clone();
+    cx.observe_new(|_workspace: &mut Workspace, window, cx| {
+        let Some(window) = window else {
+            return;
+        };
+        let workspace = cx.model().clone();
         InlineAssistant::update_global(cx, |inline_assistant, cx| {
-            inline_assistant.register_workspace(&workspace, cx)
+            inline_assistant.register_workspace(&workspace, window, cx)
         });
 
-        cx.observe_flag::<Assistant2FeatureFlag, _>({
-            |is_assistant2_enabled, _view, cx| {
+        cx.observe_flag::<Assistant2FeatureFlag, _>(window, {
+            |is_assistant2_enabled, _workspace, _window, cx| {
                 InlineAssistant::update_global(cx, |inline_assistant, _cx| {
                     inline_assistant.is_assistant2_enabled = is_assistant2_enabled;
                 });
@@ -75,17 +78,17 @@ pub fn init(
 const PROMPT_HISTORY_MAX_LEN: usize = 20;
 
 enum InlineAssistTarget {
-    Editor(View<Editor>),
-    Terminal(View<TerminalView>),
+    Editor(Entity<Editor>),
+    Terminal(Entity<TerminalView>),
 }
 
 pub struct InlineAssistant {
     next_assist_id: InlineAssistId,
     next_assist_group_id: InlineAssistGroupId,
     assists: HashMap<InlineAssistId, InlineAssist>,
-    assists_by_editor: HashMap<WeakView<Editor>, EditorInlineAssists>,
+    assists_by_editor: HashMap<WeakEntity<Editor>, EditorInlineAssists>,
     assist_groups: HashMap<InlineAssistGroupId, InlineAssistGroup>,
-    confirmed_assists: HashMap<InlineAssistId, Model<CodegenAlternative>>,
+    confirmed_assists: HashMap<InlineAssistId, Entity<CodegenAlternative>>,
     prompt_history: VecDeque<String>,
     prompt_builder: Arc<PromptBuilder>,
     telemetry: Arc<Telemetry>,
@@ -116,13 +119,19 @@ impl InlineAssistant {
         }
     }
 
-    pub fn register_workspace(&mut self, workspace: &View<Workspace>, cx: &mut WindowContext) {
-        cx.subscribe(workspace, |workspace, event, cx| {
-            Self::update_global(cx, |this, cx| {
-                this.handle_workspace_event(workspace, event, cx)
-            });
-        })
-        .detach();
+    pub fn register_workspace(
+        &mut self,
+        workspace: &Entity<Workspace>,
+        window: &mut Window,
+        cx: &mut App,
+    ) {
+        window
+            .subscribe(workspace, cx, |workspace, event, window, cx| {
+                Self::update_global(cx, |this, cx| {
+                    this.handle_workspace_event(workspace, event, window, cx)
+                });
+            })
+            .detach();
 
         let workspace = workspace.downgrade();
         cx.observe_global::<SettingsStore>(move |cx| {
@@ -142,9 +151,10 @@ impl InlineAssistant {
 
     fn handle_workspace_event(
         &mut self,
-        workspace: View<Workspace>,
+        workspace: Entity<Workspace>,
         event: &workspace::Event,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         match event {
             workspace::Event::UserSavedItem { item, .. } => {
@@ -154,14 +164,14 @@ impl InlineAssistant {
                         for assist_id in editor_assists.assist_ids.clone() {
                             let assist = &self.assists[&assist_id];
                             if let CodegenStatus::Done = assist.codegen.read(cx).status(cx) {
-                                self.finish_assist(assist_id, false, cx)
+                                self.finish_assist(assist_id, false, window, cx)
                             }
                         }
                     }
                 }
             }
             workspace::Event::ItemAdded { item } => {
-                self.register_workspace_item(&workspace, item.as_ref(), cx);
+                self.register_workspace_item(&workspace, item.as_ref(), window, cx);
             }
             _ => (),
         }
@@ -169,9 +179,10 @@ impl InlineAssistant {
 
     fn register_workspace_item(
         &mut self,
-        workspace: &View<Workspace>,
+        workspace: &Entity<Workspace>,
         item: &dyn ItemHandle,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let is_assistant2_enabled = self.is_assistant2_enabled;
 
@@ -185,18 +196,22 @@ impl InlineAssistant {
 
                     editor.add_code_action_provider(
                         Rc::new(AssistantCodeActionProvider {
-                            editor: cx.view().downgrade(),
+                            editor: cx.model().downgrade(),
                             workspace: workspace.downgrade(),
                             thread_store,
                         }),
+                        window,
                         cx,
                     );
 
                     // Remove the Assistant1 code action provider, as it still might be registered.
-                    editor.remove_code_action_provider("assistant".into(), cx);
+                    editor.remove_code_action_provider("assistant".into(), window, cx);
                 } else {
-                    editor
-                        .remove_code_action_provider(ASSISTANT_CODE_ACTION_PROVIDER_ID.into(), cx);
+                    editor.remove_code_action_provider(
+                        ASSISTANT_CODE_ACTION_PROVIDER_ID.into(),
+                        window,
+                        cx,
+                    );
                 }
             });
         }
@@ -205,14 +220,16 @@ impl InlineAssistant {
     pub fn inline_assist(
         workspace: &mut Workspace,
         _action: &zed_actions::assistant::InlineAssist,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
         let settings = AssistantSettings::get_global(cx);
         if !settings.enabled {
             return;
         }
 
-        let Some(inline_assist_target) = Self::resolve_inline_assist_target(workspace, cx) else {
+        let Some(inline_assist_target) = Self::resolve_inline_assist_target(workspace, window, cx)
+        else {
             return;
         };
 
@@ -226,24 +243,37 @@ impl InlineAssistant {
             .panel::<AssistantPanel>(cx)
             .map(|assistant_panel| assistant_panel.read(cx).thread_store().downgrade());
 
-        let handle_assist = |cx: &mut ViewContext<Workspace>| match inline_assist_target {
-            InlineAssistTarget::Editor(active_editor) => {
-                InlineAssistant::update_global(cx, |assistant, cx| {
-                    assistant.assist(&active_editor, cx.view().downgrade(), thread_store, cx)
-                })
-            }
-            InlineAssistTarget::Terminal(active_terminal) => {
-                TerminalInlineAssistant::update_global(cx, |assistant, cx| {
-                    assistant.assist(&active_terminal, cx.view().downgrade(), thread_store, cx)
-                })
-            }
-        };
+        let handle_assist =
+            |window: &mut Window, cx: &mut Context<Workspace>| match inline_assist_target {
+                InlineAssistTarget::Editor(active_editor) => {
+                    InlineAssistant::update_global(cx, |assistant, cx| {
+                        assistant.assist(
+                            &active_editor,
+                            cx.model().downgrade(),
+                            thread_store,
+                            window,
+                            cx,
+                        )
+                    })
+                }
+                InlineAssistTarget::Terminal(active_terminal) => {
+                    TerminalInlineAssistant::update_global(cx, |assistant, cx| {
+                        assistant.assist(
+                            &active_terminal,
+                            cx.model().downgrade(),
+                            thread_store,
+                            window,
+                            cx,
+                        )
+                    })
+                }
+            };
 
         if is_authenticated() {
-            handle_assist(cx);
+            handle_assist(window, cx);
         } else {
-            cx.spawn(|_workspace, mut cx| async move {
-                let Some(task) = cx.update(|cx| {
+            cx.spawn_in(window, |_workspace, mut cx| async move {
+                let Some(task) = cx.update(|_, cx| {
                     LanguageModelRegistry::read_global(cx)
                         .active_provider()
                         .map_or(None, |provider| Some(provider.authenticate(cx)))
@@ -260,8 +290,10 @@ impl InlineAssistant {
                         .ok();
                     if let Some(answer) = answer {
                         if answer == 0 {
-                            cx.update(|cx| cx.dispatch_action(Box::new(ShowConfiguration)))
-                                .ok();
+                            cx.update(|window, cx| {
+                                window.dispatch_action(Box::new(ShowConfiguration), cx)
+                            })
+                            .ok();
                         }
                     }
                     return Ok(());
@@ -273,17 +305,18 @@ impl InlineAssistant {
             .detach_and_log_err(cx);
 
             if is_authenticated() {
-                handle_assist(cx);
+                handle_assist(window, cx);
             }
         }
     }
 
     pub fn assist(
         &mut self,
-        editor: &View<Editor>,
-        workspace: WeakView<Workspace>,
-        thread_store: Option<WeakModel<ThreadStore>>,
-        cx: &mut WindowContext,
+        editor: &Entity<Editor>,
+        workspace: WeakEntity<Workspace>,
+        thread_store: Option<WeakEntity<ThreadStore>>,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let (snapshot, initial_selections) = editor.update(cx, |editor, cx| {
             (
@@ -349,16 +382,15 @@ impl InlineAssistant {
         }
 
         let assist_group_id = self.next_assist_group_id.post_inc();
-        let prompt_buffer = cx.new_model(|cx| {
-            MultiBuffer::singleton(cx.new_model(|cx| Buffer::local(String::new(), cx)), cx)
-        });
+        let prompt_buffer =
+            cx.new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(String::new(), cx)), cx));
 
         let mut assists = Vec::new();
         let mut assist_to_focus = None;
         for range in codegen_ranges {
             let assist_id = self.next_assist_id.post_inc();
-            let context_store = cx.new_model(|_cx| ContextStore::new(workspace.clone()));
-            let codegen = cx.new_model(|cx| {
+            let context_store = cx.new(|_cx| ContextStore::new(workspace.clone()));
+            let codegen = cx.new(|cx| {
                 BufferCodegen::new(
                     editor.read(cx).buffer().clone(),
                     range.clone(),
@@ -371,7 +403,7 @@ impl InlineAssistant {
             });
 
             let gutter_dimensions = Arc::new(Mutex::new(GutterDimensions::default()));
-            let prompt_editor = cx.new_view(|cx| {
+            let prompt_editor = cx.new(|cx| {
                 PromptEditor::new_buffer(
                     assist_id,
                     gutter_dimensions.clone(),
@@ -382,6 +414,7 @@ impl InlineAssistant {
                     context_store,
                     workspace.clone(),
                     thread_store.clone(),
+                    window,
                     cx,
                 )
             });
@@ -412,7 +445,7 @@ impl InlineAssistant {
         let editor_assists = self
             .assists_by_editor
             .entry(editor.downgrade())
-            .or_insert_with(|| EditorInlineAssists::new(&editor, cx));
+            .or_insert_with(|| EditorInlineAssists::new(&editor, window, cx));
         let mut assist_group = InlineAssistGroup::new();
         for (assist_id, range, prompt_editor, prompt_block_id, end_block_id) in assists {
             let codegen = prompt_editor.read(cx).codegen().clone();
@@ -429,6 +462,7 @@ impl InlineAssistant {
                     range,
                     codegen,
                     workspace.clone(),
+                    window,
                     cx,
                 ),
             );
@@ -438,25 +472,26 @@ impl InlineAssistant {
         self.assist_groups.insert(assist_group_id, assist_group);
 
         if let Some(assist_id) = assist_to_focus {
-            self.focus_assist(assist_id, cx);
+            self.focus_assist(assist_id, window, cx);
         }
     }
 
     #[allow(clippy::too_many_arguments)]
     pub fn suggest_assist(
         &mut self,
-        editor: &View<Editor>,
+        editor: &Entity<Editor>,
         mut range: Range<Anchor>,
         initial_prompt: String,
         initial_transaction_id: Option<TransactionId>,
         focus: bool,
-        workspace: WeakView<Workspace>,
-        thread_store: Option<WeakModel<ThreadStore>>,
-        cx: &mut WindowContext,
+        workspace: WeakEntity<Workspace>,
+        thread_store: Option<WeakEntity<ThreadStore>>,
+        window: &mut Window,
+        cx: &mut App,
     ) -> InlineAssistId {
         let assist_group_id = self.next_assist_group_id.post_inc();
-        let prompt_buffer = cx.new_model(|cx| Buffer::local(&initial_prompt, cx));
-        let prompt_buffer = cx.new_model(|cx| MultiBuffer::singleton(prompt_buffer, cx));
+        let prompt_buffer = cx.new(|cx| Buffer::local(&initial_prompt, cx));
+        let prompt_buffer = cx.new(|cx| MultiBuffer::singleton(prompt_buffer, cx));
 
         let assist_id = self.next_assist_id.post_inc();
 
@@ -467,9 +502,9 @@ impl InlineAssistant {
             range.end = range.end.bias_right(&snapshot);
         }
 
-        let context_store = cx.new_model(|_cx| ContextStore::new(workspace.clone()));
+        let context_store = cx.new(|_cx| ContextStore::new(workspace.clone()));
 
-        let codegen = cx.new_model(|cx| {
+        let codegen = cx.new(|cx| {
             BufferCodegen::new(
                 editor.read(cx).buffer().clone(),
                 range.clone(),
@@ -482,7 +517,7 @@ impl InlineAssistant {
         });
 
         let gutter_dimensions = Arc::new(Mutex::new(GutterDimensions::default()));
-        let prompt_editor = cx.new_view(|cx| {
+        let prompt_editor = cx.new(|cx| {
             PromptEditor::new_buffer(
                 assist_id,
                 gutter_dimensions.clone(),
@@ -493,6 +528,7 @@ impl InlineAssistant {
                 context_store,
                 workspace.clone(),
                 thread_store,
+                window,
                 cx,
             )
         });
@@ -503,7 +539,7 @@ impl InlineAssistant {
         let editor_assists = self
             .assists_by_editor
             .entry(editor.downgrade())
-            .or_insert_with(|| EditorInlineAssists::new(&editor, cx));
+            .or_insert_with(|| EditorInlineAssists::new(&editor, window, cx));
 
         let mut assist_group = InlineAssistGroup::new();
         self.assists.insert(
@@ -518,6 +554,7 @@ impl InlineAssistant {
                 range,
                 codegen.clone(),
                 workspace.clone(),
+                window,
                 cx,
             ),
         );
@@ -526,7 +563,7 @@ impl InlineAssistant {
         self.assist_groups.insert(assist_group_id, assist_group);
 
         if focus {
-            self.focus_assist(assist_id, cx);
+            self.focus_assist(assist_id, window, cx);
         }
 
         assist_id
@@ -534,10 +571,10 @@ impl InlineAssistant {
 
     fn insert_assist_blocks(
         &self,
-        editor: &View<Editor>,
+        editor: &Entity<Editor>,
         range: &Range<Anchor>,
-        prompt_editor: &View<PromptEditor<BufferCodegen>>,
-        cx: &mut WindowContext,
+        prompt_editor: &Entity<PromptEditor<BufferCodegen>>,
+        cx: &mut App,
     ) -> [CustomBlockId; 2] {
         let prompt_editor_height = prompt_editor.update(cx, |prompt_editor, cx| {
             prompt_editor
@@ -574,7 +611,7 @@ impl InlineAssistant {
         })
     }
 
-    fn handle_prompt_editor_focus_in(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) {
+    fn handle_prompt_editor_focus_in(&mut self, assist_id: InlineAssistId, cx: &mut App) {
         let assist = &self.assists[&assist_id];
         let Some(decorations) = assist.decorations.as_ref() else {
             return;
@@ -615,11 +652,7 @@ impl InlineAssistant {
             .ok();
     }
 
-    fn handle_prompt_editor_focus_out(
-        &mut self,
-        assist_id: InlineAssistId,
-        cx: &mut WindowContext,
-    ) {
+    fn handle_prompt_editor_focus_out(&mut self, assist_id: InlineAssistId, cx: &mut App) {
         let assist = &self.assists[&assist_id];
         let assist_group = self.assist_groups.get_mut(&assist.group_id).unwrap();
         if assist_group.active_assist_id == Some(assist_id) {
@@ -638,26 +671,27 @@ impl InlineAssistant {
 
     fn handle_prompt_editor_event(
         &mut self,
-        prompt_editor: View<PromptEditor<BufferCodegen>>,
+        prompt_editor: Entity<PromptEditor<BufferCodegen>>,
         event: &PromptEditorEvent,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let assist_id = prompt_editor.read(cx).id();
         match event {
             PromptEditorEvent::StartRequested => {
-                self.start_assist(assist_id, cx);
+                self.start_assist(assist_id, window, cx);
             }
             PromptEditorEvent::StopRequested => {
                 self.stop_assist(assist_id, cx);
             }
             PromptEditorEvent::ConfirmRequested { execute: _ } => {
-                self.finish_assist(assist_id, false, cx);
+                self.finish_assist(assist_id, false, window, cx);
             }
             PromptEditorEvent::CancelRequested => {
-                self.finish_assist(assist_id, true, cx);
+                self.finish_assist(assist_id, true, window, cx);
             }
             PromptEditorEvent::DismissRequested => {
-                self.dismiss_assist(assist_id, cx);
+                self.dismiss_assist(assist_id, window, cx);
             }
             PromptEditorEvent::Resized { .. } => {
                 // This only matters for the terminal inline assistant
@@ -665,7 +699,7 @@ impl InlineAssistant {
         }
     }
 
-    fn handle_editor_newline(&mut self, editor: View<Editor>, cx: &mut WindowContext) {
+    fn handle_editor_newline(&mut self, editor: Entity<Editor>, window: &mut Window, cx: &mut App) {
         let Some(editor_assists) = self.assists_by_editor.get(&editor.downgrade()) else {
             return;
         };
@@ -683,9 +717,9 @@ impl InlineAssistant {
                 if assist_range.contains(&selection.start) && assist_range.contains(&selection.end)
                 {
                     if matches!(assist.codegen.read(cx).status(cx), CodegenStatus::Pending) {
-                        self.dismiss_assist(*assist_id, cx);
+                        self.dismiss_assist(*assist_id, window, cx);
                     } else {
-                        self.finish_assist(*assist_id, false, cx);
+                        self.finish_assist(*assist_id, false, window, cx);
                     }
 
                     return;
@@ -696,7 +730,7 @@ impl InlineAssistant {
         cx.propagate();
     }
 
-    fn handle_editor_cancel(&mut self, editor: View<Editor>, cx: &mut WindowContext) {
+    fn handle_editor_cancel(&mut self, editor: Entity<Editor>, window: &mut Window, cx: &mut App) {
         let Some(editor_assists) = self.assists_by_editor.get(&editor.downgrade()) else {
             return;
         };
@@ -716,7 +750,7 @@ impl InlineAssistant {
                     if assist_range.contains(&selection.start)
                         && assist_range.contains(&selection.end)
                     {
-                        self.focus_assist(*assist_id, cx);
+                        self.focus_assist(*assist_id, window, cx);
                         return;
                     } else {
                         let distance_from_selection = assist_range
@@ -743,22 +777,27 @@ impl InlineAssistant {
             }
 
             if let Some((&assist_id, _)) = closest_assist_fallback {
-                self.focus_assist(assist_id, cx);
+                self.focus_assist(assist_id, window, cx);
             }
         }
 
         cx.propagate();
     }
 
-    fn handle_editor_release(&mut self, editor: WeakView<Editor>, cx: &mut WindowContext) {
+    fn handle_editor_release(
+        &mut self,
+        editor: WeakEntity<Editor>,
+        window: &mut Window,
+        cx: &mut App,
+    ) {
         if let Some(editor_assists) = self.assists_by_editor.get_mut(&editor) {
             for assist_id in editor_assists.assist_ids.clone() {
-                self.finish_assist(assist_id, true, cx);
+                self.finish_assist(assist_id, true, window, cx);
             }
         }
     }
 
-    fn handle_editor_change(&mut self, editor: View<Editor>, cx: &mut WindowContext) {
+    fn handle_editor_change(&mut self, editor: Entity<Editor>, window: &mut Window, cx: &mut App) {
         let Some(editor_assists) = self.assists_by_editor.get(&editor.downgrade()) else {
             return;
         };
@@ -778,16 +817,17 @@ impl InlineAssistant {
                 .0 as f32
                 - scroll_lock.distance_from_top;
             if target_scroll_top != scroll_position.y {
-                editor.set_scroll_position(point(scroll_position.x, target_scroll_top), cx);
+                editor.set_scroll_position(point(scroll_position.x, target_scroll_top), window, cx);
             }
         });
     }
 
     fn handle_editor_event(
         &mut self,
-        editor: View<Editor>,
+        editor: Entity<Editor>,
         event: &EditorEvent,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let Some(editor_assists) = self.assists_by_editor.get_mut(&editor.downgrade()) else {
             return;
@@ -811,7 +851,7 @@ impl InlineAssistant {
                             .iter()
                             .any(|range| range.overlaps(&assist_range))
                         {
-                            self.finish_assist(assist_id, false, cx);
+                            self.finish_assist(assist_id, false, window, cx);
                         }
                     }
                 }
@@ -839,7 +879,11 @@ impl InlineAssistant {
                 for assist_id in editor_assists.assist_ids.clone() {
                     let assist = &self.assists[&assist_id];
                     if let Some(decorations) = assist.decorations.as_ref() {
-                        if decorations.prompt_editor.focus_handle(cx).is_focused(cx) {
+                        if decorations
+                            .prompt_editor
+                            .focus_handle(cx)
+                            .is_focused(window)
+                        {
                             return;
                         }
                     }
@@ -851,18 +895,24 @@ impl InlineAssistant {
         }
     }
 
-    pub fn finish_assist(&mut self, assist_id: InlineAssistId, undo: bool, cx: &mut WindowContext) {
+    pub fn finish_assist(
+        &mut self,
+        assist_id: InlineAssistId,
+        undo: bool,
+        window: &mut Window,
+        cx: &mut App,
+    ) {
         if let Some(assist) = self.assists.get(&assist_id) {
             let assist_group_id = assist.group_id;
             if self.assist_groups[&assist_group_id].linked {
-                for assist_id in self.unlink_assist_group(assist_group_id, cx) {
-                    self.finish_assist(assist_id, undo, cx);
+                for assist_id in self.unlink_assist_group(assist_group_id, window, cx) {
+                    self.finish_assist(assist_id, undo, window, cx);
                 }
                 return;
             }
         }
 
-        self.dismiss_assist(assist_id, cx);
+        self.dismiss_assist(assist_id, window, cx);
 
         if let Some(assist) = self.assists.remove(&assist_id) {
             if let hash_map::Entry::Occupied(mut entry) = self.assist_groups.entry(assist.group_id)
@@ -931,7 +981,12 @@ impl InlineAssistant {
         }
     }
 
-    fn dismiss_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) -> bool {
+    fn dismiss_assist(
+        &mut self,
+        assist_id: InlineAssistId,
+        window: &mut Window,
+        cx: &mut App,
+    ) -> bool {
         let Some(assist) = self.assists.get_mut(&assist_id) else {
             return false;
         };
@@ -952,9 +1007,9 @@ impl InlineAssistant {
         if decorations
             .prompt_editor
             .focus_handle(cx)
-            .contains_focused(cx)
+            .contains_focused(window, cx)
         {
-            self.focus_next_assist(assist_id, cx);
+            self.focus_next_assist(assist_id, window, cx);
         }
 
         if let Some(editor_assists) = self.assists_by_editor.get_mut(&editor.downgrade()) {
@@ -971,7 +1026,7 @@ impl InlineAssistant {
         true
     }
 
-    fn focus_next_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) {
+    fn focus_next_assist(&mut self, assist_id: InlineAssistId, window: &mut Window, cx: &mut App) {
         let Some(assist) = self.assists.get(&assist_id) else {
             return;
         };
@@ -991,15 +1046,18 @@ impl InlineAssistant {
         for assist_id in assist_ids {
             let assist = &self.assists[assist_id];
             if assist.decorations.is_some() {
-                self.focus_assist(*assist_id, cx);
+                self.focus_assist(*assist_id, window, cx);
                 return;
             }
         }
 
-        assist.editor.update(cx, |editor, cx| editor.focus(cx)).ok();
+        assist
+            .editor
+            .update(cx, |editor, cx| window.focus(&editor.focus_handle(cx)))
+            .ok();
     }
 
-    fn focus_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) {
+    fn focus_assist(&mut self, assist_id: InlineAssistId, window: &mut Window, cx: &mut App) {
         let Some(assist) = self.assists.get(&assist_id) else {
             return;
         };
@@ -1007,16 +1065,21 @@ impl InlineAssistant {
         if let Some(decorations) = assist.decorations.as_ref() {
             decorations.prompt_editor.update(cx, |prompt_editor, cx| {
                 prompt_editor.editor.update(cx, |editor, cx| {
-                    editor.focus(cx);
-                    editor.select_all(&SelectAll, cx);
+                    window.focus(&editor.focus_handle(cx));
+                    editor.select_all(&SelectAll, window, cx);
                 })
             });
         }
 
-        self.scroll_to_assist(assist_id, cx);
+        self.scroll_to_assist(assist_id, window, cx);
     }
 
-    pub fn scroll_to_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) {
+    pub fn scroll_to_assist(
+        &mut self,
+        assist_id: InlineAssistId,
+        window: &mut Window,
+        cx: &mut App,
+    ) {
         let Some(assist) = self.assists.get(&assist_id) else {
             return;
         };
@@ -1026,7 +1089,7 @@ impl InlineAssistant {
 
         let position = assist.range.start;
         editor.update(cx, |editor, cx| {
-            editor.change_selections(None, cx, |selections| {
+            editor.change_selections(None, window, cx, |selections| {
                 selections.select_anchor_ranges([position..position])
             });
 
@@ -1042,7 +1105,7 @@ impl InlineAssistant {
                     .unwrap()
                     .0 as f32;
             } else {
-                let snapshot = editor.snapshot(cx);
+                let snapshot = editor.snapshot(window, cx);
                 let start_row = assist
                     .range
                     .start
@@ -1059,13 +1122,16 @@ impl InlineAssistant {
             let scroll_bottom = scroll_top + height_in_lines;
 
             if scroll_target_top < scroll_top {
-                editor.set_scroll_position(point(0., scroll_target_top), cx);
+                editor.set_scroll_position(point(0., scroll_target_top), window, cx);
             } else if scroll_target_bottom > scroll_bottom {
                 if (scroll_target_bottom - scroll_target_top) <= height_in_lines {
-                    editor
-                        .set_scroll_position(point(0., scroll_target_bottom - height_in_lines), cx);
+                    editor.set_scroll_position(
+                        point(0., scroll_target_bottom - height_in_lines),
+                        window,
+                        cx,
+                    );
                 } else {
-                    editor.set_scroll_position(point(0., scroll_target_top), cx);
+                    editor.set_scroll_position(point(0., scroll_target_top), window, cx);
                 }
             }
         });
@@ -1074,7 +1140,8 @@ impl InlineAssistant {
     fn unlink_assist_group(
         &mut self,
         assist_group_id: InlineAssistGroupId,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Vec<InlineAssistId> {
         let assist_group = self.assist_groups.get_mut(&assist_group_id).unwrap();
         assist_group.linked = false;
@@ -1083,13 +1150,13 @@ impl InlineAssistant {
             if let Some(editor_decorations) = assist.decorations.as_ref() {
                 editor_decorations
                     .prompt_editor
-                    .update(cx, |prompt_editor, cx| prompt_editor.unlink(cx));
+                    .update(cx, |prompt_editor, cx| prompt_editor.unlink(window, cx));
             }
         }
         assist_group.assist_ids.clone()
     }
 
-    pub fn start_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) {
+    pub fn start_assist(&mut self, assist_id: InlineAssistId, window: &mut Window, cx: &mut App) {
         let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
             assist
         } else {
@@ -1098,8 +1165,8 @@ impl InlineAssistant {
 
         let assist_group_id = assist.group_id;
         if self.assist_groups[&assist_group_id].linked {
-            for assist_id in self.unlink_assist_group(assist_group_id, cx) {
-                self.start_assist(assist_id, cx);
+            for assist_id in self.unlink_assist_group(assist_group_id, window, cx) {
+                self.start_assist(assist_id, window, cx);
             }
             return;
         }
@@ -1120,7 +1187,7 @@ impl InlineAssistant {
             .log_err();
     }
 
-    pub fn stop_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) {
+    pub fn stop_assist(&mut self, assist_id: InlineAssistId, cx: &mut App) {
         let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
             assist
         } else {
@@ -1130,7 +1197,7 @@ impl InlineAssistant {
         assist.codegen.update(cx, |codegen, cx| codegen.stop(cx));
     }
 
-    fn update_editor_highlights(&self, editor: &View<Editor>, cx: &mut WindowContext) {
+    fn update_editor_highlights(&self, editor: &Entity<Editor>, cx: &mut App) {
         let mut gutter_pending_ranges = Vec::new();
         let mut gutter_transformed_ranges = Vec::new();
         let mut foreground_ranges = Vec::new();
@@ -1223,9 +1290,10 @@ impl InlineAssistant {
 
     fn update_editor_blocks(
         &mut self,
-        editor: &View<Editor>,
+        editor: &Entity<Editor>,
         assist_id: InlineAssistId,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let Some(assist) = self.assists.get_mut(&assist_id) else {
             return;
@@ -1255,10 +1323,9 @@ impl InlineAssistant {
                     ))
                     .unwrap();
 
-                let deleted_lines_editor = cx.new_view(|cx| {
-                    let multi_buffer = cx.new_model(|_| {
-                        MultiBuffer::without_headers(language::Capability::ReadOnly)
-                    });
+                let deleted_lines_editor = cx.new(|cx| {
+                    let multi_buffer =
+                        cx.new(|_| MultiBuffer::without_headers(language::Capability::ReadOnly));
                     multi_buffer.update(cx, |multi_buffer, cx| {
                         multi_buffer.push_excerpts(
                             old_buffer.clone(),
@@ -1271,14 +1338,14 @@ impl InlineAssistant {
                     });
 
                     enum DeletedLines {}
-                    let mut editor = Editor::for_multibuffer(multi_buffer, None, true, cx);
+                    let mut editor = Editor::for_multibuffer(multi_buffer, None, true, window, cx);
                     editor.set_soft_wrap_mode(language::language_settings::SoftWrap::None, cx);
                     editor.set_show_wrap_guides(false, cx);
                     editor.set_show_gutter(false, cx);
                     editor.scroll_manager.set_forbid_vertical_scroll(true);
                     editor.set_show_scrollbars(false, cx);
                     editor.set_read_only(true);
-                    editor.set_show_inline_completions(Some(false), cx);
+                    editor.set_show_inline_completions(Some(false), window, cx);
                     editor.highlight_rows::<DeletedLines>(
                         Anchor::min()..Anchor::max(),
                         cx.theme().status().deleted_background,
@@ -1299,7 +1366,7 @@ impl InlineAssistant {
                             .block_mouse_down()
                             .bg(cx.theme().status().deleted_background)
                             .size_full()
-                            .h(height as f32 * cx.line_height())
+                            .h(height as f32 * cx.window.line_height())
                             .pl(cx.gutter_dimensions.full_width())
                             .child(deleted_lines_editor.clone())
                             .into_any_element()
@@ -1317,13 +1384,14 @@ impl InlineAssistant {
 
     fn resolve_inline_assist_target(
         workspace: &mut Workspace,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<InlineAssistTarget> {
         if let Some(terminal_panel) = workspace.panel::<TerminalPanel>(cx) {
             if terminal_panel
                 .read(cx)
                 .focus_handle(cx)
-                .contains_focused(cx)
+                .contains_focused(window, cx)
             {
                 if let Some(terminal_view) = terminal_panel.read(cx).pane().and_then(|pane| {
                     pane.read(cx)
@@ -1366,13 +1434,13 @@ struct InlineAssistScrollLock {
 
 impl EditorInlineAssists {
     #[allow(clippy::too_many_arguments)]
-    fn new(editor: &View<Editor>, cx: &mut WindowContext) -> Self {
+    fn new(editor: &Entity<Editor>, window: &mut Window, cx: &mut App) -> Self {
         let (highlight_updates_tx, mut highlight_updates_rx) = async_watch::channel(());
         Self {
             assist_ids: Vec::new(),
             scroll_lock: None,
             highlight_updates: highlight_updates_tx,
-            _update_highlights: cx.spawn(|mut cx| {
+            _update_highlights: cx.spawn(|cx| {
                 let editor = editor.downgrade();
                 async move {
                     while let Ok(()) = highlight_updates_rx.changed().await {
@@ -1385,47 +1453,43 @@ impl EditorInlineAssists {
                 }
             }),
             _subscriptions: vec![
-                cx.observe_release(editor, {
+                cx.observe_release_in(editor, window, {
                     let editor = editor.downgrade();
-                    |_, cx| {
+                    |_, window, cx| {
                         InlineAssistant::update_global(cx, |this, cx| {
-                            this.handle_editor_release(editor, cx);
+                            this.handle_editor_release(editor, window, cx);
                         })
                     }
                 }),
-                cx.observe(editor, move |editor, cx| {
+                window.observe(editor, cx, move |editor, window, cx| {
                     InlineAssistant::update_global(cx, |this, cx| {
-                        this.handle_editor_change(editor, cx)
+                        this.handle_editor_change(editor, window, cx)
                     })
                 }),
-                cx.subscribe(editor, move |editor, event, cx| {
+                window.subscribe(editor, cx, move |editor, event, window, cx| {
                     InlineAssistant::update_global(cx, |this, cx| {
-                        this.handle_editor_event(editor, event, cx)
+                        this.handle_editor_event(editor, event, window, cx)
                     })
                 }),
                 editor.update(cx, |editor, cx| {
-                    let editor_handle = cx.view().downgrade();
-                    editor.register_action(
-                        move |_: &editor::actions::Newline, cx: &mut WindowContext| {
-                            InlineAssistant::update_global(cx, |this, cx| {
-                                if let Some(editor) = editor_handle.upgrade() {
-                                    this.handle_editor_newline(editor, cx)
-                                }
-                            })
-                        },
-                    )
+                    let editor_handle = cx.model().downgrade();
+                    editor.register_action(move |_: &editor::actions::Newline, window, cx| {
+                        InlineAssistant::update_global(cx, |this, cx| {
+                            if let Some(editor) = editor_handle.upgrade() {
+                                this.handle_editor_newline(editor, window, cx)
+                            }
+                        })
+                    })
                 }),
                 editor.update(cx, |editor, cx| {
-                    let editor_handle = cx.view().downgrade();
-                    editor.register_action(
-                        move |_: &editor::actions::Cancel, cx: &mut WindowContext| {
-                            InlineAssistant::update_global(cx, |this, cx| {
-                                if let Some(editor) = editor_handle.upgrade() {
-                                    this.handle_editor_cancel(editor, cx)
-                                }
-                            })
-                        },
-                    )
+                    let editor_handle = cx.model().downgrade();
+                    editor.register_action(move |_: &editor::actions::Cancel, window, cx| {
+                        InlineAssistant::update_global(cx, |this, cx| {
+                            if let Some(editor) = editor_handle.upgrade() {
+                                this.handle_editor_cancel(editor, window, cx)
+                            }
+                        })
+                    })
                 }),
             ],
         }
@@ -1448,7 +1512,7 @@ impl InlineAssistGroup {
     }
 }
 
-fn build_assist_editor_renderer(editor: &View<PromptEditor<BufferCodegen>>) -> RenderBlock {
+fn build_assist_editor_renderer(editor: &Entity<PromptEditor<BufferCodegen>>) -> RenderBlock {
     let editor = editor.clone();
 
     Arc::new(move |cx: &mut BlockContext| {
@@ -1473,11 +1537,11 @@ impl InlineAssistGroupId {
 pub struct InlineAssist {
     group_id: InlineAssistGroupId,
     range: Range<Anchor>,
-    editor: WeakView<Editor>,
+    editor: WeakEntity<Editor>,
     decorations: Option<InlineAssistDecorations>,
-    codegen: Model<BufferCodegen>,
+    codegen: Entity<BufferCodegen>,
     _subscriptions: Vec<Subscription>,
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
 }
 
 impl InlineAssist {
@@ -1485,14 +1549,15 @@ impl InlineAssist {
     fn new(
         assist_id: InlineAssistId,
         group_id: InlineAssistGroupId,
-        editor: &View<Editor>,
-        prompt_editor: &View<PromptEditor<BufferCodegen>>,
+        editor: &Entity<Editor>,
+        prompt_editor: &Entity<PromptEditor<BufferCodegen>>,
         prompt_block_id: CustomBlockId,
         end_block_id: CustomBlockId,
         range: Range<Anchor>,
-        codegen: Model<BufferCodegen>,
-        workspace: WeakView<Workspace>,
-        cx: &mut WindowContext,
+        codegen: Entity<BufferCodegen>,
+        workspace: WeakEntity<Workspace>,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Self {
         let prompt_editor_focus_handle = prompt_editor.focus_handle(cx);
         InlineAssist {

crates/assistant2/src/inline_prompt_editor.rs 🔗

@@ -16,9 +16,8 @@ use editor::{
 use feature_flags::{FeatureFlagAppExt as _, ZedPro};
 use fs::Fs;
 use gpui::{
-    anchored, deferred, point, AnyElement, AppContext, ClickEvent, CursorStyle, EventEmitter,
-    FocusHandle, FocusableView, FontWeight, Model, Subscription, TextStyle, View, ViewContext,
-    WeakModel, WeakView, WindowContext,
+    anchored, deferred, point, AnyElement, App, ClickEvent, Context, CursorStyle, Entity,
+    EventEmitter, FocusHandle, Focusable, FontWeight, Subscription, TextStyle, WeakEntity, Window,
 };
 use language_model::{LanguageModel, LanguageModelRegistry};
 use language_model_selector::LanguageModelSelector;
@@ -35,12 +34,12 @@ use util::ResultExt;
 use workspace::Workspace;
 
 pub struct PromptEditor<T> {
-    pub editor: View<Editor>,
+    pub editor: Entity<Editor>,
     mode: PromptEditorMode,
-    context_store: Model<ContextStore>,
-    context_strip: View<ContextStrip>,
+    context_store: Entity<ContextStore>,
+    context_strip: Entity<ContextStrip>,
     context_picker_menu_handle: PopoverMenuHandle<ContextPicker>,
-    model_selector: View<AssistantModelSelector>,
+    model_selector: Entity<AssistantModelSelector>,
     model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
     edited_since_done: bool,
     prompt_history: VecDeque<String>,
@@ -56,7 +55,7 @@ pub struct PromptEditor<T> {
 impl<T: 'static> EventEmitter<PromptEditorEvent> for PromptEditor<T> {}
 
 impl<T: 'static> Render for PromptEditor<T> {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
         let mut buttons = Vec::new();
 
@@ -87,7 +86,7 @@ impl<T: 'static> Render for PromptEditor<T> {
             PromptEditorMode::Terminal { .. } => Pixels::from(8.0),
         };
 
-        buttons.extend(self.render_buttons(cx));
+        buttons.extend(self.render_buttons(window, cx));
 
         v_flex()
             .key_context("PromptEditor")
@@ -163,9 +162,7 @@ impl<T: 'static> Render for PromptEditor<T> {
                                     el.child(
                                         div()
                                             .id("error")
-                                            .tooltip(move |cx| {
-                                                Tooltip::text(error_message.clone(), cx)
-                                            })
+                                            .tooltip(Tooltip::text(error_message))
                                             .child(
                                                 Icon::new(IconName::XCircle)
                                                     .size(IconSize::Small)
@@ -179,7 +176,7 @@ impl<T: 'static> Render for PromptEditor<T> {
                         h_flex()
                             .w_full()
                             .justify_between()
-                            .child(div().flex_1().child(self.render_editor(cx)))
+                            .child(div().flex_1().child(self.render_editor(window, cx)))
                             .child(
                                 WithRemSize::new(ui_font_size)
                                     .flex()
@@ -209,8 +206,8 @@ impl<T: 'static> Render for PromptEditor<T> {
     }
 }
 
-impl<T: 'static> FocusableView for PromptEditor<T> {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl<T: 'static> Focusable for PromptEditor<T> {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.editor.focus_handle(cx)
     }
 }
@@ -218,47 +215,50 @@ impl<T: 'static> FocusableView for PromptEditor<T> {
 impl<T: 'static> PromptEditor<T> {
     const MAX_LINES: u8 = 8;
 
-    fn codegen_status<'a>(&'a self, cx: &'a AppContext) -> &'a CodegenStatus {
+    fn codegen_status<'a>(&'a self, cx: &'a App) -> &'a CodegenStatus {
         match &self.mode {
             PromptEditorMode::Buffer { codegen, .. } => codegen.read(cx).status(cx),
             PromptEditorMode::Terminal { codegen, .. } => &codegen.read(cx).status,
         }
     }
 
-    fn subscribe_to_editor(&mut self, cx: &mut ViewContext<Self>) {
+    fn subscribe_to_editor(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.editor_subscriptions.clear();
-        self.editor_subscriptions
-            .push(cx.subscribe(&self.editor, Self::handle_prompt_editor_events));
+        self.editor_subscriptions.push(cx.subscribe_in(
+            &self.editor,
+            window,
+            Self::handle_prompt_editor_events,
+        ));
     }
 
     pub fn set_show_cursor_when_unfocused(
         &mut self,
         show_cursor_when_unfocused: bool,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.editor.update(cx, |editor, cx| {
             editor.set_show_cursor_when_unfocused(show_cursor_when_unfocused, cx)
         });
     }
 
-    pub fn unlink(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn unlink(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let prompt = self.prompt(cx);
-        let focus = self.editor.focus_handle(cx).contains_focused(cx);
-        self.editor = cx.new_view(|cx| {
-            let mut editor = Editor::auto_height(Self::MAX_LINES as usize, cx);
+        let focus = self.editor.focus_handle(cx).contains_focused(window, cx);
+        self.editor = cx.new(|cx| {
+            let mut editor = Editor::auto_height(Self::MAX_LINES as usize, window, cx);
             editor.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
-            editor.set_placeholder_text(Self::placeholder_text(&self.mode, cx), cx);
+            editor.set_placeholder_text(Self::placeholder_text(&self.mode, window, cx), cx);
             editor.set_placeholder_text("Add a prompt…", cx);
-            editor.set_text(prompt, cx);
+            editor.set_text(prompt, window, cx);
             if focus {
-                editor.focus(cx);
+                window.focus(&editor.focus_handle(cx));
             }
             editor
         });
-        self.subscribe_to_editor(cx);
+        self.subscribe_to_editor(window, cx);
     }
 
-    pub fn placeholder_text(mode: &PromptEditorMode, cx: &WindowContext) -> String {
+    pub fn placeholder_text(mode: &PromptEditorMode, window: &mut Window, cx: &mut App) -> String {
         let action = match mode {
             PromptEditorMode::Buffer { codegen, .. } => {
                 if codegen.read(cx).is_insertion {
@@ -271,36 +271,42 @@ impl<T: 'static> PromptEditor<T> {
         };
 
         let assistant_panel_keybinding =
-            ui::text_for_action(&zed_actions::assistant::ToggleFocus, cx)
+            ui::text_for_action(&zed_actions::assistant::ToggleFocus, window)
                 .map(|keybinding| format!("{keybinding} to chat ― "))
                 .unwrap_or_default();
 
         format!("{action}… ({assistant_panel_keybinding}↓↑ for history)")
     }
 
-    pub fn prompt(&self, cx: &AppContext) -> String {
+    pub fn prompt(&self, cx: &App) -> String {
         self.editor.read(cx).text(cx)
     }
 
-    fn toggle_rate_limit_notice(&mut self, _: &ClickEvent, cx: &mut ViewContext<Self>) {
+    fn toggle_rate_limit_notice(
+        &mut self,
+        _: &ClickEvent,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.show_rate_limit_notice = !self.show_rate_limit_notice;
         if self.show_rate_limit_notice {
-            cx.focus_view(&self.editor);
+            window.focus(&self.editor.focus_handle(cx));
         }
         cx.notify();
     }
 
     fn handle_prompt_editor_events(
         &mut self,
-        _: View<Editor>,
+        _: &Entity<Editor>,
         event: &EditorEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match event {
             EditorEvent::Edited { .. } => {
-                if let Some(workspace) = cx.window_handle().downcast::<Workspace>() {
+                if let Some(workspace) = window.window_handle().downcast::<Workspace>() {
                     workspace
-                        .update(cx, |workspace, cx| {
+                        .update(cx, |workspace, _, cx| {
                             let is_via_ssh = workspace
                                 .project()
                                 .update(cx, |project, _| project.is_via_ssh());
@@ -334,20 +340,40 @@ impl<T: 'static> PromptEditor<T> {
         }
     }
 
-    fn toggle_context_picker(&mut self, _: &ToggleContextPicker, cx: &mut ViewContext<Self>) {
-        self.context_picker_menu_handle.toggle(cx);
+    fn toggle_context_picker(
+        &mut self,
+        _: &ToggleContextPicker,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.context_picker_menu_handle.toggle(window, cx);
     }
 
-    fn toggle_model_selector(&mut self, _: &ToggleModelSelector, cx: &mut ViewContext<Self>) {
-        self.model_selector_menu_handle.toggle(cx);
+    fn toggle_model_selector(
+        &mut self,
+        _: &ToggleModelSelector,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.model_selector_menu_handle.toggle(window, cx);
     }
 
-    pub fn remove_all_context(&mut self, _: &RemoveAllContext, cx: &mut ViewContext<Self>) {
+    pub fn remove_all_context(
+        &mut self,
+        _: &RemoveAllContext,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.context_store.update(cx, |store, _cx| store.clear());
         cx.notify();
     }
 
-    fn cancel(&mut self, _: &editor::actions::Cancel, cx: &mut ViewContext<Self>) {
+    fn cancel(
+        &mut self,
+        _: &editor::actions::Cancel,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         match self.codegen_status(cx) {
             CodegenStatus::Idle | CodegenStatus::Done | CodegenStatus::Error(_) => {
                 cx.emit(PromptEditorEvent::CancelRequested);
@@ -358,7 +384,7 @@ impl<T: 'static> PromptEditor<T> {
         }
     }
 
-    fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
+    fn confirm(&mut self, _: &menu::Confirm, _window: &mut Window, cx: &mut Context<Self>) {
         match self.codegen_status(cx) {
             CodegenStatus::Idle => {
                 cx.emit(PromptEditorEvent::StartRequested);
@@ -379,49 +405,49 @@ impl<T: 'static> PromptEditor<T> {
         }
     }
 
-    fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
+    fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(ix) = self.prompt_history_ix {
             if ix > 0 {
                 self.prompt_history_ix = Some(ix - 1);
                 let prompt = self.prompt_history[ix - 1].as_str();
                 self.editor.update(cx, |editor, cx| {
-                    editor.set_text(prompt, cx);
-                    editor.move_to_beginning(&Default::default(), cx);
+                    editor.set_text(prompt, window, cx);
+                    editor.move_to_beginning(&Default::default(), window, cx);
                 });
             }
         } else if !self.prompt_history.is_empty() {
             self.prompt_history_ix = Some(self.prompt_history.len() - 1);
             let prompt = self.prompt_history[self.prompt_history.len() - 1].as_str();
             self.editor.update(cx, |editor, cx| {
-                editor.set_text(prompt, cx);
-                editor.move_to_beginning(&Default::default(), cx);
+                editor.set_text(prompt, window, cx);
+                editor.move_to_beginning(&Default::default(), window, cx);
             });
         }
     }
 
-    fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
+    fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(ix) = self.prompt_history_ix {
             if ix < self.prompt_history.len() - 1 {
                 self.prompt_history_ix = Some(ix + 1);
                 let prompt = self.prompt_history[ix + 1].as_str();
                 self.editor.update(cx, |editor, cx| {
-                    editor.set_text(prompt, cx);
-                    editor.move_to_end(&Default::default(), cx)
+                    editor.set_text(prompt, window, cx);
+                    editor.move_to_end(&Default::default(), window, cx)
                 });
             } else {
                 self.prompt_history_ix = None;
                 let prompt = self.pending_prompt.as_str();
                 self.editor.update(cx, |editor, cx| {
-                    editor.set_text(prompt, cx);
-                    editor.move_to_end(&Default::default(), cx)
+                    editor.set_text(prompt, window, cx);
+                    editor.move_to_end(&Default::default(), window, cx)
                 });
             }
         } else {
-            cx.focus_view(&self.context_strip);
+            self.context_strip.focus_handle(cx).focus(window);
         }
     }
 
-    fn render_buttons(&self, cx: &mut ViewContext<Self>) -> Vec<AnyElement> {
+    fn render_buttons(&self, _window: &mut Window, cx: &mut Context<Self>) -> Vec<AnyElement> {
         let mode = match &self.mode {
             PromptEditorMode::Buffer { codegen, .. } => {
                 let codegen = codegen.read(cx);
@@ -443,21 +469,22 @@ impl<T: 'static> PromptEditor<T> {
                     .icon(IconName::Return)
                     .icon_size(IconSize::XSmall)
                     .icon_color(Color::Muted)
-                    .on_click(cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::StartRequested)))
+                    .on_click(cx.listener(|_, _, _, cx| cx.emit(PromptEditorEvent::StartRequested)))
                     .into_any_element()]
             }
             CodegenStatus::Pending => vec![IconButton::new("stop", IconName::Stop)
                 .icon_color(Color::Error)
                 .shape(IconButtonShape::Square)
-                .tooltip(move |cx| {
+                .tooltip(move |window, cx| {
                     Tooltip::with_meta(
                         mode.tooltip_interrupt(),
                         Some(&menu::Cancel),
                         "Changes won't be discarded",
+                        window,
                         cx,
                     )
                 })
-                .on_click(cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::StopRequested)))
+                .on_click(cx.listener(|_, _, _, cx| cx.emit(PromptEditorEvent::StopRequested)))
                 .into_any_element()],
             CodegenStatus::Done | CodegenStatus::Error(_) => {
                 let has_error = matches!(codegen_status, CodegenStatus::Error(_));
@@ -465,15 +492,16 @@ impl<T: 'static> PromptEditor<T> {
                     vec![IconButton::new("restart", IconName::RotateCw)
                         .icon_color(Color::Info)
                         .shape(IconButtonShape::Square)
-                        .tooltip(move |cx| {
+                        .tooltip(move |window, cx| {
                             Tooltip::with_meta(
                                 mode.tooltip_restart(),
                                 Some(&menu::Confirm),
                                 "Changes will be discarded",
+                                window,
                                 cx,
                             )
                         })
-                        .on_click(cx.listener(|_, _, cx| {
+                        .on_click(cx.listener(|_, _, _, cx| {
                             cx.emit(PromptEditorEvent::StartRequested);
                         }))
                         .into_any_element()]
@@ -481,10 +509,10 @@ impl<T: 'static> PromptEditor<T> {
                     let accept = IconButton::new("accept", IconName::Check)
                         .icon_color(Color::Info)
                         .shape(IconButtonShape::Square)
-                        .tooltip(move |cx| {
-                            Tooltip::for_action(mode.tooltip_accept(), &menu::Confirm, cx)
+                        .tooltip(move |window, cx| {
+                            Tooltip::for_action(mode.tooltip_accept(), &menu::Confirm, window, cx)
                         })
-                        .on_click(cx.listener(|_, _, cx| {
+                        .on_click(cx.listener(|_, _, _, cx| {
                             cx.emit(PromptEditorEvent::ConfirmRequested { execute: false });
                         }))
                         .into_any_element();
@@ -495,14 +523,15 @@ impl<T: 'static> PromptEditor<T> {
                             IconButton::new("confirm", IconName::Play)
                                 .icon_color(Color::Info)
                                 .shape(IconButtonShape::Square)
-                                .tooltip(|cx| {
+                                .tooltip(|window, cx| {
                                     Tooltip::for_action(
                                         "Execute Generated Command",
                                         &menu::SecondaryConfirm,
+                                        window,
                                         cx,
                                     )
                                 })
-                                .on_click(cx.listener(|_, _, cx| {
+                                .on_click(cx.listener(|_, _, _, cx| {
                                     cx.emit(PromptEditorEvent::ConfirmRequested { execute: true });
                                 }))
                                 .into_any_element(),
@@ -514,7 +543,12 @@ impl<T: 'static> PromptEditor<T> {
         }
     }
 
-    fn cycle_prev(&mut self, _: &CyclePreviousInlineAssist, cx: &mut ViewContext<Self>) {
+    fn cycle_prev(
+        &mut self,
+        _: &CyclePreviousInlineAssist,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         match &self.mode {
             PromptEditorMode::Buffer { codegen, .. } => {
                 codegen.update(cx, |codegen, cx| codegen.cycle_prev(cx));
@@ -525,7 +559,7 @@ impl<T: 'static> PromptEditor<T> {
         }
     }
 
-    fn cycle_next(&mut self, _: &CycleNextInlineAssist, cx: &mut ViewContext<Self>) {
+    fn cycle_next(&mut self, _: &CycleNextInlineAssist, _: &mut Window, cx: &mut Context<Self>) {
         match &self.mode {
             PromptEditorMode::Buffer { codegen, .. } => {
                 codegen.update(cx, |codegen, cx| codegen.cycle_next(cx));
@@ -536,16 +570,16 @@ impl<T: 'static> PromptEditor<T> {
         }
     }
 
-    fn render_close_button(&self, cx: &ViewContext<Self>) -> AnyElement {
+    fn render_close_button(&self, cx: &mut Context<Self>) -> AnyElement {
         IconButton::new("cancel", IconName::Close)
             .icon_color(Color::Muted)
             .shape(IconButtonShape::Square)
-            .tooltip(|cx| Tooltip::text("Close Assistant", cx))
-            .on_click(cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::CancelRequested)))
+            .tooltip(Tooltip::text("Close Assistant"))
+            .on_click(cx.listener(|_, _, _, cx| cx.emit(PromptEditorEvent::CancelRequested)))
             .into_any_element()
     }
 
-    fn render_cycle_controls(&self, codegen: &BufferCodegen, cx: &ViewContext<Self>) -> AnyElement {
+    fn render_cycle_controls(&self, codegen: &BufferCodegen, cx: &Context<Self>) -> AnyElement {
         let disabled = matches!(codegen.status(cx), CodegenStatus::Idle);
 
         let model_registry = LanguageModelRegistry::read_global(cx);
@@ -585,13 +619,13 @@ impl<T: 'static> PromptEditor<T> {
                     .shape(IconButtonShape::Square)
                     .tooltip({
                         let focus_handle = self.editor.focus_handle(cx);
-                        move |cx| {
-                            cx.new_view(|cx| {
+                        move |window, cx| {
+                            cx.new(|_| {
                                 let mut tooltip = Tooltip::new("Previous Alternative").key_binding(
                                     KeyBinding::for_action_in(
                                         &CyclePreviousInlineAssist,
                                         &focus_handle,
-                                        cx,
+                                        window,
                                     ),
                                 );
                                 if !disabled && current_index != 0 {
@@ -602,8 +636,8 @@ impl<T: 'static> PromptEditor<T> {
                             .into()
                         }
                     })
-                    .on_click(cx.listener(|this, _, cx| {
-                        this.cycle_prev(&CyclePreviousInlineAssist, cx);
+                    .on_click(cx.listener(|this, _, window, cx| {
+                        this.cycle_prev(&CyclePreviousInlineAssist, window, cx);
                     })),
             )
             .child(
@@ -626,13 +660,13 @@ impl<T: 'static> PromptEditor<T> {
                     .shape(IconButtonShape::Square)
                     .tooltip({
                         let focus_handle = self.editor.focus_handle(cx);
-                        move |cx| {
-                            cx.new_view(|cx| {
+                        move |window, cx| {
+                            cx.new(|_| {
                                 let mut tooltip = Tooltip::new("Next Alternative").key_binding(
                                     KeyBinding::for_action_in(
                                         &CycleNextInlineAssist,
                                         &focus_handle,
-                                        cx,
+                                        window,
                                     ),
                                 );
                                 if !disabled && current_index != total_models - 1 {
@@ -643,14 +677,14 @@ impl<T: 'static> PromptEditor<T> {
                             .into()
                         }
                     })
-                    .on_click(
-                        cx.listener(|this, _, cx| this.cycle_next(&CycleNextInlineAssist, cx)),
-                    ),
+                    .on_click(cx.listener(|this, _, window, cx| {
+                        this.cycle_next(&CycleNextInlineAssist, window, cx)
+                    })),
             )
             .into_any_element()
     }
 
-    fn render_rate_limit_notice(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render_rate_limit_notice(&self, cx: &mut Context<Self>) -> impl IntoElement {
         Popover::new().child(
             v_flex()
                 .occlude()
@@ -674,7 +708,7 @@ impl<T: 'static> PromptEditor<T> {
                             } else {
                                 ui::ToggleState::Unselected
                             },
-                            |selection, cx| {
+                            |selection, _, cx| {
                                 let is_dismissed = match selection {
                                     ui::ToggleState::Unselected => false,
                                     ui::ToggleState::Indeterminate => return,
@@ -693,10 +727,11 @@ impl<T: 'static> PromptEditor<T> {
                                         .on_click(cx.listener(Self::toggle_rate_limit_notice)),
                                 )
                                 .child(Button::new("more-info", "More Info").on_click(
-                                    |_event, cx| {
-                                        cx.dispatch_action(Box::new(
-                                            zed_actions::OpenAccountSettings,
-                                        ))
+                                    |_event, window, cx| {
+                                        window.dispatch_action(
+                                            Box::new(zed_actions::OpenAccountSettings),
+                                            cx,
+                                        )
                                     },
                                 )),
                         ),
@@ -704,9 +739,9 @@ impl<T: 'static> PromptEditor<T> {
         )
     }
 
-    fn render_editor(&mut self, cx: &mut ViewContext<Self>) -> AnyElement {
+    fn render_editor(&mut self, window: &mut Window, cx: &mut Context<Self>) -> AnyElement {
         let font_size = TextSize::Default.rems(cx);
-        let line_height = font_size.to_pixels(cx.rem_size()) * 1.3;
+        let line_height = font_size.to_pixels(window.rem_size()) * 1.3;
 
         div()
             .key_context("InlineAssistEditor")
@@ -740,17 +775,15 @@ impl<T: 'static> PromptEditor<T> {
 
     fn handle_context_strip_event(
         &mut self,
-        _context_strip: View<ContextStrip>,
+        _context_strip: &Entity<ContextStrip>,
         event: &ContextStripEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match event {
             ContextStripEvent::PickerDismissed
             | ContextStripEvent::BlurredEmpty
-            | ContextStripEvent::BlurredUp => {
-                let editor_focus_handle = self.editor.focus_handle(cx);
-                cx.focus(&editor_focus_handle);
-            }
+            | ContextStripEvent::BlurredUp => self.editor.focus_handle(cx).focus(window),
             ContextStripEvent::BlurredDown => {}
         }
     }
@@ -759,12 +792,12 @@ impl<T: 'static> PromptEditor<T> {
 pub enum PromptEditorMode {
     Buffer {
         id: InlineAssistId,
-        codegen: Model<BufferCodegen>,
+        codegen: Entity<BufferCodegen>,
         gutter_dimensions: Arc<Mutex<GutterDimensions>>,
     },
     Terminal {
         id: TerminalInlineAssistId,
-        codegen: Model<TerminalCodegen>,
+        codegen: Entity<TerminalCodegen>,
         height_in_lines: u8,
     },
 }
@@ -795,13 +828,14 @@ impl PromptEditor<BufferCodegen> {
         id: InlineAssistId,
         gutter_dimensions: Arc<Mutex<GutterDimensions>>,
         prompt_history: VecDeque<String>,
-        prompt_buffer: Model<MultiBuffer>,
-        codegen: Model<BufferCodegen>,
+        prompt_buffer: Entity<MultiBuffer>,
+        codegen: Entity<BufferCodegen>,
         fs: Arc<dyn Fs>,
-        context_store: Model<ContextStore>,
-        workspace: WeakView<Workspace>,
-        thread_store: Option<WeakModel<ThreadStore>>,
-        cx: &mut ViewContext<PromptEditor<BufferCodegen>>,
+        context_store: Entity<ContextStore>,
+        workspace: WeakEntity<Workspace>,
+        thread_store: Option<WeakEntity<ThreadStore>>,
+        window: &mut Window,
+        cx: &mut Context<PromptEditor<BufferCodegen>>,
     ) -> PromptEditor<BufferCodegen> {
         let codegen_subscription = cx.observe(&codegen, Self::handle_codegen_changed);
         let mode = PromptEditorMode::Buffer {
@@ -810,7 +844,7 @@ impl PromptEditor<BufferCodegen> {
             gutter_dimensions,
         };
 
-        let prompt_editor = cx.new_view(|cx| {
+        let prompt_editor = cx.new(|cx| {
             let mut editor = Editor::new(
                 EditorMode::AutoHeight {
                     max_lines: Self::MAX_LINES as usize,
@@ -818,6 +852,7 @@ impl PromptEditor<BufferCodegen> {
                 prompt_buffer,
                 None,
                 false,
+                window,
                 cx,
             );
             editor.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
@@ -825,13 +860,13 @@ impl PromptEditor<BufferCodegen> {
             // always show the cursor (even when it isn't focused) because
             // typing in one will make what you typed appear in all of them.
             editor.set_show_cursor_when_unfocused(true, cx);
-            editor.set_placeholder_text(Self::placeholder_text(&mode, cx), cx);
+            editor.set_placeholder_text(Self::placeholder_text(&mode, window, cx), cx);
             editor
         });
         let context_picker_menu_handle = PopoverMenuHandle::default();
         let model_selector_menu_handle = PopoverMenuHandle::default();
 
-        let context_strip = cx.new_view(|cx| {
+        let context_strip = cx.new(|cx| {
             ContextStrip::new(
                 context_store.clone(),
                 workspace.clone(),
@@ -839,23 +874,25 @@ impl PromptEditor<BufferCodegen> {
                 thread_store.clone(),
                 context_picker_menu_handle.clone(),
                 SuggestContextKind::Thread,
+                window,
                 cx,
             )
         });
 
         let context_strip_subscription =
-            cx.subscribe(&context_strip, Self::handle_context_strip_event);
+            cx.subscribe_in(&context_strip, window, Self::handle_context_strip_event);
 
         let mut this: PromptEditor<BufferCodegen> = PromptEditor {
             editor: prompt_editor.clone(),
             context_store,
             context_strip,
             context_picker_menu_handle,
-            model_selector: cx.new_view(|cx| {
+            model_selector: cx.new(|cx| {
                 AssistantModelSelector::new(
                     fs,
                     model_selector_menu_handle.clone(),
                     prompt_editor.focus_handle(cx),
+                    window,
                     cx,
                 )
             }),
@@ -872,14 +909,14 @@ impl PromptEditor<BufferCodegen> {
             _phantom: Default::default(),
         };
 
-        this.subscribe_to_editor(cx);
+        this.subscribe_to_editor(window, cx);
         this
     }
 
     fn handle_codegen_changed(
         &mut self,
-        _: Model<BufferCodegen>,
-        cx: &mut ViewContext<PromptEditor<BufferCodegen>>,
+        _: Entity<BufferCodegen>,
+        cx: &mut Context<PromptEditor<BufferCodegen>>,
     ) {
         match self.codegen_status(cx) {
             CodegenStatus::Idle => {
@@ -918,7 +955,7 @@ impl PromptEditor<BufferCodegen> {
         }
     }
 
-    pub fn codegen(&self) -> &Model<BufferCodegen> {
+    pub fn codegen(&self) -> &Entity<BufferCodegen> {
         match &self.mode {
             PromptEditorMode::Buffer { codegen, .. } => codegen,
             PromptEditorMode::Terminal { .. } => unreachable!(),
@@ -951,13 +988,14 @@ impl PromptEditor<TerminalCodegen> {
     pub fn new_terminal(
         id: TerminalInlineAssistId,
         prompt_history: VecDeque<String>,
-        prompt_buffer: Model<MultiBuffer>,
-        codegen: Model<TerminalCodegen>,
+        prompt_buffer: Entity<MultiBuffer>,
+        codegen: Entity<TerminalCodegen>,
         fs: Arc<dyn Fs>,
-        context_store: Model<ContextStore>,
-        workspace: WeakView<Workspace>,
-        thread_store: Option<WeakModel<ThreadStore>>,
-        cx: &mut ViewContext<Self>,
+        context_store: Entity<ContextStore>,
+        workspace: WeakEntity<Workspace>,
+        thread_store: Option<WeakEntity<ThreadStore>>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let codegen_subscription = cx.observe(&codegen, Self::handle_codegen_changed);
         let mode = PromptEditorMode::Terminal {
@@ -966,7 +1004,7 @@ impl PromptEditor<TerminalCodegen> {
             height_in_lines: 1,
         };
 
-        let prompt_editor = cx.new_view(|cx| {
+        let prompt_editor = cx.new(|cx| {
             let mut editor = Editor::new(
                 EditorMode::AutoHeight {
                     max_lines: Self::MAX_LINES as usize,
@@ -974,16 +1012,17 @@ impl PromptEditor<TerminalCodegen> {
                 prompt_buffer,
                 None,
                 false,
+                window,
                 cx,
             );
             editor.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
-            editor.set_placeholder_text(Self::placeholder_text(&mode, cx), cx);
+            editor.set_placeholder_text(Self::placeholder_text(&mode, window, cx), cx);
             editor
         });
         let context_picker_menu_handle = PopoverMenuHandle::default();
         let model_selector_menu_handle = PopoverMenuHandle::default();
 
-        let context_strip = cx.new_view(|cx| {
+        let context_strip = cx.new(|cx| {
             ContextStrip::new(
                 context_store.clone(),
                 workspace.clone(),
@@ -991,23 +1030,25 @@ impl PromptEditor<TerminalCodegen> {
                 thread_store.clone(),
                 context_picker_menu_handle.clone(),
                 SuggestContextKind::Thread,
+                window,
                 cx,
             )
         });
 
         let context_strip_subscription =
-            cx.subscribe(&context_strip, Self::handle_context_strip_event);
+            cx.subscribe_in(&context_strip, window, Self::handle_context_strip_event);
 
         let mut this = Self {
             editor: prompt_editor.clone(),
             context_store,
             context_strip,
             context_picker_menu_handle,
-            model_selector: cx.new_view(|cx| {
+            model_selector: cx.new(|cx| {
                 AssistantModelSelector::new(
                     fs,
                     model_selector_menu_handle.clone(),
                     prompt_editor.focus_handle(cx),
+                    window,
                     cx,
                 )
             }),
@@ -1024,11 +1065,11 @@ impl PromptEditor<TerminalCodegen> {
             _phantom: Default::default(),
         };
         this.count_lines(cx);
-        this.subscribe_to_editor(cx);
+        this.subscribe_to_editor(window, cx);
         this
     }
 
-    fn count_lines(&mut self, cx: &mut ViewContext<Self>) {
+    fn count_lines(&mut self, cx: &mut Context<Self>) {
         let height_in_lines = cmp::max(
             2, // Make the editor at least two lines tall, to account for padding and buttons.
             cmp::min(
@@ -1052,7 +1093,7 @@ impl PromptEditor<TerminalCodegen> {
         }
     }
 
-    fn handle_codegen_changed(&mut self, _: Model<TerminalCodegen>, cx: &mut ViewContext<Self>) {
+    fn handle_codegen_changed(&mut self, _: Entity<TerminalCodegen>, cx: &mut Context<Self>) {
         match &self.codegen().read(cx).status {
             CodegenStatus::Idle => {
                 self.editor
@@ -1070,7 +1111,7 @@ impl PromptEditor<TerminalCodegen> {
         }
     }
 
-    pub fn codegen(&self) -> &Model<TerminalCodegen> {
+    pub fn codegen(&self) -> &Entity<TerminalCodegen> {
         match &self.mode {
             PromptEditorMode::Buffer { .. } => unreachable!(),
             PromptEditorMode::Terminal { codegen, .. } => codegen,
@@ -1094,7 +1135,7 @@ fn dismissed_rate_limit_notice() -> bool {
         .map_or(false, |s| s.is_some())
 }
 
-fn set_rate_limit_notice_dismissed(is_dismissed: bool, cx: &mut AppContext) {
+fn set_rate_limit_notice_dismissed(is_dismissed: bool, cx: &mut App) {
     db::write_and_log(cx, move || async move {
         if is_dismissed {
             db::kvp::KEY_VALUE_STORE

crates/assistant2/src/message_editor.rs 🔗

@@ -4,8 +4,8 @@ use editor::actions::MoveUp;
 use editor::{Editor, EditorElement, EditorEvent, EditorStyle};
 use fs::Fs;
 use gpui::{
-    pulsating_between, Animation, AnimationExt, AppContext, DismissEvent, FocusableView, Model,
-    Subscription, TextStyle, View, WeakModel, WeakView,
+    pulsating_between, Animation, AnimationExt, App, DismissEvent, Entity, Focusable, Subscription,
+    TextStyle, WeakEntity,
 };
 use language_model::{LanguageModelRegistry, LanguageModelRequestTool};
 use language_model_selector::LanguageModelSelector;
@@ -27,14 +27,14 @@ use crate::thread_store::ThreadStore;
 use crate::{Chat, ChatMode, RemoveAllContext, ToggleContextPicker, ToggleModelSelector};
 
 pub struct MessageEditor {
-    thread: Model<Thread>,
-    editor: View<Editor>,
-    context_store: Model<ContextStore>,
-    context_strip: View<ContextStrip>,
+    thread: Entity<Thread>,
+    editor: Entity<Editor>,
+    context_store: Entity<ContextStore>,
+    context_strip: Entity<ContextStrip>,
     context_picker_menu_handle: PopoverMenuHandle<ContextPicker>,
-    inline_context_picker: View<ContextPicker>,
+    inline_context_picker: Entity<ContextPicker>,
     inline_context_picker_menu_handle: PopoverMenuHandle<ContextPicker>,
-    model_selector: View<AssistantModelSelector>,
+    model_selector: Entity<AssistantModelSelector>,
     model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
     use_tools: bool,
     _subscriptions: Vec<Subscription>,
@@ -43,36 +43,38 @@ pub struct MessageEditor {
 impl MessageEditor {
     pub fn new(
         fs: Arc<dyn Fs>,
-        workspace: WeakView<Workspace>,
-        thread_store: WeakModel<ThreadStore>,
-        thread: Model<Thread>,
-        cx: &mut ViewContext<Self>,
+        workspace: WeakEntity<Workspace>,
+        thread_store: WeakEntity<ThreadStore>,
+        thread: Entity<Thread>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
-        let context_store = cx.new_model(|_cx| ContextStore::new(workspace.clone()));
+        let context_store = cx.new(|_cx| ContextStore::new(workspace.clone()));
         let context_picker_menu_handle = PopoverMenuHandle::default();
         let inline_context_picker_menu_handle = PopoverMenuHandle::default();
         let model_selector_menu_handle = PopoverMenuHandle::default();
 
-        let editor = cx.new_view(|cx| {
-            let mut editor = Editor::auto_height(10, cx);
+        let editor = cx.new(|cx| {
+            let mut editor = Editor::auto_height(10, window, cx);
             editor.set_placeholder_text("Ask anything, @ to mention, ↑ to select", cx);
             editor.set_show_indent_guides(false, cx);
 
             editor
         });
 
-        let inline_context_picker = cx.new_view(|cx| {
+        let inline_context_picker = cx.new(|cx| {
             ContextPicker::new(
                 workspace.clone(),
                 Some(thread_store.clone()),
                 context_store.downgrade(),
                 editor.downgrade(),
                 ConfirmBehavior::Close,
+                window,
                 cx,
             )
         });
 
-        let context_strip = cx.new_view(|cx| {
+        let context_strip = cx.new(|cx| {
             ContextStrip::new(
                 context_store.clone(),
                 workspace.clone(),
@@ -80,17 +82,19 @@ impl MessageEditor {
                 Some(thread_store.clone()),
                 context_picker_menu_handle.clone(),
                 SuggestContextKind::File,
+                window,
                 cx,
             )
         });
 
         let subscriptions = vec![
-            cx.subscribe(&editor, Self::handle_editor_event),
-            cx.subscribe(
+            cx.subscribe_in(&editor, window, Self::handle_editor_event),
+            cx.subscribe_in(
                 &inline_context_picker,
+                window,
                 Self::handle_inline_context_picker_event,
             ),
-            cx.subscribe(&context_strip, Self::handle_context_strip_event),
+            cx.subscribe_in(&context_strip, window, Self::handle_context_strip_event),
         ];
 
         Self {
@@ -101,11 +105,12 @@ impl MessageEditor {
             context_picker_menu_handle,
             inline_context_picker,
             inline_context_picker_menu_handle,
-            model_selector: cx.new_view(|cx| {
+            model_selector: cx.new(|cx| {
                 AssistantModelSelector::new(
                     fs,
                     model_selector_menu_handle.clone(),
                     editor.focus_handle(cx),
+                    window,
                     cx,
                 )
             }),
@@ -115,39 +120,59 @@ impl MessageEditor {
         }
     }
 
-    fn toggle_model_selector(&mut self, _: &ToggleModelSelector, cx: &mut ViewContext<Self>) {
-        self.model_selector_menu_handle.toggle(cx)
+    fn toggle_model_selector(
+        &mut self,
+        _: &ToggleModelSelector,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.model_selector_menu_handle.toggle(window, cx)
     }
 
-    fn toggle_chat_mode(&mut self, _: &ChatMode, cx: &mut ViewContext<Self>) {
+    fn toggle_chat_mode(&mut self, _: &ChatMode, _window: &mut Window, cx: &mut Context<Self>) {
         self.use_tools = !self.use_tools;
         cx.notify();
     }
 
-    fn toggle_context_picker(&mut self, _: &ToggleContextPicker, cx: &mut ViewContext<Self>) {
-        self.context_picker_menu_handle.toggle(cx);
+    fn toggle_context_picker(
+        &mut self,
+        _: &ToggleContextPicker,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.context_picker_menu_handle.toggle(window, cx);
     }
 
-    pub fn remove_all_context(&mut self, _: &RemoveAllContext, cx: &mut ViewContext<Self>) {
+    pub fn remove_all_context(
+        &mut self,
+        _: &RemoveAllContext,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.context_store.update(cx, |store, _cx| store.clear());
         cx.notify();
     }
 
-    fn chat(&mut self, _: &Chat, cx: &mut ViewContext<Self>) {
-        self.send_to_model(RequestKind::Chat, cx);
+    fn chat(&mut self, _: &Chat, window: &mut Window, cx: &mut Context<Self>) {
+        self.send_to_model(RequestKind::Chat, window, cx);
     }
 
-    fn is_editor_empty(&self, cx: &AppContext) -> bool {
+    fn is_editor_empty(&self, cx: &App) -> bool {
         self.editor.read(cx).text(cx).is_empty()
     }
 
-    fn is_model_selected(&self, cx: &AppContext) -> bool {
+    fn is_model_selected(&self, cx: &App) -> bool {
         LanguageModelRegistry::read_global(cx)
             .active_model()
             .is_some()
     }
 
-    fn send_to_model(&mut self, request_kind: RequestKind, cx: &mut ViewContext<Self>) {
+    fn send_to_model(
+        &mut self,
+        request_kind: RequestKind,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let provider = LanguageModelRegistry::read_global(cx).active_provider();
         if provider
             .as_ref()
@@ -164,7 +189,7 @@ impl MessageEditor {
 
         let user_message = self.editor.update(cx, |editor, cx| {
             let text = editor.text(cx);
-            editor.clear(cx);
+            editor.clear(window, cx);
             text
         });
 
@@ -203,9 +228,10 @@ impl MessageEditor {
 
     fn handle_editor_event(
         &mut self,
-        editor: View<Editor>,
+        editor: &Entity<Editor>,
         event: &EditorEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match event {
             EditorEvent::SelectionsChanged { .. } => {
@@ -216,7 +242,7 @@ impl MessageEditor {
                         let behind_cursor = Point::new(newest_cursor.row, newest_cursor.column - 1);
                         let char_behind_cursor = snapshot.chars_at(behind_cursor).next();
                         if char_behind_cursor == Some('@') {
-                            self.inline_context_picker_menu_handle.show(cx);
+                            self.inline_context_picker_menu_handle.show(window, cx);
                         }
                     }
                 });
@@ -227,52 +253,54 @@ impl MessageEditor {
 
     fn handle_inline_context_picker_event(
         &mut self,
-        _inline_context_picker: View<ContextPicker>,
+        _inline_context_picker: &Entity<ContextPicker>,
         _event: &DismissEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let editor_focus_handle = self.editor.focus_handle(cx);
-        cx.focus(&editor_focus_handle);
+        window.focus(&editor_focus_handle);
     }
 
     fn handle_context_strip_event(
         &mut self,
-        _context_strip: View<ContextStrip>,
+        _context_strip: &Entity<ContextStrip>,
         event: &ContextStripEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match event {
             ContextStripEvent::PickerDismissed
             | ContextStripEvent::BlurredEmpty
             | ContextStripEvent::BlurredDown => {
                 let editor_focus_handle = self.editor.focus_handle(cx);
-                cx.focus(&editor_focus_handle);
+                window.focus(&editor_focus_handle);
             }
             ContextStripEvent::BlurredUp => {}
         }
     }
 
-    fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
+    fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
         if self.context_picker_menu_handle.is_deployed()
             || self.inline_context_picker_menu_handle.is_deployed()
         {
             cx.propagate();
         } else {
-            cx.focus_view(&self.context_strip);
+            self.context_strip.focus_handle(cx).focus(window);
         }
     }
 }
 
-impl FocusableView for MessageEditor {
-    fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
+impl Focusable for MessageEditor {
+    fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
         self.editor.focus_handle(cx)
     }
 }
 
 impl Render for MessageEditor {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let font_size = TextSize::Default.rems(cx);
-        let line_height = font_size.to_pixels(cx.rem_size()) * 1.5;
+        let line_height = font_size.to_pixels(window.rem_size()) * 1.5;
         let focus_handle = self.editor.focus_handle(cx);
         let inline_context_picker = self.inline_context_picker.clone();
         let bg_color = cx.theme().colors().editor_background;
@@ -326,9 +354,9 @@ impl Render for MessageEditor {
                     })
                     .child(
                         PopoverMenu::new("inline-context-picker")
-                            .menu(move |cx| {
+                            .menu(move |window, cx| {
                                 inline_context_picker.update(cx, |this, cx| {
-                                    this.init(cx);
+                                    this.init(window, cx);
                                 });
 
                                 Some(inline_context_picker.clone())
@@ -351,7 +379,7 @@ impl Render for MessageEditor {
                             .child(
                                 Switch::new("use-tools", self.use_tools.into())
                                     .label("Tools")
-                                    .on_click(cx.listener(|this, selection, _cx| {
+                                    .on_click(cx.listener(|this, selection, _window, _cx| {
                                         this.use_tools = match selection {
                                             ToggleState::Selected => true,
                                             ToggleState::Unselected
@@ -361,7 +389,7 @@ impl Render for MessageEditor {
                                     .key_binding(KeyBinding::for_action_in(
                                         &ChatMode,
                                         &focus_handle,
-                                        cx,
+                                        window,
                                     )),
                             )
                             .child(h_flex().gap_1().child(self.model_selector.clone()).child(
@@ -390,14 +418,17 @@ impl Render for MessageEditor {
                                                     KeyBinding::for_action_in(
                                                         &editor::actions::Cancel,
                                                         &focus_handle,
-                                                        cx,
+                                                        window,
                                                     )
                                                     .map(|binding| binding.into_any_element()),
                                                 ),
                                         )
-                                        .on_click(move |_event, cx| {
-                                            focus_handle
-                                                .dispatch_action(&editor::actions::Cancel, cx);
+                                        .on_click(move |_event, window, cx| {
+                                            focus_handle.dispatch_action(
+                                                &editor::actions::Cancel,
+                                                window,
+                                                cx,
+                                            );
                                         })
                                 } else {
                                     ButtonLike::new("submit-message")
@@ -417,23 +448,22 @@ impl Render for MessageEditor {
                                                     KeyBinding::for_action_in(
                                                         &Chat,
                                                         &focus_handle,
-                                                        cx,
+                                                        window,
                                                     )
                                                     .map(|binding| binding.into_any_element()),
                                                 ),
                                         )
-                                        .on_click(move |_event, cx| {
-                                            focus_handle.dispatch_action(&Chat, cx);
+                                        .on_click(move |_event, window, cx| {
+                                            focus_handle.dispatch_action(&Chat, window, cx);
                                         })
                                         .when(is_editor_empty, |button| {
-                                            button.tooltip(|cx| {
-                                                Tooltip::text("Type a message to submit", cx)
-                                            })
+                                            button
+                                                .tooltip(Tooltip::text("Type a message to submit"))
                                         })
                                         .when(!is_model_selected, |button| {
-                                            button.tooltip(|cx| {
-                                                Tooltip::text("Select a model to continue", cx)
-                                            })
+                                            button.tooltip(Tooltip::text(
+                                                "Select a model to continue",
+                                            ))
                                         })
                                 },
                             )),

crates/assistant2/src/terminal_codegen.rs 🔗

@@ -1,7 +1,7 @@
 use crate::inline_prompt_editor::CodegenStatus;
 use client::telemetry::Telemetry;
 use futures::{channel::mpsc, SinkExt, StreamExt};
-use gpui::{AppContext, EventEmitter, Model, ModelContext, Task};
+use gpui::{App, Context, Entity, EventEmitter, Task};
 use language_model::{LanguageModelRegistry, LanguageModelRequest};
 use language_models::report_assistant_event;
 use std::{sync::Arc, time::Instant};
@@ -11,7 +11,7 @@ use terminal::Terminal;
 pub struct TerminalCodegen {
     pub status: CodegenStatus,
     pub telemetry: Option<Arc<Telemetry>>,
-    terminal: Model<Terminal>,
+    terminal: Entity<Terminal>,
     generation: Task<()>,
     pub message_id: Option<String>,
     transaction: Option<TerminalTransaction>,
@@ -20,7 +20,7 @@ pub struct TerminalCodegen {
 impl EventEmitter<CodegenEvent> for TerminalCodegen {}
 
 impl TerminalCodegen {
-    pub fn new(terminal: Model<Terminal>, telemetry: Option<Arc<Telemetry>>) -> Self {
+    pub fn new(terminal: Entity<Terminal>, telemetry: Option<Arc<Telemetry>>) -> Self {
         Self {
             terminal,
             telemetry,
@@ -31,7 +31,7 @@ impl TerminalCodegen {
         }
     }
 
-    pub fn start(&mut self, prompt: LanguageModelRequest, cx: &mut ModelContext<Self>) {
+    pub fn start(&mut self, prompt: LanguageModelRequest, cx: &mut Context<Self>) {
         let Some(model) = LanguageModelRegistry::read_global(cx).active_model() else {
             return;
         };
@@ -131,20 +131,20 @@ impl TerminalCodegen {
         cx.notify();
     }
 
-    pub fn stop(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn stop(&mut self, cx: &mut Context<Self>) {
         self.status = CodegenStatus::Done;
         self.generation = Task::ready(());
         cx.emit(CodegenEvent::Finished);
         cx.notify();
     }
 
-    pub fn complete(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn complete(&mut self, cx: &mut Context<Self>) {
         if let Some(transaction) = self.transaction.take() {
             transaction.complete(cx);
         }
     }
 
-    pub fn undo(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn undo(&mut self, cx: &mut Context<Self>) {
         if let Some(transaction) = self.transaction.take() {
             transaction.undo(cx);
         }
@@ -160,27 +160,27 @@ pub const CLEAR_INPUT: &str = "\x15";
 const CARRIAGE_RETURN: &str = "\x0d";
 
 struct TerminalTransaction {
-    terminal: Model<Terminal>,
+    terminal: Entity<Terminal>,
 }
 
 impl TerminalTransaction {
-    pub fn start(terminal: Model<Terminal>) -> Self {
+    pub fn start(terminal: Entity<Terminal>) -> Self {
         Self { terminal }
     }
 
-    pub fn push(&mut self, hunk: String, cx: &mut AppContext) {
+    pub fn push(&mut self, hunk: String, cx: &mut App) {
         // Ensure that the assistant cannot accidentally execute commands that are streamed into the terminal
         let input = Self::sanitize_input(hunk);
         self.terminal
             .update(cx, |terminal, _| terminal.input(input));
     }
 
-    pub fn undo(&self, cx: &mut AppContext) {
+    pub fn undo(&self, cx: &mut App) {
         self.terminal
             .update(cx, |terminal, _| terminal.input(CLEAR_INPUT.to_string()));
     }
 
-    pub fn complete(&self, cx: &mut AppContext) {
+    pub fn complete(&self, cx: &mut App) {
         self.terminal.update(cx, |terminal, _| {
             terminal.input(CARRIAGE_RETURN.to_string())
         });

crates/assistant2/src/terminal_inline_assistant.rs 🔗

@@ -10,10 +10,7 @@ use client::telemetry::Telemetry;
 use collections::{HashMap, VecDeque};
 use editor::{actions::SelectAll, MultiBuffer};
 use fs::Fs;
-use gpui::{
-    AppContext, Context, FocusableView, Global, Model, Subscription, UpdateGlobal, View, WeakModel,
-    WeakView,
-};
+use gpui::{App, Entity, Focusable, Global, Subscription, UpdateGlobal, WeakEntity};
 use language::Buffer;
 use language_model::{
     LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
@@ -31,7 +28,7 @@ pub fn init(
     fs: Arc<dyn Fs>,
     prompt_builder: Arc<PromptBuilder>,
     telemetry: Arc<Telemetry>,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) {
     cx.set_global(TerminalInlineAssistant::new(fs, prompt_builder, telemetry));
 }
@@ -68,20 +65,20 @@ impl TerminalInlineAssistant {
 
     pub fn assist(
         &mut self,
-        terminal_view: &View<TerminalView>,
-        workspace: WeakView<Workspace>,
-        thread_store: Option<WeakModel<ThreadStore>>,
-        cx: &mut WindowContext,
+        terminal_view: &Entity<TerminalView>,
+        workspace: WeakEntity<Workspace>,
+        thread_store: Option<WeakEntity<ThreadStore>>,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let terminal = terminal_view.read(cx).terminal().clone();
         let assist_id = self.next_assist_id.post_inc();
-        let prompt_buffer = cx.new_model(|cx| {
-            MultiBuffer::singleton(cx.new_model(|cx| Buffer::local(String::new(), cx)), cx)
-        });
-        let context_store = cx.new_model(|_cx| ContextStore::new(workspace.clone()));
-        let codegen = cx.new_model(|_| TerminalCodegen::new(terminal, self.telemetry.clone()));
+        let prompt_buffer =
+            cx.new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(String::new(), cx)), cx));
+        let context_store = cx.new(|_cx| ContextStore::new(workspace.clone()));
+        let codegen = cx.new(|_| TerminalCodegen::new(terminal, self.telemetry.clone()));
 
-        let prompt_editor = cx.new_view(|cx| {
+        let prompt_editor = cx.new(|cx| {
             PromptEditor::new_terminal(
                 assist_id,
                 self.prompt_history.clone(),
@@ -91,6 +88,7 @@ impl TerminalInlineAssistant {
                 context_store.clone(),
                 workspace.clone(),
                 thread_store.clone(),
+                window,
                 cx,
             )
         });
@@ -100,7 +98,7 @@ impl TerminalInlineAssistant {
             render: Box::new(move |_| prompt_editor_render.clone().into_any_element()),
         };
         terminal_view.update(cx, |terminal_view, cx| {
-            terminal_view.set_block_below_cursor(block, cx);
+            terminal_view.set_block_below_cursor(block, window, cx);
         });
 
         let terminal_assistant = TerminalInlineAssist::new(
@@ -109,21 +107,27 @@ impl TerminalInlineAssistant {
             prompt_editor,
             workspace.clone(),
             context_store,
+            window,
             cx,
         );
 
         self.assists.insert(assist_id, terminal_assistant);
 
-        self.focus_assist(assist_id, cx);
+        self.focus_assist(assist_id, window, cx);
     }
 
-    fn focus_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut WindowContext) {
+    fn focus_assist(
+        &mut self,
+        assist_id: TerminalInlineAssistId,
+        window: &mut Window,
+        cx: &mut App,
+    ) {
         let assist = &self.assists[&assist_id];
         if let Some(prompt_editor) = assist.prompt_editor.as_ref() {
             prompt_editor.update(cx, |this, cx| {
                 this.editor.update(cx, |editor, cx| {
-                    editor.focus(cx);
-                    editor.select_all(&SelectAll, cx);
+                    window.focus(&editor.focus_handle(cx));
+                    editor.select_all(&SelectAll, window, cx);
                 });
             });
         }
@@ -131,9 +135,10 @@ impl TerminalInlineAssistant {
 
     fn handle_prompt_editor_event(
         &mut self,
-        prompt_editor: View<PromptEditor<TerminalCodegen>>,
+        prompt_editor: Entity<PromptEditor<TerminalCodegen>>,
         event: &PromptEditorEvent,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let assist_id = prompt_editor.read(cx).id();
         match event {
@@ -144,21 +149,21 @@ impl TerminalInlineAssistant {
                 self.stop_assist(assist_id, cx);
             }
             PromptEditorEvent::ConfirmRequested { execute } => {
-                self.finish_assist(assist_id, false, *execute, cx);
+                self.finish_assist(assist_id, false, *execute, window, cx);
             }
             PromptEditorEvent::CancelRequested => {
-                self.finish_assist(assist_id, true, false, cx);
+                self.finish_assist(assist_id, true, false, window, cx);
             }
             PromptEditorEvent::DismissRequested => {
-                self.dismiss_assist(assist_id, cx);
+                self.dismiss_assist(assist_id, window, cx);
             }
             PromptEditorEvent::Resized { height_in_lines } => {
-                self.insert_prompt_editor_into_terminal(assist_id, *height_in_lines, cx);
+                self.insert_prompt_editor_into_terminal(assist_id, *height_in_lines, window, cx);
             }
         }
     }
 
-    fn start_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut WindowContext) {
+    fn start_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut App) {
         let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
             assist
         } else {
@@ -196,7 +201,7 @@ impl TerminalInlineAssistant {
         codegen.update(cx, |codegen, cx| codegen.start(request, cx));
     }
 
-    fn stop_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut WindowContext) {
+    fn stop_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut App) {
         let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
             assist
         } else {
@@ -209,7 +214,7 @@ impl TerminalInlineAssistant {
     fn request_for_inline_assist(
         &self,
         assist_id: TerminalInlineAssistId,
-        cx: &mut WindowContext,
+        cx: &mut App,
     ) -> Result<LanguageModelRequest> {
         let assist = self.assists.get(&assist_id).context("invalid assist")?;
 
@@ -265,16 +270,17 @@ impl TerminalInlineAssistant {
         assist_id: TerminalInlineAssistId,
         undo: bool,
         execute: bool,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        self.dismiss_assist(assist_id, cx);
+        self.dismiss_assist(assist_id, window, cx);
 
         if let Some(assist) = self.assists.remove(&assist_id) {
             assist
                 .terminal
                 .update(cx, |this, cx| {
                     this.clear_block_below_cursor(cx);
-                    this.focus_handle(cx).focus(cx);
+                    this.focus_handle(cx).focus(window);
                 })
                 .log_err();
 
@@ -317,7 +323,8 @@ impl TerminalInlineAssistant {
     fn dismiss_assist(
         &mut self,
         assist_id: TerminalInlineAssistId,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> bool {
         let Some(assist) = self.assists.get_mut(&assist_id) else {
             return false;
@@ -330,7 +337,7 @@ impl TerminalInlineAssistant {
             .terminal
             .update(cx, |this, cx| {
                 this.clear_block_below_cursor(cx);
-                this.focus_handle(cx).focus(cx);
+                this.focus_handle(cx).focus(window);
             })
             .is_ok()
     }
@@ -339,7 +346,8 @@ impl TerminalInlineAssistant {
         &mut self,
         assist_id: TerminalInlineAssistId,
         height: u8,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         if let Some(assist) = self.assists.get_mut(&assist_id) {
             if let Some(prompt_editor) = assist.prompt_editor.as_ref().cloned() {
@@ -351,7 +359,7 @@ impl TerminalInlineAssistant {
                             height,
                             render: Box::new(move |_| prompt_editor.clone().into_any_element()),
                         };
-                        terminal.set_block_below_cursor(block, cx);
+                        terminal.set_block_below_cursor(block, window, cx);
                     })
                     .log_err();
             }
@@ -360,22 +368,23 @@ impl TerminalInlineAssistant {
 }
 
 struct TerminalInlineAssist {
-    terminal: WeakView<TerminalView>,
-    prompt_editor: Option<View<PromptEditor<TerminalCodegen>>>,
-    codegen: Model<TerminalCodegen>,
-    workspace: WeakView<Workspace>,
-    context_store: Model<ContextStore>,
+    terminal: WeakEntity<TerminalView>,
+    prompt_editor: Option<Entity<PromptEditor<TerminalCodegen>>>,
+    codegen: Entity<TerminalCodegen>,
+    workspace: WeakEntity<Workspace>,
+    context_store: Entity<ContextStore>,
     _subscriptions: Vec<Subscription>,
 }
 
 impl TerminalInlineAssist {
     pub fn new(
         assist_id: TerminalInlineAssistId,
-        terminal: &View<TerminalView>,
-        prompt_editor: View<PromptEditor<TerminalCodegen>>,
-        workspace: WeakView<Workspace>,
-        context_store: Model<ContextStore>,
-        cx: &mut WindowContext,
+        terminal: &Entity<TerminalView>,
+        prompt_editor: Entity<PromptEditor<TerminalCodegen>>,
+        workspace: WeakEntity<Workspace>,
+        context_store: Entity<ContextStore>,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Self {
         let codegen = prompt_editor.read(cx).codegen().clone();
         Self {
@@ -385,12 +394,12 @@ impl TerminalInlineAssist {
             workspace: workspace.clone(),
             context_store,
             _subscriptions: vec![
-                cx.subscribe(&prompt_editor, |prompt_editor, event, cx| {
+                window.subscribe(&prompt_editor, cx, |prompt_editor, event, window, cx| {
                     TerminalInlineAssistant::update_global(cx, |this, cx| {
-                        this.handle_prompt_editor_event(prompt_editor, event, cx)
+                        this.handle_prompt_editor_event(prompt_editor, event, window, cx)
                     })
                 }),
-                cx.subscribe(&codegen, move |codegen, event, cx| {
+                window.subscribe(&codegen, cx, move |codegen, event, window, cx| {
                     TerminalInlineAssistant::update_global(cx, |this, cx| match event {
                         CodegenEvent::Finished => {
                             let assist = if let Some(assist) = this.assists.get(&assist_id) {
@@ -419,7 +428,7 @@ impl TerminalInlineAssist {
                             }
 
                             if assist.prompt_editor.is_none() {
-                                this.finish_assist(assist_id, false, false, cx);
+                                this.finish_assist(assist_id, false, false, window, cx);
                             }
                         }
                     })

crates/assistant2/src/thread.rs 🔗

@@ -6,7 +6,7 @@ use chrono::{DateTime, Utc};
 use collections::{BTreeMap, HashMap, HashSet};
 use futures::future::Shared;
 use futures::{FutureExt as _, StreamExt as _};
-use gpui::{AppContext, EventEmitter, ModelContext, SharedString, Task};
+use gpui::{App, Context, EventEmitter, SharedString, Task};
 use language_model::{
     LanguageModel, LanguageModelCompletionEvent, LanguageModelRegistry, LanguageModelRequest,
     LanguageModelRequestMessage, LanguageModelToolResult, LanguageModelToolUse,
@@ -76,7 +76,7 @@ pub struct Thread {
 }
 
 impl Thread {
-    pub fn new(tools: Arc<ToolWorkingSet>, _cx: &mut ModelContext<Self>) -> Self {
+    pub fn new(tools: Arc<ToolWorkingSet>, _cx: &mut Context<Self>) -> Self {
         Self {
             id: ThreadId::new(),
             updated_at: Utc::now(),
@@ -99,7 +99,7 @@ impl Thread {
         id: ThreadId,
         saved: SavedThread,
         tools: Arc<ToolWorkingSet>,
-        _cx: &mut ModelContext<Self>,
+        _cx: &mut Context<Self>,
     ) -> Self {
         let next_message_id = MessageId(saved.messages.len());
 
@@ -154,7 +154,7 @@ impl Thread {
         self.summary.clone().unwrap_or(DEFAULT)
     }
 
-    pub fn set_summary(&mut self, summary: impl Into<SharedString>, cx: &mut ModelContext<Self>) {
+    pub fn set_summary(&mut self, summary: impl Into<SharedString>, cx: &mut Context<Self>) {
         self.summary = Some(summary.into());
         cx.emit(ThreadEvent::SummaryChanged);
     }
@@ -194,7 +194,7 @@ impl Thread {
         &mut self,
         text: impl Into<String>,
         context: Vec<ContextSnapshot>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let message_id = self.insert_message(Role::User, text, cx);
         let context_ids = context.iter().map(|context| context.id).collect::<Vec<_>>();
@@ -207,7 +207,7 @@ impl Thread {
         &mut self,
         role: Role,
         text: impl Into<String>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> MessageId {
         let id = self.next_message_id.post_inc();
         self.messages.push(Message {
@@ -244,7 +244,7 @@ impl Thread {
     pub fn to_completion_request(
         &self,
         _request_kind: RequestKind,
-        _cx: &AppContext,
+        _cx: &App,
     ) -> LanguageModelRequest {
         let mut request = LanguageModelRequest {
             messages: vec![],
@@ -314,7 +314,7 @@ impl Thread {
         &mut self,
         request: LanguageModelRequest,
         model: Arc<dyn LanguageModel>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let pending_completion_id = post_inc(&mut self.completion_count);
 
@@ -439,7 +439,7 @@ impl Thread {
         });
     }
 
-    pub fn summarize(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn summarize(&mut self, cx: &mut Context<Self>) {
         let Some(provider) = LanguageModelRegistry::read_global(cx).active_provider() else {
             return;
         };
@@ -497,7 +497,7 @@ impl Thread {
         assistant_message_id: MessageId,
         tool_use_id: LanguageModelToolUseId,
         output: Task<Result<String>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let insert_output_task = cx.spawn(|thread, mut cx| {
             let tool_use_id = tool_use_id.clone();

crates/assistant2/src/thread_history.rs 🔗

@@ -1,6 +1,6 @@
 use gpui::{
-    uniform_list, AppContext, FocusHandle, FocusableView, Model, ScrollStrategy,
-    UniformListScrollHandle, WeakView,
+    uniform_list, App, Entity, FocusHandle, Focusable, ScrollStrategy, UniformListScrollHandle,
+    WeakEntity,
 };
 use time::{OffsetDateTime, UtcOffset};
 use ui::{prelude::*, IconButtonShape, ListItem, ListItemSpacing, Tooltip};
@@ -10,17 +10,18 @@ use crate::{AssistantPanel, RemoveSelectedThread};
 
 pub struct ThreadHistory {
     focus_handle: FocusHandle,
-    assistant_panel: WeakView<AssistantPanel>,
-    thread_store: Model<ThreadStore>,
+    assistant_panel: WeakEntity<AssistantPanel>,
+    thread_store: Entity<ThreadStore>,
     scroll_handle: UniformListScrollHandle,
     selected_index: usize,
 }
 
 impl ThreadHistory {
     pub(crate) fn new(
-        assistant_panel: WeakView<AssistantPanel>,
-        thread_store: Model<ThreadStore>,
-        cx: &mut ViewContext<Self>,
+        assistant_panel: WeakEntity<AssistantPanel>,
+        thread_store: Entity<ThreadStore>,
+
+        cx: &mut Context<Self>,
     ) -> Self {
         Self {
             focus_handle: cx.focus_handle(),
@@ -31,62 +32,77 @@ impl ThreadHistory {
         }
     }
 
-    pub fn select_prev(&mut self, _: &menu::SelectPrev, cx: &mut ViewContext<Self>) {
+    pub fn select_prev(
+        &mut self,
+        _: &menu::SelectPrev,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let count = self.thread_store.read(cx).thread_count();
         if count > 0 {
             if self.selected_index == 0 {
-                self.set_selected_index(count - 1, cx);
+                self.set_selected_index(count - 1, window, cx);
             } else {
-                self.set_selected_index(self.selected_index - 1, cx);
+                self.set_selected_index(self.selected_index - 1, window, cx);
             }
         }
     }
 
-    pub fn select_next(&mut self, _: &menu::SelectNext, cx: &mut ViewContext<Self>) {
+    pub fn select_next(
+        &mut self,
+        _: &menu::SelectNext,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let count = self.thread_store.read(cx).thread_count();
         if count > 0 {
             if self.selected_index == count - 1 {
-                self.set_selected_index(0, cx);
+                self.set_selected_index(0, window, cx);
             } else {
-                self.set_selected_index(self.selected_index + 1, cx);
+                self.set_selected_index(self.selected_index + 1, window, cx);
             }
         }
     }
 
-    fn select_first(&mut self, _: &menu::SelectFirst, cx: &mut ViewContext<Self>) {
+    fn select_first(&mut self, _: &menu::SelectFirst, window: &mut Window, cx: &mut Context<Self>) {
         let count = self.thread_store.read(cx).thread_count();
         if count > 0 {
-            self.set_selected_index(0, cx);
+            self.set_selected_index(0, window, cx);
         }
     }
 
-    fn select_last(&mut self, _: &menu::SelectLast, cx: &mut ViewContext<Self>) {
+    fn select_last(&mut self, _: &menu::SelectLast, window: &mut Window, cx: &mut Context<Self>) {
         let count = self.thread_store.read(cx).thread_count();
         if count > 0 {
-            self.set_selected_index(count - 1, cx);
+            self.set_selected_index(count - 1, window, cx);
         }
     }
 
-    fn set_selected_index(&mut self, index: usize, cx: &mut ViewContext<Self>) {
+    fn set_selected_index(&mut self, index: usize, _window: &mut Window, cx: &mut Context<Self>) {
         self.selected_index = index;
         self.scroll_handle
             .scroll_to_item(index, ScrollStrategy::Top);
         cx.notify();
     }
 
-    fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
+    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
         let threads = self.thread_store.update(cx, |this, _cx| this.threads());
 
         if let Some(thread) = threads.get(self.selected_index) {
             self.assistant_panel
-                .update(cx, move |this, cx| this.open_thread(&thread.id, cx))
+                .update(cx, move |this, cx| this.open_thread(&thread.id, window, cx))
                 .ok();
 
             cx.notify();
         }
     }
 
-    fn remove_selected_thread(&mut self, _: &RemoveSelectedThread, cx: &mut ViewContext<Self>) {
+    fn remove_selected_thread(
+        &mut self,
+        _: &RemoveSelectedThread,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let threads = self.thread_store.update(cx, |this, _cx| this.threads());
 
         if let Some(thread) = threads.get(self.selected_index) {
@@ -101,14 +117,14 @@ impl ThreadHistory {
     }
 }
 
-impl FocusableView for ThreadHistory {
-    fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
+impl Focusable for ThreadHistory {
+    fn focus_handle(&self, _cx: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
 
 impl Render for ThreadHistory {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let threads = self.thread_store.update(cx, |this, _cx| this.threads());
         let selected_index = self.selected_index;
 
@@ -138,10 +154,10 @@ impl Render for ThreadHistory {
                 } else {
                     history.child(
                         uniform_list(
-                            cx.view().clone(),
+                            cx.model().clone(),
                             "thread-history",
                             threads.len(),
-                            move |history, range, _cx| {
+                            move |history, range, _window, _cx| {
                                 threads[range]
                                     .iter()
                                     .enumerate()
@@ -166,14 +182,14 @@ impl Render for ThreadHistory {
 #[derive(IntoElement)]
 pub struct PastThread {
     thread: SavedThreadMetadata,
-    assistant_panel: WeakView<AssistantPanel>,
+    assistant_panel: WeakEntity<AssistantPanel>,
     selected: bool,
 }
 
 impl PastThread {
     pub fn new(
         thread: SavedThreadMetadata,
-        assistant_panel: WeakView<AssistantPanel>,
+        assistant_panel: WeakEntity<AssistantPanel>,
         selected: bool,
     ) -> Self {
         Self {
@@ -185,7 +201,7 @@ impl PastThread {
 }
 
 impl RenderOnce for PastThread {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let summary = self.thread.summary;
 
         let thread_timestamp = time_format::format_localized_timestamp(
@@ -219,11 +235,11 @@ impl RenderOnce for PastThread {
                         IconButton::new("delete", IconName::TrashAlt)
                             .shape(IconButtonShape::Square)
                             .icon_size(IconSize::Small)
-                            .tooltip(|cx| Tooltip::text("Delete Thread", cx))
+                            .tooltip(Tooltip::text("Delete Thread"))
                             .on_click({
                                 let assistant_panel = self.assistant_panel.clone();
                                 let id = self.thread.id.clone();
-                                move |_event, cx| {
+                                move |_event, _window, cx| {
                                     assistant_panel
                                         .update(cx, |this, cx| {
                                             this.delete_thread(&id, cx);
@@ -236,10 +252,10 @@ impl RenderOnce for PastThread {
             .on_click({
                 let assistant_panel = self.assistant_panel.clone();
                 let id = self.thread.id.clone();
-                move |_event, cx| {
+                move |_event, window, cx| {
                     assistant_panel
                         .update(cx, |this, cx| {
-                            this.open_thread(&id, cx).detach_and_log_err(cx);
+                            this.open_thread(&id, window, cx).detach_and_log_err(cx);
                         })
                         .ok();
                 }

crates/assistant2/src/thread_store.rs 🔗

@@ -9,7 +9,7 @@ use context_server::manager::ContextServerManager;
 use context_server::{ContextServerFactoryRegistry, ContextServerTool};
 use futures::future::{self, BoxFuture, Shared};
 use futures::FutureExt as _;
-use gpui::{prelude::*, AppContext, BackgroundExecutor, Model, ModelContext, SharedString, Task};
+use gpui::{prelude::*, App, BackgroundExecutor, Context, Entity, SharedString, Task};
 use heed::types::SerdeBincode;
 use heed::Database;
 use language_model::Role;
@@ -21,9 +21,9 @@ use crate::thread::{MessageId, Thread, ThreadId};
 
 pub struct ThreadStore {
     #[allow(unused)]
-    project: Model<Project>,
+    project: Entity<Project>,
     tools: Arc<ToolWorkingSet>,
-    context_server_manager: Model<ContextServerManager>,
+    context_server_manager: Entity<ContextServerManager>,
     context_server_tool_ids: HashMap<Arc<str>, Vec<ToolId>>,
     threads: Vec<SavedThreadMetadata>,
     database_future: Shared<BoxFuture<'static, Result<Arc<ThreadsDatabase>, Arc<anyhow::Error>>>>,
@@ -31,15 +31,15 @@ pub struct ThreadStore {
 
 impl ThreadStore {
     pub fn new(
-        project: Model<Project>,
+        project: Entity<Project>,
         tools: Arc<ToolWorkingSet>,
-        cx: &mut AppContext,
-    ) -> Task<Result<Model<Self>>> {
+        cx: &mut App,
+    ) -> Task<Result<Entity<Self>>> {
         cx.spawn(|mut cx| async move {
-            let this = cx.new_model(|cx: &mut ModelContext<Self>| {
+            let this = cx.new(|cx: &mut Context<Self>| {
                 let context_server_factory_registry =
                     ContextServerFactoryRegistry::default_global(cx);
-                let context_server_manager = cx.new_model(|cx| {
+                let context_server_manager = cx.new(|cx| {
                     ContextServerManager::new(context_server_factory_registry, project.clone(), cx)
                 });
 
@@ -88,15 +88,15 @@ impl ThreadStore {
         self.threads().into_iter().take(limit).collect()
     }
 
-    pub fn create_thread(&mut self, cx: &mut ModelContext<Self>) -> Model<Thread> {
-        cx.new_model(|cx| Thread::new(self.tools.clone(), cx))
+    pub fn create_thread(&mut self, cx: &mut Context<Self>) -> Entity<Thread> {
+        cx.new(|cx| Thread::new(self.tools.clone(), cx))
     }
 
     pub fn open_thread(
         &self,
         id: &ThreadId,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<Thread>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<Thread>>> {
         let id = id.clone();
         let database_future = self.database_future.clone();
         cx.spawn(|this, mut cx| async move {
@@ -107,16 +107,12 @@ impl ThreadStore {
                 .ok_or_else(|| anyhow!("no thread found with ID: {id:?}"))?;
 
             this.update(&mut cx, |this, cx| {
-                cx.new_model(|cx| Thread::from_saved(id.clone(), thread, this.tools.clone(), cx))
+                cx.new(|cx| Thread::from_saved(id.clone(), thread, this.tools.clone(), cx))
             })
         })
     }
 
-    pub fn save_thread(
-        &self,
-        thread: &Model<Thread>,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<()>> {
+    pub fn save_thread(&self, thread: &Entity<Thread>, cx: &mut Context<Self>) -> Task<Result<()>> {
         let (metadata, thread) = thread.update(cx, |thread, _cx| {
             let id = thread.id().clone();
             let thread = SavedThread {
@@ -144,11 +140,7 @@ impl ThreadStore {
         })
     }
 
-    pub fn delete_thread(
-        &mut self,
-        id: &ThreadId,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<()>> {
+    pub fn delete_thread(&mut self, id: &ThreadId, cx: &mut Context<Self>) -> Task<Result<()>> {
         let id = id.clone();
         let database_future = self.database_future.clone();
         cx.spawn(|this, mut cx| async move {
@@ -161,7 +153,7 @@ impl ThreadStore {
         })
     }
 
-    fn reload(&self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn reload(&self, cx: &mut Context<Self>) -> Task<Result<()>> {
         let database_future = self.database_future.clone();
         cx.spawn(|this, mut cx| async move {
             let threads = database_future
@@ -177,7 +169,7 @@ impl ThreadStore {
         })
     }
 
-    fn register_context_server_handlers(&self, cx: &mut ModelContext<Self>) {
+    fn register_context_server_handlers(&self, cx: &mut Context<Self>) {
         cx.subscribe(
             &self.context_server_manager.clone(),
             Self::handle_context_server_event,
@@ -187,9 +179,9 @@ impl ThreadStore {
 
     fn handle_context_server_event(
         &mut self,
-        context_server_manager: Model<ContextServerManager>,
+        context_server_manager: Entity<ContextServerManager>,
         event: &context_server::manager::Event,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let tool_working_set = self.tools.clone();
         match event {

crates/assistant2/src/ui/context_pill.rs 🔗

@@ -11,15 +11,15 @@ pub enum ContextPill {
         context: ContextSnapshot,
         dupe_name: bool,
         focused: bool,
-        on_click: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext)>>,
-        on_remove: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext)>>,
+        on_click: Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App)>>,
+        on_remove: Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App)>>,
     },
     Suggested {
         name: SharedString,
         icon_path: Option<SharedString>,
         kind: ContextKind,
         focused: bool,
-        on_click: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext)>>,
+        on_click: Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App)>>,
     },
 }
 
@@ -28,7 +28,7 @@ impl ContextPill {
         context: ContextSnapshot,
         dupe_name: bool,
         focused: bool,
-        on_remove: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext)>>,
+        on_remove: Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App)>>,
     ) -> Self {
         Self::Added {
             context,
@@ -54,7 +54,7 @@ impl ContextPill {
         }
     }
 
-    pub fn on_click(mut self, listener: Rc<dyn Fn(&ClickEvent, &mut WindowContext)>) -> Self {
+    pub fn on_click(mut self, listener: Rc<dyn Fn(&ClickEvent, &mut Window, &mut App)>) -> Self {
         match &mut self {
             ContextPill::Added { on_click, .. } => {
                 *on_click = Some(listener);
@@ -95,7 +95,7 @@ impl ContextPill {
 }
 
 impl RenderOnce for ContextPill {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let color = cx.theme().colors();
 
         let base_pill = h_flex()
@@ -139,7 +139,7 @@ impl RenderOnce for ContextPill {
                             }
                         })
                         .when_some(context.tooltip.clone(), |element, tooltip| {
-                            element.tooltip(move |cx| Tooltip::text(tooltip.clone(), cx))
+                            element.tooltip(Tooltip::text(tooltip.clone()))
                         }),
                 )
                 .when_some(on_remove.as_ref(), |element, on_remove| {
@@ -147,16 +147,16 @@ impl RenderOnce for ContextPill {
                         IconButton::new(("remove", context.id.0), IconName::Close)
                             .shape(IconButtonShape::Square)
                             .icon_size(IconSize::XSmall)
-                            .tooltip(|cx| Tooltip::text("Remove Context", cx))
+                            .tooltip(Tooltip::text("Remove Context"))
                             .on_click({
                                 let on_remove = on_remove.clone();
-                                move |event, cx| on_remove(event, cx)
+                                move |event, window, cx| on_remove(event, window, cx)
                             }),
                     )
                 })
                 .when_some(on_click.as_ref(), |element, on_click| {
                     let on_click = on_click.clone();
-                    element.on_click(move |event, cx| on_click(event, cx))
+                    element.on_click(move |event, window, cx| on_click(event, window, cx))
                 }),
             ContextPill::Suggested {
                 name,
@@ -195,10 +195,12 @@ impl RenderOnce for ContextPill {
                         .size(IconSize::XSmall)
                         .into_any_element(),
                 )
-                .tooltip(|cx| Tooltip::with_meta("Suggested Context", None, "Click to add it", cx))
+                .tooltip(|window, cx| {
+                    Tooltip::with_meta("Suggested Context", None, "Click to add it", window, cx)
+                })
                 .when_some(on_click.as_ref(), |element, on_click| {
                     let on_click = on_click.clone();
-                    element.on_click(move |event, cx| on_click(event, cx))
+                    element.on_click(move |event, window, cx| on_click(event, window, cx))
                 }),
         }
     }

crates/assistant_context_editor/src/assistant_context_editor.rs 🔗

@@ -9,7 +9,7 @@ mod slash_command_picker;
 use std::sync::Arc;
 
 use client::Client;
-use gpui::AppContext;
+use gpui::App;
 
 pub use crate::context::*;
 pub use crate::context_editor::*;
@@ -18,6 +18,6 @@ pub use crate::context_store::*;
 pub use crate::patch::*;
 pub use crate::slash_command::*;
 
-pub fn init(client: Arc<Client>, _cx: &mut AppContext) {
+pub fn init(client: Arc<Client>, _cx: &mut App) {
     context_store::init(&client.into());
 }

crates/assistant_context_editor/src/context.rs 🔗

@@ -16,8 +16,8 @@ use feature_flags::{FeatureFlagAppExt, ToolUseFeatureFlag};
 use fs::{Fs, RemoveOptions};
 use futures::{future::Shared, FutureExt, StreamExt};
 use gpui::{
-    AppContext, Context as _, EventEmitter, Model, ModelContext, RenderImage, SharedString,
-    Subscription, Task,
+    App, AppContext as _, Context, Entity, EventEmitter, RenderImage, SharedString, Subscription,
+    Task,
 };
 use language::{AnchorRangeExt, Bias, Buffer, LanguageRegistry, OffsetRangeExt, Point, ToOffset};
 use language_model::{
@@ -588,13 +588,13 @@ pub enum XmlTagKind {
     Operation,
 }
 
-pub struct Context {
+pub struct AssistantContext {
     id: ContextId,
     timestamp: clock::Lamport,
     version: clock::Global,
     pending_ops: Vec<ContextOperation>,
     operations: Vec<ContextOperation>,
-    buffer: Model<Buffer>,
+    buffer: Entity<Buffer>,
     parsed_slash_commands: Vec<ParsedSlashCommand>,
     invoked_slash_commands: HashMap<InvokedSlashCommandId, InvokedSlashCommand>,
     edits_since_last_parse: language::Subscription,
@@ -619,7 +619,7 @@ pub struct Context {
     language_registry: Arc<LanguageRegistry>,
     patches: Vec<AssistantPatch>,
     xml_tags: Vec<XmlTag>,
-    project: Option<Model<Project>>,
+    project: Option<Entity<Project>>,
     prompt_builder: Arc<PromptBuilder>,
 }
 
@@ -645,17 +645,17 @@ impl ContextAnnotation for XmlTag {
     }
 }
 
-impl EventEmitter<ContextEvent> for Context {}
+impl EventEmitter<ContextEvent> for AssistantContext {}
 
-impl Context {
+impl AssistantContext {
     pub fn local(
         language_registry: Arc<LanguageRegistry>,
-        project: Option<Model<Project>>,
+        project: Option<Entity<Project>>,
         telemetry: Option<Arc<Telemetry>>,
         prompt_builder: Arc<PromptBuilder>,
         slash_commands: Arc<SlashCommandWorkingSet>,
         tools: Arc<ToolWorkingSet>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Self {
         Self::new(
             ContextId::new(),
@@ -680,11 +680,11 @@ impl Context {
         prompt_builder: Arc<PromptBuilder>,
         slash_commands: Arc<SlashCommandWorkingSet>,
         tools: Arc<ToolWorkingSet>,
-        project: Option<Model<Project>>,
+        project: Option<Entity<Project>>,
         telemetry: Option<Arc<Telemetry>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Self {
-        let buffer = cx.new_model(|_cx| {
+        let buffer = cx.new(|_cx| {
             let buffer = Buffer::remote(
                 language::BufferId::new(1).unwrap(),
                 replica_id,
@@ -755,7 +755,7 @@ impl Context {
         this
     }
 
-    pub(crate) fn serialize(&self, cx: &AppContext) -> SavedContext {
+    pub(crate) fn serialize(&self, cx: &App) -> SavedContext {
         let buffer = self.buffer.read(cx);
         SavedContext {
             id: Some(self.id.clone()),
@@ -803,9 +803,9 @@ impl Context {
         prompt_builder: Arc<PromptBuilder>,
         slash_commands: Arc<SlashCommandWorkingSet>,
         tools: Arc<ToolWorkingSet>,
-        project: Option<Model<Project>>,
+        project: Option<Entity<Project>>,
         telemetry: Option<Arc<Telemetry>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Self {
         let id = saved_context.id.clone().unwrap_or_else(ContextId::new);
         let mut this = Self::new(
@@ -837,7 +837,7 @@ impl Context {
         self.timestamp.replica_id
     }
 
-    pub fn version(&self, cx: &AppContext) -> ContextVersion {
+    pub fn version(&self, cx: &App) -> ContextVersion {
         ContextVersion {
             context: self.version.clone(),
             buffer: self.buffer.read(cx).version(),
@@ -852,11 +852,7 @@ impl Context {
         &self.tools
     }
 
-    pub fn set_capability(
-        &mut self,
-        capability: language::Capability,
-        cx: &mut ModelContext<Self>,
-    ) {
+    pub fn set_capability(&mut self, capability: language::Capability, cx: &mut Context<Self>) {
         self.buffer
             .update(cx, |buffer, cx| buffer.set_capability(capability, cx));
     }
@@ -870,7 +866,7 @@ impl Context {
     pub fn serialize_ops(
         &self,
         since: &ContextVersion,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Vec<proto::ContextOperation>> {
         let buffer_ops = self
             .buffer
@@ -905,7 +901,7 @@ impl Context {
     pub fn apply_ops(
         &mut self,
         ops: impl IntoIterator<Item = ContextOperation>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let mut buffer_ops = Vec::new();
         for op in ops {
@@ -919,7 +915,7 @@ impl Context {
         self.flush_ops(cx);
     }
 
-    fn flush_ops(&mut self, cx: &mut ModelContext<Context>) {
+    fn flush_ops(&mut self, cx: &mut Context<AssistantContext>) {
         let mut changed_messages = HashSet::default();
         let mut summary_changed = false;
 
@@ -1038,7 +1034,7 @@ impl Context {
         }
     }
 
-    fn can_apply_op(&self, op: &ContextOperation, cx: &AppContext) -> bool {
+    fn can_apply_op(&self, op: &ContextOperation, cx: &App) -> bool {
         if !self.version.observed_all(op.version()) {
             return false;
         }
@@ -1069,7 +1065,7 @@ impl Context {
     fn has_received_operations_for_anchor_range(
         &self,
         range: Range<text::Anchor>,
-        cx: &AppContext,
+        cx: &App,
     ) -> bool {
         let version = &self.buffer.read(cx).version;
         let observed_start = range.start == language::Anchor::MIN
@@ -1081,12 +1077,12 @@ impl Context {
         observed_start && observed_end
     }
 
-    fn push_op(&mut self, op: ContextOperation, cx: &mut ModelContext<Self>) {
+    fn push_op(&mut self, op: ContextOperation, cx: &mut Context<Self>) {
         self.operations.push(op.clone());
         cx.emit(ContextEvent::Operation(op));
     }
 
-    pub fn buffer(&self) -> &Model<Buffer> {
+    pub fn buffer(&self) -> &Entity<Buffer> {
         &self.buffer
     }
 
@@ -1094,7 +1090,7 @@ impl Context {
         self.language_registry.clone()
     }
 
-    pub fn project(&self) -> Option<Model<Project>> {
+    pub fn project(&self) -> Option<Entity<Project>> {
         self.project.clone()
     }
 
@@ -1110,7 +1106,7 @@ impl Context {
         self.summary.as_ref()
     }
 
-    pub fn patch_containing(&self, position: Point, cx: &AppContext) -> Option<&AssistantPatch> {
+    pub fn patch_containing(&self, position: Point, cx: &App) -> Option<&AssistantPatch> {
         let buffer = self.buffer.read(cx);
         let index = self.patches.binary_search_by(|patch| {
             let patch_range = patch.range.to_point(&buffer);
@@ -1136,7 +1132,7 @@ impl Context {
     pub fn patch_for_range(
         &self,
         range: &Range<language::Anchor>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<&AssistantPatch> {
         let buffer = self.buffer.read(cx);
         let index = self.patch_index_for_range(range, buffer).ok()?;
@@ -1167,7 +1163,7 @@ impl Context {
         &self.slash_command_output_sections
     }
 
-    pub fn contains_files(&self, cx: &AppContext) -> bool {
+    pub fn contains_files(&self, cx: &App) -> bool {
         let buffer = self.buffer.read(cx);
         self.slash_command_output_sections.iter().any(|section| {
             section.is_valid(buffer)
@@ -1189,7 +1185,7 @@ impl Context {
         self.pending_tool_uses_by_id.get(id)
     }
 
-    fn set_language(&mut self, cx: &mut ModelContext<Self>) {
+    fn set_language(&mut self, cx: &mut Context<Self>) {
         let markdown = self.language_registry.language_for_name("Markdown");
         cx.spawn(|this, mut cx| async move {
             let markdown = markdown.await?;
@@ -1203,9 +1199,9 @@ impl Context {
 
     fn handle_buffer_event(
         &mut self,
-        _: Model<Buffer>,
+        _: Entity<Buffer>,
         event: &language::BufferEvent,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match event {
             language::BufferEvent::Operation {
@@ -1227,7 +1223,7 @@ impl Context {
         self.token_count
     }
 
-    pub(crate) fn count_remaining_tokens(&mut self, cx: &mut ModelContext<Self>) {
+    pub(crate) fn count_remaining_tokens(&mut self, cx: &mut Context<Self>) {
         // Assume it will be a Chat request, even though that takes fewer tokens (and risks going over the limit),
         // because otherwise you see in the UI that your empty message has a bunch of tokens already used.
         let request = self.to_completion_request(RequestType::Chat, cx);
@@ -1255,7 +1251,7 @@ impl Context {
         &mut self,
         cache_configuration: &Option<LanguageModelCacheConfiguration>,
         speculative: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> bool {
         let cache_configuration =
             cache_configuration
@@ -1357,7 +1353,7 @@ impl Context {
         new_anchor_needs_caching
     }
 
-    fn start_cache_warming(&mut self, model: &Arc<dyn LanguageModel>, cx: &mut ModelContext<Self>) {
+    fn start_cache_warming(&mut self, model: &Arc<dyn LanguageModel>, cx: &mut Context<Self>) {
         let cache_configuration = model.cache_configuration();
 
         if !self.mark_cache_anchors(&cache_configuration, true, cx) {
@@ -1407,7 +1403,7 @@ impl Context {
         });
     }
 
-    pub fn update_cache_status_for_completion(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn update_cache_status_for_completion(&mut self, cx: &mut Context<Self>) {
         let cached_message_ids: Vec<MessageId> = self
             .messages_metadata
             .iter()
@@ -1432,7 +1428,7 @@ impl Context {
         cx.notify();
     }
 
-    pub fn reparse(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn reparse(&mut self, cx: &mut Context<Self>) {
         let buffer = self.buffer.read(cx).text_snapshot();
         let mut row_ranges = self
             .edits_since_last_parse
@@ -1505,7 +1501,7 @@ impl Context {
         buffer: &BufferSnapshot,
         updated: &mut Vec<ParsedSlashCommand>,
         removed: &mut Vec<Range<text::Anchor>>,
-        cx: &AppContext,
+        cx: &App,
     ) {
         let old_range = self.pending_command_indices_for_range(range.clone(), cx);
 
@@ -1559,7 +1555,7 @@ impl Context {
     fn invalidate_pending_slash_commands(
         &mut self,
         buffer: &BufferSnapshot,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let mut invalidated_command_ids = Vec::new();
         for (&command_id, command) in self.invoked_slash_commands.iter_mut() {
@@ -1593,7 +1589,7 @@ impl Context {
         buffer: &BufferSnapshot,
         updated: &mut Vec<Range<text::Anchor>>,
         removed: &mut Vec<Range<text::Anchor>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         // Rebuild the XML tags in the edited range.
         let intersecting_tags_range =
@@ -1636,7 +1632,7 @@ impl Context {
         &self,
         buffer: &BufferSnapshot,
         range: Range<text::Anchor>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Vec<XmlTag> {
         let mut messages = self.messages(cx).peekable();
 
@@ -1693,7 +1689,7 @@ impl Context {
         tags_start_ix: usize,
         buffer_end: text::Anchor,
         buffer: &BufferSnapshot,
-        cx: &AppContext,
+        cx: &App,
     ) -> Vec<AssistantPatch> {
         let mut new_patches = Vec::new();
         let mut pending_patch = None;
@@ -1851,7 +1847,7 @@ impl Context {
     pub fn pending_command_for_position(
         &mut self,
         position: language::Anchor,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<&mut ParsedSlashCommand> {
         let buffer = self.buffer.read(cx);
         match self
@@ -1875,7 +1871,7 @@ impl Context {
     pub fn pending_commands_for_range(
         &self,
         range: Range<language::Anchor>,
-        cx: &AppContext,
+        cx: &App,
     ) -> &[ParsedSlashCommand] {
         let range = self.pending_command_indices_for_range(range, cx);
         &self.parsed_slash_commands[range]
@@ -1884,7 +1880,7 @@ impl Context {
     fn pending_command_indices_for_range(
         &self,
         range: Range<language::Anchor>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Range<usize> {
         self.indices_intersecting_buffer_range(&self.parsed_slash_commands, range, cx)
     }
@@ -1893,7 +1889,7 @@ impl Context {
         &self,
         all_annotations: &[T],
         range: Range<language::Anchor>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Range<usize> {
         let buffer = self.buffer.read(cx);
         let start_ix = match all_annotations
@@ -1916,7 +1912,7 @@ impl Context {
         name: &str,
         output: Task<SlashCommandResult>,
         ensure_trailing_newline: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let version = self.version.clone();
         let command_id = InvokedSlashCommandId(self.next_timestamp());
@@ -2184,7 +2180,7 @@ impl Context {
     fn insert_slash_command_output_section(
         &mut self,
         section: SlashCommandOutputSection<language::Anchor>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let buffer = self.buffer.read(cx);
         let insertion_ix = match self
@@ -2214,7 +2210,7 @@ impl Context {
         &mut self,
         tool_use_id: LanguageModelToolUseId,
         output: Task<Result<String>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let insert_output_task = cx.spawn(|this, mut cx| {
             let tool_use_id = tool_use_id.clone();
@@ -2272,11 +2268,11 @@ impl Context {
         }
     }
 
-    pub fn completion_provider_changed(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn completion_provider_changed(&mut self, cx: &mut Context<Self>) {
         self.count_remaining_tokens(cx);
     }
 
-    fn get_last_valid_message_id(&self, cx: &ModelContext<Self>) -> Option<MessageId> {
+    fn get_last_valid_message_id(&self, cx: &Context<Self>) -> Option<MessageId> {
         self.message_anchors.iter().rev().find_map(|message| {
             message
                 .start
@@ -2288,7 +2284,7 @@ impl Context {
     pub fn assist(
         &mut self,
         request_type: RequestType,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<MessageAnchor> {
         let model_registry = LanguageModelRegistry::read_global(cx);
         let provider = model_registry.active_provider()?;
@@ -2519,7 +2515,7 @@ impl Context {
     pub fn to_completion_request(
         &self,
         request_type: RequestType,
-        cx: &AppContext,
+        cx: &App,
     ) -> LanguageModelRequest {
         let buffer = self.buffer.read(cx);
 
@@ -2631,7 +2627,7 @@ impl Context {
         completion_request
     }
 
-    pub fn cancel_last_assist(&mut self, cx: &mut ModelContext<Self>) -> bool {
+    pub fn cancel_last_assist(&mut self, cx: &mut Context<Self>) -> bool {
         if let Some(pending_completion) = self.pending_completions.pop() {
             self.update_metadata(pending_completion.assistant_message_id, cx, |metadata| {
                 if metadata.status == MessageStatus::Pending {
@@ -2644,7 +2640,7 @@ impl Context {
         }
     }
 
-    pub fn cycle_message_roles(&mut self, ids: HashSet<MessageId>, cx: &mut ModelContext<Self>) {
+    pub fn cycle_message_roles(&mut self, ids: HashSet<MessageId>, cx: &mut Context<Self>) {
         for id in &ids {
             if let Some(metadata) = self.messages_metadata.get(id) {
                 let role = metadata.role.cycle();
@@ -2655,7 +2651,7 @@ impl Context {
         self.message_roles_updated(ids, cx);
     }
 
-    fn message_roles_updated(&mut self, ids: HashSet<MessageId>, cx: &mut ModelContext<Self>) {
+    fn message_roles_updated(&mut self, ids: HashSet<MessageId>, cx: &mut Context<Self>) {
         let mut ranges = Vec::new();
         for message in self.messages(cx) {
             if ids.contains(&message.id) {
@@ -2678,7 +2674,7 @@ impl Context {
     pub fn update_metadata(
         &mut self,
         id: MessageId,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
         f: impl FnOnce(&mut MessageMetadata),
     ) {
         let version = self.version.clone();
@@ -2702,7 +2698,7 @@ impl Context {
         message_id: MessageId,
         role: Role,
         status: MessageStatus,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<MessageAnchor> {
         if let Some(prev_message_ix) = self
             .message_anchors
@@ -2736,7 +2732,7 @@ impl Context {
         offset: usize,
         role: Role,
         status: MessageStatus,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> MessageAnchor {
         let start = self.buffer.update(cx, |buffer, cx| {
             buffer.edit([(offset..offset, "\n")], None, cx);
@@ -2766,7 +2762,7 @@ impl Context {
         anchor
     }
 
-    pub fn insert_content(&mut self, content: Content, cx: &mut ModelContext<Self>) {
+    pub fn insert_content(&mut self, content: Content, cx: &mut Context<Self>) {
         let buffer = self.buffer.read(cx);
         let insertion_ix = match self
             .contents
@@ -2782,7 +2778,7 @@ impl Context {
         cx.emit(ContextEvent::MessagesEdited);
     }
 
-    pub fn contents<'a>(&'a self, cx: &'a AppContext) -> impl 'a + Iterator<Item = Content> {
+    pub fn contents<'a>(&'a self, cx: &'a App) -> impl 'a + Iterator<Item = Content> {
         let buffer = self.buffer.read(cx);
         self.contents
             .iter()
@@ -2796,7 +2792,7 @@ impl Context {
     pub fn split_message(
         &mut self,
         range: Range<usize>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> (Option<MessageAnchor>, Option<MessageAnchor>) {
         let start_message = self.message_for_offset(range.start, cx);
         let end_message = self.message_for_offset(range.end, cx);
@@ -2922,7 +2918,7 @@ impl Context {
         &mut self,
         new_anchor: MessageAnchor,
         new_metadata: MessageMetadata,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         cx.emit(ContextEvent::MessagesEdited);
 
@@ -2940,7 +2936,7 @@ impl Context {
         self.message_anchors.insert(insertion_ix, new_anchor);
     }
 
-    pub fn summarize(&mut self, replace_old: bool, cx: &mut ModelContext<Self>) {
+    pub fn summarize(&mut self, replace_old: bool, cx: &mut Context<Self>) {
         let Some(provider) = LanguageModelRegistry::read_global(cx).active_provider() else {
             return;
         };
@@ -3018,14 +3014,14 @@ impl Context {
         }
     }
 
-    fn message_for_offset(&self, offset: usize, cx: &AppContext) -> Option<Message> {
+    fn message_for_offset(&self, offset: usize, cx: &App) -> Option<Message> {
         self.messages_for_offsets([offset], cx).pop()
     }
 
     pub fn messages_for_offsets(
         &self,
         offsets: impl IntoIterator<Item = usize>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Vec<Message> {
         let mut result = Vec::new();
 
@@ -3058,14 +3054,14 @@ impl Context {
     fn messages_from_anchors<'a>(
         &'a self,
         message_anchors: impl Iterator<Item = &'a MessageAnchor> + 'a,
-        cx: &'a AppContext,
+        cx: &'a App,
     ) -> impl 'a + Iterator<Item = Message> {
         let buffer = self.buffer.read(cx);
 
         Self::messages_from_iters(buffer, &self.messages_metadata, message_anchors.enumerate())
     }
 
-    pub fn messages<'a>(&'a self, cx: &'a AppContext) -> impl 'a + Iterator<Item = Message> {
+    pub fn messages<'a>(&'a self, cx: &'a App) -> impl 'a + Iterator<Item = Message> {
         self.messages_from_anchors(self.message_anchors.iter(), cx)
     }
 
@@ -3113,7 +3109,7 @@ impl Context {
         &mut self,
         debounce: Option<Duration>,
         fs: Arc<dyn Fs>,
-        cx: &mut ModelContext<Context>,
+        cx: &mut Context<AssistantContext>,
     ) {
         if self.replica_id() != ReplicaId::default() {
             // Prevent saving a remote context for now.
@@ -3179,7 +3175,7 @@ impl Context {
         });
     }
 
-    pub fn custom_summary(&mut self, custom_summary: String, cx: &mut ModelContext<Self>) {
+    pub fn custom_summary(&mut self, custom_summary: String, cx: &mut Context<Self>) {
         let timestamp = self.next_timestamp();
         let summary = self.summary.get_or_insert(ContextSummary::default());
         summary.timestamp = timestamp;
@@ -3339,8 +3335,8 @@ impl SavedContext {
 
     fn into_ops(
         self,
-        buffer: &Model<Buffer>,
-        cx: &mut ModelContext<Context>,
+        buffer: &Entity<Buffer>,
+        cx: &mut Context<AssistantContext>,
     ) -> Vec<ContextOperation> {
         let mut operations = Vec::new();
         let mut version = clock::Global::new();

crates/assistant_context_editor/src/context/context_tests.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{
-    AssistantEdit, AssistantEditKind, CacheStatus, Context, ContextEvent, ContextId,
+    AssistantContext, AssistantEdit, AssistantEditKind, CacheStatus, ContextEvent, ContextId,
     ContextOperation, InvokedSlashCommandId, MessageCacheMetadata, MessageId, MessageStatus,
 };
 use anyhow::Result;
@@ -15,7 +15,7 @@ use futures::{
     channel::mpsc,
     stream::{self, StreamExt},
 };
-use gpui::{prelude::*, AppContext, Model, SharedString, Task, TestAppContext, WeakView};
+use gpui::{prelude::*, App, Entity, SharedString, Task, TestAppContext, WeakEntity};
 use language::{Buffer, BufferSnapshot, LanguageRegistry, LspAdapterDelegate};
 use language_model::{LanguageModelCacheConfiguration, LanguageModelRegistry, Role};
 use parking_lot::Mutex;
@@ -34,7 +34,7 @@ use std::{
     sync::{atomic::AtomicBool, Arc},
 };
 use text::{network::Network, OffsetRangeExt as _, ReplicaId, ToOffset};
-use ui::{IconName, WindowContext};
+use ui::{IconName, Window};
 use unindent::Unindent;
 use util::{
     test::{generate_marked_text, marked_text_ranges},
@@ -43,14 +43,14 @@ use util::{
 use workspace::Workspace;
 
 #[gpui::test]
-fn test_inserting_and_removing_messages(cx: &mut AppContext) {
+fn test_inserting_and_removing_messages(cx: &mut App) {
     let settings_store = SettingsStore::test(cx);
     LanguageModelRegistry::test(cx);
     cx.set_global(settings_store);
     let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
     let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
-    let context = cx.new_model(|cx| {
-        Context::local(
+    let context = cx.new(|cx| {
+        AssistantContext::local(
             registry,
             None,
             None,
@@ -183,15 +183,15 @@ fn test_inserting_and_removing_messages(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_message_splitting(cx: &mut AppContext) {
+fn test_message_splitting(cx: &mut App) {
     let settings_store = SettingsStore::test(cx);
     cx.set_global(settings_store);
     LanguageModelRegistry::test(cx);
     let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
 
     let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
-    let context = cx.new_model(|cx| {
-        Context::local(
+    let context = cx.new(|cx| {
+        AssistantContext::local(
             registry.clone(),
             None,
             None,
@@ -287,14 +287,14 @@ fn test_message_splitting(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_messages_for_offsets(cx: &mut AppContext) {
+fn test_messages_for_offsets(cx: &mut App) {
     let settings_store = SettingsStore::test(cx);
     LanguageModelRegistry::test(cx);
     cx.set_global(settings_store);
     let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
     let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
-    let context = cx.new_model(|cx| {
-        Context::local(
+    let context = cx.new(|cx| {
+        AssistantContext::local(
             registry,
             None,
             None,
@@ -367,9 +367,9 @@ fn test_messages_for_offsets(cx: &mut AppContext) {
     );
 
     fn message_ids_for_offsets(
-        context: &Model<Context>,
+        context: &Entity<AssistantContext>,
         offsets: &[usize],
-        cx: &AppContext,
+        cx: &App,
     ) -> Vec<MessageId> {
         context
             .read(cx)
@@ -407,8 +407,8 @@ async fn test_slash_commands(cx: &mut TestAppContext) {
 
     let registry = Arc::new(LanguageRegistry::test(cx.executor()));
     let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
-    let context = cx.new_model(|cx| {
-        Context::local(
+    let context = cx.new(|cx| {
+        AssistantContext::local(
             registry.clone(),
             None,
             None,
@@ -608,7 +608,7 @@ async fn test_slash_commands(cx: &mut TestAppContext) {
 
     #[track_caller]
     fn assert_text_and_context_ranges(
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         ranges: &RefCell<ContextRanges>,
         expected_marked_text: &str,
         cx: &mut TestAppContext,
@@ -697,8 +697,8 @@ async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
 
     // Create a new context
     let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
-    let context = cx.new_model(|cx| {
-        Context::local(
+    let context = cx.new(|cx| {
+        AssistantContext::local(
             registry.clone(),
             Some(project),
             None,
@@ -962,8 +962,8 @@ async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
 
     // Ensure steps are re-parsed when deserializing.
     let serialized_context = context.read_with(cx, |context, cx| context.serialize(cx));
-    let deserialized_context = cx.new_model(|cx| {
-        Context::deserialize(
+    let deserialized_context = cx.new(|cx| {
+        AssistantContext::deserialize(
             serialized_context,
             Default::default(),
             registry.clone(),
@@ -1006,7 +1006,11 @@ async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
         cx,
     );
 
-    fn edit(context: &Model<Context>, new_text_marked_with_edits: &str, cx: &mut TestAppContext) {
+    fn edit(
+        context: &Entity<AssistantContext>,
+        new_text_marked_with_edits: &str,
+        cx: &mut TestAppContext,
+    ) {
         context.update(cx, |context, cx| {
             context.buffer.update(cx, |buffer, cx| {
                 buffer.edit_via_marked_text(&new_text_marked_with_edits.unindent(), None, cx);
@@ -1017,7 +1021,7 @@ async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
 
     #[track_caller]
     fn expect_patches(
-        context: &Model<Context>,
+        context: &Entity<AssistantContext>,
         expected_marked_text: &str,
         expected_suggestions: &[&[AssistantEdit]],
         cx: &mut TestAppContext,
@@ -1077,8 +1081,8 @@ async fn test_serialization(cx: &mut TestAppContext) {
     cx.update(LanguageModelRegistry::test);
     let registry = Arc::new(LanguageRegistry::test(cx.executor()));
     let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
-    let context = cx.new_model(|cx| {
-        Context::local(
+    let context = cx.new(|cx| {
+        AssistantContext::local(
             registry.clone(),
             None,
             None,
@@ -1121,8 +1125,8 @@ async fn test_serialization(cx: &mut TestAppContext) {
     );
 
     let serialized_context = context.read_with(cx, |context, cx| context.serialize(cx));
-    let deserialized_context = cx.new_model(|cx| {
-        Context::deserialize(
+    let deserialized_context = cx.new(|cx| {
+        AssistantContext::deserialize(
             serialized_context,
             Default::default(),
             registry.clone(),
@@ -1179,8 +1183,8 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
     let context_id = ContextId::new();
     let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
     for i in 0..num_peers {
-        let context = cx.new_model(|cx| {
-            Context::new(
+        let context = cx.new(|cx| {
+            AssistantContext::new(
                 context_id.clone(),
                 i as ReplicaId,
                 language::Capability::ReadWrite,
@@ -1434,14 +1438,14 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
 }
 
 #[gpui::test]
-fn test_mark_cache_anchors(cx: &mut AppContext) {
+fn test_mark_cache_anchors(cx: &mut App) {
     let settings_store = SettingsStore::test(cx);
     LanguageModelRegistry::test(cx);
     cx.set_global(settings_store);
     let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
     let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
-    let context = cx.new_model(|cx| {
-        Context::local(
+    let context = cx.new(|cx| {
+        AssistantContext::local(
             registry,
             None,
             None,
@@ -1594,7 +1598,7 @@ fn test_mark_cache_anchors(cx: &mut AppContext) {
     );
 }
 
-fn messages(context: &Model<Context>, cx: &AppContext) -> Vec<(MessageId, Role, Range<usize>)> {
+fn messages(context: &Entity<AssistantContext>, cx: &App) -> Vec<(MessageId, Role, Range<usize>)> {
     context
         .read(cx)
         .messages(cx)
@@ -1603,8 +1607,8 @@ fn messages(context: &Model<Context>, cx: &AppContext) -> Vec<(MessageId, Role,
 }
 
 fn messages_cache(
-    context: &Model<Context>,
-    cx: &AppContext,
+    context: &Entity<AssistantContext>,
+    cx: &App,
 ) -> Vec<(MessageId, Option<MessageCacheMetadata>)> {
     context
         .read(cx)
@@ -1633,8 +1637,9 @@ impl SlashCommand for FakeSlashCommand {
         self: Arc<Self>,
         _arguments: &[String],
         _cancel: Arc<AtomicBool>,
-        _workspace: Option<WeakView<Workspace>>,
-        _cx: &mut WindowContext,
+        _workspace: Option<WeakEntity<Workspace>>,
+        _window: &mut Window,
+        _cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         Task::ready(Ok(vec![]))
     }
@@ -1648,9 +1653,10 @@ impl SlashCommand for FakeSlashCommand {
         _arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         _context_buffer: BufferSnapshot,
-        _workspace: WeakView<Workspace>,
+        _workspace: WeakEntity<Workspace>,
         _delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        _cx: &mut WindowContext,
+        _window: &mut Window,
+        _cx: &mut App,
     ) -> Task<SlashCommandResult> {
         Task::ready(Ok(SlashCommandOutput {
             text: format!("Executed fake command: {}", self.0),

crates/assistant_context_editor/src/context_editor.rs 🔗

@@ -23,11 +23,11 @@ use fs::Fs;
 use futures::FutureExt;
 use gpui::{
     actions, div, img, impl_internal_actions, percentage, point, prelude::*, pulsating_between,
-    size, Animation, AnimationExt, AnyElement, AnyView, AppContext, AsyncWindowContext,
+    size, Animation, AnimationExt, AnyElement, AnyView, AnyWindowHandle, App, AsyncWindowContext,
     ClipboardEntry, ClipboardItem, CursorStyle, Empty, Entity, EventEmitter, FocusHandle,
-    FocusableView, FontWeight, Global, InteractiveElement, IntoElement, Model, ParentElement,
-    Pixels, Render, RenderImage, SharedString, Size, StatefulInteractiveElement, Styled,
-    Subscription, Task, Transformation, View, WeakModel, WeakView,
+    Focusable, FontWeight, Global, InteractiveElement, IntoElement, ParentElement, Pixels, Render,
+    RenderImage, SharedString, Size, StatefulInteractiveElement, Styled, Subscription, Task,
+    Transformation, WeakEntity,
 };
 use indexed_docs::IndexedDocsStore;
 use language::{language_settings::SoftWrap, BufferSnapshot, LspAdapterDelegate, ToOffset};
@@ -62,9 +62,9 @@ use workspace::{
 
 use crate::{slash_command::SlashCommandCompletionProvider, slash_command_picker};
 use crate::{
-    AssistantPatch, AssistantPatchStatus, CacheStatus, Content, Context, ContextEvent, ContextId,
-    InvokedSlashCommandId, InvokedSlashCommandStatus, Message, MessageId, MessageMetadata,
-    MessageStatus, ParsedSlashCommand, PendingSlashCommandStatus, RequestType,
+    AssistantContext, AssistantPatch, AssistantPatchStatus, CacheStatus, Content, ContextEvent,
+    ContextId, InvokedSlashCommandId, InvokedSlashCommandStatus, Message, MessageId,
+    MessageMetadata, MessageStatus, ParsedSlashCommand, PendingSlashCommandStatus, RequestType,
 };
 
 actions!(
@@ -103,7 +103,7 @@ struct PatchViewState {
 }
 
 struct PatchEditorState {
-    editor: WeakView<ProposedChangesEditor>,
+    editor: WeakEntity<ProposedChangesEditor>,
     opened_patch: AssistantPatch,
 }
 
@@ -121,40 +121,44 @@ pub trait AssistantPanelDelegate {
     fn active_context_editor(
         &self,
         workspace: &mut Workspace,
-        cx: &mut ViewContext<Workspace>,
-    ) -> Option<View<ContextEditor>>;
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Option<Entity<ContextEditor>>;
 
     fn open_saved_context(
         &self,
         workspace: &mut Workspace,
         path: PathBuf,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) -> Task<Result<()>>;
 
     fn open_remote_context(
         &self,
         workspace: &mut Workspace,
         context_id: ContextId,
-        cx: &mut ViewContext<Workspace>,
-    ) -> Task<Result<View<ContextEditor>>>;
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Task<Result<Entity<ContextEditor>>>;
 
     fn quote_selection(
         &self,
         workspace: &mut Workspace,
         creases: Vec<(String, String)>,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     );
 }
 
 impl dyn AssistantPanelDelegate {
     /// Returns the global [`AssistantPanelDelegate`], if it exists.
-    pub fn try_global(cx: &AppContext) -> Option<Arc<Self>> {
+    pub fn try_global(cx: &App) -> Option<Arc<Self>> {
         cx.try_global::<GlobalAssistantPanelDelegate>()
             .map(|global| global.0.clone())
     }
 
     /// Sets the global [`AssistantPanelDelegate`].
-    pub fn set_global(delegate: Arc<Self>, cx: &mut AppContext) {
+    pub fn set_global(delegate: Arc<Self>, cx: &mut App) {
         cx.set_global(GlobalAssistantPanelDelegate(delegate));
     }
 }
@@ -164,14 +168,14 @@ struct GlobalAssistantPanelDelegate(Arc<dyn AssistantPanelDelegate>);
 impl Global for GlobalAssistantPanelDelegate {}
 
 pub struct ContextEditor {
-    context: Model<Context>,
+    context: Entity<AssistantContext>,
     fs: Arc<dyn Fs>,
     slash_commands: Arc<SlashCommandWorkingSet>,
     tools: Arc<ToolWorkingSet>,
-    workspace: WeakView<Workspace>,
-    project: Model<Project>,
+    workspace: WeakEntity<Workspace>,
+    project: Entity<Project>,
     lsp_adapter_delegate: Option<Arc<dyn LspAdapterDelegate>>,
-    editor: View<Editor>,
+    editor: Entity<Editor>,
     blocks: HashMap<MessageId, (MessageHeader, CustomBlockId)>,
     image_blocks: HashSet<CustomBlockId>,
     scroll_position: Option<ScrollPosition>,
@@ -191,7 +195,7 @@ pub struct ContextEditor {
     // the worktree is not part of the project panel, it would be dropped as soon as
     // the file is opened. In order to keep the worktree alive for the duration of the
     // context editor, we keep a reference here.
-    dragged_file_worktrees: Vec<Model<Worktree>>,
+    dragged_file_worktrees: Vec<Entity<Worktree>>,
 }
 
 pub const DEFAULT_TAB_TITLE: &str = "New Chat";
@@ -199,21 +203,23 @@ const MAX_TAB_TITLE_LEN: usize = 16;
 
 impl ContextEditor {
     pub fn for_context(
-        context: Model<Context>,
+        context: Entity<AssistantContext>,
         fs: Arc<dyn Fs>,
-        workspace: WeakView<Workspace>,
-        project: Model<Project>,
+        workspace: WeakEntity<Workspace>,
+        project: Entity<Project>,
         lsp_adapter_delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let completion_provider = SlashCommandCompletionProvider::new(
             context.read(cx).slash_commands().clone(),
-            Some(cx.view().downgrade()),
+            Some(cx.model().downgrade()),
             Some(workspace.clone()),
         );
 
-        let editor = cx.new_view(|cx| {
-            let mut editor = Editor::for_buffer(context.read(cx).buffer().clone(), None, cx);
+        let editor = cx.new(|cx| {
+            let mut editor =
+                Editor::for_buffer(context.read(cx).buffer().clone(), None, window, cx);
             editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
             editor.set_show_line_numbers(false, cx);
             editor.set_show_scrollbars(false, cx);
@@ -230,9 +236,9 @@ impl ContextEditor {
 
         let _subscriptions = vec![
             cx.observe(&context, |_, _, cx| cx.notify()),
-            cx.subscribe(&context, Self::handle_context_event),
-            cx.subscribe(&editor, Self::handle_editor_event),
-            cx.subscribe(&editor, Self::handle_editor_search_event),
+            cx.subscribe_in(&context, window, Self::handle_context_event),
+            cx.subscribe_in(&editor, window, Self::handle_editor_event),
+            cx.subscribe_in(&editor, window, Self::handle_editor_search_event),
         ];
 
         let sections = context.read(cx).slash_command_output_sections().to_vec();
@@ -265,23 +271,23 @@ impl ContextEditor {
         };
         this.update_message_headers(cx);
         this.update_image_blocks(cx);
-        this.insert_slash_command_output_sections(sections, false, cx);
-        this.patches_updated(&Vec::new(), &patch_ranges, cx);
+        this.insert_slash_command_output_sections(sections, false, window, cx);
+        this.patches_updated(&Vec::new(), &patch_ranges, window, cx);
         this
     }
 
-    pub fn context(&self) -> &Model<Context> {
+    pub fn context(&self) -> &Entity<AssistantContext> {
         &self.context
     }
 
-    pub fn editor(&self) -> &View<Editor> {
+    pub fn editor(&self) -> &Entity<Editor> {
         &self.editor
     }
 
-    pub fn insert_default_prompt(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn insert_default_prompt(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let command_name = DefaultSlashCommand.name();
         self.editor.update(cx, |editor, cx| {
-            editor.insert(&format!("/{command_name}\n\n"), cx)
+            editor.insert(&format!("/{command_name}\n\n"), window, cx)
         });
         let command = self.context.update(cx, |context, cx| {
             context.reparse(cx);
@@ -293,26 +299,27 @@ impl ContextEditor {
             &command.arguments,
             false,
             self.workspace.clone(),
+            window,
             cx,
         );
     }
 
-    fn assist(&mut self, _: &Assist, cx: &mut ViewContext<Self>) {
-        self.send_to_model(RequestType::Chat, cx);
+    fn assist(&mut self, _: &Assist, window: &mut Window, cx: &mut Context<Self>) {
+        self.send_to_model(RequestType::Chat, window, cx);
     }
 
-    fn edit(&mut self, _: &Edit, cx: &mut ViewContext<Self>) {
-        self.send_to_model(RequestType::SuggestEdits, cx);
+    fn edit(&mut self, _: &Edit, window: &mut Window, cx: &mut Context<Self>) {
+        self.send_to_model(RequestType::SuggestEdits, window, cx);
     }
 
-    fn focus_active_patch(&mut self, cx: &mut ViewContext<Self>) -> bool {
+    fn focus_active_patch(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
         if let Some((_range, patch)) = self.active_patch() {
             if let Some(editor) = patch
                 .editor
                 .as_ref()
                 .and_then(|state| state.editor.upgrade())
             {
-                cx.focus_view(&editor);
+                editor.focus_handle(cx).focus(window);
                 return true;
             }
         }
@@ -320,7 +327,12 @@ impl ContextEditor {
         false
     }
 
-    fn send_to_model(&mut self, request_type: RequestType, cx: &mut ViewContext<Self>) {
+    fn send_to_model(
+        &mut self,
+        request_type: RequestType,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let provider = LanguageModelRegistry::read_global(cx).active_provider();
         if provider
             .as_ref()
@@ -331,7 +343,7 @@ impl ContextEditor {
             return;
         }
 
-        if self.focus_active_patch(cx) {
+        if self.focus_active_patch(window, cx) {
             return;
         }
 
@@ -353,18 +365,24 @@ impl ContextEditor {
             self.editor.update(cx, |editor, cx| {
                 editor.change_selections(
                     Some(Autoscroll::Strategy(AutoscrollStrategy::Fit)),
+                    window,
                     cx,
                     |selections| selections.select_ranges([new_selection]),
                 );
             });
             // Avoid scrolling to the new cursor position so the assistant's output is stable.
-            cx.defer(|this, _| this.scroll_position = None);
+            cx.defer_in(window, |this, _, _| this.scroll_position = None);
         }
 
         cx.notify();
     }
 
-    fn cancel(&mut self, _: &editor::actions::Cancel, cx: &mut ViewContext<Self>) {
+    fn cancel(
+        &mut self,
+        _: &editor::actions::Cancel,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.last_error = None;
 
         if self
@@ -377,7 +395,12 @@ impl ContextEditor {
         cx.propagate();
     }
 
-    fn cycle_message_role(&mut self, _: &CycleMessageRole, cx: &mut ViewContext<Self>) {
+    fn cycle_message_role(
+        &mut self,
+        _: &CycleMessageRole,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let cursors = self.cursors(cx);
         self.context.update(cx, |context, cx| {
             let messages = context
@@ -389,7 +412,7 @@ impl ContextEditor {
         });
     }
 
-    fn cursors(&self, cx: &mut WindowContext) -> Vec<usize> {
+    fn cursors(&self, cx: &mut App) -> Vec<usize> {
         let selections = self
             .editor
             .update(cx, |editor, cx| editor.selections.all::<usize>(cx));
@@ -399,11 +422,12 @@ impl ContextEditor {
             .collect()
     }
 
-    pub fn insert_command(&mut self, name: &str, cx: &mut ViewContext<Self>) {
+    pub fn insert_command(&mut self, name: &str, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(command) = self.slash_commands.command(name, cx) {
             self.editor.update(cx, |editor, cx| {
-                editor.transact(cx, |editor, cx| {
-                    editor.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel());
+                editor.transact(window, cx, |editor, window, cx| {
+                    editor
+                        .change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel());
                     let snapshot = editor.buffer().read(cx).snapshot(cx);
                     let newest_cursor = editor.selections.newest::<Point>(cx).head();
                     if newest_cursor.column > 0
@@ -416,25 +440,31 @@ impl ContextEditor {
                             &MoveToEndOfLine {
                                 stop_at_soft_wraps: false,
                             },
+                            window,
                             cx,
                         );
-                        editor.newline(&Newline, cx);
+                        editor.newline(&Newline, window, cx);
                     }
 
-                    editor.insert(&format!("/{name}"), cx);
+                    editor.insert(&format!("/{name}"), window, cx);
                     if command.accepts_arguments() {
-                        editor.insert(" ", cx);
-                        editor.show_completions(&ShowCompletions::default(), cx);
+                        editor.insert(" ", window, cx);
+                        editor.show_completions(&ShowCompletions::default(), window, cx);
                     }
                 });
             });
             if !command.requires_argument() {
-                self.confirm_command(&ConfirmCommand, cx);
+                self.confirm_command(&ConfirmCommand, window, cx);
             }
         }
     }
 
-    pub fn confirm_command(&mut self, _: &ConfirmCommand, cx: &mut ViewContext<Self>) {
+    pub fn confirm_command(
+        &mut self,
+        _: &ConfirmCommand,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if self.editor.read(cx).has_active_completions_menu() {
             return;
         }
@@ -465,6 +495,7 @@ impl ContextEditor {
                     &command.arguments,
                     true,
                     workspace.clone(),
+                    window,
                     cx,
                 );
             }
@@ -479,8 +510,9 @@ impl ContextEditor {
         name: &str,
         arguments: &[String],
         ensure_trailing_newline: bool,
-        workspace: WeakView<Workspace>,
-        cx: &mut ViewContext<Self>,
+        workspace: WeakEntity<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         if let Some(command) = self.slash_commands.command(name, cx) {
             let context = self.context.read(cx);
@@ -497,6 +529,7 @@ impl ContextEditor {
                 snapshot,
                 workspace,
                 self.lsp_adapter_delegate.clone(),
+                window,
                 cx,
             );
             self.context.update(cx, |context, cx| {
@@ -513,11 +546,12 @@ impl ContextEditor {
 
     fn handle_context_event(
         &mut self,
-        _: Model<Context>,
+        _: &Entity<AssistantContext>,
         event: &ContextEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
-        let context_editor = cx.view().downgrade();
+        let context_editor = cx.model().downgrade();
 
         match event {
             ContextEvent::MessagesEdited => {
@@ -536,12 +570,13 @@ impl ContextEditor {
             ContextEvent::StreamedCompletion => {
                 self.editor.update(cx, |editor, cx| {
                     if let Some(scroll_position) = self.scroll_position {
-                        let snapshot = editor.snapshot(cx);
+                        let snapshot = editor.snapshot(window, cx);
                         let cursor_point = scroll_position.cursor.to_display_point(&snapshot);
                         let scroll_top =
                             cursor_point.row().as_f32() - scroll_position.offset_before_cursor.y;
                         editor.set_scroll_position(
                             point(scroll_position.offset_before_cursor.x, scroll_top),
+                            window,
                             cx,
                         );
                     }
@@ -570,14 +605,16 @@ impl ContextEditor {
                         .map(|tool_use| {
                             let placeholder = FoldPlaceholder {
                                 render: render_fold_icon_button(
-                                    cx.view().downgrade(),
+                                    cx.model().downgrade(),
                                     IconName::PocketKnife,
                                     tool_use.name.clone().into(),
                                 ),
                                 ..Default::default()
                             };
                             let render_trailer =
-                                move |_row, _unfold, _cx: &mut WindowContext| Empty.into_any();
+                                move |_row, _unfold, _window: &mut Window, _cx: &mut App| {
+                                    Empty.into_any()
+                                };
 
                             let start = buffer
                                 .anchor_in_excerpt(excerpt_id, tool_use.source_range.start)
@@ -615,7 +652,7 @@ impl ContextEditor {
                     let crease_ids = editor.insert_creases(creases, cx);
 
                     for buffer_row in buffer_rows_to_fold.into_iter().rev() {
-                        editor.fold_at(&FoldAt { buffer_row }, cx);
+                        editor.fold_at(&FoldAt { buffer_row }, window, cx);
                     }
 
                     self.pending_tool_use_creases.extend(
@@ -627,7 +664,7 @@ impl ContextEditor {
                 });
             }
             ContextEvent::PatchesUpdated { removed, updated } => {
-                self.patches_updated(removed, updated, cx);
+                self.patches_updated(removed, updated, window, cx);
             }
             ContextEvent::ParsedSlashCommandsUpdated { removed, updated } => {
                 self.editor.update(cx, |editor, cx| {
@@ -647,7 +684,7 @@ impl ContextEditor {
                             let confirm_command = Arc::new({
                                 let context_editor = context_editor.clone();
                                 let command = command.clone();
-                                move |cx: &mut WindowContext| {
+                                move |window: &mut Window, cx: &mut App| {
                                     context_editor
                                         .update(cx, |context_editor, cx| {
                                             context_editor.run_command(
@@ -656,6 +693,7 @@ impl ContextEditor {
                                                 &command.arguments,
                                                 false,
                                                 workspace.clone(),
+                                                window,
                                                 cx,
                                             );
                                         })
@@ -663,13 +701,13 @@ impl ContextEditor {
                                 }
                             });
                             let placeholder = FoldPlaceholder {
-                                render: Arc::new(move |_, _, _| Empty.into_any()),
+                                render: Arc::new(move |_, _, _, _| Empty.into_any()),
                                 ..Default::default()
                             };
                             let render_toggle = {
                                 let confirm_command = confirm_command.clone();
                                 let command = command.clone();
-                                move |row, _, _, _cx: &mut WindowContext| {
+                                move |row, _, _, _window: &mut Window, _cx: &mut App| {
                                     render_pending_slash_command_gutter_decoration(
                                         row,
                                         &command.status,
@@ -679,7 +717,7 @@ impl ContextEditor {
                             };
                             let render_trailer = {
                                 let command = command.clone();
-                                move |row, _unfold, cx: &mut WindowContext| {
+                                move |row, _unfold, _window: &mut Window, cx: &mut App| {
                                     // TODO: In the future we should investigate how we can expose
                                     // this as a hook on the `SlashCommand` trait so that we don't
                                     // need to special-case it here.
@@ -715,10 +753,10 @@ impl ContextEditor {
                 })
             }
             ContextEvent::InvokedSlashCommandChanged { command_id } => {
-                self.update_invoked_slash_command(*command_id, cx);
+                self.update_invoked_slash_command(*command_id, window, cx);
             }
             ContextEvent::SlashCommandOutputSectionAdded { section } => {
-                self.insert_slash_command_output_sections([section.clone()], false, cx);
+                self.insert_slash_command_output_sections([section.clone()], false, window, cx);
             }
             ContextEvent::UsePendingTools => {
                 let pending_tool_uses = self
@@ -732,7 +770,7 @@ impl ContextEditor {
 
                 for tool_use in pending_tool_uses {
                     if let Some(tool) = self.tools.tool(&tool_use.name, cx) {
-                        let task = tool.run(tool_use.input, self.workspace.clone(), cx);
+                        let task = tool.run(tool_use.input, self.workspace.clone(), window, cx);
 
                         self.context.update(cx, |context, cx| {
                             context.insert_tool_output(tool_use.id.clone(), task, cx);
@@ -751,14 +789,14 @@ impl ContextEditor {
 
                     let placeholder = FoldPlaceholder {
                         render: render_fold_icon_button(
-                            cx.view().downgrade(),
+                            cx.model().downgrade(),
                             IconName::PocketKnife,
                             format!("Tool Result: {tool_use_id}").into(),
                         ),
                         ..Default::default()
                     };
                     let render_trailer =
-                        move |_row, _unfold, _cx: &mut WindowContext| Empty.into_any();
+                        move |_row, _unfold, _window: &mut Window, _cx: &mut App| Empty.into_any();
 
                     let start = buffer
                         .anchor_in_excerpt(excerpt_id, output_range.start)
@@ -777,7 +815,7 @@ impl ContextEditor {
                     );
 
                     editor.insert_creases([crease], cx);
-                    editor.fold_at(&FoldAt { buffer_row }, cx);
+                    editor.fold_at(&FoldAt { buffer_row }, window, cx);
                 });
             }
             ContextEvent::Operation(_) => {}
@@ -796,7 +834,8 @@ impl ContextEditor {
     fn update_invoked_slash_command(
         &mut self,
         command_id: InvokedSlashCommandId,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         if let Some(invoked_slash_command) =
             self.context.read(cx).invoked_slash_command(&command_id)
@@ -822,6 +861,7 @@ impl ContextEditor {
                             &command.arguments,
                             false,
                             self.workspace.clone(),
+                            window,
                             cx,
                         );
                     }
@@ -872,10 +912,10 @@ impl ContextEditor {
                         crease_start..crease_end,
                         invoked_slash_command_fold_placeholder(command_id, context),
                         fold_toggle("invoked-slash-command"),
-                        |_row, _folded, _cx| Empty.into_any(),
+                        |_row, _folded, _window, _cx| Empty.into_any(),
                     );
                     let crease_ids = editor.insert_creases([crease.clone()], cx);
-                    editor.fold_creases(vec![crease], false, cx);
+                    editor.fold_creases(vec![crease], false, window, cx);
                     entry.insert(crease_ids[0]);
                 } else {
                     cx.notify()
@@ -894,13 +934,14 @@ impl ContextEditor {
         &mut self,
         removed: &Vec<Range<text::Anchor>>,
         updated: &Vec<Range<text::Anchor>>,
-        cx: &mut ViewContext<ContextEditor>,
+        window: &mut Window,
+        cx: &mut Context<ContextEditor>,
     ) {
-        let this = cx.view().downgrade();
+        let this = cx.model().downgrade();
         let mut editors_to_close = Vec::new();
 
         self.editor.update(cx, |editor, cx| {
-            let snapshot = editor.snapshot(cx);
+            let snapshot = editor.snapshot(window, cx);
             let multibuffer = &snapshot.buffer_snapshot;
             let (&excerpt_id, _, _) = multibuffer.as_singleton().unwrap();
 
@@ -937,6 +978,7 @@ impl ContextEditor {
                     .unwrap();
                 let render_block: RenderBlock = Arc::new({
                     let this = this.clone();
+                    let window_handle = window.window_handle();
                     let patch_range = range.clone();
                     move |cx: &mut BlockContext<'_, '_>| {
                         let max_width = cx.max_width;
@@ -950,6 +992,7 @@ impl ContextEditor {
                                 gutter_width,
                                 block_id,
                                 selected,
+                                window_handle,
                                 cx,
                             )
                         })
@@ -973,7 +1016,7 @@ impl ContextEditor {
                         if editor_state.opened_patch != patch {
                             state.update_task = Some({
                                 let this = this.clone();
-                                cx.spawn(|_, cx| async move {
+                                cx.spawn_in(window, |_, cx| async move {
                                     Self::update_patch_editor(this.clone(), patch, cx)
                                         .await
                                         .log_err();
@@ -1000,23 +1043,24 @@ impl ContextEditor {
 
                 if should_refold {
                     editor.unfold_ranges(&[patch_start..patch_end], true, false, cx);
-                    editor.fold_creases(vec![crease], false, cx);
+                    editor.fold_creases(vec![crease], false, window, cx);
                 }
             }
         });
 
         for editor in editors_to_close {
-            self.close_patch_editor(editor, cx);
+            self.close_patch_editor(editor, window, cx);
         }
 
-        self.update_active_patch(cx);
+        self.update_active_patch(window, cx);
     }
 
     fn insert_slash_command_output_sections(
         &mut self,
         sections: impl IntoIterator<Item = SlashCommandOutputSection<language::Anchor>>,
         expand_result: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.editor.update(cx, |editor, cx| {
             let buffer = editor.buffer().read(cx).snapshot(cx);
@@ -1037,7 +1081,7 @@ impl ContextEditor {
                         start..end,
                         FoldPlaceholder {
                             render: render_fold_icon_button(
-                                cx.view().downgrade(),
+                                cx.model().downgrade(),
                                 section.icon,
                                 section.label.clone(),
                             ),
@@ -1045,7 +1089,7 @@ impl ContextEditor {
                             ..Default::default()
                         },
                         render_slash_command_output_toggle,
-                        |_, _, _| Empty.into_any_element(),
+                        |_, _, _, _| Empty.into_any_element(),
                     )
                     .with_metadata(CreaseMetadata {
                         icon: section.icon,
@@ -1060,20 +1104,21 @@ impl ContextEditor {
                 buffer_rows_to_fold.clear();
             }
             for buffer_row in buffer_rows_to_fold.into_iter().rev() {
-                editor.fold_at(&FoldAt { buffer_row }, cx);
+                editor.fold_at(&FoldAt { buffer_row }, window, cx);
             }
         });
     }
 
     fn handle_editor_event(
         &mut self,
-        _: View<Editor>,
+        _: &Entity<Editor>,
         event: &EditorEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match event {
             EditorEvent::ScrollPositionChanged { autoscroll, .. } => {
-                let cursor_scroll_position = self.cursor_scroll_position(cx);
+                let cursor_scroll_position = self.cursor_scroll_position(window, cx);
                 if *autoscroll {
                     self.scroll_position = cursor_scroll_position;
                 } else if self.scroll_position != cursor_scroll_position {
@@ -1081,8 +1126,8 @@ impl ContextEditor {
                 }
             }
             EditorEvent::SelectionsChanged { .. } => {
-                self.scroll_position = self.cursor_scroll_position(cx);
-                self.update_active_patch(cx);
+                self.scroll_position = self.cursor_scroll_position(window, cx);
+                self.update_active_patch(window, cx);
             }
             _ => {}
         }
@@ -1094,7 +1139,7 @@ impl ContextEditor {
         Some((patch.clone(), self.patches.get(&patch)?))
     }
 
-    fn update_active_patch(&mut self, cx: &mut ViewContext<Self>) {
+    fn update_active_patch(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let newest_cursor = self.editor.update(cx, |editor, cx| {
             editor.selections.newest::<Point>(cx).head()
         });
@@ -1110,7 +1155,7 @@ impl ContextEditor {
             if let Some(patch_state) = self.patches.get_mut(&old_patch_range) {
                 if let Some(state) = patch_state.editor.take() {
                     if let Some(editor) = state.editor.upgrade() {
-                        self.close_patch_editor(editor, cx);
+                        self.close_patch_editor(editor, window, cx);
                     }
                 }
             }
@@ -1130,13 +1175,14 @@ impl ContextEditor {
                 if let Some(editor) = editor {
                     self.workspace
                         .update(cx, |workspace, cx| {
-                            workspace.activate_item(&editor, true, false, cx);
+                            workspace.activate_item(&editor, true, false, window, cx);
                         })
                         .ok();
                 } else {
-                    patch_state.update_task = Some(cx.spawn(move |this, cx| async move {
-                        Self::open_patch_editor(this, new_patch, cx).await.log_err();
-                    }));
+                    patch_state.update_task =
+                        Some(cx.spawn_in(window, move |this, cx| async move {
+                            Self::open_patch_editor(this, new_patch, cx).await.log_err();
+                        }));
                 }
             }
         }
@@ -1144,16 +1190,17 @@ impl ContextEditor {
 
     fn close_patch_editor(
         &mut self,
-        editor: View<ProposedChangesEditor>,
-        cx: &mut ViewContext<ContextEditor>,
+        editor: Entity<ProposedChangesEditor>,
+        window: &mut Window,
+        cx: &mut Context<ContextEditor>,
     ) {
         self.workspace
             .update(cx, |workspace, cx| {
                 if let Some(pane) = workspace.pane_for(&editor) {
                     pane.update(cx, |pane, cx| {
                         let item_id = editor.entity_id();
-                        if !editor.read(cx).focus_handle(cx).is_focused(cx) {
-                            pane.close_item_by_id(item_id, SaveIntent::Skip, cx)
+                        if !editor.read(cx).focus_handle(cx).is_focused(window) {
+                            pane.close_item_by_id(item_id, SaveIntent::Skip, window, cx)
                                 .detach_and_log_err(cx);
                         }
                     });
@@ -1163,14 +1210,14 @@ impl ContextEditor {
     }
 
     async fn open_patch_editor(
-        this: WeakView<Self>,
+        this: WeakEntity<Self>,
         patch: AssistantPatch,
         mut cx: AsyncWindowContext,
     ) -> Result<()> {
         let project = this.update(&mut cx, |this, _| this.project.clone())?;
         let resolved_patch = patch.resolve(project.clone(), &mut cx).await;
 
-        let editor = cx.new_view(|cx| {
+        let editor = cx.new_window_model(|window, cx| {
             let editor = ProposedChangesEditor::new(
                 patch.title.clone(),
                 resolved_patch
@@ -1185,13 +1232,14 @@ impl ContextEditor {
                     })
                     .collect(),
                 Some(project.clone()),
+                window,
                 cx,
             );
             resolved_patch.apply(&editor, cx);
             editor
         })?;
 
-        this.update(&mut cx, |this, cx| {
+        this.update_in(&mut cx, |this, window, cx| {
             if let Some(patch_state) = this.patches.get_mut(&patch.range) {
                 patch_state.editor = Some(PatchEditorState {
                     editor: editor.downgrade(),
@@ -1202,7 +1250,13 @@ impl ContextEditor {
 
             this.workspace
                 .update(cx, |workspace, cx| {
-                    workspace.add_item_to_active_pane(Box::new(editor.clone()), None, false, cx)
+                    workspace.add_item_to_active_pane(
+                        Box::new(editor.clone()),
+                        None,
+                        false,
+                        window,
+                        cx,
+                    )
                 })
                 .log_err();
         })?;
@@ -1211,13 +1265,13 @@ impl ContextEditor {
     }
 
     async fn update_patch_editor(
-        this: WeakView<Self>,
+        this: WeakEntity<Self>,
         patch: AssistantPatch,
         mut cx: AsyncWindowContext,
     ) -> Result<()> {
         let project = this.update(&mut cx, |this, _| this.project.clone())?;
         let resolved_patch = patch.resolve(project.clone(), &mut cx).await;
-        this.update(&mut cx, |this, cx| {
+        this.update_in(&mut cx, |this, window, cx| {
             let patch_state = this.patches.get_mut(&patch.range)?;
 
             let locations = resolved_patch
@@ -1236,7 +1290,7 @@ impl ContextEditor {
                 if let Some(editor) = state.editor.upgrade() {
                     editor.update(cx, |editor, cx| {
                         editor.set_title(patch.title.clone(), cx);
-                        editor.reset_locations(locations, cx);
+                        editor.reset_locations(locations, window, cx);
                         resolved_patch.apply(editor, cx);
                     });
 
@@ -1254,16 +1308,21 @@ impl ContextEditor {
 
     fn handle_editor_search_event(
         &mut self,
-        _: View<Editor>,
+        _: &Entity<Editor>,
         event: &SearchEvent,
-        cx: &mut ViewContext<Self>,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         cx.emit(event.clone());
     }
 
-    fn cursor_scroll_position(&self, cx: &mut ViewContext<Self>) -> Option<ScrollPosition> {
+    fn cursor_scroll_position(
+        &self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<ScrollPosition> {
         self.editor.update(cx, |editor, cx| {
-            let snapshot = editor.snapshot(cx);
+            let snapshot = editor.snapshot(window, cx);
             let cursor = editor.selections.newest_anchor().head();
             let cursor_row = cursor
                 .to_display_point(&snapshot.display_snapshot)
@@ -1286,7 +1345,7 @@ impl ContextEditor {
         })
     }
 
-    fn esc_kbd(cx: &WindowContext) -> Div {
+    fn esc_kbd(cx: &App) -> Div {
         let colors = cx.theme().colors().clone();
 
         h_flex()
@@ -1309,7 +1368,7 @@ impl ContextEditor {
             .child("to cancel")
     }
 
-    fn update_message_headers(&mut self, cx: &mut ViewContext<Self>) {
+    fn update_message_headers(&mut self, cx: &mut Context<Self>) {
         self.editor.update(cx, |editor, cx| {
             let buffer = editor.buffer().read(cx).snapshot(cx);
 
@@ -1395,17 +1454,18 @@ impl ContextEditor {
                                             .child(label)
                                             .children(spinner),
                                     )
-                                    .tooltip(|cx| {
+                                    .tooltip(|window, cx| {
                                         Tooltip::with_meta(
                                             "Toggle message role",
                                             None,
                                             "Available roles: You (User), Assistant, System",
+                                            window,
                                             cx,
                                         )
                                     })
                                     .on_click({
                                         let context = context.clone();
-                                        move |_, cx| {
+                                        move |_, _window, cx| {
                                             context.update(cx, |context, cx| {
                                                 context.cycle_message_roles(
                                                     HashSet::from_iter(Some(message_id)),
@@ -1435,11 +1495,12 @@ impl ContextEditor {
                                                     .size(IconSize::XSmall)
                                                     .color(Color::Hint),
                                             )
-                                            .tooltip(|cx| {
+                                            .tooltip(|window, cx| {
                                                 Tooltip::with_meta(
                                                     "Context Cached",
                                                     None,
                                                     "Large messages cached to optimize performance",
+                                                    window,
                                                     cx,
                                                 )
                                             })
@@ -1467,11 +1528,11 @@ impl ContextEditor {
                                         .icon_color(Color::Error)
                                         .icon_size(IconSize::XSmall)
                                         .icon_position(IconPosition::Start)
-                                        .tooltip(move |cx| Tooltip::text("View Details", cx))
+                                        .tooltip(Tooltip::text("View Details"))
                                         .on_click({
                                             let context = context.clone();
                                             let error = error.clone();
-                                            move |_, cx| {
+                                            move |_, _window, cx| {
                                                 context.update(cx, |_, cx| {
                                                     cx.emit(ContextEvent::ShowAssistError(
                                                         error.clone(),
@@ -1552,8 +1613,8 @@ impl ContextEditor {
     /// Returns either the selected text, or the content of the Markdown code
     /// block surrounding the cursor.
     fn get_selection_or_code_block(
-        context_editor_view: &View<ContextEditor>,
-        cx: &mut ViewContext<Workspace>,
+        context_editor_view: &Entity<ContextEditor>,
+        cx: &mut Context<Workspace>,
     ) -> Option<(String, bool)> {
         const CODE_FENCE_DELIMITER: &'static str = "```";
 
@@ -1596,13 +1657,14 @@ impl ContextEditor {
     pub fn insert_selection(
         workspace: &mut Workspace,
         _: &InsertIntoEditor,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
         let Some(assistant_panel_delegate) = <dyn AssistantPanelDelegate>::try_global(cx) else {
             return;
         };
         let Some(context_editor_view) =
-            assistant_panel_delegate.active_context_editor(workspace, cx)
+            assistant_panel_delegate.active_context_editor(workspace, window, cx)
         else {
             return;
         };
@@ -1615,17 +1677,22 @@ impl ContextEditor {
 
         if let Some((text, _)) = Self::get_selection_or_code_block(&context_editor_view, cx) {
             active_editor_view.update(cx, |editor, cx| {
-                editor.insert(&text, cx);
-                editor.focus(cx);
+                editor.insert(&text, window, cx);
+                editor.focus_handle(cx).focus(window);
             })
         }
     }
 
-    pub fn copy_code(workspace: &mut Workspace, _: &CopyCode, cx: &mut ViewContext<Workspace>) {
+    pub fn copy_code(
+        workspace: &mut Workspace,
+        _: &CopyCode,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) {
         let result = maybe!({
             let assistant_panel_delegate = <dyn AssistantPanelDelegate>::try_global(cx)?;
             let context_editor_view =
-                assistant_panel_delegate.active_context_editor(workspace, cx)?;
+                assistant_panel_delegate.active_context_editor(workspace, window, cx)?;
             Self::get_selection_or_code_block(&context_editor_view, cx)
         });
         let Some((text, is_code_block)) = result else {
@@ -1655,13 +1722,14 @@ impl ContextEditor {
     pub fn insert_dragged_files(
         workspace: &mut Workspace,
         action: &InsertDraggedFiles,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
         let Some(assistant_panel_delegate) = <dyn AssistantPanelDelegate>::try_global(cx) else {
             return;
         };
         let Some(context_editor_view) =
-            assistant_panel_delegate.active_context_editor(workspace, cx)
+            assistant_panel_delegate.active_context_editor(workspace, window, cx)
         else {
             return;
         };
@@ -1700,38 +1768,40 @@ impl ContextEditor {
             }
         };
 
-        cx.spawn(|_, mut cx| async move {
-            let (paths, dragged_file_worktrees) = paths.await;
-            let cmd_name = FileSlashCommand.name();
-
-            context_editor_view
-                .update(&mut cx, |context_editor, cx| {
-                    let file_argument = paths
-                        .into_iter()
-                        .map(|path| path.to_string_lossy().to_string())
-                        .collect::<Vec<_>>()
-                        .join(" ");
-
-                    context_editor.editor.update(cx, |editor, cx| {
-                        editor.insert("\n", cx);
-                        editor.insert(&format!("/{} {}", cmd_name, file_argument), cx);
-                    });
+        window
+            .spawn(cx, |mut cx| async move {
+                let (paths, dragged_file_worktrees) = paths.await;
+                let cmd_name = FileSlashCommand.name();
+
+                context_editor_view
+                    .update_in(&mut cx, |context_editor, window, cx| {
+                        let file_argument = paths
+                            .into_iter()
+                            .map(|path| path.to_string_lossy().to_string())
+                            .collect::<Vec<_>>()
+                            .join(" ");
+
+                        context_editor.editor.update(cx, |editor, cx| {
+                            editor.insert("\n", window, cx);
+                            editor.insert(&format!("/{} {}", cmd_name, file_argument), window, cx);
+                        });
 
-                    context_editor.confirm_command(&ConfirmCommand, cx);
+                        context_editor.confirm_command(&ConfirmCommand, window, cx);
 
-                    context_editor
-                        .dragged_file_worktrees
-                        .extend(dragged_file_worktrees);
-                })
-                .log_err();
-        })
-        .detach();
+                        context_editor
+                            .dragged_file_worktrees
+                            .extend(dragged_file_worktrees);
+                    })
+                    .log_err();
+            })
+            .detach();
     }
 
     pub fn quote_selection(
         workspace: &mut Workspace,
         _: &QuoteSelection,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
         let Some(assistant_panel_delegate) = <dyn AssistantPanelDelegate>::try_global(cx) else {
             return;

crates/assistant_context_editor/src/context_history.rs 🔗

@@ -1,8 +1,6 @@
 use std::sync::Arc;
 
-use gpui::{
-    AppContext, EventEmitter, FocusHandle, FocusableView, Model, Subscription, Task, View, WeakView,
-};
+use gpui::{App, Entity, EventEmitter, FocusHandle, Focusable, Subscription, Task, WeakEntity};
 use picker::{Picker, PickerDelegate};
 use project::Project;
 use ui::utils::{format_distance_from_now, DateTimeType};
@@ -25,21 +23,23 @@ enum SavedContextPickerEvent {
 }
 
 pub struct ContextHistory {
-    picker: View<Picker<SavedContextPickerDelegate>>,
+    picker: Entity<Picker<SavedContextPickerDelegate>>,
     _subscriptions: Vec<Subscription>,
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
 }
 
 impl ContextHistory {
     pub fn new(
-        project: Model<Project>,
-        context_store: Model<ContextStore>,
-        workspace: WeakView<Workspace>,
-        cx: &mut ViewContext<Self>,
+        project: Entity<Project>,
+        context_store: Entity<ContextStore>,
+        workspace: WeakEntity<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
-        let picker = cx.new_view(|cx| {
+        let picker = cx.new(|cx| {
             Picker::uniform_list(
                 SavedContextPickerDelegate::new(project, context_store.clone()),
+                window,
                 cx,
             )
             .modal(false)
@@ -47,10 +47,11 @@ impl ContextHistory {
         });
 
         let subscriptions = vec![
-            cx.observe(&context_store, |this, _, cx| {
-                this.picker.update(cx, |picker, cx| picker.refresh(cx));
+            cx.observe_in(&context_store, window, |this, _, window, cx| {
+                this.picker
+                    .update(cx, |picker, cx| picker.refresh(window, cx));
             }),
-            cx.subscribe(&picker, Self::handle_picker_event),
+            cx.subscribe_in(&picker, window, Self::handle_picker_event),
         ];
 
         Self {
@@ -62,9 +63,10 @@ impl ContextHistory {
 
     fn handle_picker_event(
         &mut self,
-        _: View<Picker<SavedContextPickerDelegate>>,
+        _: &Entity<Picker<SavedContextPickerDelegate>>,
         event: &SavedContextPickerEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let SavedContextPickerEvent::Confirmed(context) = event;
 
@@ -76,12 +78,12 @@ impl ContextHistory {
             .update(cx, |workspace, cx| match context {
                 ContextMetadata::Remote(metadata) => {
                     assistant_panel_delegate
-                        .open_remote_context(workspace, metadata.id.clone(), cx)
+                        .open_remote_context(workspace, metadata.id.clone(), window, cx)
                         .detach_and_log_err(cx);
                 }
                 ContextMetadata::Saved(metadata) => {
                     assistant_panel_delegate
-                        .open_saved_context(workspace, metadata.path.clone(), cx)
+                        .open_saved_context(workspace, metadata.path.clone(), window, cx)
                         .detach_and_log_err(cx);
                 }
             })
@@ -90,13 +92,13 @@ impl ContextHistory {
 }
 
 impl Render for ContextHistory {
-    fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         div().size_full().child(self.picker.clone())
     }
 }
 
-impl FocusableView for ContextHistory {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for ContextHistory {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
@@ -106,14 +108,14 @@ impl EventEmitter<()> for ContextHistory {}
 impl Item for ContextHistory {
     type Event = ();
 
-    fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
+    fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option<SharedString> {
         Some("History".into())
     }
 }
 
 struct SavedContextPickerDelegate {
-    store: Model<ContextStore>,
-    project: Model<Project>,
+    store: Entity<ContextStore>,
+    project: Entity<Project>,
     matches: Vec<ContextMetadata>,
     selected_index: usize,
 }
@@ -121,7 +123,7 @@ struct SavedContextPickerDelegate {
 impl EventEmitter<SavedContextPickerEvent> for Picker<SavedContextPickerDelegate> {}
 
 impl SavedContextPickerDelegate {
-    fn new(project: Model<Project>, store: Model<ContextStore>) -> Self {
+    fn new(project: Entity<Project>, store: Entity<ContextStore>) -> Self {
         Self {
             project,
             store,
@@ -142,15 +144,25 @@ impl PickerDelegate for SavedContextPickerDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, _cx: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
+    ) {
         self.selected_index = ix;
     }
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Search...".into()
     }
 
-    fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
+    fn update_matches(
+        &mut self,
+        query: String,
+        _window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Task<()> {
         let search = self.store.read(cx).search(query, cx);
         cx.spawn(|this, mut cx| async move {
             let matches = search.await;
@@ -169,19 +181,20 @@ impl PickerDelegate for SavedContextPickerDelegate {
         })
     }
 
-    fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _secondary: bool, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
         if let Some(metadata) = self.matches.get(self.selected_index) {
             cx.emit(SavedContextPickerEvent::Confirmed(metadata.clone()));
         }
     }
 
-    fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
+    fn dismissed(&mut self, _window: &mut Window, _cx: &mut Context<Picker<Self>>) {}
 
     fn render_match(
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        _window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let context = self.matches.get(ix)?;
         let item = match context {

crates/assistant_context_editor/src/context_store.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{
-    Context, ContextEvent, ContextId, ContextOperation, ContextVersion, SavedContext,
+    AssistantContext, ContextEvent, ContextId, ContextOperation, ContextVersion, SavedContext,
     SavedContextMetadata,
 };
 use anyhow::{anyhow, Context as _, Result};
@@ -14,7 +14,7 @@ use fs::Fs;
 use futures::StreamExt;
 use fuzzy::StringMatchCandidate;
 use gpui::{
-    AppContext, AsyncAppContext, Context as _, EventEmitter, Model, ModelContext, Task, WeakModel,
+    App, AppContext as _, AsyncAppContext, Context, Entity, EventEmitter, Task, WeakEntity,
 };
 use language::LanguageRegistry;
 use paths::contexts_dir;
@@ -50,7 +50,7 @@ pub struct RemoteContextMetadata {
 pub struct ContextStore {
     contexts: Vec<ContextHandle>,
     contexts_metadata: Vec<SavedContextMetadata>,
-    context_server_manager: Model<ContextServerManager>,
+    context_server_manager: Entity<ContextServerManager>,
     context_server_slash_command_ids: HashMap<Arc<str>, Vec<SlashCommandId>>,
     context_server_tool_ids: HashMap<Arc<str>, Vec<ToolId>>,
     host_contexts: Vec<RemoteContextMetadata>,
@@ -61,7 +61,7 @@ pub struct ContextStore {
     telemetry: Arc<Telemetry>,
     _watch_updates: Task<Option<()>>,
     client: Arc<Client>,
-    project: Model<Project>,
+    project: Entity<Project>,
     project_is_shared: bool,
     client_subscription: Option<client::Subscription>,
     _project_subscriptions: Vec<gpui::Subscription>,
@@ -75,19 +75,19 @@ pub enum ContextStoreEvent {
 impl EventEmitter<ContextStoreEvent> for ContextStore {}
 
 enum ContextHandle {
-    Weak(WeakModel<Context>),
-    Strong(Model<Context>),
+    Weak(WeakEntity<AssistantContext>),
+    Strong(Entity<AssistantContext>),
 }
 
 impl ContextHandle {
-    fn upgrade(&self) -> Option<Model<Context>> {
+    fn upgrade(&self) -> Option<Entity<AssistantContext>> {
         match self {
             ContextHandle::Weak(weak) => weak.upgrade(),
             ContextHandle::Strong(strong) => Some(strong.clone()),
         }
     }
 
-    fn downgrade(&self) -> WeakModel<Context> {
+    fn downgrade(&self) -> WeakEntity<AssistantContext> {
         match self {
             ContextHandle::Weak(weak) => weak.clone(),
             ContextHandle::Strong(strong) => strong.downgrade(),
@@ -97,12 +97,12 @@ impl ContextHandle {
 
 impl ContextStore {
     pub fn new(
-        project: Model<Project>,
+        project: Entity<Project>,
         prompt_builder: Arc<PromptBuilder>,
         slash_commands: Arc<SlashCommandWorkingSet>,
         tools: Arc<ToolWorkingSet>,
-        cx: &mut AppContext,
-    ) -> Task<Result<Model<Self>>> {
+        cx: &mut App,
+    ) -> Task<Result<Entity<Self>>> {
         let fs = project.read(cx).fs().clone();
         let languages = project.read(cx).languages().clone();
         let telemetry = project.read(cx).client().telemetry().clone();
@@ -110,10 +110,10 @@ impl ContextStore {
             const CONTEXT_WATCH_DURATION: Duration = Duration::from_millis(100);
             let (mut events, _) = fs.watch(contexts_dir(), CONTEXT_WATCH_DURATION).await;
 
-            let this = cx.new_model(|cx: &mut ModelContext<Self>| {
+            let this = cx.new(|cx: &mut Context<Self>| {
                 let context_server_factory_registry =
                     ContextServerFactoryRegistry::default_global(cx);
-                let context_server_manager = cx.new_model(|cx| {
+                let context_server_manager = cx.new(|cx| {
                     ContextServerManager::new(context_server_factory_registry, project.clone(), cx)
                 });
                 let mut this = Self {
@@ -163,7 +163,7 @@ impl ContextStore {
     }
 
     async fn handle_advertise_contexts(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::AdvertiseContexts>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -182,7 +182,7 @@ impl ContextStore {
     }
 
     async fn handle_open_context(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::OpenContext>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::OpenContextResponse> {
@@ -212,7 +212,7 @@ impl ContextStore {
     }
 
     async fn handle_create_context(
-        this: Model<Self>,
+        this: Entity<Self>,
         _: TypedEnvelope<proto::CreateContext>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::CreateContextResponse> {
@@ -240,7 +240,7 @@ impl ContextStore {
     }
 
     async fn handle_update_context(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::UpdateContext>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -256,7 +256,7 @@ impl ContextStore {
     }
 
     async fn handle_synchronize_contexts(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::SynchronizeContexts>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::SynchronizeContextsResponse> {
@@ -299,7 +299,7 @@ impl ContextStore {
         })?
     }
 
-    fn handle_project_changed(&mut self, _: Model<Project>, cx: &mut ModelContext<Self>) {
+    fn handle_project_changed(&mut self, _: Entity<Project>, cx: &mut Context<Self>) {
         let is_shared = self.project.read(cx).is_shared();
         let was_shared = mem::replace(&mut self.project_is_shared, is_shared);
         if is_shared == was_shared {
@@ -320,7 +320,7 @@ impl ContextStore {
                 .client
                 .subscribe_to_entity(remote_id)
                 .log_err()
-                .map(|subscription| subscription.set_model(&cx.handle(), &mut cx.to_async()));
+                .map(|subscription| subscription.set_model(&cx.model(), &mut cx.to_async()));
             self.advertise_contexts(cx);
         } else {
             self.client_subscription = None;
@@ -329,9 +329,9 @@ impl ContextStore {
 
     fn handle_project_event(
         &mut self,
-        _: Model<Project>,
+        _: Entity<Project>,
         event: &project::Event,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match event {
             project::Event::Reshared => {
@@ -361,9 +361,9 @@ impl ContextStore {
         }
     }
 
-    pub fn create(&mut self, cx: &mut ModelContext<Self>) -> Model<Context> {
-        let context = cx.new_model(|cx| {
-            Context::local(
+    pub fn create(&mut self, cx: &mut Context<Self>) -> Entity<AssistantContext> {
+        let context = cx.new(|cx| {
+            AssistantContext::local(
                 self.languages.clone(),
                 Some(self.project.clone()),
                 Some(self.telemetry.clone()),
@@ -379,8 +379,8 @@ impl ContextStore {
 
     pub fn create_remote_context(
         &mut self,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<Context>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<AssistantContext>>> {
         let project = self.project.read(cx);
         let Some(project_id) = project.remote_id() else {
             return Task::ready(Err(anyhow!("project was not remote")));
@@ -399,8 +399,8 @@ impl ContextStore {
             let response = request.await?;
             let context_id = ContextId::from_proto(response.context_id);
             let context_proto = response.context.context("invalid context")?;
-            let context = cx.new_model(|cx| {
-                Context::new(
+            let context = cx.new(|cx| {
+                AssistantContext::new(
                     context_id.clone(),
                     replica_id,
                     capability,
@@ -439,8 +439,8 @@ impl ContextStore {
     pub fn open_local_context(
         &mut self,
         path: PathBuf,
-        cx: &ModelContext<Self>,
-    ) -> Task<Result<Model<Context>>> {
+        cx: &Context<Self>,
+    ) -> Task<Result<Entity<AssistantContext>>> {
         if let Some(existing_context) = self.loaded_context_for_path(&path, cx) {
             return Task::ready(Ok(existing_context));
         }
@@ -462,8 +462,8 @@ impl ContextStore {
 
         cx.spawn(|this, mut cx| async move {
             let saved_context = load.await?;
-            let context = cx.new_model(|cx| {
-                Context::deserialize(
+            let context = cx.new(|cx| {
+                AssistantContext::deserialize(
                     saved_context,
                     path.clone(),
                     languages,
@@ -486,7 +486,7 @@ impl ContextStore {
         })
     }
 
-    fn loaded_context_for_path(&self, path: &Path, cx: &AppContext) -> Option<Model<Context>> {
+    fn loaded_context_for_path(&self, path: &Path, cx: &App) -> Option<Entity<AssistantContext>> {
         self.contexts.iter().find_map(|context| {
             let context = context.upgrade()?;
             if context.read(cx).path() == Some(path) {
@@ -497,7 +497,11 @@ impl ContextStore {
         })
     }
 
-    pub fn loaded_context_for_id(&self, id: &ContextId, cx: &AppContext) -> Option<Model<Context>> {
+    pub fn loaded_context_for_id(
+        &self,
+        id: &ContextId,
+        cx: &App,
+    ) -> Option<Entity<AssistantContext>> {
         self.contexts.iter().find_map(|context| {
             let context = context.upgrade()?;
             if context.read(cx).id() == id {
@@ -511,8 +515,8 @@ impl ContextStore {
     pub fn open_remote_context(
         &mut self,
         context_id: ContextId,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<Context>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<AssistantContext>>> {
         let project = self.project.read(cx);
         let Some(project_id) = project.remote_id() else {
             return Task::ready(Err(anyhow!("project was not remote")));
@@ -537,8 +541,8 @@ impl ContextStore {
         cx.spawn(|this, mut cx| async move {
             let response = request.await?;
             let context_proto = response.context.context("invalid context")?;
-            let context = cx.new_model(|cx| {
-                Context::new(
+            let context = cx.new(|cx| {
+                AssistantContext::new(
                     context_id.clone(),
                     replica_id,
                     capability,
@@ -574,7 +578,7 @@ impl ContextStore {
         })
     }
 
-    fn register_context(&mut self, context: &Model<Context>, cx: &mut ModelContext<Self>) {
+    fn register_context(&mut self, context: &Entity<AssistantContext>, cx: &mut Context<Self>) {
         let handle = if self.project_is_shared {
             ContextHandle::Strong(context.clone())
         } else {
@@ -587,9 +591,9 @@ impl ContextStore {
 
     fn handle_context_event(
         &mut self,
-        context: Model<Context>,
+        context: Entity<AssistantContext>,
         event: &ContextEvent,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let Some(project_id) = self.project.read(cx).remote_id() else {
             return;
@@ -614,7 +618,7 @@ impl ContextStore {
         }
     }
 
-    fn advertise_contexts(&self, cx: &AppContext) {
+    fn advertise_contexts(&self, cx: &App) {
         let Some(project_id) = self.project.read(cx).remote_id() else {
             return;
         };
@@ -648,7 +652,7 @@ impl ContextStore {
             .ok();
     }
 
-    fn synchronize_contexts(&mut self, cx: &mut ModelContext<Self>) {
+    fn synchronize_contexts(&mut self, cx: &mut Context<Self>) {
         let Some(project_id) = self.project.read(cx).remote_id() else {
             return;
         };
@@ -703,7 +707,7 @@ impl ContextStore {
         .detach_and_log_err(cx);
     }
 
-    pub fn search(&self, query: String, cx: &AppContext) -> Task<Vec<SavedContextMetadata>> {
+    pub fn search(&self, query: String, cx: &App) -> Task<Vec<SavedContextMetadata>> {
         let metadata = self.contexts_metadata.clone();
         let executor = cx.background_executor().clone();
         cx.background_executor().spawn(async move {
@@ -737,7 +741,7 @@ impl ContextStore {
         &self.host_contexts
     }
 
-    fn reload(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn reload(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         let fs = self.fs.clone();
         cx.spawn(|this, mut cx| async move {
             fs.create_dir(contexts_dir()).await?;
@@ -786,7 +790,7 @@ impl ContextStore {
         })
     }
 
-    pub fn restart_context_servers(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn restart_context_servers(&mut self, cx: &mut Context<Self>) {
         cx.update_model(
             &self.context_server_manager,
             |context_server_manager, cx| {
@@ -799,7 +803,7 @@ impl ContextStore {
         );
     }
 
-    fn register_context_server_handlers(&self, cx: &mut ModelContext<Self>) {
+    fn register_context_server_handlers(&self, cx: &mut Context<Self>) {
         cx.subscribe(
             &self.context_server_manager.clone(),
             Self::handle_context_server_event,
@@ -809,9 +813,9 @@ impl ContextStore {
 
     fn handle_context_server_event(
         &mut self,
-        context_server_manager: Model<ContextServerManager>,
+        context_server_manager: Entity<ContextServerManager>,
         event: &context_server::manager::Event,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let slash_command_working_set = self.slash_commands.clone();
         let tool_working_set = self.tools.clone();

crates/assistant_context_editor/src/patch.rs 🔗

@@ -2,7 +2,7 @@ use anyhow::{anyhow, Context as _, Result};
 use collections::HashMap;
 use editor::ProposedChangesEditor;
 use futures::{future, TryFutureExt as _};
-use gpui::{AppContext, AsyncAppContext, Model, SharedString};
+use gpui::{App, AsyncAppContext, Entity, SharedString};
 use language::{AutoindentMode, Buffer, BufferSnapshot};
 use project::{Project, ProjectPath};
 use std::{cmp, ops::Range, path::Path, sync::Arc};
@@ -56,7 +56,7 @@ pub enum AssistantEditKind {
 
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct ResolvedPatch {
-    pub edit_groups: HashMap<Model<Buffer>, Vec<ResolvedEditGroup>>,
+    pub edit_groups: HashMap<Entity<Buffer>, Vec<ResolvedEditGroup>>,
     pub errors: Vec<AssistantPatchResolutionError>,
 }
 
@@ -121,7 +121,7 @@ impl SearchMatrix {
 }
 
 impl ResolvedPatch {
-    pub fn apply(&self, editor: &ProposedChangesEditor, cx: &mut AppContext) {
+    pub fn apply(&self, editor: &ProposedChangesEditor, cx: &mut App) {
         for (buffer, groups) in &self.edit_groups {
             let branch = editor.branch_buffer_for_base(buffer).unwrap();
             Self::apply_edit_groups(groups, &branch, cx);
@@ -129,11 +129,7 @@ impl ResolvedPatch {
         editor.recalculate_all_buffer_diffs();
     }
 
-    fn apply_edit_groups(
-        groups: &Vec<ResolvedEditGroup>,
-        buffer: &Model<Buffer>,
-        cx: &mut AppContext,
-    ) {
+    fn apply_edit_groups(groups: &Vec<ResolvedEditGroup>, buffer: &Entity<Buffer>, cx: &mut App) {
         let mut edits = Vec::new();
         for group in groups {
             for suggestion in &group.edits {
@@ -232,9 +228,9 @@ impl AssistantEdit {
 
     pub async fn resolve(
         &self,
-        project: Model<Project>,
+        project: Entity<Project>,
         mut cx: AsyncAppContext,
-    ) -> Result<(Model<Buffer>, ResolvedEdit)> {
+    ) -> Result<(Entity<Buffer>, ResolvedEdit)> {
         let path = self.path.clone();
         let kind = self.kind.clone();
         let buffer = project
@@ -427,7 +423,7 @@ impl AssistantEditKind {
 impl AssistantPatch {
     pub async fn resolve(
         &self,
-        project: Model<Project>,
+        project: Entity<Project>,
         cx: &mut AsyncAppContext,
     ) -> ResolvedPatch {
         let mut resolve_tasks = Vec::new();
@@ -555,7 +551,7 @@ impl Eq for AssistantPatch {}
 #[cfg(test)]
 mod tests {
     use super::*;
-    use gpui::{AppContext, Context};
+    use gpui::{App, AppContext as _};
     use language::{
         language_settings::AllLanguageSettings, Language, LanguageConfig, LanguageMatcher,
     };
@@ -565,7 +561,7 @@ mod tests {
     use util::test::{generate_marked_text, marked_text_ranges};
 
     #[gpui::test]
-    fn test_resolve_location(cx: &mut AppContext) {
+    fn test_resolve_location(cx: &mut App) {
         assert_location_resolution(
             concat!(
                 "    Lorem\n",
@@ -636,7 +632,7 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_resolve_edits(cx: &mut AppContext) {
+    fn test_resolve_edits(cx: &mut App) {
         init_test(cx);
 
         assert_edits(
@@ -902,7 +898,7 @@ mod tests {
         );
     }
 
-    fn init_test(cx: &mut AppContext) {
+    fn init_test(cx: &mut App) {
         let settings_store = SettingsStore::test(cx);
         cx.set_global(settings_store);
         language::init(cx);
@@ -912,13 +908,9 @@ mod tests {
     }
 
     #[track_caller]
-    fn assert_location_resolution(
-        text_with_expected_range: &str,
-        query: &str,
-        cx: &mut AppContext,
-    ) {
+    fn assert_location_resolution(text_with_expected_range: &str, query: &str, cx: &mut App) {
         let (text, _) = marked_text_ranges(text_with_expected_range, false);
-        let buffer = cx.new_model(|cx| Buffer::local(text.clone(), cx));
+        let buffer = cx.new(|cx| Buffer::local(text.clone(), cx));
         let snapshot = buffer.read(cx).snapshot();
         let range = AssistantEditKind::resolve_location(&snapshot, query).to_offset(&snapshot);
         let text_with_actual_range = generate_marked_text(&text, &[range], false);
@@ -930,10 +922,10 @@ mod tests {
         old_text: String,
         edits: Vec<AssistantEditKind>,
         new_text: String,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) {
         let buffer =
-            cx.new_model(|cx| Buffer::local(old_text, cx).with_language(Arc::new(rust_lang()), cx));
+            cx.new(|cx| Buffer::local(old_text, cx).with_language(Arc::new(rust_lang()), cx));
         let snapshot = buffer.read(cx).snapshot();
         let resolved_edits = edits
             .into_iter()

crates/assistant_context_editor/src/slash_command.rs 🔗

@@ -4,7 +4,7 @@ pub use assistant_slash_command::SlashCommand;
 use assistant_slash_command::{AfterCompletion, SlashCommandLine, SlashCommandWorkingSet};
 use editor::{CompletionProvider, Editor};
 use fuzzy::{match_strings, StringMatchCandidate};
-use gpui::{Model, Task, ViewContext, WeakView, WindowContext};
+use gpui::{App, Context, Entity, Task, WeakEntity, Window};
 use language::{Anchor, Buffer, Documentation, LanguageServerId, ToPoint};
 use parking_lot::Mutex;
 use project::CompletionIntent;
@@ -23,15 +23,15 @@ use workspace::Workspace;
 pub struct SlashCommandCompletionProvider {
     cancel_flag: Mutex<Arc<AtomicBool>>,
     slash_commands: Arc<SlashCommandWorkingSet>,
-    editor: Option<WeakView<ContextEditor>>,
-    workspace: Option<WeakView<Workspace>>,
+    editor: Option<WeakEntity<ContextEditor>>,
+    workspace: Option<WeakEntity<Workspace>>,
 }
 
 impl SlashCommandCompletionProvider {
     pub fn new(
         slash_commands: Arc<SlashCommandWorkingSet>,
-        editor: Option<WeakView<ContextEditor>>,
-        workspace: Option<WeakView<Workspace>>,
+        editor: Option<WeakEntity<ContextEditor>>,
+        workspace: Option<WeakEntity<Workspace>>,
     ) -> Self {
         Self {
             cancel_flag: Mutex::new(Arc::new(AtomicBool::new(false))),
@@ -46,7 +46,8 @@ impl SlashCommandCompletionProvider {
         command_name: &str,
         command_range: Range<Anchor>,
         name_range: Range<Anchor>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<Vec<project::Completion>>> {
         let slash_commands = self.slash_commands.clone();
         let candidates = slash_commands
@@ -58,7 +59,7 @@ impl SlashCommandCompletionProvider {
         let command_name = command_name.to_string();
         let editor = self.editor.clone();
         let workspace = self.workspace.clone();
-        cx.spawn(|mut cx| async move {
+        window.spawn(cx, |mut cx| async move {
             let matches = match_strings(
                 &candidates,
                 &command_name,
@@ -69,7 +70,7 @@ impl SlashCommandCompletionProvider {
             )
             .await;
 
-            cx.update(|cx| {
+            cx.update(|_, cx| {
                 matches
                     .into_iter()
                     .filter_map(|mat| {
@@ -91,28 +92,31 @@ impl SlashCommandCompletionProvider {
                                     let editor = editor.clone();
                                     let workspace = workspace.clone();
                                     Arc::new(
-                                        move |intent: CompletionIntent, cx: &mut WindowContext| {
-                                            if !requires_argument
-                                                && (!accepts_arguments || intent.is_complete())
-                                            {
-                                                editor
-                                                    .update(cx, |editor, cx| {
-                                                        editor.run_command(
-                                                            command_range.clone(),
-                                                            &command_name,
-                                                            &[],
-                                                            true,
-                                                            workspace.clone(),
-                                                            cx,
-                                                        );
-                                                    })
-                                                    .ok();
-                                                false
-                                            } else {
-                                                requires_argument || accepts_arguments
-                                            }
-                                        },
-                                    ) as Arc<_>
+                                    move |intent: CompletionIntent,
+                                          window: &mut Window,
+                                          cx: &mut App| {
+                                        if !requires_argument
+                                            && (!accepts_arguments || intent.is_complete())
+                                        {
+                                            editor
+                                                .update(cx, |editor, cx| {
+                                                    editor.run_command(
+                                                        command_range.clone(),
+                                                        &command_name,
+                                                        &[],
+                                                        true,
+                                                        workspace.clone(),
+                                                        window,
+                                                        cx,
+                                                    );
+                                                })
+                                                .ok();
+                                            false
+                                        } else {
+                                            requires_argument || accepts_arguments
+                                        }
+                                    },
+                                ) as Arc<_>
                                 });
                         Some(project::Completion {
                             old_range: name_range.clone(),
@@ -130,6 +134,7 @@ impl SlashCommandCompletionProvider {
         })
     }
 
+    #[allow(clippy::too_many_arguments)]
     fn complete_command_argument(
         &self,
         command_name: &str,
@@ -137,7 +142,8 @@ impl SlashCommandCompletionProvider {
         command_range: Range<Anchor>,
         argument_range: Range<Anchor>,
         last_argument_range: Range<Anchor>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<Vec<project::Completion>>> {
         let new_cancel_flag = Arc::new(AtomicBool::new(false));
         let mut flag = self.cancel_flag.lock();
@@ -148,6 +154,7 @@ impl SlashCommandCompletionProvider {
                 arguments,
                 new_cancel_flag.clone(),
                 self.workspace.clone(),
+                window,
                 cx,
             );
             let command_name: Arc<str> = command_name.into();
@@ -175,7 +182,9 @@ impl SlashCommandCompletionProvider {
 
                                         let command_range = command_range.clone();
                                         let command_name = command_name.clone();
-                                        move |intent: CompletionIntent, cx: &mut WindowContext| {
+                                        move |intent: CompletionIntent,
+                                              window: &mut Window,
+                                              cx: &mut App| {
                                             if new_argument.after_completion.run()
                                                 || intent.is_complete()
                                             {
@@ -187,6 +196,7 @@ impl SlashCommandCompletionProvider {
                                                             &completed_arguments,
                                                             true,
                                                             workspace.clone(),
+                                                            window,
                                                             cx,
                                                         );
                                                     })
@@ -230,10 +240,11 @@ impl SlashCommandCompletionProvider {
 impl CompletionProvider for SlashCommandCompletionProvider {
     fn completions(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         buffer_position: Anchor,
         _: editor::CompletionContext,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) -> Task<Result<Vec<project::Completion>>> {
         let Some((name, arguments, command_range, last_argument_range)) =
             buffer.update(cx, |buffer, _cx| {
@@ -288,30 +299,31 @@ impl CompletionProvider for SlashCommandCompletionProvider {
                 command_range,
                 argument_range,
                 last_argument_range,
+                window,
                 cx,
             )
         } else {
-            self.complete_command_name(&name, command_range, last_argument_range, cx)
+            self.complete_command_name(&name, command_range, last_argument_range, window, cx)
         }
     }
 
     fn resolve_completions(
         &self,
-        _: Model<Buffer>,
+        _: Entity<Buffer>,
         _: Vec<usize>,
         _: Rc<RefCell<Box<[project::Completion]>>>,
-        _: &mut ViewContext<Editor>,
+        _: &mut Context<Editor>,
     ) -> Task<Result<bool>> {
         Task::ready(Ok(true))
     }
 
     fn is_completion_trigger(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         position: language::Anchor,
         _text: &str,
         _trigger_in_words: bool,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) -> bool {
         let buffer = buffer.read(cx);
         let position = position.to_point(buffer);

crates/assistant_context_editor/src/slash_command_picker.rs 🔗

@@ -1,7 +1,7 @@
 use std::sync::Arc;
 
 use assistant_slash_command::SlashCommandWorkingSet;
-use gpui::{AnyElement, DismissEvent, SharedString, Task, WeakView};
+use gpui::{AnyElement, DismissEvent, SharedString, Task, WeakEntity};
 use picker::{Picker, PickerDelegate, PickerEditorPosition};
 use ui::{prelude::*, ListItem, ListItemSpacing, PopoverMenu, PopoverTrigger, Tooltip};
 
@@ -10,7 +10,7 @@ use crate::context_editor::ContextEditor;
 #[derive(IntoElement)]
 pub(super) struct SlashCommandSelector<T: PopoverTrigger> {
     working_set: Arc<SlashCommandWorkingSet>,
-    active_context_editor: WeakView<ContextEditor>,
+    active_context_editor: WeakEntity<ContextEditor>,
     trigger: T,
 }
 
@@ -27,8 +27,8 @@ enum SlashCommandEntry {
     Info(SlashCommandInfo),
     Advert {
         name: SharedString,
-        renderer: fn(&mut WindowContext) -> AnyElement,
-        on_confirm: fn(&mut WindowContext),
+        renderer: fn(&mut Window, &mut App) -> AnyElement,
+        on_confirm: fn(&mut Window, &mut App),
     },
 }
 
@@ -44,14 +44,14 @@ impl AsRef<str> for SlashCommandEntry {
 pub(crate) struct SlashCommandDelegate {
     all_commands: Vec<SlashCommandEntry>,
     filtered_commands: Vec<SlashCommandEntry>,
-    active_context_editor: WeakView<ContextEditor>,
+    active_context_editor: WeakEntity<ContextEditor>,
     selected_index: usize,
 }
 
 impl<T: PopoverTrigger> SlashCommandSelector<T> {
     pub(crate) fn new(
         working_set: Arc<SlashCommandWorkingSet>,
-        active_context_editor: WeakView<ContextEditor>,
+        active_context_editor: WeakEntity<ContextEditor>,
         trigger: T,
     ) -> Self {
         SlashCommandSelector {
@@ -73,18 +73,23 @@ impl PickerDelegate for SlashCommandDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(&mut self, ix: usize, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.selected_index = ix.min(self.filtered_commands.len().saturating_sub(1));
         cx.notify();
     }
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Select a command...".into()
     }
 
-    fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
+    fn update_matches(
+        &mut self,
+        query: String,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Task<()> {
         let all_commands = self.all_commands.clone();
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let filtered_commands = cx
                 .background_executor()
                 .spawn(async move {
@@ -104,9 +109,9 @@ impl PickerDelegate for SlashCommandDelegate {
                 })
                 .await;
 
-            this.update(&mut cx, |this, cx| {
+            this.update_in(&mut cx, |this, window, cx| {
                 this.delegate.filtered_commands = filtered_commands;
-                this.delegate.set_selected_index(0, cx);
+                this.delegate.set_selected_index(0, window, cx);
                 cx.notify();
             })
             .ok();
@@ -139,25 +144,25 @@ impl PickerDelegate for SlashCommandDelegate {
         ret
     }
 
-    fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
         if let Some(command) = self.filtered_commands.get(self.selected_index) {
             match command {
                 SlashCommandEntry::Info(info) => {
                     self.active_context_editor
                         .update(cx, |context_editor, cx| {
-                            context_editor.insert_command(&info.name, cx)
+                            context_editor.insert_command(&info.name, window, cx)
                         })
                         .ok();
                 }
                 SlashCommandEntry::Advert { on_confirm, .. } => {
-                    on_confirm(cx);
+                    on_confirm(window, cx);
                 }
             }
             cx.emit(DismissEvent);
         }
     }
 
-    fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
+    fn dismissed(&mut self, _window: &mut Window, _cx: &mut Context<Picker<Self>>) {}
 
     fn editor_position(&self) -> PickerEditorPosition {
         PickerEditorPosition::End
@@ -167,7 +172,8 @@ impl PickerDelegate for SlashCommandDelegate {
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let command_info = self.filtered_commands.get(ix)?;
 
@@ -179,7 +185,7 @@ impl PickerDelegate for SlashCommandDelegate {
                     .toggle_state(selected)
                     .tooltip({
                         let description = info.description.clone();
-                        move |cx| cx.new_view(|_| Tooltip::new(description.clone())).into()
+                        move |_, cx| cx.new(|_| Tooltip::new(description.clone())).into()
                     })
                     .child(
                         v_flex()
@@ -229,14 +235,14 @@ impl PickerDelegate for SlashCommandDelegate {
                     .inset(true)
                     .spacing(ListItemSpacing::Dense)
                     .toggle_state(selected)
-                    .child(renderer(cx)),
+                    .child(renderer(window, cx)),
             ),
         }
     }
 }
 
 impl<T: PopoverTrigger> RenderOnce for SlashCommandSelector<T> {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
         let all_models = self
             .working_set
             .featured_command_names(cx)
@@ -259,7 +265,7 @@ impl<T: PopoverTrigger> RenderOnce for SlashCommandSelector<T> {
             })
             .chain([SlashCommandEntry::Advert {
                 name: "create-your-command".into(),
-                renderer: |cx| {
+                renderer: |_, cx| {
                     v_flex()
                         .w_full()
                         .child(
@@ -293,7 +299,7 @@ impl<T: PopoverTrigger> RenderOnce for SlashCommandSelector<T> {
                         )
                         .into_any_element()
                 },
-                on_confirm: |cx| cx.open_url("https://zed.dev/docs/extensions/slash-commands"),
+                on_confirm: |_, cx| cx.open_url("https://zed.dev/docs/extensions/slash-commands"),
             }])
             .collect::<Vec<_>>();
 
@@ -304,8 +310,9 @@ impl<T: PopoverTrigger> RenderOnce for SlashCommandSelector<T> {
             selected_index: 0,
         };
 
-        let picker_view = cx.new_view(|cx| {
-            let picker = Picker::uniform_list(delegate, cx).max_height(Some(rems(20.).into()));
+        let picker_view = cx.new(|cx| {
+            let picker =
+                Picker::uniform_list(delegate, window, cx).max_height(Some(rems(20.).into()));
             picker
         });
 
@@ -314,7 +321,7 @@ impl<T: PopoverTrigger> RenderOnce for SlashCommandSelector<T> {
             .update(cx, |this, _| this.slash_menu_handle.clone())
             .ok();
         PopoverMenu::new("model-switcher")
-            .menu(move |_cx| Some(picker_view.clone()))
+            .menu(move |_window, _cx| Some(picker_view.clone()))
             .trigger(self.trigger)
             .attach(gpui::Corner::TopLeft)
             .anchor(gpui::Corner::BottomLeft)

crates/assistant_settings/src/assistant_settings.rs 🔗

@@ -3,7 +3,7 @@ use std::sync::Arc;
 use ::open_ai::Model as OpenAiModel;
 use anthropic::Model as AnthropicModel;
 use feature_flags::FeatureFlagAppExt;
-use gpui::{AppContext, Pixels};
+use gpui::{App, Pixels};
 use language_model::{CloudModel, LanguageModel};
 use lmstudio::Model as LmStudioModel;
 use ollama::Model as OllamaModel;
@@ -62,7 +62,7 @@ pub struct AssistantSettings {
 }
 
 impl AssistantSettings {
-    pub fn are_live_diffs_enabled(&self, cx: &AppContext) -> bool {
+    pub fn are_live_diffs_enabled(&self, cx: &App) -> bool {
         cx.is_staff() || self.enable_experimental_live_diffs
     }
 }
@@ -422,7 +422,7 @@ impl Settings for AssistantSettings {
 
     fn load(
         sources: SettingsSources<Self::FileContent>,
-        _: &mut gpui::AppContext,
+        _: &mut gpui::App,
     ) -> anyhow::Result<Self> {
         let mut settings = AssistantSettings::default();
 

crates/assistant_slash_command/src/assistant_slash_command.rs 🔗

@@ -8,7 +8,7 @@ pub use crate::slash_command_working_set::*;
 use anyhow::Result;
 use futures::stream::{self, BoxStream};
 use futures::StreamExt;
-use gpui::{AppContext, SharedString, Task, WeakView, WindowContext};
+use gpui::{App, SharedString, Task, WeakEntity, Window};
 use language::{BufferSnapshot, CodeLabel, LspAdapterDelegate, OffsetRangeExt};
 pub use language_model::Role;
 use serde::{Deserialize, Serialize};
@@ -18,7 +18,7 @@ use std::{
 };
 use workspace::{ui::IconName, Workspace};
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     SlashCommandRegistry::default_global(cx);
     extension_slash_command::init(cx);
 }
@@ -71,7 +71,7 @@ pub trait SlashCommand: 'static + Send + Sync {
     fn icon(&self) -> IconName {
         IconName::Slash
     }
-    fn label(&self, _cx: &AppContext) -> CodeLabel {
+    fn label(&self, _cx: &App) -> CodeLabel {
         CodeLabel::plain(self.name(), None)
     }
     fn description(&self) -> String;
@@ -80,26 +80,29 @@ pub trait SlashCommand: 'static + Send + Sync {
         self: Arc<Self>,
         arguments: &[String],
         cancel: Arc<AtomicBool>,
-        workspace: Option<WeakView<Workspace>>,
-        cx: &mut WindowContext,
+        workspace: Option<WeakEntity<Workspace>>,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>>;
     fn requires_argument(&self) -> bool;
     fn accepts_arguments(&self) -> bool {
         self.requires_argument()
     }
+    #[allow(clippy::too_many_arguments)]
     fn run(
         self: Arc<Self>,
         arguments: &[String],
         context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         context_buffer: BufferSnapshot,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         // TODO: We're just using the `LspAdapterDelegate` here because that is
         // what the extension API is already expecting.
         //
         // It may be that `LspAdapterDelegate` needs a more general name, or
         // perhaps another kind of delegate is needed here.
         delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult>;
 }
 

crates/assistant_slash_command/src/extension_slash_command.rs 🔗

@@ -4,7 +4,7 @@ use std::sync::{atomic::AtomicBool, Arc};
 use anyhow::Result;
 use async_trait::async_trait;
 use extension::{Extension, ExtensionHostProxy, ExtensionSlashCommandProxy, WorktreeDelegate};
-use gpui::{AppContext, Task, WeakView, WindowContext};
+use gpui::{App, Task, WeakEntity, Window};
 use language::{BufferSnapshot, LspAdapterDelegate};
 use ui::prelude::*;
 use workspace::Workspace;
@@ -14,7 +14,7 @@ use crate::{
     SlashCommandRegistry, SlashCommandResult,
 };
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     let proxy = ExtensionHostProxy::default_global(cx);
     proxy.register_slash_command_proxy(SlashCommandRegistryProxy {
         slash_command_registry: SlashCommandRegistry::global(cx),
@@ -97,8 +97,9 @@ impl SlashCommand for ExtensionSlashCommand {
         self: Arc<Self>,
         arguments: &[String],
         _cancel: Arc<AtomicBool>,
-        _workspace: Option<WeakView<Workspace>>,
-        cx: &mut WindowContext,
+        _workspace: Option<WeakEntity<Workspace>>,
+        _window: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         let command = self.command.clone();
         let arguments = arguments.to_owned();
@@ -127,9 +128,10 @@ impl SlashCommand for ExtensionSlashCommand {
         arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         _context_buffer: BufferSnapshot,
-        _workspace: WeakView<Workspace>,
+        _workspace: WeakEntity<Workspace>,
         delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        _window: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult> {
         let command = self.command.clone();
         let arguments = arguments.to_owned();

crates/assistant_slash_command/src/slash_command_registry.rs 🔗

@@ -3,7 +3,7 @@ use std::sync::Arc;
 use collections::{BTreeSet, HashMap};
 use derive_more::{Deref, DerefMut};
 use gpui::Global;
-use gpui::{AppContext, ReadGlobal};
+use gpui::{App, ReadGlobal};
 use parking_lot::RwLock;
 
 use crate::SlashCommand;
@@ -26,14 +26,14 @@ pub struct SlashCommandRegistry {
 
 impl SlashCommandRegistry {
     /// Returns the global [`SlashCommandRegistry`].
-    pub fn global(cx: &AppContext) -> Arc<Self> {
+    pub fn global(cx: &App) -> Arc<Self> {
         GlobalSlashCommandRegistry::global(cx).0.clone()
     }
 
     /// Returns the global [`SlashCommandRegistry`].
     ///
     /// Inserts a default [`SlashCommandRegistry`] if one does not yet exist.
-    pub fn default_global(cx: &mut AppContext) -> Arc<Self> {
+    pub fn default_global(cx: &mut App) -> Arc<Self> {
         cx.default_global::<GlobalSlashCommandRegistry>().0.clone()
     }
 

crates/assistant_slash_command/src/slash_command_working_set.rs 🔗

@@ -1,7 +1,7 @@
 use std::sync::Arc;
 
 use collections::HashMap;
-use gpui::AppContext;
+use gpui::App;
 use parking_lot::Mutex;
 
 use crate::{SlashCommand, SlashCommandRegistry};
@@ -23,7 +23,7 @@ struct WorkingSetState {
 }
 
 impl SlashCommandWorkingSet {
-    pub fn command(&self, name: &str, cx: &AppContext) -> Option<Arc<dyn SlashCommand>> {
+    pub fn command(&self, name: &str, cx: &App) -> Option<Arc<dyn SlashCommand>> {
         self.state
             .lock()
             .context_server_commands_by_name
@@ -32,7 +32,7 @@ impl SlashCommandWorkingSet {
             .or_else(|| SlashCommandRegistry::global(cx).command(name))
     }
 
-    pub fn command_names(&self, cx: &AppContext) -> Vec<Arc<str>> {
+    pub fn command_names(&self, cx: &App) -> Vec<Arc<str>> {
         let mut command_names = SlashCommandRegistry::global(cx).command_names();
         command_names.extend(
             self.state
@@ -45,7 +45,7 @@ impl SlashCommandWorkingSet {
         command_names
     }
 
-    pub fn featured_command_names(&self, cx: &AppContext) -> Vec<Arc<str>> {
+    pub fn featured_command_names(&self, cx: &App) -> Vec<Arc<str>> {
         SlashCommandRegistry::global(cx).featured_command_names()
     }
 

crates/assistant_slash_commands/src/assistant_slash_commands.rs 🔗

@@ -17,7 +17,7 @@ mod symbols_command;
 mod tab_command;
 mod terminal_command;
 
-use gpui::AppContext;
+use gpui::App;
 use language::{CodeLabel, HighlightId};
 use ui::ActiveTheme as _;
 
@@ -40,11 +40,7 @@ pub use crate::symbols_command::*;
 pub use crate::tab_command::*;
 pub use crate::terminal_command::*;
 
-pub fn create_label_for_command(
-    command_name: &str,
-    arguments: &[&str],
-    cx: &AppContext,
-) -> CodeLabel {
+pub fn create_label_for_command(command_name: &str, arguments: &[&str], cx: &App) -> CodeLabel {
     let mut label = CodeLabel::default();
     label.push_str(command_name, None);
     label.push_str(" ", None);

crates/assistant_slash_commands/src/auto_command.rs 🔗

@@ -5,7 +5,7 @@ use assistant_slash_command::{
 };
 use feature_flags::FeatureFlag;
 use futures::StreamExt;
-use gpui::{AppContext, AsyncAppContext, AsyncWindowContext, Task, WeakView, WindowContext};
+use gpui::{App, AsyncAppContext, Task, WeakEntity, Window};
 use language::{CodeLabel, LspAdapterDelegate};
 use language_model::{
     LanguageModelCompletionEvent, LanguageModelRegistry, LanguageModelRequest,
@@ -45,7 +45,7 @@ impl SlashCommand for AutoCommand {
         self.description()
     }
 
-    fn label(&self, cx: &AppContext) -> CodeLabel {
+    fn label(&self, cx: &App) -> CodeLabel {
         create_label_for_command("auto", &["--prompt"], cx)
     }
 
@@ -53,8 +53,9 @@ impl SlashCommand for AutoCommand {
         self: Arc<Self>,
         _arguments: &[String],
         _cancel: Arc<AtomicBool>,
-        workspace: Option<WeakView<Workspace>>,
-        cx: &mut WindowContext,
+        workspace: Option<WeakEntity<Workspace>>,
+        _window: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         // There's no autocomplete for a prompt, since it's arbitrary text.
         // However, we can use this opportunity to kick off a drain of the backlog.
@@ -74,7 +75,7 @@ impl SlashCommand for AutoCommand {
             return Task::ready(Err(anyhow!("No project indexer, cannot use /auto")));
         };
 
-        let cx: &mut AppContext = cx;
+        let cx: &mut App = cx;
 
         cx.spawn(|cx: gpui::AsyncAppContext| async move {
             let task = project_index.read_with(&cx, |project_index, cx| {
@@ -96,9 +97,10 @@ impl SlashCommand for AutoCommand {
         arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         _context_buffer: language::BufferSnapshot,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         _delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult> {
         let Some(workspace) = workspace.upgrade() else {
             return Task::ready(Err(anyhow::anyhow!("workspace was dropped")));
@@ -115,7 +117,7 @@ impl SlashCommand for AutoCommand {
             return Task::ready(Err(anyhow!("no project indexer")));
         };
 
-        let task = cx.spawn(|cx: AsyncWindowContext| async move {
+        let task = window.spawn(cx, |cx| async move {
             let summaries = project_index
                 .read_with(&cx, |project_index, cx| project_index.all_summaries(cx))?
                 .await?;

crates/assistant_slash_commands/src/cargo_workspace_command.rs 🔗

@@ -1,10 +1,10 @@
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use assistant_slash_command::{
     ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
     SlashCommandResult,
 };
 use fs::Fs;
-use gpui::{AppContext, Model, Task, WeakView};
+use gpui::{App, Entity, Task, WeakEntity};
 use language::{BufferSnapshot, LspAdapterDelegate};
 use project::{Project, ProjectPath};
 use std::{
@@ -76,7 +76,7 @@ impl CargoWorkspaceSlashCommand {
         Ok(message)
     }
 
-    fn path_to_cargo_toml(project: Model<Project>, cx: &mut AppContext) -> Option<Arc<Path>> {
+    fn path_to_cargo_toml(project: Entity<Project>, cx: &mut App) -> Option<Arc<Path>> {
         let worktree = project.read(cx).worktrees(cx).next()?;
         let worktree = worktree.read(cx);
         let entry = worktree.entry_for_path("Cargo.toml")?;
@@ -107,8 +107,9 @@ impl SlashCommand for CargoWorkspaceSlashCommand {
         self: Arc<Self>,
         _arguments: &[String],
         _cancel: Arc<AtomicBool>,
-        _workspace: Option<WeakView<Workspace>>,
-        _cx: &mut WindowContext,
+        _workspace: Option<WeakEntity<Workspace>>,
+        _window: &mut Window,
+        _cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         Task::ready(Err(anyhow!("this command does not require argument")))
     }
@@ -122,9 +123,10 @@ impl SlashCommand for CargoWorkspaceSlashCommand {
         _arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         _context_buffer: BufferSnapshot,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         _delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        _window: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult> {
         let output = workspace.update(cx, |workspace, cx| {
             let project = workspace.project().clone();

crates/assistant_slash_commands/src/context_server_command.rs 🔗

@@ -8,7 +8,7 @@ use context_server::{
     manager::{ContextServer, ContextServerManager},
     types::Prompt,
 };
-use gpui::{AppContext, Model, Task, WeakView, WindowContext};
+use gpui::{App, Entity, Task, WeakEntity, Window};
 use language::{BufferSnapshot, CodeLabel, LspAdapterDelegate};
 use std::sync::atomic::AtomicBool;
 use std::sync::Arc;
@@ -19,14 +19,14 @@ use workspace::Workspace;
 use crate::create_label_for_command;
 
 pub struct ContextServerSlashCommand {
-    server_manager: Model<ContextServerManager>,
+    server_manager: Entity<ContextServerManager>,
     server_id: Arc<str>,
     prompt: Prompt,
 }
 
 impl ContextServerSlashCommand {
     pub fn new(
-        server_manager: Model<ContextServerManager>,
+        server_manager: Entity<ContextServerManager>,
         server: &Arc<ContextServer>,
         prompt: Prompt,
     ) -> Self {
@@ -43,7 +43,7 @@ impl SlashCommand for ContextServerSlashCommand {
         self.prompt.name.clone()
     }
 
-    fn label(&self, cx: &AppContext) -> language::CodeLabel {
+    fn label(&self, cx: &App) -> language::CodeLabel {
         let mut parts = vec![self.prompt.name.as_str()];
         if let Some(args) = &self.prompt.arguments {
             if let Some(arg) = args.first() {
@@ -77,8 +77,9 @@ impl SlashCommand for ContextServerSlashCommand {
         self: Arc<Self>,
         arguments: &[String],
         _cancel: Arc<AtomicBool>,
-        _workspace: Option<WeakView<Workspace>>,
-        cx: &mut WindowContext,
+        _workspace: Option<WeakEntity<Workspace>>,
+        _window: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         let Ok((arg_name, arg_value)) = completion_argument(&self.prompt, arguments) else {
             return Task::ready(Err(anyhow!("Failed to complete argument")));
@@ -128,9 +129,10 @@ impl SlashCommand for ContextServerSlashCommand {
         arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         _context_buffer: BufferSnapshot,
-        _workspace: WeakView<Workspace>,
+        _workspace: WeakEntity<Workspace>,
         _delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        _window: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult> {
         let server_id = self.server_id.clone();
         let prompt_name = self.prompt.name.clone();

crates/assistant_slash_commands/src/default_command.rs 🔗

@@ -3,7 +3,7 @@ use assistant_slash_command::{
     ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
     SlashCommandResult,
 };
-use gpui::{Task, WeakView};
+use gpui::{Task, WeakEntity};
 use language::{BufferSnapshot, LspAdapterDelegate};
 use prompt_library::PromptStore;
 use std::{
@@ -36,8 +36,9 @@ impl SlashCommand for DefaultSlashCommand {
         self: Arc<Self>,
         _arguments: &[String],
         _cancellation_flag: Arc<AtomicBool>,
-        _workspace: Option<WeakView<Workspace>>,
-        _cx: &mut WindowContext,
+        _workspace: Option<WeakEntity<Workspace>>,
+        _window: &mut Window,
+        _cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         Task::ready(Err(anyhow!("this command does not require argument")))
     }
@@ -47,9 +48,10 @@ impl SlashCommand for DefaultSlashCommand {
         _arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         _context_buffer: BufferSnapshot,
-        _workspace: WeakView<Workspace>,
+        _workspace: WeakEntity<Workspace>,
         _delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        _window: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult> {
         let store = PromptStore::global(cx);
         cx.background_executor().spawn(async move {

crates/assistant_slash_commands/src/delta_command.rs 🔗

@@ -6,7 +6,7 @@ use assistant_slash_command::{
 };
 use collections::HashSet;
 use futures::future;
-use gpui::{Task, WeakView, WindowContext};
+use gpui::{App, Task, WeakEntity, Window};
 use language::{BufferSnapshot, LspAdapterDelegate};
 use std::sync::{atomic::AtomicBool, Arc};
 use text::OffsetRangeExt;
@@ -40,8 +40,9 @@ impl SlashCommand for DeltaSlashCommand {
         self: Arc<Self>,
         _arguments: &[String],
         _cancellation_flag: Arc<AtomicBool>,
-        _workspace: Option<WeakView<Workspace>>,
-        _cx: &mut WindowContext,
+        _workspace: Option<WeakEntity<Workspace>>,
+        _window: &mut Window,
+        _cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         Task::ready(Err(anyhow!("this command does not require argument")))
     }
@@ -51,9 +52,10 @@ impl SlashCommand for DeltaSlashCommand {
         _arguments: &[String],
         context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         context_buffer: BufferSnapshot,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult> {
         let mut paths = HashSet::default();
         let mut file_command_old_outputs = Vec::new();
@@ -77,6 +79,7 @@ impl SlashCommand for DeltaSlashCommand {
                         context_buffer.clone(),
                         workspace.clone(),
                         delegate.clone(),
+                        window,
                         cx,
                     ));
                 }

crates/assistant_slash_commands/src/diagnostics_command.rs 🔗

@@ -4,7 +4,7 @@ use assistant_slash_command::{
     SlashCommandResult,
 };
 use fuzzy::{PathMatch, StringMatchCandidate};
-use gpui::{AppContext, Model, Task, View, WeakView};
+use gpui::{App, Entity, Task, WeakEntity};
 use language::{
     Anchor, BufferSnapshot, DiagnosticEntry, DiagnosticSeverity, LspAdapterDelegate,
     OffsetRangeExt, ToOffset,
@@ -30,8 +30,8 @@ impl DiagnosticsSlashCommand {
         &self,
         query: String,
         cancellation_flag: Arc<AtomicBool>,
-        workspace: &View<Workspace>,
-        cx: &mut AppContext,
+        workspace: &Entity<Workspace>,
+        cx: &mut App,
     ) -> Task<Vec<PathMatch>> {
         if query.is_empty() {
             let workspace = workspace.read(cx);
@@ -90,7 +90,7 @@ impl SlashCommand for DiagnosticsSlashCommand {
         "diagnostics".into()
     }
 
-    fn label(&self, cx: &AppContext) -> language::CodeLabel {
+    fn label(&self, cx: &App) -> language::CodeLabel {
         create_label_for_command("diagnostics", &[INCLUDE_WARNINGS_ARGUMENT], cx)
     }
 
@@ -118,8 +118,9 @@ impl SlashCommand for DiagnosticsSlashCommand {
         self: Arc<Self>,
         arguments: &[String],
         cancellation_flag: Arc<AtomicBool>,
-        workspace: Option<WeakView<Workspace>>,
-        cx: &mut WindowContext,
+        workspace: Option<WeakEntity<Workspace>>,
+        _: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         let Some(workspace) = workspace.and_then(|workspace| workspace.upgrade()) else {
             return Task::ready(Err(anyhow!("workspace was dropped")));
@@ -172,9 +173,10 @@ impl SlashCommand for DiagnosticsSlashCommand {
         arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         _context_buffer: BufferSnapshot,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         _delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult> {
         let Some(workspace) = workspace.upgrade() else {
             return Task::ready(Err(anyhow!("workspace was dropped")));
@@ -184,7 +186,7 @@ impl SlashCommand for DiagnosticsSlashCommand {
 
         let task = collect_diagnostics(workspace.read(cx).project().clone(), options, cx);
 
-        cx.spawn(move |_| async move {
+        window.spawn(cx, move |_| async move {
             task.await?
                 .map(|output| output.to_event_stream())
                 .ok_or_else(|| anyhow!("No diagnostics found"))
@@ -223,9 +225,9 @@ impl Options {
 }
 
 fn collect_diagnostics(
-    project: Model<Project>,
+    project: Entity<Project>,
     options: Options,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) -> Task<Result<Option<SlashCommandOutput>>> {
     let error_source = if let Some(path_matcher) = &options.path_matcher {
         debug_assert_eq!(path_matcher.sources().len(), 1);

crates/assistant_slash_commands/src/docs_command.rs 🔗

@@ -8,7 +8,7 @@ use assistant_slash_command::{
     ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
     SlashCommandResult,
 };
-use gpui::{AppContext, BackgroundExecutor, Model, Task, WeakView};
+use gpui::{App, BackgroundExecutor, Entity, Task, WeakEntity};
 use indexed_docs::{
     DocsDotRsProvider, IndexedDocsRegistry, IndexedDocsStore, LocalRustdocProvider, PackageName,
     ProviderId,
@@ -24,7 +24,7 @@ pub struct DocsSlashCommand;
 impl DocsSlashCommand {
     pub const NAME: &'static str = "docs";
 
-    fn path_to_cargo_toml(project: Model<Project>, cx: &mut AppContext) -> Option<Arc<Path>> {
+    fn path_to_cargo_toml(project: Entity<Project>, cx: &mut App) -> Option<Arc<Path>> {
         let worktree = project.read(cx).worktrees(cx).next()?;
         let worktree = worktree.read(cx);
         let entry = worktree.entry_for_path("Cargo.toml")?;
@@ -43,8 +43,8 @@ impl DocsSlashCommand {
     /// access the workspace so we can read the project.
     fn ensure_rust_doc_providers_are_registered(
         &self,
-        workspace: Option<WeakView<Workspace>>,
-        cx: &mut AppContext,
+        workspace: Option<WeakEntity<Workspace>>,
+        cx: &mut App,
     ) {
         let indexed_docs_registry = IndexedDocsRegistry::global(cx);
         if indexed_docs_registry
@@ -164,8 +164,9 @@ impl SlashCommand for DocsSlashCommand {
         self: Arc<Self>,
         arguments: &[String],
         _cancel: Arc<AtomicBool>,
-        workspace: Option<WeakView<Workspace>>,
-        cx: &mut WindowContext,
+        workspace: Option<WeakEntity<Workspace>>,
+        _: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         self.ensure_rust_doc_providers_are_registered(workspace, cx);
 
@@ -272,9 +273,10 @@ impl SlashCommand for DocsSlashCommand {
         arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         _context_buffer: BufferSnapshot,
-        _workspace: WeakView<Workspace>,
+        _workspace: WeakEntity<Workspace>,
         _delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        _: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult> {
         if arguments.is_empty() {
             return Task::ready(Err(anyhow!("missing an argument")));

crates/assistant_slash_commands/src/fetch_command.rs 🔗

@@ -9,7 +9,7 @@ use assistant_slash_command::{
     SlashCommandResult,
 };
 use futures::AsyncReadExt;
-use gpui::{Task, WeakView};
+use gpui::{Task, WeakEntity};
 use html_to_markdown::{convert_html_to_markdown, markdown, TagHandler};
 use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
 use language::{BufferSnapshot, LspAdapterDelegate};
@@ -124,8 +124,9 @@ impl SlashCommand for FetchSlashCommand {
         self: Arc<Self>,
         _arguments: &[String],
         _cancel: Arc<AtomicBool>,
-        _workspace: Option<WeakView<Workspace>>,
-        _cx: &mut WindowContext,
+        _workspace: Option<WeakEntity<Workspace>>,
+        _window: &mut Window,
+        _cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         Task::ready(Ok(Vec::new()))
     }
@@ -135,9 +136,10 @@ impl SlashCommand for FetchSlashCommand {
         arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         _context_buffer: BufferSnapshot,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         _delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        _: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult> {
         let Some(argument) = arguments.first() else {
             return Task::ready(Err(anyhow!("missing URL")));

crates/assistant_slash_commands/src/file_command.rs 🔗

@@ -6,7 +6,7 @@ use assistant_slash_command::{
 use futures::channel::mpsc;
 use futures::Stream;
 use fuzzy::PathMatch;
-use gpui::{AppContext, Model, Task, View, WeakView};
+use gpui::{App, Entity, Task, WeakEntity};
 use language::{BufferSnapshot, CodeLabel, HighlightId, LineEnding, LspAdapterDelegate};
 use project::{PathMatchCandidateSet, Project};
 use serde::{Deserialize, Serialize};
@@ -28,8 +28,8 @@ impl FileSlashCommand {
         &self,
         query: String,
         cancellation_flag: Arc<AtomicBool>,
-        workspace: &View<Workspace>,
-        cx: &mut AppContext,
+        workspace: &Entity<Workspace>,
+        cx: &mut App,
     ) -> Task<Vec<PathMatch>> {
         if query.is_empty() {
             let workspace = workspace.read(cx);
@@ -134,8 +134,9 @@ impl SlashCommand for FileSlashCommand {
         self: Arc<Self>,
         arguments: &[String],
         cancellation_flag: Arc<AtomicBool>,
-        workspace: Option<WeakView<Workspace>>,
-        cx: &mut WindowContext,
+        workspace: Option<WeakEntity<Workspace>>,
+        _: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         let Some(workspace) = workspace.and_then(|workspace| workspace.upgrade()) else {
             return Task::ready(Err(anyhow!("workspace was dropped")));
@@ -187,9 +188,10 @@ impl SlashCommand for FileSlashCommand {
         arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         _context_buffer: BufferSnapshot,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         _delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        _: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult> {
         let Some(workspace) = workspace.upgrade() else {
             return Task::ready(Err(anyhow!("workspace was dropped")));
@@ -209,9 +211,9 @@ impl SlashCommand for FileSlashCommand {
 }
 
 fn collect_files(
-    project: Model<Project>,
+    project: Entity<Project>,
     glob_inputs: &[String],
-    cx: &mut AppContext,
+    cx: &mut App,
 ) -> impl Stream<Item = Result<SlashCommandEvent>> {
     let Ok(matchers) = glob_inputs
         .into_iter()

crates/assistant_slash_commands/src/now_command.rs 🔗

@@ -7,7 +7,7 @@ use assistant_slash_command::{
     SlashCommandResult,
 };
 use chrono::Local;
-use gpui::{Task, WeakView};
+use gpui::{Task, WeakEntity};
 use language::{BufferSnapshot, LspAdapterDelegate};
 use ui::prelude::*;
 use workspace::Workspace;
@@ -35,8 +35,9 @@ impl SlashCommand for NowSlashCommand {
         self: Arc<Self>,
         _arguments: &[String],
         _cancel: Arc<AtomicBool>,
-        _workspace: Option<WeakView<Workspace>>,
-        _cx: &mut WindowContext,
+        _workspace: Option<WeakEntity<Workspace>>,
+        _window: &mut Window,
+        _cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         Task::ready(Ok(Vec::new()))
     }
@@ -46,9 +47,10 @@ impl SlashCommand for NowSlashCommand {
         _arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         _context_buffer: BufferSnapshot,
-        _workspace: WeakView<Workspace>,
+        _workspace: WeakEntity<Workspace>,
         _delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        _cx: &mut WindowContext,
+        _window: &mut Window,
+        _cx: &mut App,
     ) -> Task<SlashCommandResult> {
         let now = Local::now();
         let text = format!("Today is {now}.", now = now.to_rfc2822());

crates/assistant_slash_commands/src/project_command.rs 🔗

@@ -10,7 +10,7 @@ use assistant_slash_command::{
     SlashCommandResult,
 };
 use feature_flags::FeatureFlag;
-use gpui::{AppContext, Task, WeakView, WindowContext};
+use gpui::{App, Task, WeakEntity};
 use language::{Anchor, CodeLabel, LspAdapterDelegate};
 use language_model::{LanguageModelRegistry, LanguageModelTool};
 use prompt_library::PromptBuilder;
@@ -43,7 +43,7 @@ impl SlashCommand for ProjectSlashCommand {
         "project".into()
     }
 
-    fn label(&self, cx: &AppContext) -> CodeLabel {
+    fn label(&self, cx: &App) -> CodeLabel {
         create_label_for_command("project", &[], cx)
     }
 
@@ -67,8 +67,9 @@ impl SlashCommand for ProjectSlashCommand {
         self: Arc<Self>,
         _arguments: &[String],
         _cancel: Arc<AtomicBool>,
-        _workspace: Option<WeakView<Workspace>>,
-        _cx: &mut WindowContext,
+        _workspace: Option<WeakEntity<Workspace>>,
+        _window: &mut Window,
+        _cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         Task::ready(Ok(Vec::new()))
     }
@@ -78,9 +79,10 @@ impl SlashCommand for ProjectSlashCommand {
         _arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<Anchor>],
         context_buffer: language::BufferSnapshot,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         _delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult> {
         let model_registry = LanguageModelRegistry::read_global(cx);
         let current_model = model_registry.active_model();
@@ -97,7 +99,7 @@ impl SlashCommand for ProjectSlashCommand {
             return Task::ready(Err(anyhow::anyhow!("no project indexer")));
         };
 
-        cx.spawn(|mut cx| async move {
+        window.spawn(cx, |mut cx| async move {
             let current_model = current_model.ok_or_else(|| anyhow!("no model selected"))?;
 
             let prompt =

crates/assistant_slash_commands/src/prompt_command.rs 🔗

@@ -1,9 +1,9 @@
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use assistant_slash_command::{
     ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
     SlashCommandResult,
 };
-use gpui::{Task, WeakView};
+use gpui::{Task, WeakEntity};
 use language::{BufferSnapshot, LspAdapterDelegate};
 use prompt_library::PromptStore;
 use std::sync::{atomic::AtomicBool, Arc};
@@ -37,8 +37,9 @@ impl SlashCommand for PromptSlashCommand {
         self: Arc<Self>,
         arguments: &[String],
         _cancellation_flag: Arc<AtomicBool>,
-        _workspace: Option<WeakView<Workspace>>,
-        cx: &mut WindowContext,
+        _workspace: Option<WeakEntity<Workspace>>,
+        _: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         let store = PromptStore::global(cx);
         let query = arguments.to_owned().join(" ");
@@ -64,9 +65,10 @@ impl SlashCommand for PromptSlashCommand {
         arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         _context_buffer: BufferSnapshot,
-        _workspace: WeakView<Workspace>,
+        _workspace: WeakEntity<Workspace>,
         _delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        _: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult> {
         let title = arguments.to_owned().join(" ");
         if title.trim().is_empty() {

crates/assistant_slash_commands/src/search_command.rs 🔗

@@ -4,7 +4,7 @@ use assistant_slash_command::{
     SlashCommandResult,
 };
 use feature_flags::FeatureFlag;
-use gpui::{AppContext, Task, WeakView};
+use gpui::{App, Task, WeakEntity};
 use language::{CodeLabel, LspAdapterDelegate};
 use semantic_index::{LoadedSearchResult, SemanticDb};
 use std::{
@@ -34,7 +34,7 @@ impl SlashCommand for SearchSlashCommand {
         "search".into()
     }
 
-    fn label(&self, cx: &AppContext) -> CodeLabel {
+    fn label(&self, cx: &App) -> CodeLabel {
         create_label_for_command("search", &["--n"], cx)
     }
 
@@ -58,8 +58,9 @@ impl SlashCommand for SearchSlashCommand {
         self: Arc<Self>,
         _arguments: &[String],
         _cancel: Arc<AtomicBool>,
-        _workspace: Option<WeakView<Workspace>>,
-        _cx: &mut WindowContext,
+        _workspace: Option<WeakEntity<Workspace>>,
+        _window: &mut Window,
+        _cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         Task::ready(Ok(Vec::new()))
     }
@@ -69,9 +70,10 @@ impl SlashCommand for SearchSlashCommand {
         arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         _context_buffer: language::BufferSnapshot,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         _delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult> {
         let Some(workspace) = workspace.upgrade() else {
             return Task::ready(Err(anyhow::anyhow!("workspace was dropped")));
@@ -107,7 +109,7 @@ impl SlashCommand for SearchSlashCommand {
             return Task::ready(Err(anyhow::anyhow!("no project indexer")));
         };
 
-        cx.spawn(|cx| async move {
+        window.spawn(cx, |cx| async move {
             let results = project_index
                 .read_with(&cx, |project_index, cx| {
                     project_index.search(vec![query.clone()], limit.unwrap_or(5), cx)

crates/assistant_slash_commands/src/selection_command.rs 🔗

@@ -5,8 +5,7 @@ use assistant_slash_command::{
 };
 use editor::Editor;
 use futures::StreamExt;
-use gpui::{AppContext, Task, WeakView};
-use gpui::{SharedString, ViewContext, WindowContext};
+use gpui::{App, Context, SharedString, Task, WeakEntity, Window};
 use language::{BufferSnapshot, CodeLabel, LspAdapterDelegate};
 use std::sync::atomic::AtomicBool;
 use std::sync::Arc;
@@ -22,7 +21,7 @@ impl SlashCommand for SelectionCommand {
         "selection".into()
     }
 
-    fn label(&self, _cx: &AppContext) -> CodeLabel {
+    fn label(&self, _cx: &App) -> CodeLabel {
         CodeLabel::plain(self.name(), None)
     }
 
@@ -50,8 +49,9 @@ impl SlashCommand for SelectionCommand {
         self: Arc<Self>,
         _arguments: &[String],
         _cancel: Arc<AtomicBool>,
-        _workspace: Option<WeakView<Workspace>>,
-        _cx: &mut WindowContext,
+        _workspace: Option<WeakEntity<Workspace>>,
+        _window: &mut Window,
+        _cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         Task::ready(Err(anyhow!("this command does not require argument")))
     }
@@ -61,9 +61,10 @@ impl SlashCommand for SelectionCommand {
         _arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         _context_buffer: BufferSnapshot,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         _delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        _window: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult> {
         let mut events = vec![];
 
@@ -102,7 +103,7 @@ impl SlashCommand for SelectionCommand {
 
 pub fn selections_creases(
     workspace: &mut workspace::Workspace,
-    cx: &mut ViewContext<Workspace>,
+    cx: &mut Context<Workspace>,
 ) -> Option<Vec<(String, String)>> {
     let editor = workspace
         .active_item(cx)

crates/assistant_slash_commands/src/streaming_example_command.rs 🔗

@@ -9,7 +9,7 @@ use assistant_slash_command::{
 };
 use feature_flags::FeatureFlag;
 use futures::channel::mpsc;
-use gpui::{Task, WeakView};
+use gpui::{Task, WeakEntity};
 use language::{BufferSnapshot, LspAdapterDelegate};
 use smol::stream::StreamExt;
 use smol::Timer;
@@ -45,8 +45,9 @@ impl SlashCommand for StreamingExampleSlashCommand {
         self: Arc<Self>,
         _arguments: &[String],
         _cancel: Arc<AtomicBool>,
-        _workspace: Option<WeakView<Workspace>>,
-        _cx: &mut WindowContext,
+        _workspace: Option<WeakEntity<Workspace>>,
+        _window: &mut Window,
+        _cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         Task::ready(Ok(Vec::new()))
     }
@@ -56,9 +57,10 @@ impl SlashCommand for StreamingExampleSlashCommand {
         _arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         _context_buffer: BufferSnapshot,
-        _workspace: WeakView<Workspace>,
+        _workspace: WeakEntity<Workspace>,
         _delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        _: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult> {
         let (events_tx, events_rx) = mpsc::unbounded();
         cx.background_executor()

crates/assistant_slash_commands/src/symbols_command.rs 🔗

@@ -4,11 +4,11 @@ use assistant_slash_command::{
     SlashCommandResult,
 };
 use editor::Editor;
-use gpui::{Task, WeakView};
+use gpui::{Task, WeakEntity};
 use language::{BufferSnapshot, LspAdapterDelegate};
 use std::sync::Arc;
 use std::{path::Path, sync::atomic::AtomicBool};
-use ui::{IconName, WindowContext};
+use ui::{App, IconName, Window};
 use workspace::Workspace;
 
 pub struct OutlineSlashCommand;
@@ -34,8 +34,9 @@ impl SlashCommand for OutlineSlashCommand {
         self: Arc<Self>,
         _arguments: &[String],
         _cancel: Arc<AtomicBool>,
-        _workspace: Option<WeakView<Workspace>>,
-        _cx: &mut WindowContext,
+        _workspace: Option<WeakEntity<Workspace>>,
+        _window: &mut Window,
+        _cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         Task::ready(Err(anyhow!("this command does not require argument")))
     }
@@ -49,9 +50,10 @@ impl SlashCommand for OutlineSlashCommand {
         _arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         _context_buffer: BufferSnapshot,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         _delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        _: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult> {
         let output = workspace.update(cx, |workspace, cx| {
             let Some(active_item) = workspace.active_item(cx) else {

crates/assistant_slash_commands/src/tab_command.rs 🔗

@@ -1,4 +1,4 @@
-use anyhow::{Context, Result};
+use anyhow::{Context as _, Result};
 use assistant_slash_command::{
     ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
     SlashCommandResult,
@@ -6,13 +6,13 @@ use assistant_slash_command::{
 use collections::{HashMap, HashSet};
 use editor::Editor;
 use futures::future::join_all;
-use gpui::{Entity, Task, WeakView};
+use gpui::{Task, WeakEntity};
 use language::{BufferSnapshot, CodeLabel, HighlightId, LspAdapterDelegate};
 use std::{
     path::PathBuf,
     sync::{atomic::AtomicBool, Arc},
 };
-use ui::{prelude::*, ActiveTheme, WindowContext};
+use ui::{prelude::*, ActiveTheme, App, Window};
 use util::ResultExt;
 use workspace::Workspace;
 
@@ -51,8 +51,9 @@ impl SlashCommand for TabSlashCommand {
         self: Arc<Self>,
         arguments: &[String],
         cancel: Arc<AtomicBool>,
-        workspace: Option<WeakView<Workspace>>,
-        cx: &mut WindowContext,
+        workspace: Option<WeakEntity<Workspace>>,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         let mut has_all_tabs_completion_item = false;
         let argument_set = arguments
@@ -82,10 +83,10 @@ impl SlashCommand for TabSlashCommand {
         });
         let current_query = arguments.last().cloned().unwrap_or_default();
         let tab_items_search =
-            tab_items_for_queries(workspace, &[current_query], cancel, false, cx);
+            tab_items_for_queries(workspace, &[current_query], cancel, false, window, cx);
 
         let comment_id = cx.theme().syntax().highlight_id("comment").map(HighlightId);
-        cx.spawn(|_| async move {
+        window.spawn(cx, |_| async move {
             let tab_items = tab_items_search.await?;
             let run_command = tab_items.len() == 1;
             let tab_completion_items = tab_items.into_iter().filter_map(|(path, ..)| {
@@ -137,15 +138,17 @@ impl SlashCommand for TabSlashCommand {
         arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         _context_buffer: BufferSnapshot,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         _delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult> {
         let tab_items_search = tab_items_for_queries(
             Some(workspace),
             arguments,
             Arc::new(AtomicBool::new(false)),
             true,
+            window,
             cx,
         );
 
@@ -160,15 +163,16 @@ impl SlashCommand for TabSlashCommand {
 }
 
 fn tab_items_for_queries(
-    workspace: Option<WeakView<Workspace>>,
+    workspace: Option<WeakEntity<Workspace>>,
     queries: &[String],
     cancel: Arc<AtomicBool>,
     strict_match: bool,
-    cx: &mut WindowContext,
+    window: &mut Window,
+    cx: &mut App,
 ) -> Task<anyhow::Result<Vec<(Option<PathBuf>, BufferSnapshot, usize)>>> {
     let empty_query = queries.is_empty() || queries.iter().all(|query| query.trim().is_empty());
     let queries = queries.to_owned();
-    cx.spawn(|mut cx| async move {
+    window.spawn(cx, |mut cx| async move {
         let mut open_buffers =
             workspace
                 .context("no workspace")?
@@ -281,7 +285,7 @@ fn tab_items_for_queries(
 
 fn active_item_buffer(
     workspace: &mut Workspace,
-    cx: &mut ViewContext<Workspace>,
+    cx: &mut Context<Workspace>,
 ) -> anyhow::Result<BufferSnapshot> {
     let active_editor = workspace
         .active_item(cx)

crates/assistant_slash_commands/src/terminal_command.rs 🔗

@@ -6,7 +6,7 @@ use assistant_slash_command::{
     ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
     SlashCommandResult,
 };
-use gpui::{AppContext, Task, View, WeakView};
+use gpui::{App, Entity, Task, WeakEntity};
 use language::{BufferSnapshot, CodeLabel, LspAdapterDelegate};
 use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
 use ui::prelude::*;
@@ -25,7 +25,7 @@ impl SlashCommand for TerminalSlashCommand {
         "terminal".into()
     }
 
-    fn label(&self, cx: &AppContext) -> CodeLabel {
+    fn label(&self, cx: &App) -> CodeLabel {
         create_label_for_command("terminal", &[LINE_COUNT_ARG], cx)
     }
 
@@ -53,8 +53,9 @@ impl SlashCommand for TerminalSlashCommand {
         self: Arc<Self>,
         _arguments: &[String],
         _cancel: Arc<AtomicBool>,
-        _workspace: Option<WeakView<Workspace>>,
-        _cx: &mut WindowContext,
+        _workspace: Option<WeakEntity<Workspace>>,
+        _window: &mut Window,
+        _cx: &mut App,
     ) -> Task<Result<Vec<ArgumentCompletion>>> {
         Task::ready(Ok(Vec::new()))
     }
@@ -64,9 +65,10 @@ impl SlashCommand for TerminalSlashCommand {
         arguments: &[String],
         _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
         _context_buffer: BufferSnapshot,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         _delegate: Option<Arc<dyn LspAdapterDelegate>>,
-        cx: &mut WindowContext,
+        _: &mut Window,
+        cx: &mut App,
     ) -> Task<SlashCommandResult> {
         let Some(workspace) = workspace.upgrade() else {
             return Task::ready(Err(anyhow::anyhow!("workspace was dropped")));
@@ -107,9 +109,9 @@ impl SlashCommand for TerminalSlashCommand {
 }
 
 fn resolve_active_terminal(
-    workspace: &View<Workspace>,
-    cx: &WindowContext,
-) -> Option<View<TerminalView>> {
+    workspace: &Entity<Workspace>,
+    cx: &mut App,
+) -> Option<Entity<TerminalView>> {
     if let Some(terminal_view) = workspace
         .read(cx)
         .active_item(cx)

crates/assistant_tool/src/assistant_tool.rs 🔗

@@ -4,13 +4,13 @@ mod tool_working_set;
 use std::sync::Arc;
 
 use anyhow::Result;
-use gpui::{AppContext, Task, WeakView, WindowContext};
+use gpui::{App, Task, WeakEntity, Window};
 use workspace::Workspace;
 
 pub use crate::tool_registry::*;
 pub use crate::tool_working_set::*;
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     ToolRegistry::default_global(cx);
 }
 
@@ -31,7 +31,8 @@ pub trait Tool: 'static + Send + Sync {
     fn run(
         self: Arc<Self>,
         input: serde_json::Value,
-        workspace: WeakView<Workspace>,
-        cx: &mut WindowContext,
+        workspace: WeakEntity<Workspace>,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<String>>;
 }

crates/assistant_tool/src/tool_registry.rs 🔗

@@ -3,7 +3,7 @@ use std::sync::Arc;
 use collections::HashMap;
 use derive_more::{Deref, DerefMut};
 use gpui::Global;
-use gpui::{AppContext, ReadGlobal};
+use gpui::{App, ReadGlobal};
 use parking_lot::RwLock;
 
 use crate::Tool;
@@ -25,14 +25,14 @@ pub struct ToolRegistry {
 
 impl ToolRegistry {
     /// Returns the global [`ToolRegistry`].
-    pub fn global(cx: &AppContext) -> Arc<Self> {
+    pub fn global(cx: &App) -> Arc<Self> {
         GlobalToolRegistry::global(cx).0.clone()
     }
 
     /// Returns the global [`ToolRegistry`].
     ///
     /// Inserts a default [`ToolRegistry`] if one does not yet exist.
-    pub fn default_global(cx: &mut AppContext) -> Arc<Self> {
+    pub fn default_global(cx: &mut App) -> Arc<Self> {
         cx.default_global::<GlobalToolRegistry>().0.clone()
     }
 

crates/assistant_tool/src/tool_working_set.rs 🔗

@@ -1,7 +1,7 @@
 use std::sync::Arc;
 
 use collections::HashMap;
-use gpui::AppContext;
+use gpui::App;
 use parking_lot::Mutex;
 
 use crate::{Tool, ToolRegistry};
@@ -23,7 +23,7 @@ struct WorkingSetState {
 }
 
 impl ToolWorkingSet {
-    pub fn tool(&self, name: &str, cx: &AppContext) -> Option<Arc<dyn Tool>> {
+    pub fn tool(&self, name: &str, cx: &App) -> Option<Arc<dyn Tool>> {
         self.state
             .lock()
             .context_server_tools_by_name
@@ -32,7 +32,7 @@ impl ToolWorkingSet {
             .or_else(|| ToolRegistry::global(cx).tool(name))
     }
 
-    pub fn tools(&self, cx: &AppContext) -> Vec<Arc<dyn Tool>> {
+    pub fn tools(&self, cx: &App) -> Vec<Arc<dyn Tool>> {
         let mut tools = ToolRegistry::global(cx).tools();
         tools.extend(
             self.state

crates/assistant_tools/src/assistant_tools.rs 🔗

@@ -1,11 +1,11 @@
 mod now_tool;
 
 use assistant_tool::ToolRegistry;
-use gpui::AppContext;
+use gpui::App;
 
 use crate::now_tool::NowTool;
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     assistant_tool::init(cx);
 
     let registry = ToolRegistry::global(cx);

crates/assistant_tools/src/now_tool.rs 🔗

@@ -3,7 +3,7 @@ use std::sync::Arc;
 use anyhow::{anyhow, Result};
 use assistant_tool::Tool;
 use chrono::{Local, Utc};
-use gpui::{Task, WeakView, WindowContext};
+use gpui::{App, Task, WeakEntity, Window};
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 
@@ -41,8 +41,9 @@ impl Tool for NowTool {
     fn run(
         self: Arc<Self>,
         input: serde_json::Value,
-        _workspace: WeakView<workspace::Workspace>,
-        _cx: &mut WindowContext,
+        _workspace: WeakEntity<workspace::Workspace>,
+        _window: &mut Window,
+        _cx: &mut App,
     ) -> Task<Result<String>> {
         let input: FileToolInput = match serde_json::from_value(input) {
             Ok(input) => input,

crates/audio/src/assets.rs 🔗

@@ -2,7 +2,7 @@ use std::{io::Cursor, sync::Arc};
 
 use anyhow::Result;
 use collections::HashMap;
-use gpui::{AppContext, AssetSource, Global};
+use gpui::{App, AssetSource, Global};
 use rodio::{
     source::{Buffered, SamplesConverter},
     Decoder, Source,
@@ -27,11 +27,11 @@ impl SoundRegistry {
         })
     }
 
-    pub fn global(cx: &AppContext) -> Arc<Self> {
+    pub fn global(cx: &App) -> Arc<Self> {
         cx.global::<GlobalSoundRegistry>().0.clone()
     }
 
-    pub(crate) fn set_global(source: impl AssetSource, cx: &mut AppContext) {
+    pub(crate) fn set_global(source: impl AssetSource, cx: &mut App) {
         cx.set_global(GlobalSoundRegistry(SoundRegistry::new(source)));
     }
 

crates/audio/src/audio.rs 🔗

@@ -1,12 +1,12 @@
 use assets::SoundRegistry;
 use derive_more::{Deref, DerefMut};
-use gpui::{AppContext, AssetSource, BorrowAppContext, Global};
+use gpui::{App, AssetSource, BorrowAppContext, Global};
 use rodio::{OutputStream, OutputStreamHandle};
 use util::ResultExt;
 
 mod assets;
 
-pub fn init(source: impl AssetSource, cx: &mut AppContext) {
+pub fn init(source: impl AssetSource, cx: &mut App) {
     SoundRegistry::set_global(source, cx);
     cx.set_global(GlobalAudio(Audio::new()));
 }
@@ -59,7 +59,7 @@ impl Audio {
         self.output_handle.as_ref()
     }
 
-    pub fn play_sound(sound: Sound, cx: &mut AppContext) {
+    pub fn play_sound(sound: Sound, cx: &mut App) {
         if !cx.has_global::<GlobalAudio>() {
             return;
         }
@@ -72,7 +72,7 @@ impl Audio {
         });
     }
 
-    pub fn end_call(cx: &mut AppContext) {
+    pub fn end_call(cx: &mut App) {
         if !cx.has_global::<GlobalAudio>() {
             return;
         }

crates/auto_update/src/auto_update.rs 🔗

@@ -1,10 +1,10 @@
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use client::{Client, TelemetrySettings};
 use db::kvp::KEY_VALUE_STORE;
 use db::RELEASE_CHANNEL;
 use gpui::{
-    actions, AppContext, AsyncAppContext, Context as _, Global, Model, ModelContext,
-    SemanticVersion, Task, WindowContext,
+    actions, App, AppContext as _, AsyncAppContext, Context, Entity, Global, SemanticVersion, Task,
+    Window,
 };
 use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
 use paths::remote_servers_dir;
@@ -112,7 +112,7 @@ impl Settings for AutoUpdateSetting {
 
     type FileContent = Option<AutoUpdateSettingContent>;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
         let auto_update = [sources.server, sources.release_channel, sources.user]
             .into_iter()
             .find_map(|value| value.copied().flatten())
@@ -123,24 +123,24 @@ impl Settings for AutoUpdateSetting {
 }
 
 #[derive(Default)]
-struct GlobalAutoUpdate(Option<Model<AutoUpdater>>);
+struct GlobalAutoUpdate(Option<Entity<AutoUpdater>>);
 
 impl Global for GlobalAutoUpdate {}
 
-pub fn init(http_client: Arc<HttpClientWithUrl>, cx: &mut AppContext) {
+pub fn init(http_client: Arc<HttpClientWithUrl>, cx: &mut App) {
     AutoUpdateSetting::register(cx);
 
-    cx.observe_new_views(|workspace: &mut Workspace, _cx| {
-        workspace.register_action(|_, action: &Check, cx| check(action, cx));
+    cx.observe_new(|workspace: &mut Workspace, _window, _cx| {
+        workspace.register_action(|_, action: &Check, window, cx| check(action, window, cx));
 
-        workspace.register_action(|_, action, cx| {
+        workspace.register_action(|_, action, _, cx| {
             view_release_notes(action, cx);
         });
     })
     .detach();
 
     let version = release_channel::AppVersion::global(cx);
-    let auto_updater = cx.new_model(|cx| {
+    let auto_updater = cx.new(|cx| {
         let updater = AutoUpdater::new(version, http_client);
 
         let poll_for_updates = ReleaseChannel::try_global(cx)
@@ -155,7 +155,7 @@ pub fn init(http_client: Arc<HttpClientWithUrl>, cx: &mut AppContext) {
                 .0
                 .then(|| updater.start_polling(cx));
 
-            cx.observe_global::<SettingsStore>(move |updater, cx| {
+            cx.observe_global::<SettingsStore>(move |updater: &mut AutoUpdater, cx| {
                 if AutoUpdateSetting::get_global(cx).0 {
                     if update_subscription.is_none() {
                         update_subscription = Some(updater.start_polling(cx))
@@ -172,23 +172,25 @@ pub fn init(http_client: Arc<HttpClientWithUrl>, cx: &mut AppContext) {
     cx.set_global(GlobalAutoUpdate(Some(auto_updater)));
 }
 
-pub fn check(_: &Check, cx: &mut WindowContext) {
+pub fn check(_: &Check, window: &mut Window, cx: &mut App) {
     if let Some(message) = option_env!("ZED_UPDATE_EXPLANATION") {
-        drop(cx.prompt(
+        drop(window.prompt(
             gpui::PromptLevel::Info,
             "Zed was installed via a package manager.",
             Some(message),
             &["Ok"],
+            cx,
         ));
         return;
     }
 
     if let Ok(message) = env::var("ZED_UPDATE_EXPLANATION") {
-        drop(cx.prompt(
+        drop(window.prompt(
             gpui::PromptLevel::Info,
             "Zed was installed via a package manager.",
             Some(&message),
             &["Ok"],
+            cx,
         ));
         return;
     }
@@ -203,16 +205,17 @@ pub fn check(_: &Check, cx: &mut WindowContext) {
     if let Some(updater) = AutoUpdater::get(cx) {
         updater.update(cx, |updater, cx| updater.poll(cx));
     } else {
-        drop(cx.prompt(
+        drop(window.prompt(
             gpui::PromptLevel::Info,
             "Could not check for updates",
             Some("Auto-updates disabled for non-bundled app."),
             &["Ok"],
+            cx,
         ));
     }
 }
 
-pub fn view_release_notes(_: &ViewReleaseNotes, cx: &mut AppContext) -> Option<()> {
+pub fn view_release_notes(_: &ViewReleaseNotes, cx: &mut App) -> Option<()> {
     let auto_updater = AutoUpdater::get(cx)?;
     let release_channel = ReleaseChannel::try_global(cx)?;
 
@@ -236,7 +239,7 @@ pub fn view_release_notes(_: &ViewReleaseNotes, cx: &mut AppContext) -> Option<(
 }
 
 impl AutoUpdater {
-    pub fn get(cx: &mut AppContext) -> Option<Model<Self>> {
+    pub fn get(cx: &mut App) -> Option<Entity<Self>> {
         cx.default_global::<GlobalAutoUpdate>().0.clone()
     }
 
@@ -249,7 +252,7 @@ impl AutoUpdater {
         }
     }
 
-    pub fn start_polling(&self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    pub fn start_polling(&self, cx: &mut Context<Self>) -> Task<Result<()>> {
         cx.spawn(|this, mut cx| async move {
             loop {
                 this.update(&mut cx, |this, cx| this.poll(cx))?;
@@ -258,7 +261,7 @@ impl AutoUpdater {
         })
     }
 
-    pub fn poll(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn poll(&mut self, cx: &mut Context<Self>) {
         if self.pending_poll.is_some() || self.status.is_updated() {
             return;
         }
@@ -287,7 +290,7 @@ impl AutoUpdater {
         self.status.clone()
     }
 
-    pub fn dismiss_error(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn dismiss_error(&mut self, cx: &mut Context<Self>) {
         self.status = AutoUpdateStatus::Idle;
         cx.notify();
     }
@@ -371,7 +374,7 @@ impl AutoUpdater {
     }
 
     async fn get_release(
-        this: &Model<Self>,
+        this: &Entity<Self>,
         asset: &str,
         os: &str,
         arch: &str,
@@ -421,7 +424,7 @@ impl AutoUpdater {
     }
 
     async fn get_latest_release(
-        this: &Model<Self>,
+        this: &Entity<Self>,
         asset: &str,
         os: &str,
         arch: &str,
@@ -431,7 +434,7 @@ impl AutoUpdater {
         Self::get_release(this, asset, os, arch, None, release_channel, cx).await
     }
 
-    async fn update(this: Model<Self>, mut cx: AsyncAppContext) -> Result<()> {
+    async fn update(this: Entity<Self>, mut cx: AsyncAppContext) -> Result<()> {
         let (client, current_version, release_channel) = this.update(&mut cx, |this, cx| {
             this.status = AutoUpdateStatus::Checking;
             cx.notify();
@@ -509,7 +512,7 @@ impl AutoUpdater {
     pub fn set_should_show_update_notification(
         &self,
         should_show: bool,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Result<()>> {
         cx.background_executor().spawn(async move {
             if should_show {
@@ -528,7 +531,7 @@ impl AutoUpdater {
         })
     }
 
-    pub fn should_show_update_notification(&self, cx: &AppContext) -> Task<Result<bool>> {
+    pub fn should_show_update_notification(&self, cx: &App) -> Task<Result<bool>> {
         cx.background_executor().spawn(async move {
             Ok(KEY_VALUE_STORE
                 .read_kvp(SHOULD_SHOW_UPDATE_NOTIFICATION_KEY)?

crates/auto_update_ui/src/auto_update_ui.rs 🔗

@@ -2,7 +2,7 @@ mod update_notification;
 
 use auto_update::AutoUpdater;
 use editor::{Editor, MultiBuffer};
-use gpui::{actions, prelude::*, AppContext, SharedString, View, ViewContext};
+use gpui::{actions, prelude::*, App, Context, Entity, SharedString, Window};
 use http_client::HttpClient;
 use markdown_preview::markdown_preview_view::{MarkdownPreviewMode, MarkdownPreviewView};
 use release_channel::{AppVersion, ReleaseChannel};
@@ -16,10 +16,10 @@ use crate::update_notification::UpdateNotification;
 
 actions!(auto_update, [ViewReleaseNotesLocally]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(|workspace: &mut Workspace, _cx| {
-        workspace.register_action(|workspace, _: &ViewReleaseNotesLocally, cx| {
-            view_release_notes_locally(workspace, cx);
+pub fn init(cx: &mut App) {
+    cx.observe_new(|workspace: &mut Workspace, _window, _cx| {
+        workspace.register_action(|workspace, _: &ViewReleaseNotesLocally, window, cx| {
+            view_release_notes_locally(workspace, window, cx);
         });
     })
     .detach();
@@ -31,7 +31,11 @@ struct ReleaseNotesBody {
     release_notes: String,
 }
 
-fn view_release_notes_locally(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
+fn view_release_notes_locally(
+    workspace: &mut Workspace,
+    window: &mut Window,
+    cx: &mut Context<Workspace>,
+) {
     let release_channel = ReleaseChannel::global(cx);
 
     let url = match release_channel {
@@ -60,8 +64,8 @@ fn view_release_notes_locally(workspace: &mut Workspace, cx: &mut ViewContext<Wo
         .language_for_name("Markdown");
 
     workspace
-        .with_local_workspace(cx, move |_, cx| {
-            cx.spawn(|workspace, mut cx| async move {
+        .with_local_workspace(window, cx, move |_, window, cx| {
+            cx.spawn_in(window, |workspace, mut cx| async move {
                 let markdown = markdown.await.log_err();
                 let response = client.get(&url, Default::default(), true).await;
                 let Some(mut response) = response.log_err() else {
@@ -76,7 +80,7 @@ fn view_release_notes_locally(workspace: &mut Workspace, cx: &mut ViewContext<Wo
 
                 if let Ok(body) = body {
                     workspace
-                        .update(&mut cx, |workspace, cx| {
+                        .update_in(&mut cx, |workspace, window, cx| {
                             let project = workspace.project().clone();
                             let buffer = project.update(cx, |project, cx| {
                                 project.create_local_buffer("", markdown, cx)
@@ -86,25 +90,28 @@ fn view_release_notes_locally(workspace: &mut Workspace, cx: &mut ViewContext<Wo
                             });
                             let language_registry = project.read(cx).languages().clone();
 
-                            let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
+                            let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 
                             let tab_description = SharedString::from(body.title.to_string());
-                            let editor = cx.new_view(|cx| {
-                                Editor::for_multibuffer(buffer, Some(project), true, cx)
+                            let editor = cx.new(|cx| {
+                                Editor::for_multibuffer(buffer, Some(project), true, window, cx)
                             });
                             let workspace_handle = workspace.weak_handle();
-                            let view: View<MarkdownPreviewView> = MarkdownPreviewView::new(
-                                MarkdownPreviewMode::Default,
-                                editor,
-                                workspace_handle,
-                                language_registry,
-                                Some(tab_description),
-                                cx,
-                            );
+                            let markdown_preview: Entity<MarkdownPreviewView> =
+                                MarkdownPreviewView::new(
+                                    MarkdownPreviewMode::Default,
+                                    editor,
+                                    workspace_handle,
+                                    language_registry,
+                                    Some(tab_description),
+                                    window,
+                                    cx,
+                                );
                             workspace.add_item_to_active_pane(
-                                Box::new(view.clone()),
+                                Box::new(markdown_preview.clone()),
                                 None,
                                 true,
+                                window,
                                 cx,
                             );
                             cx.notify();
@@ -117,12 +124,12 @@ fn view_release_notes_locally(workspace: &mut Workspace, cx: &mut ViewContext<Wo
         .detach();
 }
 
-pub fn notify_of_any_new_update(cx: &mut ViewContext<Workspace>) -> Option<()> {
+pub fn notify_of_any_new_update(window: &mut Window, cx: &mut Context<Workspace>) -> Option<()> {
     let updater = AutoUpdater::get(cx)?;
     let version = updater.read(cx).current_version();
     let should_show_notification = updater.read(cx).should_show_update_notification(cx);
 
-    cx.spawn(|workspace, mut cx| async move {
+    cx.spawn_in(window, |workspace, mut cx| async move {
         let should_show_notification = should_show_notification.await?;
         if should_show_notification {
             workspace.update(&mut cx, |workspace, cx| {
@@ -130,7 +137,7 @@ pub fn notify_of_any_new_update(cx: &mut ViewContext<Workspace>) -> Option<()> {
                 workspace.show_notification(
                     NotificationId::unique::<UpdateNotification>(),
                     cx,
-                    |cx| cx.new_view(|_| UpdateNotification::new(version, workspace_handle)),
+                    |cx| cx.new(|_| UpdateNotification::new(version, workspace_handle)),
                 );
                 updater.update(cx, |updater, cx| {
                     updater

crates/auto_update_ui/src/update_notification.rs 🔗

@@ -1,6 +1,6 @@
 use gpui::{
-    div, DismissEvent, EventEmitter, InteractiveElement, IntoElement, ParentElement, Render,
-    SemanticVersion, StatefulInteractiveElement, Styled, ViewContext, WeakView,
+    div, Context, DismissEvent, EventEmitter, InteractiveElement, IntoElement, ParentElement,
+    Render, SemanticVersion, StatefulInteractiveElement, Styled, WeakEntity, Window,
 };
 use menu::Cancel;
 use release_channel::ReleaseChannel;
@@ -12,13 +12,13 @@ use workspace::{
 
 pub struct UpdateNotification {
     version: SemanticVersion,
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
 }
 
 impl EventEmitter<DismissEvent> for UpdateNotification {}
 
 impl Render for UpdateNotification {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let app_name = ReleaseChannel::global(cx).display_name();
 
         v_flex()
@@ -37,7 +37,9 @@ impl Render for UpdateNotification {
                             .id("cancel")
                             .child(Icon::new(IconName::Close))
                             .cursor_pointer()
-                            .on_click(cx.listener(|this, _, cx| this.dismiss(&menu::Cancel, cx))),
+                            .on_click(cx.listener(|this, _, window, cx| {
+                                this.dismiss(&menu::Cancel, window, cx)
+                            })),
                     ),
             )
             .child(
@@ -45,24 +47,24 @@ impl Render for UpdateNotification {
                     .id("notes")
                     .child(Label::new("View the release notes"))
                     .cursor_pointer()
-                    .on_click(cx.listener(|this, _, cx| {
+                    .on_click(cx.listener(|this, _, window, cx| {
                         this.workspace
                             .update(cx, |workspace, cx| {
-                                crate::view_release_notes_locally(workspace, cx);
+                                crate::view_release_notes_locally(workspace, window, cx);
                             })
                             .log_err();
-                        this.dismiss(&menu::Cancel, cx)
+                        this.dismiss(&menu::Cancel, window, cx)
                     })),
             )
     }
 }
 
 impl UpdateNotification {
-    pub fn new(version: SemanticVersion, workspace: WeakView<Workspace>) -> Self {
+    pub fn new(version: SemanticVersion, workspace: WeakEntity<Workspace>) -> Self {
         Self { version, workspace }
     }
 
-    pub fn dismiss(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
+    pub fn dismiss(&mut self, _: &Cancel, _: &mut Window, cx: &mut Context<Self>) {
         cx.emit(DismissEvent);
     }
 }

crates/breadcrumbs/src/breadcrumbs.rs 🔗

@@ -1,7 +1,7 @@
 use editor::Editor;
 use gpui::{
-    Element, EventEmitter, FocusableView, IntoElement, ParentElement, Render, StyledText,
-    Subscription, ViewContext,
+    Context, Element, EventEmitter, Focusable, IntoElement, ParentElement, Render, StyledText,
+    Subscription, Window,
 };
 use itertools::Itertools;
 use std::cmp;
@@ -37,7 +37,7 @@ impl Breadcrumbs {
 impl EventEmitter<ToolbarItemEvent> for Breadcrumbs {}
 
 impl Render for Breadcrumbs {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         const MAX_SEGMENTS: usize = 12;
 
         let element = h_flex()
@@ -72,7 +72,7 @@ impl Render for Breadcrumbs {
         }
 
         let highlighted_segments = segments.into_iter().map(|segment| {
-            let mut text_style = cx.text_style();
+            let mut text_style = window.text_style();
             if let Some(font) = segment.font {
                 text_style.font_family = font.family;
                 text_style.font_features = font.features;
@@ -101,28 +101,30 @@ impl Render for Breadcrumbs {
                     .style(ButtonStyle::Transparent)
                     .on_click({
                         let editor = editor.clone();
-                        move |_, cx| {
+                        move |_, window, cx| {
                             if let Some((editor, callback)) = editor
                                 .upgrade()
                                 .zip(zed_actions::outline::TOGGLE_OUTLINE.get())
                             {
-                                callback(editor.to_any(), cx);
+                                callback(editor.to_any(), window, cx);
                             }
                         }
                     })
-                    .tooltip(move |cx| {
+                    .tooltip(move |window, cx| {
                         if let Some(editor) = editor.upgrade() {
                             let focus_handle = editor.read(cx).focus_handle(cx);
                             Tooltip::for_action_in(
                                 "Show Symbol Outline",
                                 &zed_actions::outline::ToggleOutline,
                                 &focus_handle,
+                                window,
                                 cx,
                             )
                         } else {
                             Tooltip::for_action(
                                 "Show Symbol Outline",
                                 &zed_actions::outline::ToggleOutline,
+                                window,
                                 cx,
                             )
                         }
@@ -140,7 +142,8 @@ impl ToolbarItemView for Breadcrumbs {
     fn set_active_pane_item(
         &mut self,
         active_pane_item: Option<&dyn ItemHandle>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> ToolbarItemLocation {
         cx.notify();
         self.active_item = None;
@@ -149,10 +152,11 @@ impl ToolbarItemView for Breadcrumbs {
             return ToolbarItemLocation::Hidden;
         };
 
-        let this = cx.view().downgrade();
+        let this = cx.model().downgrade();
         self.subscription = Some(item.subscribe_to_item_events(
+            window,
             cx,
-            Box::new(move |event, cx| {
+            Box::new(move |event, _, cx| {
                 if let ItemEvent::UpdateBreadcrumbs = event {
                     this.update(cx, |this, cx| {
                         cx.notify();
@@ -170,7 +174,12 @@ impl ToolbarItemView for Breadcrumbs {
         item.breadcrumb_location(cx)
     }
 
-    fn pane_focus_update(&mut self, pane_focused: bool, _: &mut ViewContext<Self>) {
+    fn pane_focus_update(
+        &mut self,
+        pane_focused: bool,
+        _window: &mut Window,
+        _: &mut Context<Self>,
+    ) {
         self.pane_focused = pane_focused;
     }
 }

crates/call/src/call_settings.rs 🔗

@@ -1,5 +1,5 @@
 use anyhow::Result;
-use gpui::AppContext;
+use gpui::App;
 use schemars::JsonSchema;
 use serde_derive::{Deserialize, Serialize};
 use settings::{Settings, SettingsSources};
@@ -29,7 +29,7 @@ impl Settings for CallSettings {
 
     type FileContent = CallSettingsContent;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
         sources.json_merge()
     }
 }

crates/call/src/cross_platform/mod.rs 🔗

@@ -8,8 +8,8 @@ use client::{proto, ChannelId, Client, TypedEnvelope, User, UserStore, ZED_ALWAY
 use collections::HashSet;
 use futures::{channel::oneshot, future::Shared, Future, FutureExt};
 use gpui::{
-    AppContext, AsyncAppContext, Context, EventEmitter, Global, Model, ModelContext, Subscription,
-    Task, WeakModel,
+    App, AppContext as _, AsyncAppContext, Context, Entity, EventEmitter, Global, Subscription,
+    Task, WeakEntity,
 };
 use postage::watch;
 use project::Project;
@@ -23,18 +23,18 @@ pub use livekit_client::{
 pub use participant::ParticipantLocation;
 pub use room::Room;
 
-struct GlobalActiveCall(Model<ActiveCall>);
+struct GlobalActiveCall(Entity<ActiveCall>);
 
 impl Global for GlobalActiveCall {}
 
-pub fn init(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut AppContext) {
+pub fn init(client: Arc<Client>, user_store: Entity<UserStore>, cx: &mut App) {
     livekit_client::init(
         cx.background_executor().dispatcher.clone(),
         cx.http_client(),
     );
     CallSettings::register(cx);
 
-    let active_call = cx.new_model(|cx| ActiveCall::new(client, user_store, cx));
+    let active_call = cx.new(|cx| ActiveCall::new(client, user_store, cx));
     cx.set_global(GlobalActiveCall(active_call));
 }
 
@@ -46,7 +46,7 @@ impl OneAtATime {
     /// spawn a task in the given context.
     /// if another task is spawned before that resolves, or if the OneAtATime itself is dropped, the first task will be cancelled and return Ok(None)
     /// otherwise you'll see the result of the task.
-    fn spawn<F, Fut, R>(&mut self, cx: &mut AppContext, f: F) -> Task<Result<Option<R>>>
+    fn spawn<F, Fut, R>(&mut self, cx: &mut App, f: F) -> Task<Result<Option<R>>>
     where
         F: 'static + FnOnce(AsyncAppContext) -> Fut,
         Fut: Future<Output = Result<R>>,
@@ -79,9 +79,9 @@ pub struct IncomingCall {
 
 /// Singleton global maintaining the user's participation in a room across workspaces.
 pub struct ActiveCall {
-    room: Option<(Model<Room>, Vec<Subscription>)>,
-    pending_room_creation: Option<Shared<Task<Result<Model<Room>, Arc<anyhow::Error>>>>>,
-    location: Option<WeakModel<Project>>,
+    room: Option<(Entity<Room>, Vec<Subscription>)>,
+    pending_room_creation: Option<Shared<Task<Result<Entity<Room>, Arc<anyhow::Error>>>>>,
+    location: Option<WeakEntity<Project>>,
     _join_debouncer: OneAtATime,
     pending_invites: HashSet<u64>,
     incoming_call: (
@@ -89,14 +89,14 @@ pub struct ActiveCall {
         watch::Receiver<Option<IncomingCall>>,
     ),
     client: Arc<Client>,
-    user_store: Model<UserStore>,
+    user_store: Entity<UserStore>,
     _subscriptions: Vec<client::Subscription>,
 }
 
 impl EventEmitter<Event> for ActiveCall {}
 
 impl ActiveCall {
-    fn new(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut ModelContext<Self>) -> Self {
+    fn new(client: Arc<Client>, user_store: Entity<UserStore>, cx: &mut Context<Self>) -> Self {
         Self {
             room: None,
             pending_room_creation: None,
@@ -113,12 +113,12 @@ impl ActiveCall {
         }
     }
 
-    pub fn channel_id(&self, cx: &AppContext) -> Option<ChannelId> {
+    pub fn channel_id(&self, cx: &App) -> Option<ChannelId> {
         self.room()?.read(cx).channel_id()
     }
 
     async fn handle_incoming_call(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::IncomingCall>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::Ack> {
@@ -145,7 +145,7 @@ impl ActiveCall {
     }
 
     async fn handle_call_canceled(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::CallCanceled>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -161,11 +161,11 @@ impl ActiveCall {
         Ok(())
     }
 
-    pub fn global(cx: &AppContext) -> Model<Self> {
+    pub fn global(cx: &App) -> Entity<Self> {
         cx.global::<GlobalActiveCall>().0.clone()
     }
 
-    pub fn try_global(cx: &AppContext) -> Option<Model<Self>> {
+    pub fn try_global(cx: &App) -> Option<Entity<Self>> {
         cx.try_global::<GlobalActiveCall>()
             .map(|call| call.0.clone())
     }
@@ -173,8 +173,8 @@ impl ActiveCall {
     pub fn invite(
         &mut self,
         called_user_id: u64,
-        initial_project: Option<Model<Project>>,
-        cx: &mut ModelContext<Self>,
+        initial_project: Option<Entity<Project>>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         if !self.pending_invites.insert(called_user_id) {
             return Task::ready(Err(anyhow!("user was already invited")));
@@ -269,7 +269,7 @@ impl ActiveCall {
     pub fn cancel_invite(
         &mut self,
         called_user_id: u64,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let room_id = if let Some(room) = self.room() {
             room.read(cx).id()
@@ -293,7 +293,7 @@ impl ActiveCall {
         self.incoming_call.1.clone()
     }
 
-    pub fn accept_incoming(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    pub fn accept_incoming(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         if self.room.is_some() {
             return Task::ready(Err(anyhow!("cannot join while on another call")));
         }
@@ -326,7 +326,7 @@ impl ActiveCall {
         })
     }
 
-    pub fn decline_incoming(&mut self, _: &mut ModelContext<Self>) -> Result<()> {
+    pub fn decline_incoming(&mut self, _: &mut Context<Self>) -> Result<()> {
         let call = self
             .incoming_call
             .0
@@ -343,8 +343,8 @@ impl ActiveCall {
     pub fn join_channel(
         &mut self,
         channel_id: ChannelId,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Option<Model<Room>>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Option<Entity<Room>>>> {
         if let Some(room) = self.room().cloned() {
             if room.read(cx).channel_id() == Some(channel_id) {
                 return Task::ready(Ok(Some(room)));
@@ -374,7 +374,7 @@ impl ActiveCall {
         })
     }
 
-    pub fn hang_up(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    pub fn hang_up(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         cx.notify();
         self.report_call_event("Call Ended", cx);
 
@@ -391,8 +391,8 @@ impl ActiveCall {
 
     pub fn share_project(
         &mut self,
-        project: Model<Project>,
-        cx: &mut ModelContext<Self>,
+        project: Entity<Project>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<u64>> {
         if let Some((room, _)) = self.room.as_ref() {
             self.report_call_event("Project Shared", cx);
@@ -404,8 +404,8 @@ impl ActiveCall {
 
     pub fn unshare_project(
         &mut self,
-        project: Model<Project>,
-        cx: &mut ModelContext<Self>,
+        project: Entity<Project>,
+        cx: &mut Context<Self>,
     ) -> Result<()> {
         if let Some((room, _)) = self.room.as_ref() {
             self.report_call_event("Project Unshared", cx);
@@ -415,14 +415,14 @@ impl ActiveCall {
         }
     }
 
-    pub fn location(&self) -> Option<&WeakModel<Project>> {
+    pub fn location(&self) -> Option<&WeakEntity<Project>> {
         self.location.as_ref()
     }
 
     pub fn set_location(
         &mut self,
-        project: Option<&Model<Project>>,
-        cx: &mut ModelContext<Self>,
+        project: Option<&Entity<Project>>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         if project.is_some() || !*ZED_ALWAYS_ACTIVE {
             self.location = project.map(|project| project.downgrade());
@@ -433,11 +433,7 @@ impl ActiveCall {
         Task::ready(Ok(()))
     }
 
-    fn set_room(
-        &mut self,
-        room: Option<Model<Room>>,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<()>> {
+    fn set_room(&mut self, room: Option<Entity<Room>>, cx: &mut Context<Self>) -> Task<Result<()>> {
         if room.as_ref() == self.room.as_ref().map(|room| &room.0) {
             Task::ready(Ok(()))
         } else {
@@ -473,7 +469,7 @@ impl ActiveCall {
         }
     }
 
-    pub fn room(&self) -> Option<&Model<Room>> {
+    pub fn room(&self) -> Option<&Entity<Room>> {
         self.room.as_ref().map(|(room, _)| room)
     }
 
@@ -485,7 +481,7 @@ impl ActiveCall {
         &self.pending_invites
     }
 
-    pub fn report_call_event(&self, operation: &'static str, cx: &mut AppContext) {
+    pub fn report_call_event(&self, operation: &'static str, cx: &mut App) {
         if let Some(room) = self.room() {
             let room = room.read(cx);
             telemetry::event!(

crates/call/src/cross_platform/participant.rs 🔗

@@ -1,7 +1,7 @@
 use anyhow::{anyhow, Result};
 use client::{proto, ParticipantIndex, User};
 use collections::HashMap;
-use gpui::WeakModel;
+use gpui::WeakEntity;
 use livekit_client::AudioStream;
 use project::Project;
 use std::sync::Arc;
@@ -36,7 +36,7 @@ impl ParticipantLocation {
 #[derive(Clone, Default)]
 pub struct LocalParticipant {
     pub projects: Vec<proto::ParticipantProject>,
-    pub active_project: Option<WeakModel<Project>>,
+    pub active_project: Option<WeakEntity<Project>>,
     pub role: proto::ChannelRole,
 }
 

crates/call/src/cross_platform/room.rs 🔗

@@ -11,9 +11,7 @@ use client::{
 use collections::{BTreeMap, HashMap, HashSet};
 use fs::Fs;
 use futures::{FutureExt, StreamExt};
-use gpui::{
-    AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task, WeakModel,
-};
+use gpui::{App, AppContext, AsyncAppContext, Context, Entity, EventEmitter, Task, WeakEntity};
 use language::LanguageRegistry;
 use livekit::{
     capture_local_audio_track, capture_local_video_track,
@@ -71,8 +69,8 @@ pub struct Room {
     channel_id: Option<ChannelId>,
     live_kit: Option<LiveKitRoom>,
     status: RoomStatus,
-    shared_projects: HashSet<WeakModel<Project>>,
-    joined_projects: HashSet<WeakModel<Project>>,
+    shared_projects: HashSet<WeakEntity<Project>>,
+    joined_projects: HashSet<WeakEntity<Project>>,
     local_participant: LocalParticipant,
     remote_participants: BTreeMap<u64, RemoteParticipant>,
     pending_participants: Vec<Arc<User>>,
@@ -80,7 +78,7 @@ pub struct Room {
     pending_call_count: usize,
     leave_when_empty: bool,
     client: Arc<Client>,
-    user_store: Model<UserStore>,
+    user_store: Entity<UserStore>,
     follows_by_leader_id_project_id: HashMap<(PeerId, u64), Vec<PeerId>>,
     client_subscriptions: Vec<client::Subscription>,
     _subscriptions: Vec<gpui::Subscription>,
@@ -115,8 +113,8 @@ impl Room {
         channel_id: Option<ChannelId>,
         livekit_connection_info: Option<proto::LiveKitConnectionInfo>,
         client: Arc<Client>,
-        user_store: Model<UserStore>,
-        cx: &mut ModelContext<Self>,
+        user_store: Entity<UserStore>,
+        cx: &mut Context<Self>,
     ) -> Self {
         spawn_room_connection(livekit_connection_info, cx);
 
@@ -161,15 +159,15 @@ impl Room {
 
     pub(crate) fn create(
         called_user_id: u64,
-        initial_project: Option<Model<Project>>,
+        initial_project: Option<Entity<Project>>,
         client: Arc<Client>,
-        user_store: Model<UserStore>,
-        cx: &mut AppContext,
-    ) -> Task<Result<Model<Self>>> {
+        user_store: Entity<UserStore>,
+        cx: &mut App,
+    ) -> Task<Result<Entity<Self>>> {
         cx.spawn(move |mut cx| async move {
             let response = client.request(proto::CreateRoom {}).await?;
             let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
-            let room = cx.new_model(|cx| {
+            let room = cx.new(|cx| {
                 let mut room = Self::new(
                     room_proto.id,
                     None,
@@ -211,9 +209,9 @@ impl Room {
     pub(crate) async fn join_channel(
         channel_id: ChannelId,
         client: Arc<Client>,
-        user_store: Model<UserStore>,
+        user_store: Entity<UserStore>,
         cx: AsyncAppContext,
-    ) -> Result<Model<Self>> {
+    ) -> Result<Entity<Self>> {
         Self::from_join_response(
             client
                 .request(proto::JoinChannel {
@@ -229,9 +227,9 @@ impl Room {
     pub(crate) async fn join(
         room_id: u64,
         client: Arc<Client>,
-        user_store: Model<UserStore>,
+        user_store: Entity<UserStore>,
         cx: AsyncAppContext,
-    ) -> Result<Model<Self>> {
+    ) -> Result<Entity<Self>> {
         Self::from_join_response(
             client.request(proto::JoinRoom { id: room_id }).await?,
             client,
@@ -240,13 +238,13 @@ impl Room {
         )
     }
 
-    fn released(&mut self, cx: &mut AppContext) {
+    fn released(&mut self, cx: &mut App) {
         if self.status.is_online() {
             self.leave_internal(cx).detach_and_log_err(cx);
         }
     }
 
-    fn app_will_quit(&mut self, cx: &mut ModelContext<Self>) -> impl Future<Output = ()> {
+    fn app_will_quit(&mut self, cx: &mut Context<Self>) -> impl Future<Output = ()> {
         let task = if self.status.is_online() {
             let leave = self.leave_internal(cx);
             Some(cx.background_executor().spawn(async move {
@@ -263,18 +261,18 @@ impl Room {
         }
     }
 
-    pub fn mute_on_join(cx: &AppContext) -> bool {
+    pub fn mute_on_join(cx: &App) -> bool {
         CallSettings::get_global(cx).mute_on_join || client::IMPERSONATE_LOGIN.is_some()
     }
 
     fn from_join_response(
         response: proto::JoinRoomResponse,
         client: Arc<Client>,
-        user_store: Model<UserStore>,
+        user_store: Entity<UserStore>,
         mut cx: AsyncAppContext,
-    ) -> Result<Model<Self>> {
+    ) -> Result<Entity<Self>> {
         let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
-        let room = cx.new_model(|cx| {
+        let room = cx.new(|cx| {
             Self::new(
                 room_proto.id,
                 response.channel_id.map(ChannelId),
@@ -300,12 +298,12 @@ impl Room {
             && self.pending_call_count == 0
     }
 
-    pub(crate) fn leave(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    pub(crate) fn leave(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         cx.notify();
         self.leave_internal(cx)
     }
 
-    fn leave_internal(&mut self, cx: &mut AppContext) -> Task<Result<()>> {
+    fn leave_internal(&mut self, cx: &mut App) -> Task<Result<()>> {
         if self.status.is_offline() {
             return Task::ready(Err(anyhow!("room is offline")));
         }
@@ -322,7 +320,7 @@ impl Room {
         })
     }
 
-    pub(crate) fn clear_state(&mut self, cx: &mut AppContext) {
+    pub(crate) fn clear_state(&mut self, cx: &mut App) {
         for project in self.shared_projects.drain() {
             if let Some(project) = project.upgrade() {
                 project.update(cx, |project, cx| {
@@ -350,7 +348,7 @@ impl Room {
     }
 
     async fn maintain_connection(
-        this: WeakModel<Self>,
+        this: WeakEntity<Self>,
         client: Arc<Client>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -436,7 +434,7 @@ impl Room {
         ))
     }
 
-    fn rejoin(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn rejoin(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         let mut projects = HashMap::default();
         let mut reshared_projects = Vec::new();
         let mut rejoined_projects = Vec::new();
@@ -562,7 +560,7 @@ impl Room {
         &mut self,
         user_id: u64,
         role: proto::ChannelRole,
-        cx: &ModelContext<Self>,
+        cx: &Context<Self>,
     ) -> Task<Result<()>> {
         let client = self.client.clone();
         let room_id = self.id;
@@ -594,7 +592,7 @@ impl Room {
     }
 
     /// Returns the most 'active' projects, defined as most people in the project
-    pub fn most_active_project(&self, cx: &AppContext) -> Option<(u64, u64)> {
+    pub fn most_active_project(&self, cx: &App) -> Option<(u64, u64)> {
         let mut project_hosts_and_guest_counts = HashMap::<u64, (Option<u64>, u32)>::default();
         for participant in self.remote_participants.values() {
             match participant.location {
@@ -631,7 +629,7 @@ impl Room {
     }
 
     async fn handle_room_updated(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::RoomUpdated>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -642,7 +640,7 @@ impl Room {
         this.update(&mut cx, |this, cx| this.apply_room_update(room, cx))?
     }
 
-    fn apply_room_update(&mut self, room: proto::Room, cx: &mut ModelContext<Self>) -> Result<()> {
+    fn apply_room_update(&mut self, room: proto::Room, cx: &mut Context<Self>) -> Result<()> {
         log::trace!(
             "client {:?}. room update: {:?}",
             self.client.user_id(),
@@ -666,11 +664,7 @@ impl Room {
         }
     }
 
-    fn start_room_connection(
-        &self,
-        mut room: proto::Room,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<()> {
+    fn start_room_connection(&self, mut room: proto::Room, cx: &mut Context<Self>) -> Task<()> {
         // Filter ourselves out from the room's participants.
         let local_participant_ix = room
             .participants
@@ -916,11 +910,7 @@ impl Room {
         })
     }
 
-    fn livekit_room_updated(
-        &mut self,
-        event: RoomEvent,
-        cx: &mut ModelContext<Self>,
-    ) -> Result<()> {
+    fn livekit_room_updated(&mut self, event: RoomEvent, cx: &mut Context<Self>) -> Result<()> {
         log::trace!(
             "client {:?}. livekit event: {:?}",
             self.client.user_id(),
@@ -1090,7 +1080,7 @@ impl Room {
         &mut self,
         called_user_id: u64,
         initial_project_id: Option<u64>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         if self.status.is_offline() {
             return Task::ready(Err(anyhow!("room is offline")));
@@ -1124,8 +1114,8 @@ impl Room {
         id: u64,
         language_registry: Arc<LanguageRegistry>,
         fs: Arc<dyn Fs>,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<Project>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<Project>>> {
         let client = self.client.clone();
         let user_store = self.user_store.clone();
         cx.emit(Event::RemoteProjectJoined { project_id: id });
@@ -1149,8 +1139,8 @@ impl Room {
 
     pub fn share_project(
         &mut self,
-        project: Model<Project>,
-        cx: &mut ModelContext<Self>,
+        project: Entity<Project>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<u64>> {
         if let Some(project_id) = project.read(cx).remote_id() {
             return Task::ready(Ok(project_id));
@@ -1187,8 +1177,8 @@ impl Room {
 
     pub(crate) fn unshare_project(
         &mut self,
-        project: Model<Project>,
-        cx: &mut ModelContext<Self>,
+        project: Entity<Project>,
+        cx: &mut Context<Self>,
     ) -> Result<()> {
         let project_id = match project.read(cx).remote_id() {
             Some(project_id) => project_id,
@@ -1206,8 +1196,8 @@ impl Room {
 
     pub(crate) fn set_location(
         &mut self,
-        project: Option<&Model<Project>>,
-        cx: &mut ModelContext<Self>,
+        project: Option<&Entity<Project>>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         if self.status.is_offline() {
             return Task::ready(Err(anyhow!("room is offline")));
@@ -1299,7 +1289,7 @@ impl Room {
     }
 
     #[track_caller]
-    pub fn share_microphone(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    pub fn share_microphone(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         if self.status.is_offline() {
             return Task::ready(Err(anyhow!("room is offline")));
         }
@@ -1375,7 +1365,7 @@ impl Room {
         })
     }
 
-    pub fn share_screen(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    pub fn share_screen(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         if self.status.is_offline() {
             return Task::ready(Err(anyhow!("room is offline")));
         }
@@ -1460,7 +1450,7 @@ impl Room {
         })
     }
 
-    pub fn toggle_mute(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn toggle_mute(&mut self, cx: &mut Context<Self>) {
         if let Some(live_kit) = self.live_kit.as_mut() {
             // When unmuting, undeafen if the user was deafened before.
             let was_deafened = live_kit.deafened;
@@ -1486,7 +1476,7 @@ impl Room {
         }
     }
 
-    pub fn toggle_deafen(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn toggle_deafen(&mut self, cx: &mut Context<Self>) {
         if let Some(live_kit) = self.live_kit.as_mut() {
             // When deafening, mute the microphone if it was not already muted.
             // When un-deafening, unmute the microphone, unless it was explicitly muted.
@@ -1504,7 +1494,7 @@ impl Room {
         }
     }
 
-    pub fn unshare_screen(&mut self, cx: &mut ModelContext<Self>) -> Result<()> {
+    pub fn unshare_screen(&mut self, cx: &mut Context<Self>) -> Result<()> {
         if self.status.is_offline() {
             return Err(anyhow!("room is offline"));
         }
@@ -1535,7 +1525,7 @@ impl Room {
         }
     }
 
-    fn set_deafened(&mut self, deafened: bool, cx: &mut ModelContext<Self>) -> Option<()> {
+    fn set_deafened(&mut self, deafened: bool, cx: &mut Context<Self>) -> Option<()> {
         let live_kit = self.live_kit.as_mut()?;
         cx.notify();
         for (_, participant) in live_kit.room.remote_participants() {
@@ -1549,11 +1539,7 @@ impl Room {
         None
     }
 
-    fn set_mute(
-        &mut self,
-        should_mute: bool,
-        cx: &mut ModelContext<Room>,
-    ) -> Option<Task<Result<()>>> {
+    fn set_mute(&mut self, should_mute: bool, cx: &mut Context<Room>) -> Option<Task<Result<()>>> {
         let live_kit = self.live_kit.as_mut()?;
         cx.notify();
 
@@ -1589,7 +1575,7 @@ impl Room {
 
 fn spawn_room_connection(
     livekit_connection_info: Option<proto::LiveKitConnectionInfo>,
-    cx: &mut ModelContext<'_, Room>,
+    cx: &mut Context<'_, Room>,
 ) {
     if let Some(connection_info) = livekit_connection_info {
         cx.spawn(|this, mut cx| async move {
@@ -1651,7 +1637,7 @@ struct LiveKitRoom {
 }
 
 impl LiveKitRoom {
-    fn stop_publishing(&mut self, cx: &mut ModelContext<Room>) {
+    fn stop_publishing(&mut self, cx: &mut Context<Room>) {
         let mut tracks_to_unpublish = Vec::new();
         if let LocalTrack::Published {
             track_publication, ..

crates/call/src/macos/mod.rs 🔗

@@ -8,8 +8,8 @@ use client::{proto, ChannelId, Client, TypedEnvelope, User, UserStore, ZED_ALWAY
 use collections::HashSet;
 use futures::{channel::oneshot, future::Shared, Future, FutureExt};
 use gpui::{
-    AppContext, AsyncAppContext, Context, EventEmitter, Global, Model, ModelContext, Subscription,
-    Task, WeakModel,
+    App, AppContext as _, AsyncAppContext, Context, Entity, EventEmitter, Global, Subscription,
+    Task, WeakEntity,
 };
 use postage::watch;
 use project::Project;
@@ -20,14 +20,14 @@ use std::sync::Arc;
 pub use participant::ParticipantLocation;
 pub use room::Room;
 
-struct GlobalActiveCall(Model<ActiveCall>);
+struct GlobalActiveCall(Entity<ActiveCall>);
 
 impl Global for GlobalActiveCall {}
 
-pub fn init(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut AppContext) {
+pub fn init(client: Arc<Client>, user_store: Entity<UserStore>, cx: &mut App) {
     CallSettings::register(cx);
 
-    let active_call = cx.new_model(|cx| ActiveCall::new(client, user_store, cx));
+    let active_call = cx.new(|cx| ActiveCall::new(client, user_store, cx));
     cx.set_global(GlobalActiveCall(active_call));
 }
 
@@ -39,7 +39,7 @@ impl OneAtATime {
     /// spawn a task in the given context.
     /// if another task is spawned before that resolves, or if the OneAtATime itself is dropped, the first task will be cancelled and return Ok(None)
     /// otherwise you'll see the result of the task.
-    fn spawn<F, Fut, R>(&mut self, cx: &mut AppContext, f: F) -> Task<Result<Option<R>>>
+    fn spawn<F, Fut, R>(&mut self, cx: &mut App, f: F) -> Task<Result<Option<R>>>
     where
         F: 'static + FnOnce(AsyncAppContext) -> Fut,
         Fut: Future<Output = Result<R>>,
@@ -72,9 +72,9 @@ pub struct IncomingCall {
 
 /// Singleton global maintaining the user's participation in a room across workspaces.
 pub struct ActiveCall {
-    room: Option<(Model<Room>, Vec<Subscription>)>,
-    pending_room_creation: Option<Shared<Task<Result<Model<Room>, Arc<anyhow::Error>>>>>,
-    location: Option<WeakModel<Project>>,
+    room: Option<(Entity<Room>, Vec<Subscription>)>,
+    pending_room_creation: Option<Shared<Task<Result<Entity<Room>, Arc<anyhow::Error>>>>>,
+    location: Option<WeakEntity<Project>>,
     _join_debouncer: OneAtATime,
     pending_invites: HashSet<u64>,
     incoming_call: (
@@ -82,14 +82,14 @@ pub struct ActiveCall {
         watch::Receiver<Option<IncomingCall>>,
     ),
     client: Arc<Client>,
-    user_store: Model<UserStore>,
+    user_store: Entity<UserStore>,
     _subscriptions: Vec<client::Subscription>,
 }
 
 impl EventEmitter<Event> for ActiveCall {}
 
 impl ActiveCall {
-    fn new(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut ModelContext<Self>) -> Self {
+    fn new(client: Arc<Client>, user_store: Entity<UserStore>, cx: &mut Context<Self>) -> Self {
         Self {
             room: None,
             pending_room_creation: None,
@@ -106,12 +106,12 @@ impl ActiveCall {
         }
     }
 
-    pub fn channel_id(&self, cx: &AppContext) -> Option<ChannelId> {
+    pub fn channel_id(&self, cx: &App) -> Option<ChannelId> {
         self.room()?.read(cx).channel_id()
     }
 
     async fn handle_incoming_call(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::IncomingCall>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::Ack> {
@@ -138,7 +138,7 @@ impl ActiveCall {
     }
 
     async fn handle_call_canceled(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::CallCanceled>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -154,11 +154,11 @@ impl ActiveCall {
         Ok(())
     }
 
-    pub fn global(cx: &AppContext) -> Model<Self> {
+    pub fn global(cx: &App) -> Entity<Self> {
         cx.global::<GlobalActiveCall>().0.clone()
     }
 
-    pub fn try_global(cx: &AppContext) -> Option<Model<Self>> {
+    pub fn try_global(cx: &App) -> Option<Entity<Self>> {
         cx.try_global::<GlobalActiveCall>()
             .map(|call| call.0.clone())
     }
@@ -166,8 +166,8 @@ impl ActiveCall {
     pub fn invite(
         &mut self,
         called_user_id: u64,
-        initial_project: Option<Model<Project>>,
-        cx: &mut ModelContext<Self>,
+        initial_project: Option<Entity<Project>>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         if !self.pending_invites.insert(called_user_id) {
             return Task::ready(Err(anyhow!("user was already invited")));
@@ -262,7 +262,7 @@ impl ActiveCall {
     pub fn cancel_invite(
         &mut self,
         called_user_id: u64,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let room_id = if let Some(room) = self.room() {
             room.read(cx).id()
@@ -286,7 +286,7 @@ impl ActiveCall {
         self.incoming_call.1.clone()
     }
 
-    pub fn accept_incoming(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    pub fn accept_incoming(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         if self.room.is_some() {
             return Task::ready(Err(anyhow!("cannot join while on another call")));
         }
@@ -319,7 +319,7 @@ impl ActiveCall {
         })
     }
 
-    pub fn decline_incoming(&mut self, _: &mut ModelContext<Self>) -> Result<()> {
+    pub fn decline_incoming(&mut self, _: &mut Context<Self>) -> Result<()> {
         let call = self
             .incoming_call
             .0
@@ -336,8 +336,8 @@ impl ActiveCall {
     pub fn join_channel(
         &mut self,
         channel_id: ChannelId,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Option<Model<Room>>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Option<Entity<Room>>>> {
         if let Some(room) = self.room().cloned() {
             if room.read(cx).channel_id() == Some(channel_id) {
                 return Task::ready(Ok(Some(room)));
@@ -367,7 +367,7 @@ impl ActiveCall {
         })
     }
 
-    pub fn hang_up(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    pub fn hang_up(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         cx.notify();
         self.report_call_event("Call Ended", cx);
 
@@ -384,8 +384,8 @@ impl ActiveCall {
 
     pub fn share_project(
         &mut self,
-        project: Model<Project>,
-        cx: &mut ModelContext<Self>,
+        project: Entity<Project>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<u64>> {
         if let Some((room, _)) = self.room.as_ref() {
             self.report_call_event("Project Shared", cx);
@@ -397,8 +397,8 @@ impl ActiveCall {
 
     pub fn unshare_project(
         &mut self,
-        project: Model<Project>,
-        cx: &mut ModelContext<Self>,
+        project: Entity<Project>,
+        cx: &mut Context<Self>,
     ) -> Result<()> {
         if let Some((room, _)) = self.room.as_ref() {
             self.report_call_event("Project Unshared", cx);
@@ -408,14 +408,14 @@ impl ActiveCall {
         }
     }
 
-    pub fn location(&self) -> Option<&WeakModel<Project>> {
+    pub fn location(&self) -> Option<&WeakEntity<Project>> {
         self.location.as_ref()
     }
 
     pub fn set_location(
         &mut self,
-        project: Option<&Model<Project>>,
-        cx: &mut ModelContext<Self>,
+        project: Option<&Entity<Project>>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         if project.is_some() || !*ZED_ALWAYS_ACTIVE {
             self.location = project.map(|project| project.downgrade());
@@ -426,11 +426,7 @@ impl ActiveCall {
         Task::ready(Ok(()))
     }
 
-    fn set_room(
-        &mut self,
-        room: Option<Model<Room>>,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<()>> {
+    fn set_room(&mut self, room: Option<Entity<Room>>, cx: &mut Context<Self>) -> Task<Result<()>> {
         if room.as_ref() == self.room.as_ref().map(|room| &room.0) {
             Task::ready(Ok(()))
         } else {
@@ -466,7 +462,7 @@ impl ActiveCall {
         }
     }
 
-    pub fn room(&self) -> Option<&Model<Room>> {
+    pub fn room(&self) -> Option<&Entity<Room>> {
         self.room.as_ref().map(|(room, _)| room)
     }
 
@@ -478,7 +474,7 @@ impl ActiveCall {
         &self.pending_invites
     }
 
-    pub fn report_call_event(&self, operation: &'static str, cx: &mut AppContext) {
+    pub fn report_call_event(&self, operation: &'static str, cx: &mut App) {
         if let Some(room) = self.room() {
             let room = room.read(cx);
             telemetry::event!(

crates/call/src/macos/participant.rs 🔗

@@ -2,7 +2,7 @@ use anyhow::{anyhow, Result};
 use client::ParticipantIndex;
 use client::{proto, User};
 use collections::HashMap;
-use gpui::WeakModel;
+use gpui::WeakEntity;
 pub use livekit_client_macos::Frame;
 pub use livekit_client_macos::{RemoteAudioTrack, RemoteVideoTrack};
 use project::Project;
@@ -35,7 +35,7 @@ impl ParticipantLocation {
 #[derive(Clone, Default)]
 pub struct LocalParticipant {
     pub projects: Vec<proto::ParticipantProject>,
-    pub active_project: Option<WeakModel<Project>>,
+    pub active_project: Option<WeakEntity<Project>>,
     pub role: proto::ChannelRole,
 }
 

crates/call/src/macos/room.rs 🔗

@@ -12,7 +12,7 @@ use collections::{BTreeMap, HashMap, HashSet};
 use fs::Fs;
 use futures::{FutureExt, StreamExt};
 use gpui::{
-    AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task, WeakModel,
+    App, AppContext as _, AsyncAppContext, Context, Entity, EventEmitter, Task, WeakEntity,
 };
 use language::LanguageRegistry;
 use livekit_client_macos::{LocalAudioTrack, LocalTrackPublication, LocalVideoTrack, RoomUpdate};
@@ -62,8 +62,8 @@ pub struct Room {
     channel_id: Option<ChannelId>,
     live_kit: Option<LiveKitRoom>,
     status: RoomStatus,
-    shared_projects: HashSet<WeakModel<Project>>,
-    joined_projects: HashSet<WeakModel<Project>>,
+    shared_projects: HashSet<WeakEntity<Project>>,
+    joined_projects: HashSet<WeakEntity<Project>>,
     local_participant: LocalParticipant,
     remote_participants: BTreeMap<u64, RemoteParticipant>,
     pending_participants: Vec<Arc<User>>,
@@ -71,7 +71,7 @@ pub struct Room {
     pending_call_count: usize,
     leave_when_empty: bool,
     client: Arc<Client>,
-    user_store: Model<UserStore>,
+    user_store: Entity<UserStore>,
     follows_by_leader_id_project_id: HashMap<(PeerId, u64), Vec<PeerId>>,
     client_subscriptions: Vec<client::Subscription>,
     _subscriptions: Vec<gpui::Subscription>,
@@ -109,8 +109,8 @@ impl Room {
         channel_id: Option<ChannelId>,
         live_kit_connection_info: Option<proto::LiveKitConnectionInfo>,
         client: Arc<Client>,
-        user_store: Model<UserStore>,
-        cx: &mut ModelContext<Self>,
+        user_store: Entity<UserStore>,
+        cx: &mut Context<Self>,
     ) -> Self {
         let live_kit_room = if let Some(connection_info) = live_kit_connection_info {
             let room = livekit_client_macos::Room::new();
@@ -225,15 +225,15 @@ impl Room {
 
     pub(crate) fn create(
         called_user_id: u64,
-        initial_project: Option<Model<Project>>,
+        initial_project: Option<Entity<Project>>,
         client: Arc<Client>,
-        user_store: Model<UserStore>,
-        cx: &mut AppContext,
-    ) -> Task<Result<Model<Self>>> {
+        user_store: Entity<UserStore>,
+        cx: &mut App,
+    ) -> Task<Result<Entity<Self>>> {
         cx.spawn(move |mut cx| async move {
             let response = client.request(proto::CreateRoom {}).await?;
             let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
-            let room = cx.new_model(|cx| {
+            let room = cx.new(|cx| {
                 let mut room = Self::new(
                     room_proto.id,
                     None,
@@ -275,9 +275,9 @@ impl Room {
     pub(crate) async fn join_channel(
         channel_id: ChannelId,
         client: Arc<Client>,
-        user_store: Model<UserStore>,
+        user_store: Entity<UserStore>,
         cx: AsyncAppContext,
-    ) -> Result<Model<Self>> {
+    ) -> Result<Entity<Self>> {
         Self::from_join_response(
             client
                 .request(proto::JoinChannel {
@@ -293,9 +293,9 @@ impl Room {
     pub(crate) async fn join(
         room_id: u64,
         client: Arc<Client>,
-        user_store: Model<UserStore>,
+        user_store: Entity<UserStore>,
         cx: AsyncAppContext,
-    ) -> Result<Model<Self>> {
+    ) -> Result<Entity<Self>> {
         Self::from_join_response(
             client.request(proto::JoinRoom { id: room_id }).await?,
             client,
@@ -304,13 +304,13 @@ impl Room {
         )
     }
 
-    fn released(&mut self, cx: &mut AppContext) {
+    fn released(&mut self, cx: &mut App) {
         if self.status.is_online() {
             self.leave_internal(cx).detach_and_log_err(cx);
         }
     }
 
-    fn app_will_quit(&mut self, cx: &mut ModelContext<Self>) -> impl Future<Output = ()> {
+    fn app_will_quit(&mut self, cx: &mut Context<Self>) -> impl Future<Output = ()> {
         let task = if self.status.is_online() {
             let leave = self.leave_internal(cx);
             Some(cx.background_executor().spawn(async move {
@@ -327,18 +327,18 @@ impl Room {
         }
     }
 
-    pub fn mute_on_join(cx: &AppContext) -> bool {
+    pub fn mute_on_join(cx: &App) -> bool {
         CallSettings::get_global(cx).mute_on_join || client::IMPERSONATE_LOGIN.is_some()
     }
 
     fn from_join_response(
         response: proto::JoinRoomResponse,
         client: Arc<Client>,
-        user_store: Model<UserStore>,
+        user_store: Entity<UserStore>,
         mut cx: AsyncAppContext,
-    ) -> Result<Model<Self>> {
+    ) -> Result<Entity<Self>> {
         let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
-        let room = cx.new_model(|cx| {
+        let room = cx.new(|cx| {
             Self::new(
                 room_proto.id,
                 response.channel_id.map(ChannelId),
@@ -364,12 +364,12 @@ impl Room {
             && self.pending_call_count == 0
     }
 
-    pub(crate) fn leave(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    pub(crate) fn leave(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         cx.notify();
         self.leave_internal(cx)
     }
 
-    fn leave_internal(&mut self, cx: &mut AppContext) -> Task<Result<()>> {
+    fn leave_internal(&mut self, cx: &mut App) -> Task<Result<()>> {
         if self.status.is_offline() {
             return Task::ready(Err(anyhow!("room is offline")));
         }
@@ -386,7 +386,7 @@ impl Room {
         })
     }
 
-    pub(crate) fn clear_state(&mut self, cx: &mut AppContext) {
+    pub(crate) fn clear_state(&mut self, cx: &mut App) {
         for project in self.shared_projects.drain() {
             if let Some(project) = project.upgrade() {
                 project.update(cx, |project, cx| {
@@ -414,7 +414,7 @@ impl Room {
     }
 
     async fn maintain_connection(
-        this: WeakModel<Self>,
+        this: WeakEntity<Self>,
         client: Arc<Client>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -500,7 +500,7 @@ impl Room {
         ))
     }
 
-    fn rejoin(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn rejoin(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         let mut projects = HashMap::default();
         let mut reshared_projects = Vec::new();
         let mut rejoined_projects = Vec::new();
@@ -626,7 +626,7 @@ impl Room {
         &mut self,
         user_id: u64,
         role: proto::ChannelRole,
-        cx: &ModelContext<Self>,
+        cx: &Context<Self>,
     ) -> Task<Result<()>> {
         let client = self.client.clone();
         let room_id = self.id;
@@ -658,7 +658,7 @@ impl Room {
     }
 
     /// Returns the most 'active' projects, defined as most people in the project
-    pub fn most_active_project(&self, cx: &AppContext) -> Option<(u64, u64)> {
+    pub fn most_active_project(&self, cx: &App) -> Option<(u64, u64)> {
         let mut project_hosts_and_guest_counts = HashMap::<u64, (Option<u64>, u32)>::default();
         for participant in self.remote_participants.values() {
             match participant.location {
@@ -695,7 +695,7 @@ impl Room {
     }
 
     async fn handle_room_updated(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::RoomUpdated>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -706,11 +706,7 @@ impl Room {
         this.update(&mut cx, |this, cx| this.apply_room_update(room, cx))?
     }
 
-    fn apply_room_update(
-        &mut self,
-        mut room: proto::Room,
-        cx: &mut ModelContext<Self>,
-    ) -> Result<()> {
+    fn apply_room_update(&mut self, mut room: proto::Room, cx: &mut Context<Self>) -> Result<()> {
         // Filter ourselves out from the room's participants.
         let local_participant_ix = room
             .participants
@@ -976,11 +972,7 @@ impl Room {
         }
     }
 
-    fn live_kit_room_updated(
-        &mut self,
-        update: RoomUpdate,
-        cx: &mut ModelContext<Self>,
-    ) -> Result<()> {
+    fn live_kit_room_updated(&mut self, update: RoomUpdate, cx: &mut Context<Self>) -> Result<()> {
         match update {
             RoomUpdate::SubscribedToRemoteVideoTrack(track) => {
                 let user_id = track.publisher_id().parse()?;
@@ -1132,7 +1124,7 @@ impl Room {
         &mut self,
         called_user_id: u64,
         initial_project_id: Option<u64>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         if self.status.is_offline() {
             return Task::ready(Err(anyhow!("room is offline")));
@@ -1166,8 +1158,8 @@ impl Room {
         id: u64,
         language_registry: Arc<LanguageRegistry>,
         fs: Arc<dyn Fs>,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<Project>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<Project>>> {
         let client = self.client.clone();
         let user_store = self.user_store.clone();
         cx.emit(Event::RemoteProjectJoined { project_id: id });
@@ -1191,8 +1183,8 @@ impl Room {
 
     pub fn share_project(
         &mut self,
-        project: Model<Project>,
-        cx: &mut ModelContext<Self>,
+        project: Entity<Project>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<u64>> {
         if let Some(project_id) = project.read(cx).remote_id() {
             return Task::ready(Ok(project_id));
@@ -1229,8 +1221,8 @@ impl Room {
 
     pub(crate) fn unshare_project(
         &mut self,
-        project: Model<Project>,
-        cx: &mut ModelContext<Self>,
+        project: Entity<Project>,
+        cx: &mut Context<Self>,
     ) -> Result<()> {
         let project_id = match project.read(cx).remote_id() {
             Some(project_id) => project_id,
@@ -1248,8 +1240,8 @@ impl Room {
 
     pub(crate) fn set_location(
         &mut self,
-        project: Option<&Model<Project>>,
-        cx: &mut ModelContext<Self>,
+        project: Option<&Entity<Project>>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         if self.status.is_offline() {
             return Task::ready(Err(anyhow!("room is offline")));
@@ -1340,7 +1332,7 @@ impl Room {
     }
 
     #[track_caller]
-    pub fn share_microphone(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    pub fn share_microphone(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         if self.status.is_offline() {
             return Task::ready(Err(anyhow!("room is offline")));
         }
@@ -1416,7 +1408,7 @@ impl Room {
         })
     }
 
-    pub fn share_screen(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    pub fn share_screen(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         if self.status.is_offline() {
             return Task::ready(Err(anyhow!("room is offline")));
         } else if self.is_screen_sharing() {
@@ -1497,7 +1489,7 @@ impl Room {
         })
     }
 
-    pub fn toggle_mute(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn toggle_mute(&mut self, cx: &mut Context<Self>) {
         if let Some(live_kit) = self.live_kit.as_mut() {
             // When unmuting, undeafen if the user was deafened before.
             let was_deafened = live_kit.deafened;
@@ -1525,7 +1517,7 @@ impl Room {
         }
     }
 
-    pub fn toggle_deafen(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn toggle_deafen(&mut self, cx: &mut Context<Self>) {
         if let Some(live_kit) = self.live_kit.as_mut() {
             // When deafening, mute the microphone if it was not already muted.
             // When un-deafening, unmute the microphone, unless it was explicitly muted.
@@ -1545,7 +1537,7 @@ impl Room {
         }
     }
 
-    pub fn unshare_screen(&mut self, cx: &mut ModelContext<Self>) -> Result<()> {
+    pub fn unshare_screen(&mut self, cx: &mut Context<Self>) -> Result<()> {
         if self.status.is_offline() {
             return Err(anyhow!("room is offline"));
         }
@@ -1572,11 +1564,7 @@ impl Room {
         }
     }
 
-    fn set_deafened(
-        &mut self,
-        deafened: bool,
-        cx: &mut ModelContext<Self>,
-    ) -> Option<Task<Result<()>>> {
+    fn set_deafened(&mut self, deafened: bool, cx: &mut Context<Self>) -> Option<Task<Result<()>>> {
         let live_kit = self.live_kit.as_mut()?;
         cx.notify();
 
@@ -1606,11 +1594,7 @@ impl Room {
         }))
     }
 
-    fn set_mute(
-        &mut self,
-        should_mute: bool,
-        cx: &mut ModelContext<Room>,
-    ) -> Option<Task<Result<()>>> {
+    fn set_mute(&mut self, should_mute: bool, cx: &mut Context<Room>) -> Option<Task<Result<()>>> {
         let live_kit = self.live_kit.as_mut()?;
         cx.notify();
 
@@ -1660,7 +1644,7 @@ struct LiveKitRoom {
 }
 
 impl LiveKitRoom {
-    fn stop_publishing(&mut self, cx: &mut ModelContext<Room>) {
+    fn stop_publishing(&mut self, cx: &mut Context<Room>) {
         if let LocalTrack::Published {
             track_publication, ..
         } = mem::replace(&mut self.microphone_track, LocalTrack::None)

crates/channel/src/channel.rs 🔗

@@ -3,7 +3,7 @@ mod channel_chat;
 mod channel_store;
 
 use client::{Client, UserStore};
-use gpui::{AppContext, Model};
+use gpui::{App, Entity};
 use std::sync::Arc;
 
 pub use channel_buffer::{ChannelBuffer, ChannelBufferEvent, ACKNOWLEDGE_DEBOUNCE_INTERVAL};
@@ -16,7 +16,7 @@ pub use channel_store::{Channel, ChannelEvent, ChannelMembership, ChannelStore};
 #[cfg(test)]
 mod channel_store_tests;
 
-pub fn init(client: &Arc<Client>, user_store: Model<UserStore>, cx: &mut AppContext) {
+pub fn init(client: &Arc<Client>, user_store: Entity<UserStore>, cx: &mut App) {
     channel_store::init(client, user_store, cx);
     channel_buffer::init(&client.clone().into());
     channel_chat::init(&client.clone().into());

crates/channel/src/channel_buffer.rs 🔗

@@ -2,7 +2,7 @@ use crate::{Channel, ChannelStore};
 use anyhow::Result;
 use client::{ChannelId, Client, Collaborator, UserStore, ZED_ALWAYS_ACTIVE};
 use collections::HashMap;
-use gpui::{AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task};
+use gpui::{App, AppContext as _, AsyncAppContext, Context, Entity, EventEmitter, Task};
 use language::proto::serialize_version;
 use rpc::{
     proto::{self, PeerId},
@@ -23,9 +23,9 @@ pub struct ChannelBuffer {
     pub channel_id: ChannelId,
     connected: bool,
     collaborators: HashMap<PeerId, Collaborator>,
-    user_store: Model<UserStore>,
-    channel_store: Model<ChannelStore>,
-    buffer: Model<language::Buffer>,
+    user_store: Entity<UserStore>,
+    channel_store: Entity<ChannelStore>,
+    buffer: Entity<language::Buffer>,
     buffer_epoch: u64,
     client: Arc<Client>,
     subscription: Option<client::Subscription>,
@@ -45,10 +45,10 @@ impl ChannelBuffer {
     pub(crate) async fn new(
         channel: Arc<Channel>,
         client: Arc<Client>,
-        user_store: Model<UserStore>,
-        channel_store: Model<ChannelStore>,
+        user_store: Entity<UserStore>,
+        channel_store: Entity<ChannelStore>,
         mut cx: AsyncAppContext,
-    ) -> Result<Model<Self>> {
+    ) -> Result<Entity<Self>> {
         let response = client
             .request(proto::JoinChannelBuffer {
                 channel_id: channel.id.0,
@@ -62,7 +62,7 @@ impl ChannelBuffer {
             .map(language::proto::deserialize_operation)
             .collect::<Result<Vec<_>, _>>()?;
 
-        let buffer = cx.new_model(|cx| {
+        let buffer = cx.new(|cx| {
             let capability = channel_store.read(cx).channel_capability(channel.id);
             language::Buffer::remote(buffer_id, response.replica_id as u16, capability, base_text)
         })?;
@@ -70,7 +70,7 @@ impl ChannelBuffer {
 
         let subscription = client.subscribe_to_entity(channel.id.0)?;
 
-        anyhow::Ok(cx.new_model(|cx| {
+        anyhow::Ok(cx.new(|cx| {
             cx.subscribe(&buffer, Self::on_buffer_update).detach();
             cx.on_release(Self::release).detach();
             let mut this = Self {
@@ -81,7 +81,7 @@ impl ChannelBuffer {
                 collaborators: Default::default(),
                 acknowledge_task: None,
                 channel_id: channel.id,
-                subscription: Some(subscription.set_model(&cx.handle(), &mut cx.to_async())),
+                subscription: Some(subscription.set_model(&cx.model(), &mut cx.to_async())),
                 user_store,
                 channel_store,
             };
@@ -90,7 +90,7 @@ impl ChannelBuffer {
         })?)
     }
 
-    fn release(&mut self, _: &mut AppContext) {
+    fn release(&mut self, _: &mut App) {
         if self.connected {
             if let Some(task) = self.acknowledge_task.take() {
                 task.detach();
@@ -103,18 +103,18 @@ impl ChannelBuffer {
         }
     }
 
-    pub fn remote_id(&self, cx: &AppContext) -> BufferId {
+    pub fn remote_id(&self, cx: &App) -> BufferId {
         self.buffer.read(cx).remote_id()
     }
 
-    pub fn user_store(&self) -> &Model<UserStore> {
+    pub fn user_store(&self) -> &Entity<UserStore> {
         &self.user_store
     }
 
     pub(crate) fn replace_collaborators(
         &mut self,
         collaborators: Vec<proto::Collaborator>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let mut new_collaborators = HashMap::default();
         for collaborator in collaborators {
@@ -136,7 +136,7 @@ impl ChannelBuffer {
     }
 
     async fn handle_update_channel_buffer(
-        this: Model<Self>,
+        this: Entity<Self>,
         update_channel_buffer: TypedEnvelope<proto::UpdateChannelBuffer>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -157,7 +157,7 @@ impl ChannelBuffer {
     }
 
     async fn handle_update_channel_buffer_collaborators(
-        this: Model<Self>,
+        this: Entity<Self>,
         message: TypedEnvelope<proto::UpdateChannelBufferCollaborators>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -170,9 +170,9 @@ impl ChannelBuffer {
 
     fn on_buffer_update(
         &mut self,
-        _: Model<language::Buffer>,
+        _: Entity<language::Buffer>,
         event: &language::BufferEvent,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match event {
             language::BufferEvent::Operation {
@@ -201,7 +201,7 @@ impl ChannelBuffer {
         }
     }
 
-    pub fn acknowledge_buffer_version(&mut self, cx: &mut ModelContext<'_, ChannelBuffer>) {
+    pub fn acknowledge_buffer_version(&mut self, cx: &mut Context<'_, ChannelBuffer>) {
         let buffer = self.buffer.read(cx);
         let version = buffer.version();
         let buffer_id = buffer.remote_id().into();
@@ -227,7 +227,7 @@ impl ChannelBuffer {
         self.buffer_epoch
     }
 
-    pub fn buffer(&self) -> Model<language::Buffer> {
+    pub fn buffer(&self) -> Entity<language::Buffer> {
         self.buffer.clone()
     }
 
@@ -235,14 +235,14 @@ impl ChannelBuffer {
         &self.collaborators
     }
 
-    pub fn channel(&self, cx: &AppContext) -> Option<Arc<Channel>> {
+    pub fn channel(&self, cx: &App) -> Option<Arc<Channel>> {
         self.channel_store
             .read(cx)
             .channel_for_id(self.channel_id)
             .cloned()
     }
 
-    pub(crate) fn disconnect(&mut self, cx: &mut ModelContext<Self>) {
+    pub(crate) fn disconnect(&mut self, cx: &mut Context<Self>) {
         log::info!("channel buffer {} disconnected", self.channel_id);
         if self.connected {
             self.connected = false;
@@ -252,7 +252,7 @@ impl ChannelBuffer {
         }
     }
 
-    pub(crate) fn channel_changed(&mut self, cx: &mut ModelContext<Self>) {
+    pub(crate) fn channel_changed(&mut self, cx: &mut Context<Self>) {
         cx.emit(ChannelBufferEvent::ChannelChanged);
         cx.notify()
     }
@@ -261,7 +261,7 @@ impl ChannelBuffer {
         self.connected
     }
 
-    pub fn replica_id(&self, cx: &AppContext) -> u16 {
+    pub fn replica_id(&self, cx: &App) -> u16 {
         self.buffer.read(cx).replica_id()
     }
 }

crates/channel/src/channel_chat.rs 🔗

@@ -8,7 +8,7 @@ use client::{
 use collections::HashSet;
 use futures::lock::Mutex;
 use gpui::{
-    AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task, WeakModel,
+    App, AppContext as _, AsyncAppContext, Context, Entity, EventEmitter, Task, WeakEntity,
 };
 use rand::prelude::*;
 use rpc::AnyProtoClient;
@@ -24,12 +24,12 @@ pub struct ChannelChat {
     pub channel_id: ChannelId,
     messages: SumTree<ChannelMessage>,
     acknowledged_message_ids: HashSet<u64>,
-    channel_store: Model<ChannelStore>,
+    channel_store: Entity<ChannelStore>,
     loaded_all_messages: bool,
     last_acknowledged_id: Option<u64>,
     next_pending_message_id: usize,
     first_loaded_message_id: Option<u64>,
-    user_store: Model<UserStore>,
+    user_store: Entity<UserStore>,
     rpc: Arc<Client>,
     outgoing_messages_lock: Arc<Mutex<()>>,
     rng: StdRng,
@@ -105,11 +105,11 @@ pub fn init(client: &AnyProtoClient) {
 impl ChannelChat {
     pub async fn new(
         channel: Arc<Channel>,
-        channel_store: Model<ChannelStore>,
-        user_store: Model<UserStore>,
+        channel_store: Entity<ChannelStore>,
+        user_store: Entity<UserStore>,
         client: Arc<Client>,
         mut cx: AsyncAppContext,
-    ) -> Result<Model<Self>> {
+    ) -> Result<Entity<Self>> {
         let channel_id = channel.id;
         let subscription = client.subscribe_to_entity(channel_id.0).unwrap();
 
@@ -119,7 +119,7 @@ impl ChannelChat {
             })
             .await?;
 
-        let handle = cx.new_model(|cx| {
+        let handle = cx.new(|cx| {
             cx.on_release(Self::release).detach();
             Self {
                 channel_id: channel.id,
@@ -134,7 +134,7 @@ impl ChannelChat {
                 last_acknowledged_id: None,
                 rng: StdRng::from_entropy(),
                 first_loaded_message_id: None,
-                _subscription: subscription.set_model(&cx.handle(), &mut cx.to_async()),
+                _subscription: subscription.set_model(&cx.model(), &mut cx.to_async()),
             }
         })?;
         Self::handle_loaded_messages(
@@ -149,7 +149,7 @@ impl ChannelChat {
         Ok(handle)
     }
 
-    fn release(&mut self, _: &mut AppContext) {
+    fn release(&mut self, _: &mut App) {
         self.rpc
             .send(proto::LeaveChannelChat {
                 channel_id: self.channel_id.0,
@@ -157,7 +157,7 @@ impl ChannelChat {
             .log_err();
     }
 
-    pub fn channel(&self, cx: &AppContext) -> Option<Arc<Channel>> {
+    pub fn channel(&self, cx: &App) -> Option<Arc<Channel>> {
         self.channel_store
             .read(cx)
             .channel_for_id(self.channel_id)
@@ -171,7 +171,7 @@ impl ChannelChat {
     pub fn send_message(
         &mut self,
         message: MessageParams,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Result<Task<Result<u64>>> {
         if message.text.trim().is_empty() {
             Err(anyhow!("message body can't be empty"))?;
@@ -231,7 +231,7 @@ impl ChannelChat {
         }))
     }
 
-    pub fn remove_message(&mut self, id: u64, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    pub fn remove_message(&mut self, id: u64, cx: &mut Context<Self>) -> Task<Result<()>> {
         let response = self.rpc.request(proto::RemoveChannelMessage {
             channel_id: self.channel_id.0,
             message_id: id,
@@ -249,7 +249,7 @@ impl ChannelChat {
         &mut self,
         id: u64,
         message: MessageParams,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Result<Task<Result<()>>> {
         self.message_update(
             ChannelMessageId::Saved(id),
@@ -274,7 +274,7 @@ impl ChannelChat {
         }))
     }
 
-    pub fn load_more_messages(&mut self, cx: &mut ModelContext<Self>) -> Option<Task<Option<()>>> {
+    pub fn load_more_messages(&mut self, cx: &mut Context<Self>) -> Option<Task<Option<()>>> {
         if self.loaded_all_messages {
             return None;
         }
@@ -323,7 +323,7 @@ impl ChannelChat {
     ///
     /// For now, we always maintain a suffix of the channel's messages.
     pub async fn load_history_since_message(
-        chat: Model<Self>,
+        chat: Entity<Self>,
         message_id: u64,
         mut cx: AsyncAppContext,
     ) -> Option<usize> {
@@ -357,7 +357,7 @@ impl ChannelChat {
         }
     }
 
-    pub fn acknowledge_last_message(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn acknowledge_last_message(&mut self, cx: &mut Context<Self>) {
         if let ChannelMessageId::Saved(latest_message_id) = self.messages.summary().max_id {
             if self
                 .last_acknowledged_id
@@ -378,8 +378,8 @@ impl ChannelChat {
     }
 
     async fn handle_loaded_messages(
-        this: WeakModel<Self>,
-        user_store: Model<UserStore>,
+        this: WeakEntity<Self>,
+        user_store: Entity<UserStore>,
         rpc: Arc<Client>,
         proto_messages: Vec<proto::ChannelMessage>,
         loaded_all_messages: bool,
@@ -437,7 +437,7 @@ impl ChannelChat {
         Ok(())
     }
 
-    pub fn rejoin(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn rejoin(&mut self, cx: &mut Context<Self>) {
         let user_store = self.user_store.clone();
         let rpc = self.rpc.clone();
         let channel_id = self.channel_id;
@@ -527,7 +527,7 @@ impl ChannelChat {
     }
 
     async fn handle_message_sent(
-        this: Model<Self>,
+        this: Entity<Self>,
         message: TypedEnvelope<proto::ChannelMessageSent>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -551,7 +551,7 @@ impl ChannelChat {
     }
 
     async fn handle_message_removed(
-        this: Model<Self>,
+        this: Entity<Self>,
         message: TypedEnvelope<proto::RemoveChannelMessage>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -562,7 +562,7 @@ impl ChannelChat {
     }
 
     async fn handle_message_updated(
-        this: Model<Self>,
+        this: Entity<Self>,
         message: TypedEnvelope<proto::ChannelMessageUpdate>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -586,7 +586,7 @@ impl ChannelChat {
         Ok(())
     }
 
-    fn insert_messages(&mut self, messages: SumTree<ChannelMessage>, cx: &mut ModelContext<Self>) {
+    fn insert_messages(&mut self, messages: SumTree<ChannelMessage>, cx: &mut Context<Self>) {
         if let Some((first_message, last_message)) = messages.first().zip(messages.last()) {
             let nonces = messages
                 .cursor::<()>(&())
@@ -645,7 +645,7 @@ impl ChannelChat {
         }
     }
 
-    fn message_removed(&mut self, id: u64, cx: &mut ModelContext<Self>) {
+    fn message_removed(&mut self, id: u64, cx: &mut Context<Self>) {
         let mut cursor = self.messages.cursor::<ChannelMessageId>(&());
         let mut messages = cursor.slice(&ChannelMessageId::Saved(id), Bias::Left, &());
         if let Some(item) = cursor.item() {
@@ -683,7 +683,7 @@ impl ChannelChat {
         body: String,
         mentions: Vec<(Range<usize>, u64)>,
         edited_at: Option<OffsetDateTime>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let mut cursor = self.messages.cursor::<ChannelMessageId>(&());
         let mut messages = cursor.slice(&id, Bias::Left, &());
@@ -712,7 +712,7 @@ impl ChannelChat {
 
 async fn messages_from_proto(
     proto_messages: Vec<proto::ChannelMessage>,
-    user_store: &Model<UserStore>,
+    user_store: &Entity<UserStore>,
     cx: &mut AsyncAppContext,
 ) -> Result<SumTree<ChannelMessage>> {
     let messages = ChannelMessage::from_proto_vec(proto_messages, user_store, cx).await?;
@@ -724,7 +724,7 @@ async fn messages_from_proto(
 impl ChannelMessage {
     pub async fn from_proto(
         message: proto::ChannelMessage,
-        user_store: &Model<UserStore>,
+        user_store: &Entity<UserStore>,
         cx: &mut AsyncAppContext,
     ) -> Result<Self> {
         let sender = user_store
@@ -769,7 +769,7 @@ impl ChannelMessage {
 
     pub async fn from_proto_vec(
         proto_messages: Vec<proto::ChannelMessage>,
-        user_store: &Model<UserStore>,
+        user_store: &Entity<UserStore>,
         cx: &mut AsyncAppContext,
     ) -> Result<Vec<Self>> {
         let unique_user_ids = proto_messages

crates/channel/src/channel_store.rs 🔗

@@ -7,8 +7,8 @@ use client::{ChannelId, Client, ClientSettings, Subscription, User, UserId, User
 use collections::{hash_map, HashMap, HashSet};
 use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt};
 use gpui::{
-    AppContext, AsyncAppContext, Context, EventEmitter, Global, Model, ModelContext, SharedString,
-    Task, WeakModel,
+    App, AppContext as _, AsyncAppContext, Context, Entity, EventEmitter, Global, SharedString,
+    Task, WeakEntity,
 };
 use language::Capability;
 use rpc::{
@@ -21,9 +21,8 @@ use util::{maybe, ResultExt};
 
 pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30);
 
-pub fn init(client: &Arc<Client>, user_store: Model<UserStore>, cx: &mut AppContext) {
-    let channel_store =
-        cx.new_model(|cx| ChannelStore::new(client.clone(), user_store.clone(), cx));
+pub fn init(client: &Arc<Client>, user_store: Entity<UserStore>, cx: &mut App) {
+    let channel_store = cx.new(|cx| ChannelStore::new(client.clone(), user_store.clone(), cx));
     cx.set_global(GlobalChannelStore(channel_store));
 }
 
@@ -44,7 +43,7 @@ pub struct ChannelStore {
     opened_chats: HashMap<ChannelId, OpenedModelHandle<ChannelChat>>,
     client: Arc<Client>,
     did_subscribe: bool,
-    user_store: Model<UserStore>,
+    user_store: Entity<UserStore>,
     _rpc_subscriptions: [Subscription; 2],
     _watch_connection_status: Task<Option<()>>,
     disconnect_channel_buffers_task: Option<Task<()>>,
@@ -69,7 +68,7 @@ pub struct ChannelState {
 }
 
 impl Channel {
-    pub fn link(&self, cx: &AppContext) -> String {
+    pub fn link(&self, cx: &App) -> String {
         format!(
             "{}/channel/{}-{}",
             ClientSettings::get_global(cx).server_url,
@@ -78,7 +77,7 @@ impl Channel {
         )
     }
 
-    pub fn notes_link(&self, heading: Option<String>, cx: &AppContext) -> String {
+    pub fn notes_link(&self, heading: Option<String>, cx: &App) -> String {
         self.link(cx)
             + "/notes"
             + &heading
@@ -144,24 +143,20 @@ pub enum ChannelEvent {
 impl EventEmitter<ChannelEvent> for ChannelStore {}
 
 enum OpenedModelHandle<E> {
-    Open(WeakModel<E>),
-    Loading(Shared<Task<Result<Model<E>, Arc<anyhow::Error>>>>),
+    Open(WeakEntity<E>),
+    Loading(Shared<Task<Result<Entity<E>, Arc<anyhow::Error>>>>),
 }
 
-struct GlobalChannelStore(Model<ChannelStore>);
+struct GlobalChannelStore(Entity<ChannelStore>);
 
 impl Global for GlobalChannelStore {}
 
 impl ChannelStore {
-    pub fn global(cx: &AppContext) -> Model<Self> {
+    pub fn global(cx: &App) -> Entity<Self> {
         cx.global::<GlobalChannelStore>().0.clone()
     }
 
-    pub fn new(
-        client: Arc<Client>,
-        user_store: Model<UserStore>,
-        cx: &mut ModelContext<Self>,
-    ) -> Self {
+    pub fn new(client: Arc<Client>, user_store: Entity<UserStore>, cx: &mut Context<Self>) -> Self {
         let rpc_subscriptions = [
             client.add_message_handler(cx.weak_model(), Self::handle_update_channels),
             client.add_message_handler(cx.weak_model(), Self::handle_update_user_channels),
@@ -295,7 +290,7 @@ impl ChannelStore {
         self.channel_index.by_id().get(&channel_id)
     }
 
-    pub fn has_open_channel_buffer(&self, channel_id: ChannelId, _cx: &AppContext) -> bool {
+    pub fn has_open_channel_buffer(&self, channel_id: ChannelId, _cx: &App) -> bool {
         if let Some(buffer) = self.opened_buffers.get(&channel_id) {
             if let OpenedModelHandle::Open(buffer) = buffer {
                 return buffer.upgrade().is_some();
@@ -307,11 +302,11 @@ impl ChannelStore {
     pub fn open_channel_buffer(
         &mut self,
         channel_id: ChannelId,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<ChannelBuffer>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<ChannelBuffer>>> {
         let client = self.client.clone();
         let user_store = self.user_store.clone();
-        let channel_store = cx.handle();
+        let channel_store = cx.model();
         self.open_channel_resource(
             channel_id,
             |this| &mut this.opened_buffers,
@@ -323,7 +318,7 @@ impl ChannelStore {
     pub fn fetch_channel_messages(
         &self,
         message_ids: Vec<u64>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<Vec<ChannelMessage>>> {
         let request = if message_ids.is_empty() {
             None
@@ -384,7 +379,7 @@ impl ChannelStore {
         &mut self,
         channel_id: ChannelId,
         message_id: u64,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.channel_states
             .entry(channel_id)
@@ -397,7 +392,7 @@ impl ChannelStore {
         &mut self,
         channel_id: ChannelId,
         message_id: u64,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.channel_states
             .entry(channel_id)
@@ -411,7 +406,7 @@ impl ChannelStore {
         channel_id: ChannelId,
         epoch: u64,
         version: &clock::Global,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.channel_states
             .entry(channel_id)
@@ -425,7 +420,7 @@ impl ChannelStore {
         channel_id: ChannelId,
         epoch: u64,
         version: &clock::Global,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.channel_states
             .entry(channel_id)
@@ -437,11 +432,11 @@ impl ChannelStore {
     pub fn open_channel_chat(
         &mut self,
         channel_id: ChannelId,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<ChannelChat>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<ChannelChat>>> {
         let client = self.client.clone();
         let user_store = self.user_store.clone();
-        let this = cx.handle();
+        let this = cx.model();
         self.open_channel_resource(
             channel_id,
             |this| &mut this.opened_chats,
@@ -460,11 +455,11 @@ impl ChannelStore {
         channel_id: ChannelId,
         get_map: fn(&mut Self) -> &mut HashMap<ChannelId, OpenedModelHandle<T>>,
         load: F,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<T>>>
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<T>>>
     where
         F: 'static + FnOnce(Arc<Channel>, AsyncAppContext) -> Fut,
-        Fut: Future<Output = Result<Model<T>>>,
+        Fut: Future<Output = Result<Entity<T>>>,
         T: 'static,
     {
         let task = loop {
@@ -572,7 +567,7 @@ impl ChannelStore {
         &self,
         name: &str,
         parent_id: Option<ChannelId>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<ChannelId>> {
         let client = self.client.clone();
         let name = name.trim_start_matches('#').to_owned();
@@ -614,7 +609,7 @@ impl ChannelStore {
         &mut self,
         channel_id: ChannelId,
         to: ChannelId,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let client = self.client.clone();
         cx.spawn(move |_, _| async move {
@@ -633,7 +628,7 @@ impl ChannelStore {
         &mut self,
         channel_id: ChannelId,
         visibility: ChannelVisibility,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let client = self.client.clone();
         cx.spawn(move |_, _| async move {
@@ -653,7 +648,7 @@ impl ChannelStore {
         channel_id: ChannelId,
         user_id: UserId,
         role: proto::ChannelRole,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         if !self.outgoing_invites.insert((channel_id, user_id)) {
             return Task::ready(Err(anyhow!("invite request already in progress")));
@@ -685,7 +680,7 @@ impl ChannelStore {
         &mut self,
         channel_id: ChannelId,
         user_id: u64,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         if !self.outgoing_invites.insert((channel_id, user_id)) {
             return Task::ready(Err(anyhow!("invite request already in progress")));
@@ -715,7 +710,7 @@ impl ChannelStore {
         channel_id: ChannelId,
         user_id: UserId,
         role: proto::ChannelRole,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         if !self.outgoing_invites.insert((channel_id, user_id)) {
             return Task::ready(Err(anyhow!("member request already in progress")));
@@ -746,7 +741,7 @@ impl ChannelStore {
         &mut self,
         channel_id: ChannelId,
         new_name: &str,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let client = self.client.clone();
         let name = new_name.to_string();
@@ -783,7 +778,7 @@ impl ChannelStore {
         &mut self,
         channel_id: ChannelId,
         accept: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let client = self.client.clone();
         cx.background_executor().spawn(async move {
@@ -801,7 +796,7 @@ impl ChannelStore {
         channel_id: ChannelId,
         query: String,
         limit: u16,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<Vec<ChannelMembership>>> {
         let client = self.client.clone();
         let user_store = self.user_store.downgrade();
@@ -851,7 +846,7 @@ impl ChannelStore {
     }
 
     async fn handle_update_channels(
-        this: Model<Self>,
+        this: Entity<Self>,
         message: TypedEnvelope<proto::UpdateChannels>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -864,7 +859,7 @@ impl ChannelStore {
     }
 
     async fn handle_update_user_channels(
-        this: Model<Self>,
+        this: Entity<Self>,
         message: TypedEnvelope<proto::UpdateUserChannels>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -896,7 +891,7 @@ impl ChannelStore {
         })
     }
 
-    fn handle_connect(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn handle_connect(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         self.channel_index.clear();
         self.channel_invitations.clear();
         self.channel_participants.clear();
@@ -1011,7 +1006,7 @@ impl ChannelStore {
         })
     }
 
-    fn handle_disconnect(&mut self, wait_for_reconnect: bool, cx: &mut ModelContext<Self>) {
+    fn handle_disconnect(&mut self, wait_for_reconnect: bool, cx: &mut Context<Self>) {
         cx.notify();
         self.did_subscribe = false;
         self.disconnect_channel_buffers_task.get_or_insert_with(|| {
@@ -1039,7 +1034,7 @@ impl ChannelStore {
     pub(crate) fn update_channels(
         &mut self,
         payload: proto::UpdateChannels,
-        cx: &mut ModelContext<ChannelStore>,
+        cx: &mut Context<ChannelStore>,
     ) -> Option<Task<Result<()>>> {
         if !payload.remove_channel_invitations.is_empty() {
             self.channel_invitations

crates/channel/src/channel_store_tests.rs 🔗

@@ -3,13 +3,13 @@ use crate::channel_chat::ChannelChatEvent;
 use super::*;
 use client::{test::FakeServer, Client, UserStore};
 use clock::FakeSystemClock;
-use gpui::{AppContext, Context, Model, SemanticVersion, TestAppContext};
+use gpui::{App, AppContext as _, Entity, SemanticVersion, TestAppContext};
 use http_client::FakeHttpClient;
 use rpc::proto::{self};
 use settings::SettingsStore;
 
 #[gpui::test]
-fn test_update_channels(cx: &mut AppContext) {
+fn test_update_channels(cx: &mut App) {
     let channel_store = init_test(cx);
 
     update_channels(
@@ -77,7 +77,7 @@ fn test_update_channels(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_dangling_channel_paths(cx: &mut AppContext) {
+fn test_dangling_channel_paths(cx: &mut App) {
     let channel_store = init_test(cx);
 
     update_channels(
@@ -343,7 +343,7 @@ async fn test_channel_messages(cx: &mut TestAppContext) {
     });
 }
 
-fn init_test(cx: &mut AppContext) -> Model<ChannelStore> {
+fn init_test(cx: &mut App) -> Entity<ChannelStore> {
     let settings_store = SettingsStore::test(cx);
     cx.set_global(settings_store);
     release_channel::init(SemanticVersion::default(), cx);
@@ -352,7 +352,7 @@ fn init_test(cx: &mut AppContext) -> Model<ChannelStore> {
     let clock = Arc::new(FakeSystemClock::new());
     let http = FakeHttpClient::with_404_response();
     let client = Client::new(clock, http.clone(), cx);
-    let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
+    let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
 
     client::init(&client, cx);
     crate::init(&client, user_store, cx);
@@ -361,9 +361,9 @@ fn init_test(cx: &mut AppContext) -> Model<ChannelStore> {
 }
 
 fn update_channels(
-    channel_store: &Model<ChannelStore>,
+    channel_store: &Entity<ChannelStore>,
     message: proto::UpdateChannels,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) {
     let task = channel_store.update(cx, |store, cx| store.update_channels(message, cx));
     assert!(task.is_none());
@@ -371,9 +371,9 @@ fn update_channels(
 
 #[track_caller]
 fn assert_channels(
-    channel_store: &Model<ChannelStore>,
+    channel_store: &Entity<ChannelStore>,
     expected_channels: &[(usize, String)],
-    cx: &mut AppContext,
+    cx: &mut App,
 ) {
     let actual = channel_store.update(cx, |store, _| {
         store

crates/cli/src/main.rs 🔗

@@ -3,7 +3,7 @@
     allow(dead_code)
 )]
 
-use anyhow::{Context, Result};
+use anyhow::{Context as _, Result};
 use clap::Parser;
 use cli::{ipc::IpcOneShotServer, CliRequest, CliResponse, IpcHandshake};
 use collections::HashMap;
@@ -536,7 +536,7 @@ mod windows {
 
 #[cfg(target_os = "macos")]
 mod mac_os {
-    use anyhow::{anyhow, Context, Result};
+    use anyhow::{anyhow, Context as _, Result};
     use core_foundation::{
         array::{CFArray, CFIndex},
         string::kCFStringEncodingUTF8,

crates/client/src/client.rs 🔗

@@ -19,7 +19,7 @@ use futures::{
     channel::oneshot, future::BoxFuture, AsyncReadExt, FutureExt, SinkExt, Stream, StreamExt,
     TryFutureExt as _, TryStreamExt,
 };
-use gpui::{actions, AppContext, AsyncAppContext, Global, Model, Task, WeakModel};
+use gpui::{actions, App, AsyncAppContext, Entity, Global, Task, WeakEntity};
 use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
 use parking_lot::RwLock;
 use postage::watch;
@@ -104,7 +104,7 @@ impl Settings for ClientSettings {
 
     type FileContent = ClientSettingsContent;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
         let mut result = sources.json_merge::<Self>()?;
         if let Some(server_url) = &*ZED_SERVER_URL {
             result.server_url.clone_from(server_url)
@@ -128,7 +128,7 @@ impl Settings for ProxySettings {
 
     type FileContent = ProxySettingsContent;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
         Ok(Self {
             proxy: sources
                 .user
@@ -139,13 +139,13 @@ impl Settings for ProxySettings {
     }
 }
 
-pub fn init_settings(cx: &mut AppContext) {
+pub fn init_settings(cx: &mut App) {
     TelemetrySettings::register(cx);
     ClientSettings::register(cx);
     ProxySettings::register(cx);
 }
 
-pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
+pub fn init(client: &Arc<Client>, cx: &mut App) {
     let client = Arc::downgrade(client);
     cx.on_action({
         let client = client.clone();
@@ -380,7 +380,7 @@ pub struct PendingEntitySubscription<T: 'static> {
 }
 
 impl<T: 'static> PendingEntitySubscription<T> {
-    pub fn set_model(mut self, model: &Model<T>, cx: &AsyncAppContext) -> Subscription {
+    pub fn set_model(mut self, model: &Entity<T>, cx: &AsyncAppContext) -> Subscription {
         self.consumed = true;
         let mut handlers = self.client.handler_set.lock();
         let id = (TypeId::of::<T>(), self.remote_id);
@@ -456,7 +456,7 @@ impl settings::Settings for TelemetrySettings {
 
     type FileContent = TelemetrySettingsContent;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
         Ok(Self {
             diagnostics: sources
                 .user
@@ -483,7 +483,7 @@ impl Client {
     pub fn new(
         clock: Arc<dyn SystemClock>,
         http: Arc<HttpClientWithUrl>,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Arc<Self> {
         let use_zed_development_auth = match ReleaseChannel::try_global(cx) {
             Some(ReleaseChannel::Dev) => *ZED_DEVELOPMENT_AUTH,
@@ -518,7 +518,7 @@ impl Client {
         })
     }
 
-    pub fn production(cx: &mut AppContext) -> Arc<Self> {
+    pub fn production(cx: &mut App) -> Arc<Self> {
         let clock = Arc::new(clock::RealSystemClock);
         let http = Arc::new(HttpClientWithUrl::new_uri(
             cx.http_client(),
@@ -576,10 +576,10 @@ impl Client {
         self
     }
 
-    pub fn global(cx: &AppContext) -> Arc<Self> {
+    pub fn global(cx: &App) -> Arc<Self> {
         cx.global::<GlobalClient>().0.clone()
     }
-    pub fn set_global(client: Arc<Client>, cx: &mut AppContext) {
+    pub fn set_global(client: Arc<Client>, cx: &mut App) {
         cx.set_global(GlobalClient(client))
     }
 
@@ -678,13 +678,13 @@ impl Client {
     #[track_caller]
     pub fn add_message_handler<M, E, H, F>(
         self: &Arc<Self>,
-        entity: WeakModel<E>,
+        entity: WeakEntity<E>,
         handler: H,
     ) -> Subscription
     where
         M: EnvelopedMessage,
         E: 'static,
-        H: 'static + Sync + Fn(Model<E>, TypedEnvelope<M>, AsyncAppContext) -> F + Send + Sync,
+        H: 'static + Sync + Fn(Entity<E>, TypedEnvelope<M>, AsyncAppContext) -> F + Send + Sync,
         F: 'static + Future<Output = Result<()>>,
     {
         self.add_message_handler_impl(entity, move |model, message, _, cx| {
@@ -694,7 +694,7 @@ impl Client {
 
     fn add_message_handler_impl<M, E, H, F>(
         self: &Arc<Self>,
-        entity: WeakModel<E>,
+        entity: WeakEntity<E>,
         handler: H,
     ) -> Subscription
     where
@@ -702,7 +702,7 @@ impl Client {
         E: 'static,
         H: 'static
             + Sync
-            + Fn(Model<E>, TypedEnvelope<M>, AnyProtoClient, AsyncAppContext) -> F
+            + Fn(Entity<E>, TypedEnvelope<M>, AnyProtoClient, AsyncAppContext) -> F
             + Send
             + Sync,
         F: 'static + Future<Output = Result<()>>,
@@ -739,13 +739,13 @@ impl Client {
 
     pub fn add_request_handler<M, E, H, F>(
         self: &Arc<Self>,
-        model: WeakModel<E>,
+        model: WeakEntity<E>,
         handler: H,
     ) -> Subscription
     where
         M: RequestMessage,
         E: 'static,
-        H: 'static + Sync + Fn(Model<E>, TypedEnvelope<M>, AsyncAppContext) -> F + Send + Sync,
+        H: 'static + Sync + Fn(Entity<E>, TypedEnvelope<M>, AsyncAppContext) -> F + Send + Sync,
         F: 'static + Future<Output = Result<M::Response>>,
     {
         self.add_message_handler_impl(model, move |handle, envelope, this, cx| {
@@ -1751,7 +1751,7 @@ pub const ZED_URL_SCHEME: &str = "zed";
 ///
 /// Returns a [`Some`] containing the unprefixed link if the link is a Zed link.
 /// Returns [`None`] otherwise.
-pub fn parse_zed_link<'a>(link: &'a str, cx: &AppContext) -> Option<&'a str> {
+pub fn parse_zed_link<'a>(link: &'a str, cx: &App) -> Option<&'a str> {
     let server_url = &ClientSettings::get_global(cx).server_url;
     if let Some(stripped) = link
         .strip_prefix(server_url)
@@ -1775,7 +1775,7 @@ mod tests {
     use crate::test::FakeServer;
 
     use clock::FakeSystemClock;
-    use gpui::{BackgroundExecutor, Context, TestAppContext};
+    use gpui::{AppContext as _, BackgroundExecutor, TestAppContext};
     use http_client::FakeHttpClient;
     use parking_lot::Mutex;
     use proto::TypedEnvelope;
@@ -1961,7 +1961,7 @@ mod tests {
         let (done_tx1, done_rx1) = smol::channel::unbounded();
         let (done_tx2, done_rx2) = smol::channel::unbounded();
         AnyProtoClient::from(client.clone()).add_model_message_handler(
-            move |model: Model<TestModel>, _: TypedEnvelope<proto::JoinProject>, mut cx| {
+            move |model: Entity<TestModel>, _: TypedEnvelope<proto::JoinProject>, mut cx| {
                 match model.update(&mut cx, |model, _| model.id).unwrap() {
                     1 => done_tx1.try_send(()).unwrap(),
                     2 => done_tx2.try_send(()).unwrap(),
@@ -1970,15 +1970,15 @@ mod tests {
                 async { Ok(()) }
             },
         );
-        let model1 = cx.new_model(|_| TestModel {
+        let model1 = cx.new(|_| TestModel {
             id: 1,
             subscription: None,
         });
-        let model2 = cx.new_model(|_| TestModel {
+        let model2 = cx.new(|_| TestModel {
             id: 2,
             subscription: None,
         });
-        let model3 = cx.new_model(|_| TestModel {
+        let model3 = cx.new(|_| TestModel {
             id: 3,
             subscription: None,
         });
@@ -2018,7 +2018,7 @@ mod tests {
         });
         let server = FakeServer::for_client(user_id, &client, cx).await;
 
-        let model = cx.new_model(|_| TestModel::default());
+        let model = cx.new(|_| TestModel::default());
         let (done_tx1, _done_rx1) = smol::channel::unbounded();
         let (done_tx2, done_rx2) = smol::channel::unbounded();
         let subscription1 = client.add_message_handler(
@@ -2053,11 +2053,11 @@ mod tests {
         });
         let server = FakeServer::for_client(user_id, &client, cx).await;
 
-        let model = cx.new_model(|_| TestModel::default());
+        let model = cx.new(|_| TestModel::default());
         let (done_tx, done_rx) = smol::channel::unbounded();
         let subscription = client.add_message_handler(
             model.clone().downgrade(),
-            move |model: Model<TestModel>, _: TypedEnvelope<proto::Ping>, mut cx| {
+            move |model: Entity<TestModel>, _: TypedEnvelope<proto::Ping>, mut cx| {
                 model
                     .update(&mut cx, |model, _| model.subscription.take())
                     .unwrap();

crates/client/src/telemetry.rs 🔗

@@ -6,7 +6,7 @@ use clock::SystemClock;
 use collections::{HashMap, HashSet};
 use futures::channel::mpsc;
 use futures::{Future, StreamExt};
-use gpui::{AppContext, BackgroundExecutor, Task};
+use gpui::{App, BackgroundExecutor, Task};
 use http_client::{self, AsyncBody, HttpClient, HttpClientWithUrl, Method, Request};
 use parking_lot::Mutex;
 use release_channel::ReleaseChannel;
@@ -178,7 +178,7 @@ impl Telemetry {
     pub fn new(
         clock: Arc<dyn SystemClock>,
         client: Arc<HttpClientWithUrl>,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Arc<Self> {
         let release_channel =
             ReleaseChannel::try_global(cx).map(|release_channel| release_channel.display_name());
@@ -299,7 +299,7 @@ impl Telemetry {
         system_id: Option<String>,
         installation_id: Option<String>,
         session_id: String,
-        cx: &AppContext,
+        cx: &App,
     ) {
         let mut state = self.state.lock();
         state.system_id = system_id.map(|id| id.into());

crates/client/src/test.rs 🔗

@@ -2,7 +2,7 @@ use crate::{Client, Connection, Credentials, EstablishConnectionError, UserStore
 use anyhow::{anyhow, Result};
 use chrono::Duration;
 use futures::{stream::BoxStream, StreamExt};
-use gpui::{BackgroundExecutor, Context, Model, TestAppContext};
+use gpui::{AppContext as _, BackgroundExecutor, Entity, TestAppContext};
 use parking_lot::Mutex;
 use rpc::{
     proto::{self, GetPrivateUserInfo, GetPrivateUserInfoResponse},
@@ -203,8 +203,8 @@ impl FakeServer {
         &self,
         client: Arc<Client>,
         cx: &mut TestAppContext,
-    ) -> Model<UserStore> {
-        let user_store = cx.new_model(|cx| UserStore::new(client, cx));
+    ) -> Entity<UserStore> {
+        let user_store = cx.new(|cx| UserStore::new(client, cx));
         assert_eq!(
             self.receive::<proto::GetUsers>()
                 .await

crates/client/src/user.rs 🔗

@@ -1,12 +1,11 @@
 use super::{proto, Client, Status, TypedEnvelope};
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use chrono::{DateTime, Utc};
 use collections::{hash_map::Entry, HashMap, HashSet};
 use feature_flags::FeatureFlagAppExt;
 use futures::{channel::mpsc, Future, StreamExt};
 use gpui::{
-    AppContext, AsyncAppContext, EventEmitter, Model, ModelContext, SharedString, SharedUri, Task,
-    WeakModel,
+    App, AsyncAppContext, Context, Entity, EventEmitter, SharedString, SharedUri, Task, WeakEntity,
 };
 use postage::{sink::Sink, watch};
 use rpc::proto::{RequestMessage, UsersResponse};
@@ -106,7 +105,7 @@ pub struct UserStore {
     client: Weak<Client>,
     _maintain_contacts: Task<()>,
     _maintain_current_user: Task<Result<()>>,
-    weak_self: WeakModel<Self>,
+    weak_self: WeakEntity<Self>,
 }
 
 #[derive(Clone)]
@@ -143,7 +142,7 @@ enum UpdateContacts {
 }
 
 impl UserStore {
-    pub fn new(client: Arc<Client>, cx: &ModelContext<Self>) -> Self {
+    pub fn new(client: Arc<Client>, cx: &Context<Self>) -> Self {
         let (mut current_user_tx, current_user_rx) = watch::channel();
         let (update_contacts_tx, mut update_contacts_rx) = mpsc::unbounded();
         let rpc_subscriptions = vec![
@@ -274,7 +273,7 @@ impl UserStore {
     }
 
     async fn handle_update_invite_info(
-        this: Model<Self>,
+        this: Entity<Self>,
         message: TypedEnvelope<proto::UpdateInviteInfo>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -289,7 +288,7 @@ impl UserStore {
     }
 
     async fn handle_show_contacts(
-        this: Model<Self>,
+        this: Entity<Self>,
         _: TypedEnvelope<proto::ShowContacts>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -302,7 +301,7 @@ impl UserStore {
     }
 
     async fn handle_update_contacts(
-        this: Model<Self>,
+        this: Entity<Self>,
         message: TypedEnvelope<proto::UpdateContacts>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -315,7 +314,7 @@ impl UserStore {
     }
 
     async fn handle_update_plan(
-        this: Model<Self>,
+        this: Entity<Self>,
         message: TypedEnvelope<proto::UpdateUserPlan>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -326,11 +325,7 @@ impl UserStore {
         Ok(())
     }
 
-    fn update_contacts(
-        &mut self,
-        message: UpdateContacts,
-        cx: &ModelContext<Self>,
-    ) -> Task<Result<()>> {
+    fn update_contacts(&mut self, message: UpdateContacts, cx: &Context<Self>) -> Task<Result<()>> {
         match message {
             UpdateContacts::Wait(barrier) => {
                 drop(barrier);
@@ -504,16 +499,12 @@ impl UserStore {
     pub fn request_contact(
         &mut self,
         responder_id: u64,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         self.perform_contact_request(responder_id, proto::RequestContact { responder_id }, cx)
     }
 
-    pub fn remove_contact(
-        &mut self,
-        user_id: u64,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<()>> {
+    pub fn remove_contact(&mut self, user_id: u64, cx: &mut Context<Self>) -> Task<Result<()>> {
         self.perform_contact_request(user_id, proto::RemoveContact { user_id }, cx)
     }
 
@@ -527,7 +518,7 @@ impl UserStore {
         &mut self,
         requester_id: u64,
         accept: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         self.perform_contact_request(
             requester_id,
@@ -546,7 +537,7 @@ impl UserStore {
     pub fn dismiss_contact_request(
         &self,
         requester_id: u64,
-        cx: &ModelContext<Self>,
+        cx: &Context<Self>,
     ) -> Task<Result<()>> {
         let client = self.client.upgrade();
         cx.spawn(move |_, _| async move {
@@ -565,7 +556,7 @@ impl UserStore {
         &mut self,
         user_id: u64,
         request: T,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let client = self.client.upgrade();
         *self.pending_contact_requests.entry(user_id).or_insert(0) += 1;
@@ -615,7 +606,7 @@ impl UserStore {
     pub fn get_users(
         &self,
         user_ids: Vec<u64>,
-        cx: &ModelContext<Self>,
+        cx: &Context<Self>,
     ) -> Task<Result<Vec<Arc<User>>>> {
         let mut user_ids_to_fetch = user_ids.clone();
         user_ids_to_fetch.retain(|id| !self.users.contains_key(id));
@@ -650,7 +641,7 @@ impl UserStore {
     pub fn fuzzy_search_users(
         &self,
         query: String,
-        cx: &ModelContext<Self>,
+        cx: &Context<Self>,
     ) -> Task<Result<Vec<Arc<User>>>> {
         self.load_users(proto::FuzzySearchUsers { query }, cx)
     }
@@ -659,7 +650,7 @@ impl UserStore {
         self.users.get(&user_id).cloned()
     }
 
-    pub fn get_user_optimistic(&self, user_id: u64, cx: &ModelContext<Self>) -> Option<Arc<User>> {
+    pub fn get_user_optimistic(&self, user_id: u64, cx: &Context<Self>) -> Option<Arc<User>> {
         if let Some(user) = self.users.get(&user_id).cloned() {
             return Some(user);
         }
@@ -668,7 +659,7 @@ impl UserStore {
         None
     }
 
-    pub fn get_user(&self, user_id: u64, cx: &ModelContext<Self>) -> Task<Result<Arc<User>>> {
+    pub fn get_user(&self, user_id: u64, cx: &Context<Self>) -> Task<Result<Arc<User>>> {
         if let Some(user) = self.users.get(&user_id).cloned() {
             return Task::ready(Ok(user));
         }
@@ -708,7 +699,7 @@ impl UserStore {
             .map(|accepted_tos_at| accepted_tos_at.is_some())
     }
 
-    pub fn accept_terms_of_service(&self, cx: &ModelContext<Self>) -> Task<Result<()>> {
+    pub fn accept_terms_of_service(&self, cx: &Context<Self>) -> Task<Result<()>> {
         if self.current_user().is_none() {
             return Task::ready(Err(anyhow!("no current user")));
         };
@@ -740,7 +731,7 @@ impl UserStore {
     fn load_users(
         &self,
         request: impl RequestMessage<Response = UsersResponse>,
-        cx: &ModelContext<Self>,
+        cx: &Context<Self>,
     ) -> Task<Result<Vec<Arc<User>>>> {
         let client = self.client.clone();
         cx.spawn(|this, mut cx| async move {
@@ -774,7 +765,7 @@ impl UserStore {
     pub fn set_participant_indices(
         &mut self,
         participant_indices: HashMap<u64, ParticipantIndex>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if participant_indices != self.participant_indices {
             self.participant_indices = participant_indices;
@@ -789,7 +780,7 @@ impl UserStore {
     pub fn participant_names(
         &self,
         user_ids: impl Iterator<Item = u64>,
-        cx: &AppContext,
+        cx: &App,
     ) -> HashMap<u64, SharedString> {
         let mut ret = HashMap::default();
         let mut missing_user_ids = Vec::new();
@@ -827,7 +818,7 @@ impl User {
 impl Contact {
     async fn from_proto(
         contact: proto::Contact,
-        user_store: &Model<UserStore>,
+        user_store: &Entity<UserStore>,
         cx: &mut AsyncAppContext,
     ) -> Result<Self> {
         let user = user_store

crates/client/src/zed_urls.rs 🔗

@@ -4,16 +4,16 @@
 //! links appropriate for the environment (e.g., by linking to a local copy of
 //! zed.dev in development).
 
-use gpui::AppContext;
+use gpui::App;
 use settings::Settings;
 
 use crate::ClientSettings;
 
-fn server_url(cx: &AppContext) -> &str {
+fn server_url(cx: &App) -> &str {
     &ClientSettings::get_global(cx).server_url
 }
 
 /// Returns the URL to the account page on zed.dev.
-pub fn account_url(cx: &AppContext) -> String {
+pub fn account_url(cx: &App) -> String {
     format!("{server_url}/account", server_url = server_url(cx))
 }

crates/collab/src/auth.rs 🔗

@@ -3,7 +3,7 @@ use crate::{
     rpc::Principal,
     AppState, Error, Result,
 };
-use anyhow::{anyhow, Context};
+use anyhow::{anyhow, Context as _};
 use axum::{
     http::{self, Request, StatusCode},
     middleware::Next,

crates/collab/src/main.rs 🔗

@@ -6,6 +6,7 @@ use axum::{
     routing::get,
     Extension, Router,
 };
+
 use collab::api::billing::sync_llm_usage_with_stripe_periodically;
 use collab::api::CloudflareIpCountryHeader;
 use collab::llm::{db::LlmDatabase, log_usage_periodically};

crates/collab/src/seed.rs 🔗

@@ -1,6 +1,6 @@
 use crate::db::{self, ChannelRole, NewUserParams};
 
-use anyhow::Context;
+use anyhow::Context as _;
 use chrono::{DateTime, Utc};
 use db::Database;
 use serde::{de::DeserializeOwned, Deserialize};

crates/collab/src/stripe_billing.rs 🔗

@@ -1,7 +1,7 @@
 use std::sync::Arc;
 
 use crate::{llm, Cents, Result};
-use anyhow::Context;
+use anyhow::Context as _;
 use chrono::{Datelike, Utc};
 use collections::HashMap;
 use serde::{Deserialize, Serialize};

crates/collab/src/tests.rs 🔗

@@ -5,7 +5,7 @@ use std::sync::Arc;
 
 use call::Room;
 use client::ChannelId;
-use gpui::{Model, TestAppContext};
+use gpui::{Entity, TestAppContext};
 
 mod channel_buffer_tests;
 mod channel_guest_tests;
@@ -33,7 +33,7 @@ struct RoomParticipants {
     pending: Vec<String>,
 }
 
-fn room_participants(room: &Model<Room>, cx: &mut TestAppContext) -> RoomParticipants {
+fn room_participants(room: &Entity<Room>, cx: &mut TestAppContext) -> RoomParticipants {
     room.read_with(cx, |room, _| {
         let mut remote = room
             .remote_participants()
@@ -51,7 +51,7 @@ fn room_participants(room: &Model<Room>, cx: &mut TestAppContext) -> RoomPartici
     })
 }
 
-fn channel_id(room: &Model<Room>, cx: &mut TestAppContext) -> Option<ChannelId> {
+fn channel_id(room: &Entity<Room>, cx: &mut TestAppContext) -> Option<ChannelId> {
     cx.read(|cx| room.read(cx).channel_id())
 }
 

crates/collab/src/tests/channel_buffer_tests.rs 🔗

@@ -9,7 +9,7 @@ use collab_ui::channel_view::ChannelView;
 use collections::HashMap;
 use editor::{Anchor, Editor, ToOffset};
 use futures::future;
-use gpui::{BackgroundExecutor, Model, TestAppContext, ViewContext};
+use gpui::{BackgroundExecutor, Context, Entity, TestAppContext, Window};
 use rpc::{proto::PeerId, RECEIVE_TIMEOUT};
 use serde_json::json;
 use std::ops::Range;
@@ -161,43 +161,43 @@ async fn test_channel_notes_participant_indices(
 
     // Clients A, B, and C open the channel notes
     let channel_view_a = cx_a
-        .update(|cx| ChannelView::open(channel_id, None, workspace_a.clone(), cx))
+        .update(|window, cx| ChannelView::open(channel_id, None, workspace_a.clone(), window, cx))
         .await
         .unwrap();
     let channel_view_b = cx_b
-        .update(|cx| ChannelView::open(channel_id, None, workspace_b.clone(), cx))
+        .update(|window, cx| ChannelView::open(channel_id, None, workspace_b.clone(), window, cx))
         .await
         .unwrap();
     let channel_view_c = cx_c
-        .update(|cx| ChannelView::open(channel_id, None, workspace_c.clone(), cx))
+        .update(|window, cx| ChannelView::open(channel_id, None, workspace_c.clone(), window, cx))
         .await
         .unwrap();
 
     // Clients A, B, and C all insert and select some text
-    channel_view_a.update(cx_a, |notes, cx| {
+    channel_view_a.update_in(cx_a, |notes, window, cx| {
         notes.editor.update(cx, |editor, cx| {
-            editor.insert("a", cx);
-            editor.change_selections(None, cx, |selections| {
+            editor.insert("a", window, cx);
+            editor.change_selections(None, window, cx, |selections| {
                 selections.select_ranges(vec![0..1]);
             });
         });
     });
     executor.run_until_parked();
-    channel_view_b.update(cx_b, |notes, cx| {
+    channel_view_b.update_in(cx_b, |notes, window, cx| {
         notes.editor.update(cx, |editor, cx| {
-            editor.move_down(&Default::default(), cx);
-            editor.insert("b", cx);
-            editor.change_selections(None, cx, |selections| {
+            editor.move_down(&Default::default(), window, cx);
+            editor.insert("b", window, cx);
+            editor.change_selections(None, window, cx, |selections| {
                 selections.select_ranges(vec![1..2]);
             });
         });
     });
     executor.run_until_parked();
-    channel_view_c.update(cx_c, |notes, cx| {
+    channel_view_c.update_in(cx_c, |notes, window, cx| {
         notes.editor.update(cx, |editor, cx| {
-            editor.move_down(&Default::default(), cx);
-            editor.insert("c", cx);
-            editor.change_selections(None, cx, |selections| {
+            editor.move_down(&Default::default(), window, cx);
+            editor.insert("c", window, cx);
+            editor.change_selections(None, window, cx, |selections| {
                 selections.select_ranges(vec![2..3]);
             });
         });
@@ -206,9 +206,9 @@ async fn test_channel_notes_participant_indices(
     // Client A sees clients B and C without assigned colors, because they aren't
     // in a call together.
     executor.run_until_parked();
-    channel_view_a.update(cx_a, |notes, cx| {
+    channel_view_a.update_in(cx_a, |notes, window, cx| {
         notes.editor.update(cx, |editor, cx| {
-            assert_remote_selections(editor, &[(None, 1..2), (None, 2..3)], cx);
+            assert_remote_selections(editor, &[(None, 1..2), (None, 2..3)], window, cx);
         });
     });
 
@@ -222,20 +222,22 @@ async fn test_channel_notes_participant_indices(
     // Clients A and B see each other with two different assigned colors. Client C
     // still doesn't have a color.
     executor.run_until_parked();
-    channel_view_a.update(cx_a, |notes, cx| {
+    channel_view_a.update_in(cx_a, |notes, window, cx| {
         notes.editor.update(cx, |editor, cx| {
             assert_remote_selections(
                 editor,
                 &[(Some(ParticipantIndex(1)), 1..2), (None, 2..3)],
+                window,
                 cx,
             );
         });
     });
-    channel_view_b.update(cx_b, |notes, cx| {
+    channel_view_b.update_in(cx_b, |notes, window, cx| {
         notes.editor.update(cx, |editor, cx| {
             assert_remote_selections(
                 editor,
                 &[(Some(ParticipantIndex(0)), 0..1), (None, 2..3)],
+                window,
                 cx,
             );
         });
@@ -252,8 +254,8 @@ async fn test_channel_notes_participant_indices(
     // Clients A and B open the same file.
     executor.start_waiting();
     let editor_a = workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.open_path((worktree_id_a, "file.txt"), None, true, cx)
+        .update_in(cx_a, |workspace, window, cx| {
+            workspace.open_path((worktree_id_a, "file.txt"), None, true, window, cx)
         })
         .await
         .unwrap()
@@ -261,32 +263,32 @@ async fn test_channel_notes_participant_indices(
         .unwrap();
     executor.start_waiting();
     let editor_b = workspace_b
-        .update(cx_b, |workspace, cx| {
-            workspace.open_path((worktree_id_a, "file.txt"), None, true, cx)
+        .update_in(cx_b, |workspace, window, cx| {
+            workspace.open_path((worktree_id_a, "file.txt"), None, true, window, cx)
         })
         .await
         .unwrap()
         .downcast::<Editor>()
         .unwrap();
 
-    editor_a.update(cx_a, |editor, cx| {
-        editor.change_selections(None, cx, |selections| {
+    editor_a.update_in(cx_a, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |selections| {
             selections.select_ranges(vec![0..1]);
         });
     });
-    editor_b.update(cx_b, |editor, cx| {
-        editor.change_selections(None, cx, |selections| {
+    editor_b.update_in(cx_b, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |selections| {
             selections.select_ranges(vec![2..3]);
         });
     });
     executor.run_until_parked();
 
     // Clients A and B see each other with the same colors as in the channel notes.
-    editor_a.update(cx_a, |editor, cx| {
-        assert_remote_selections(editor, &[(Some(ParticipantIndex(1)), 2..3)], cx);
+    editor_a.update_in(cx_a, |editor, window, cx| {
+        assert_remote_selections(editor, &[(Some(ParticipantIndex(1)), 2..3)], window, cx);
     });
-    editor_b.update(cx_b, |editor, cx| {
-        assert_remote_selections(editor, &[(Some(ParticipantIndex(0)), 0..1)], cx);
+    editor_b.update_in(cx_b, |editor, window, cx| {
+        assert_remote_selections(editor, &[(Some(ParticipantIndex(0)), 0..1)], window, cx);
     });
 }
 
@@ -294,9 +296,10 @@ async fn test_channel_notes_participant_indices(
 fn assert_remote_selections(
     editor: &mut Editor,
     expected_selections: &[(Option<ParticipantIndex>, Range<usize>)],
-    cx: &mut ViewContext<Editor>,
+    window: &mut Window,
+    cx: &mut Context<Editor>,
 ) {
-    let snapshot = editor.snapshot(cx);
+    let snapshot = editor.snapshot(window, cx);
     let range = Anchor::min()..Anchor::max();
     let remote_selections = snapshot
         .remote_selections_in_range(&range, editor.collaboration_hub().unwrap(), cx)
@@ -641,9 +644,9 @@ async fn test_channel_buffer_changes(
     });
 
     // Closing the buffer should re-enable change tracking
-    cx_b.update(|cx| {
+    cx_b.update(|window, cx| {
         workspace_b.update(cx, |workspace, cx| {
-            workspace.close_all_items_and_panes(&Default::default(), cx)
+            workspace.close_all_items_and_panes(&Default::default(), window, cx)
         });
     });
     deterministic.run_until_parked();
@@ -691,6 +694,6 @@ fn assert_collaborators(collaborators: &HashMap<PeerId, Collaborator>, ids: &[Op
     );
 }
 
-fn buffer_text(channel_buffer: &Model<language::Buffer>, cx: &mut TestAppContext) -> String {
+fn buffer_text(channel_buffer: &Entity<language::Buffer>, cx: &mut TestAppContext) -> String {
     channel_buffer.read_with(cx, |buffer, _| buffer.text())
 }

crates/collab/src/tests/channel_guest_tests.rs 🔗

@@ -107,7 +107,7 @@ async fn test_channel_guest_promotion(cx_a: &mut TestAppContext, cx_b: &mut Test
     });
     assert!(project_b.read_with(cx_b, |project, cx| project.is_read_only(cx)));
     assert!(editor_b.update(cx_b, |e, cx| e.read_only(cx)));
-    cx_b.update(|cx_b| {
+    cx_b.update(|_window, cx_b| {
         assert!(room_b.read_with(cx_b, |room, _| !room.can_use_microphone()));
     });
     assert!(room_b
@@ -135,7 +135,7 @@ async fn test_channel_guest_promotion(cx_a: &mut TestAppContext, cx_b: &mut Test
     assert!(editor_b.update(cx_b, |editor, cx| !editor.read_only(cx)));
 
     // B sees themselves as muted, and can unmute.
-    cx_b.update(|cx_b| {
+    cx_b.update(|_window, cx_b| {
         assert!(room_b.read_with(cx_b, |room, _| room.can_use_microphone()));
     });
     room_b.read_with(cx_b, |room, _| assert!(room.is_muted()));

crates/collab/src/tests/channel_message_tests.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{rpc::RECONNECT_TIMEOUT, tests::TestServer};
 use channel::{ChannelChat, ChannelMessageId, MessageParams};
 use collab_ui::chat_panel::ChatPanel;
-use gpui::{BackgroundExecutor, Model, TestAppContext};
+use gpui::{BackgroundExecutor, Entity, TestAppContext};
 use rpc::Notification;
 use workspace::dock::Panel;
 
@@ -295,7 +295,7 @@ async fn test_remove_channel_message(
 }
 
 #[track_caller]
-fn assert_messages(chat: &Model<ChannelChat>, messages: &[&str], cx: &mut TestAppContext) {
+fn assert_messages(chat: &Entity<ChannelChat>, messages: &[&str], cx: &mut TestAppContext) {
     assert_eq!(
         chat.read_with(cx, |chat, _| {
             chat.messages()
@@ -356,10 +356,10 @@ async fn test_channel_message_changes(
     let project_b = client_b.build_empty_local_project(cx_b);
     let (workspace_b, cx_b) = client_b.build_workspace(&project_b, cx_b);
 
-    let chat_panel_b = workspace_b.update(cx_b, ChatPanel::new);
+    let chat_panel_b = workspace_b.update_in(cx_b, ChatPanel::new);
     chat_panel_b
-        .update(cx_b, |chat_panel, cx| {
-            chat_panel.set_active(true, cx);
+        .update_in(cx_b, |chat_panel, window, cx| {
+            chat_panel.set_active(true, window, cx);
             chat_panel.select_channel(channel_id, None, cx)
         })
         .await
@@ -367,7 +367,7 @@ async fn test_channel_message_changes(
 
     executor.run_until_parked();
 
-    let b_has_messages = cx_b.update(|cx| {
+    let b_has_messages = cx_b.update(|_, cx| {
         client_b
             .channel_store()
             .read(cx)
@@ -384,7 +384,7 @@ async fn test_channel_message_changes(
 
     executor.run_until_parked();
 
-    let b_has_messages = cx_b.update(|cx| {
+    let b_has_messages = cx_b.update(|_, cx| {
         client_b
             .channel_store()
             .read(cx)
@@ -394,8 +394,8 @@ async fn test_channel_message_changes(
     assert!(!b_has_messages);
 
     // Sending a message while the chat is closed should change the flag.
-    chat_panel_b.update(cx_b, |chat_panel, cx| {
-        chat_panel.set_active(false, cx);
+    chat_panel_b.update_in(cx_b, |chat_panel, window, cx| {
+        chat_panel.set_active(false, window, cx);
     });
 
     // Sending a message while the chat is open should not change the flag.
@@ -406,7 +406,7 @@ async fn test_channel_message_changes(
 
     executor.run_until_parked();
 
-    let b_has_messages = cx_b.update(|cx| {
+    let b_has_messages = cx_b.update(|_, cx| {
         client_b
             .channel_store()
             .read(cx)
@@ -416,7 +416,7 @@ async fn test_channel_message_changes(
     assert!(b_has_messages);
 
     // Closing the chat should re-enable change tracking
-    cx_b.update(|_| drop(chat_panel_b));
+    cx_b.update(|_, _| drop(chat_panel_b));
 
     channel_chat_a
         .update(cx_a, |c, cx| c.send_message("four".into(), cx).unwrap())
@@ -425,7 +425,7 @@ async fn test_channel_message_changes(
 
     executor.run_until_parked();
 
-    let b_has_messages = cx_b.update(|cx| {
+    let b_has_messages = cx_b.update(|_, cx| {
         client_b
             .channel_store()
             .read(cx)

crates/collab/src/tests/channel_tests.rs 🔗

@@ -7,7 +7,7 @@ use call::ActiveCall;
 use channel::{ChannelMembership, ChannelStore};
 use client::{ChannelId, User};
 use futures::future::try_join_all;
-use gpui::{BackgroundExecutor, Model, SharedString, TestAppContext};
+use gpui::{BackgroundExecutor, Entity, SharedString, TestAppContext};
 use rpc::{
     proto::{self, ChannelRole},
     RECEIVE_TIMEOUT,
@@ -1401,7 +1401,7 @@ struct ExpectedChannel {
 
 #[track_caller]
 fn assert_channel_invitations(
-    channel_store: &Model<ChannelStore>,
+    channel_store: &Entity<ChannelStore>,
     cx: &TestAppContext,
     expected_channels: &[ExpectedChannel],
 ) {
@@ -1423,7 +1423,7 @@ fn assert_channel_invitations(
 
 #[track_caller]
 fn assert_channels(
-    channel_store: &Model<ChannelStore>,
+    channel_store: &Entity<ChannelStore>,
     cx: &TestAppContext,
     expected_channels: &[ExpectedChannel],
 ) {
@@ -1444,7 +1444,7 @@ fn assert_channels(
 
 #[track_caller]
 fn assert_channels_list_shape(
-    channel_store: &Model<ChannelStore>,
+    channel_store: &Entity<ChannelStore>,
     cx: &TestAppContext,
     expected_channels: &[(ChannelId, usize)],
 ) {

crates/collab/src/tests/editor_tests.rs 🔗

@@ -81,14 +81,21 @@ async fn test_host_disconnect(
 
     assert!(worktree_a.read_with(cx_a, |tree, _| tree.has_update_observer()));
 
-    let workspace_b = cx_b
-        .add_window(|cx| Workspace::new(None, project_b.clone(), client_b.app_state.clone(), cx));
+    let workspace_b = cx_b.add_window(|window, cx| {
+        Workspace::new(
+            None,
+            project_b.clone(),
+            client_b.app_state.clone(),
+            window,
+            cx,
+        )
+    });
     let cx_b = &mut VisualTestContext::from_window(*workspace_b, cx_b);
-    let workspace_b_view = workspace_b.root_view(cx_b).unwrap();
+    let workspace_b_view = workspace_b.root_model(cx_b).unwrap();
 
     let editor_b = workspace_b
-        .update(cx_b, |workspace, cx| {
-            workspace.open_path((worktree_id, "b.txt"), None, true, cx)
+        .update(cx_b, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "b.txt"), None, true, window, cx)
         })
         .unwrap()
         .await
@@ -97,10 +104,10 @@ async fn test_host_disconnect(
         .unwrap();
 
     //TODO: focus
-    assert!(cx_b.update_view(&editor_b, |editor, cx| editor.is_focused(cx)));
-    editor_b.update(cx_b, |editor, cx| editor.insert("X", cx));
+    assert!(cx_b.update_window_model(&editor_b, |editor, window, _| editor.is_focused(window)));
+    editor_b.update_in(cx_b, |editor, window, cx| editor.insert("X", window, cx));
 
-    cx_b.update(|cx| {
+    cx_b.update(|_, cx| {
         assert!(workspace_b_view.read(cx).is_edited());
     });
 
@@ -120,7 +127,7 @@ async fn test_host_disconnect(
 
     // Ensure client B's edited state is reset and that the whole window is blurred.
     workspace_b
-        .update(cx_b, |workspace, cx| {
+        .update(cx_b, |workspace, _, cx| {
             assert!(workspace.active_modal::<DisconnectedOverlay>(cx).is_some());
             assert!(!workspace.is_edited());
         })
@@ -128,8 +135,8 @@ async fn test_host_disconnect(
 
     // Ensure client B is not prompted to save edits when closing window after disconnecting.
     let can_close = workspace_b
-        .update(cx_b, |workspace, cx| {
-            workspace.prepare_to_close(CloseIntent::Quit, cx)
+        .update(cx_b, |workspace, window, cx| {
+            workspace.prepare_to_close(CloseIntent::Quit, window, cx)
         })
         .unwrap()
         .await
@@ -200,11 +207,12 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor(
         .await
         .unwrap();
     let cx_a = cx_a.add_empty_window();
-    let editor_a = cx_a.new_view(|cx| Editor::for_buffer(buffer_a, Some(project_a), cx));
+    let editor_a = cx_a
+        .new_window_model(|window, cx| Editor::for_buffer(buffer_a, Some(project_a), window, cx));
 
     let mut editor_cx_a = EditorTestContext {
         cx: cx_a.clone(),
-        window: cx_a.handle(),
+        window: cx_a.window_handle(),
         editor: editor_a,
         assertion_cx: AssertionContextManager::new(),
     };
@@ -215,11 +223,12 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor(
         .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
         .await
         .unwrap();
-    let editor_b = cx_b.new_view(|cx| Editor::for_buffer(buffer_b, Some(project_b), cx));
+    let editor_b = cx_b
+        .new_window_model(|window, cx| Editor::for_buffer(buffer_b, Some(project_b), window, cx));
 
     let mut editor_cx_b = EditorTestContext {
         cx: cx_b.clone(),
-        window: cx_b.handle(),
+        window: cx_b.window_handle(),
         editor: editor_b,
         assertion_cx: AssertionContextManager::new(),
     };
@@ -231,8 +240,9 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor(
     editor_cx_b.set_selections_state(indoc! {"
         Some textˇ
     "});
-    editor_cx_a
-        .update_editor(|editor, cx| editor.newline_above(&editor::actions::NewlineAbove, cx));
+    editor_cx_a.update_editor(|editor, window, cx| {
+        editor.newline_above(&editor::actions::NewlineAbove, window, cx)
+    });
     executor.run_until_parked();
     editor_cx_a.assert_editor_state(indoc! {"
         ˇ
@@ -252,8 +262,9 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor(
 
         Some textˇ
     "});
-    editor_cx_a
-        .update_editor(|editor, cx| editor.newline_below(&editor::actions::NewlineBelow, cx));
+    editor_cx_a.update_editor(|editor, window, cx| {
+        editor.newline_below(&editor::actions::NewlineBelow, window, cx)
+    });
     executor.run_until_parked();
     editor_cx_a.assert_editor_state(indoc! {"
 
@@ -316,8 +327,9 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu
         .await
         .unwrap();
     let cx_b = cx_b.add_empty_window();
-    let editor_b =
-        cx_b.new_view(|cx| Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx));
+    let editor_b = cx_b.new_window_model(|window, cx| {
+        Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), window, cx)
+    });
 
     let fake_language_server = fake_language_servers.next().await.unwrap();
     cx_a.background_executor.run_until_parked();
@@ -327,11 +339,11 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu
     });
 
     // Type a completion trigger character as the guest.
-    editor_b.update(cx_b, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-        editor.handle_input(".", cx);
+    editor_b.update_in(cx_b, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |s| s.select_ranges([13..13]));
+        editor.handle_input(".", window, cx);
     });
-    cx_b.focus_view(&editor_b);
+    cx_b.focus(&editor_b);
 
     // Receive a completion request as the host's language server.
     // Return some completions from the host's language server.
@@ -393,9 +405,9 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu
     });
 
     // Confirm a completion on the guest.
-    editor_b.update(cx_b, |editor, cx| {
+    editor_b.update_in(cx_b, |editor, window, cx| {
         assert!(editor.context_menu_visible());
-        editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, cx);
+        editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, window, cx);
         assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
     });
 
@@ -440,10 +452,10 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu
 
     // Now we do a second completion, this time to ensure that documentation/snippets are
     // resolved
-    editor_b.update(cx_b, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges([46..46]));
-        editor.handle_input("; a", cx);
-        editor.handle_input(".", cx);
+    editor_b.update_in(cx_b, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |s| s.select_ranges([46..46]));
+        editor.handle_input("; a", window, cx);
+        editor.handle_input(".", window, cx);
     });
 
     buffer_b.read_with(cx_b, |buffer, _| {
@@ -507,18 +519,18 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu
 
     completion_response.next().await.unwrap();
 
-    editor_b.update(cx_b, |editor, cx| {
+    editor_b.update_in(cx_b, |editor, window, cx| {
         assert!(editor.context_menu_visible());
-        editor.context_menu_first(&ContextMenuFirst {}, cx);
+        editor.context_menu_first(&ContextMenuFirst {}, window, cx);
     });
 
     resolve_completion_response.next().await.unwrap();
     cx_b.executor().run_until_parked();
 
     // When accepting the completion, the snippet is insert.
-    editor_b.update(cx_b, |editor, cx| {
+    editor_b.update_in(cx_b, |editor, window, cx| {
         assert!(editor.context_menu_visible());
-        editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, cx);
+        editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, window, cx);
         assert_eq!(
             editor.text(cx),
             "use d::SomeTrait;\nfn main() { a.first_method(); a.third_method(, , ) }"
@@ -568,8 +580,8 @@ async fn test_collaborating_with_code_actions(
     let project_b = client_b.join_remote_project(project_id, cx_b).await;
     let (workspace_b, cx_b) = client_b.build_workspace(&project_b, cx_b);
     let editor_b = workspace_b
-        .update(cx_b, |workspace, cx| {
-            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
+        .update_in(cx_b, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
         })
         .await
         .unwrap()
@@ -592,12 +604,12 @@ async fn test_collaborating_with_code_actions(
     requests.next().await;
 
     // Move cursor to a location that contains code actions.
-    editor_b.update(cx_b, |editor, cx| {
-        editor.change_selections(None, cx, |s| {
+    editor_b.update_in(cx_b, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |s| {
             s.select_ranges([Point::new(1, 31)..Point::new(1, 31)])
         });
     });
-    cx_b.focus_view(&editor_b);
+    cx_b.focus(&editor_b);
 
     let mut requests = fake_language_server
         .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
@@ -657,11 +669,12 @@ async fn test_collaborating_with_code_actions(
     requests.next().await;
 
     // Toggle code actions and wait for them to display.
-    editor_b.update(cx_b, |editor, cx| {
+    editor_b.update_in(cx_b, |editor, window, cx| {
         editor.toggle_code_actions(
             &ToggleCodeActions {
                 deployed_from_indicator: None,
             },
+            window,
             cx,
         );
     });
@@ -673,8 +686,8 @@ async fn test_collaborating_with_code_actions(
 
     // Confirming the code action will trigger a resolve request.
     let confirm_action = editor_b
-        .update(cx_b, |editor, cx| {
-            Editor::confirm_code_action(editor, &ConfirmCodeAction { item_ix: Some(0) }, cx)
+        .update_in(cx_b, |editor, window, cx| {
+            Editor::confirm_code_action(editor, &ConfirmCodeAction { item_ix: Some(0) }, window, cx)
         })
         .unwrap();
     fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
@@ -725,14 +738,14 @@ async fn test_collaborating_with_code_actions(
             .downcast::<Editor>()
             .unwrap()
     });
-    code_action_editor.update(cx_b, |editor, cx| {
+    code_action_editor.update_in(cx_b, |editor, window, cx| {
         assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
-        editor.undo(&Undo, cx);
+        editor.undo(&Undo, window, cx);
         assert_eq!(
             editor.text(cx),
             "mod other;\nfn main() { let foo = other::foo(); }\npub fn foo() -> usize { 4 }"
         );
-        editor.redo(&Redo, cx);
+        editor.redo(&Redo, window, cx);
         assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
     });
 }
@@ -784,8 +797,8 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
 
     let (workspace_b, cx_b) = client_b.build_workspace(&project_b, cx_b);
     let editor_b = workspace_b
-        .update(cx_b, |workspace, cx| {
-            workspace.open_path((worktree_id, "one.rs"), None, true, cx)
+        .update_in(cx_b, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "one.rs"), None, true, window, cx)
         })
         .await
         .unwrap()
@@ -794,9 +807,9 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
     let fake_language_server = fake_language_servers.next().await.unwrap();
 
     // Move cursor to a location that can be renamed.
-    let prepare_rename = editor_b.update(cx_b, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges([7..7]));
-        editor.rename(&Rename, cx).unwrap()
+    let prepare_rename = editor_b.update_in(cx_b, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |s| s.select_ranges([7..7]));
+        editor.rename(&Rename, window, cx).unwrap()
     });
 
     fake_language_server
@@ -834,12 +847,12 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
     });
 
     // Cancel the rename, and repeat the same, but use selections instead of cursor movement
-    editor_b.update(cx_b, |editor, cx| {
-        editor.cancel(&editor::actions::Cancel, cx);
+    editor_b.update_in(cx_b, |editor, window, cx| {
+        editor.cancel(&editor::actions::Cancel, window, cx);
     });
-    let prepare_rename = editor_b.update(cx_b, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges([7..8]));
-        editor.rename(&Rename, cx).unwrap()
+    let prepare_rename = editor_b.update_in(cx_b, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |s| s.select_ranges([7..8]));
+        editor.rename(&Rename, window, cx).unwrap()
     });
 
     fake_language_server
@@ -875,8 +888,8 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
         });
     });
 
-    let confirm_rename = editor_b.update(cx_b, |editor, cx| {
-        Editor::confirm_rename(editor, &ConfirmRename, cx).unwrap()
+    let confirm_rename = editor_b.update_in(cx_b, |editor, window, cx| {
+        Editor::confirm_rename(editor, &ConfirmRename, window, cx).unwrap()
     });
     fake_language_server
         .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
@@ -934,17 +947,17 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
         workspace.active_item_as::<Editor>(cx).unwrap()
     });
 
-    rename_editor.update(cx_b, |editor, cx| {
+    rename_editor.update_in(cx_b, |editor, window, cx| {
         assert_eq!(
             editor.text(cx),
             "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
         );
-        editor.undo(&Undo, cx);
+        editor.undo(&Undo, window, cx);
         assert_eq!(
             editor.text(cx),
             "const ONE: usize = 1;\nconst TWO: usize = one::ONE + one::ONE;"
         );
-        editor.redo(&Redo, cx);
+        editor.redo(&Redo, window, cx);
         assert_eq!(
             editor.text(cx),
             "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
@@ -952,12 +965,12 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
     });
 
     // Ensure temporary rename edits cannot be undone/redone.
-    editor_b.update(cx_b, |editor, cx| {
-        editor.undo(&Undo, cx);
+    editor_b.update_in(cx_b, |editor, window, cx| {
+        editor.undo(&Undo, window, cx);
         assert_eq!(editor.text(cx), "const ONE: usize = 1;");
-        editor.undo(&Undo, cx);
+        editor.undo(&Undo, window, cx);
         assert_eq!(editor.text(cx), "const ONE: usize = 1;");
-        editor.redo(&Redo, cx);
+        editor.redo(&Redo, window, cx);
         assert_eq!(editor.text(cx), "const THREE: usize = 1;");
     })
 }
@@ -1192,7 +1205,8 @@ async fn test_share_project(
         .await
         .unwrap();
 
-    let editor_b = cx_b.new_view(|cx| Editor::for_buffer(buffer_b, None, cx));
+    let editor_b =
+        cx_b.new_window_model(|window, cx| Editor::for_buffer(buffer_b, None, window, cx));
 
     // Client A sees client B's selection
     executor.run_until_parked();
@@ -1206,7 +1220,9 @@ async fn test_share_project(
     });
 
     // Edit the buffer as client B and see that edit as client A.
-    editor_b.update(cx_b, |editor, cx| editor.handle_input("ok, ", cx));
+    editor_b.update_in(cx_b, |editor, window, cx| {
+        editor.handle_input("ok, ", window, cx)
+    });
     executor.run_until_parked();
 
     buffer_a.read_with(cx_a, |buffer, _| {
@@ -1233,7 +1249,7 @@ async fn test_share_project(
     let _project_c = client_c.join_remote_project(initial_project.id, cx_c).await;
 
     // Client B closes the editor, and client A sees client B's selections removed.
-    cx_b.update(move |_| drop(editor_b));
+    cx_b.update(move |_, _| drop(editor_b));
     executor.run_until_parked();
 
     buffer_a.read_with(cx_a, |buffer, _| {
@@ -1297,7 +1313,9 @@ async fn test_on_input_format_from_host_to_guest(
         .await
         .unwrap();
     let cx_a = cx_a.add_empty_window();
-    let editor_a = cx_a.new_view(|cx| Editor::for_buffer(buffer_a, Some(project_a.clone()), cx));
+    let editor_a = cx_a.new_window_model(|window, cx| {
+        Editor::for_buffer(buffer_a, Some(project_a.clone()), window, cx)
+    });
 
     let fake_language_server = fake_language_servers.next().await.unwrap();
     executor.run_until_parked();
@@ -1329,10 +1347,10 @@ async fn test_on_input_format_from_host_to_guest(
         .unwrap();
 
     // Type a on type formatting trigger character as the guest.
-    cx_a.focus_view(&editor_a);
-    editor_a.update(cx_a, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-        editor.handle_input(">", cx);
+    cx_a.focus(&editor_a);
+    editor_a.update_in(cx_a, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |s| s.select_ranges([13..13]));
+        editor.handle_input(">", window, cx);
     });
 
     executor.run_until_parked();
@@ -1342,9 +1360,9 @@ async fn test_on_input_format_from_host_to_guest(
     });
 
     // Undo should remove LSP edits first
-    editor_a.update(cx_a, |editor, cx| {
+    editor_a.update_in(cx_a, |editor, window, cx| {
         assert_eq!(editor.text(cx), "fn main() { a>~< }");
-        editor.undo(&Undo, cx);
+        editor.undo(&Undo, window, cx);
         assert_eq!(editor.text(cx), "fn main() { a> }");
     });
     executor.run_until_parked();
@@ -1353,9 +1371,9 @@ async fn test_on_input_format_from_host_to_guest(
         assert_eq!(buffer.text(), "fn main() { a> }")
     });
 
-    editor_a.update(cx_a, |editor, cx| {
+    editor_a.update_in(cx_a, |editor, window, cx| {
         assert_eq!(editor.text(cx), "fn main() { a> }");
-        editor.undo(&Undo, cx);
+        editor.undo(&Undo, window, cx);
         assert_eq!(editor.text(cx), "fn main() { a }");
     });
     executor.run_until_parked();
@@ -1417,16 +1435,18 @@ async fn test_on_input_format_from_guest_to_host(
         .await
         .unwrap();
     let cx_b = cx_b.add_empty_window();
-    let editor_b = cx_b.new_view(|cx| Editor::for_buffer(buffer_b, Some(project_b.clone()), cx));
+    let editor_b = cx_b.new_window_model(|window, cx| {
+        Editor::for_buffer(buffer_b, Some(project_b.clone()), window, cx)
+    });
 
     let fake_language_server = fake_language_servers.next().await.unwrap();
     executor.run_until_parked();
 
     // Type a on type formatting trigger character as the guest.
-    cx_b.focus_view(&editor_b);
-    editor_b.update(cx_b, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-        editor.handle_input(":", cx);
+    cx_b.focus(&editor_b);
+    editor_b.update_in(cx_b, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |s| s.select_ranges([13..13]));
+        editor.handle_input(":", window, cx);
     });
 
     // Receive an OnTypeFormatting request as the host's language server.
@@ -1465,9 +1485,9 @@ async fn test_on_input_format_from_guest_to_host(
     });
 
     // Undo should remove LSP edits first
-    editor_b.update(cx_b, |editor, cx| {
+    editor_b.update_in(cx_b, |editor, window, cx| {
         assert_eq!(editor.text(cx), "fn main() { a:~: }");
-        editor.undo(&Undo, cx);
+        editor.undo(&Undo, window, cx);
         assert_eq!(editor.text(cx), "fn main() { a: }");
     });
     executor.run_until_parked();
@@ -1476,9 +1496,9 @@ async fn test_on_input_format_from_guest_to_host(
         assert_eq!(buffer.text(), "fn main() { a: }")
     });
 
-    editor_b.update(cx_b, |editor, cx| {
+    editor_b.update_in(cx_b, |editor, window, cx| {
         assert_eq!(editor.text(cx), "fn main() { a: }");
-        editor.undo(&Undo, cx);
+        editor.undo(&Undo, window, cx);
         assert_eq!(editor.text(cx), "fn main() { a }");
     });
     executor.run_until_parked();
@@ -1589,8 +1609,8 @@ async fn test_mutual_editor_inlay_hint_cache_update(
         .await
         .unwrap();
     let editor_a = workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
+        .update_in(cx_a, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
         })
         .await
         .unwrap()
@@ -1639,8 +1659,8 @@ async fn test_mutual_editor_inlay_hint_cache_update(
     });
     let (workspace_b, cx_b) = client_b.build_workspace(&project_b, cx_b);
     let editor_b = workspace_b
-        .update(cx_b, |workspace, cx| {
-            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
+        .update_in(cx_b, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
         })
         .await
         .unwrap()
@@ -1657,11 +1677,11 @@ async fn test_mutual_editor_inlay_hint_cache_update(
     });
 
     let after_client_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
-    editor_b.update(cx_b, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges([13..13].clone()));
-        editor.handle_input(":", cx);
+    editor_b.update_in(cx_b, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |s| s.select_ranges([13..13].clone()));
+        editor.handle_input(":", window, cx);
     });
-    cx_b.focus_view(&editor_b);
+    cx_b.focus(&editor_b);
 
     executor.run_until_parked();
     editor_a.update(cx_a, |editor, _| {
@@ -1678,11 +1698,11 @@ async fn test_mutual_editor_inlay_hint_cache_update(
     });
 
     let after_host_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
-    editor_a.update(cx_a, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-        editor.handle_input("a change to increment both buffers' versions", cx);
+    editor_a.update_in(cx_a, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |s| s.select_ranges([13..13]));
+        editor.handle_input("a change to increment both buffers' versions", window, cx);
     });
-    cx_a.focus_view(&editor_a);
+    cx_a.focus(&editor_a);
 
     executor.run_until_parked();
     editor_a.update(cx_a, |editor, _| {
@@ -1815,8 +1835,8 @@ async fn test_inlay_hint_refresh_is_forwarded(
     cx_a.background_executor.start_waiting();
 
     let editor_a = workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
+        .update_in(cx_a, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
         })
         .await
         .unwrap()
@@ -1824,8 +1844,8 @@ async fn test_inlay_hint_refresh_is_forwarded(
         .unwrap();
 
     let editor_b = workspace_b
-        .update(cx_b, |workspace, cx| {
-            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
+        .update_in(cx_b, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
         })
         .await
         .unwrap()
@@ -1985,8 +2005,8 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
     // Create editor_a
     let (workspace_a, cx_a) = client_a.build_workspace(&project_a, cx_a);
     let editor_a = workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.open_path((worktree_id, "file.txt"), None, true, cx)
+        .update_in(cx_a, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "file.txt"), None, true, window, cx)
         })
         .await
         .unwrap()
@@ -1997,8 +2017,8 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
     let project_b = client_b.join_remote_project(project_id, cx_b).await;
     let (workspace_b, cx_b) = client_b.build_workspace(&project_b, cx_b);
     let editor_b = workspace_b
-        .update(cx_b, |workspace, cx| {
-            workspace.open_path((worktree_id, "file.txt"), None, true, cx)
+        .update_in(cx_b, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "file.txt"), None, true, window, cx)
         })
         .await
         .unwrap()
@@ -2006,9 +2026,9 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
         .unwrap();
 
     // client_b now requests git blame for the open buffer
-    editor_b.update(cx_b, |editor_b, cx| {
+    editor_b.update_in(cx_b, |editor_b, window, cx| {
         assert!(editor_b.blame().is_none());
-        editor_b.toggle_git_blame(&editor::actions::ToggleGitBlame {}, cx);
+        editor_b.toggle_git_blame(&editor::actions::ToggleGitBlame {}, window, cx);
     });
 
     cx_a.executor().run_until_parked();
@@ -2054,7 +2074,7 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
 
     // editor_b updates the file, which gets sent to client_a, which updates git blame,
     // which gets back to client_b.
-    editor_b.update(cx_b, |editor_b, cx| {
+    editor_b.update_in(cx_b, |editor_b, _, cx| {
         editor_b.edit([(Point::new(0, 3)..Point::new(0, 3), "FOO")], cx);
     });
 
@@ -2089,7 +2109,7 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
     });
 
     // Now editor_a also updates the file
-    editor_a.update(cx_a, |editor_a, cx| {
+    editor_a.update_in(cx_a, |editor_a, _, cx| {
         editor_a.edit([(Point::new(1, 3)..Point::new(1, 3), "FOO")], cx);
     });
 
@@ -2175,19 +2195,21 @@ async fn test_collaborating_with_editorconfig(
         .await
         .unwrap();
     let cx_a = cx_a.add_empty_window();
-    let main_editor_a =
-        cx_a.new_view(|cx| Editor::for_buffer(main_buffer_a, Some(project_a.clone()), cx));
-    let other_editor_a =
-        cx_a.new_view(|cx| Editor::for_buffer(other_buffer_a, Some(project_a), cx));
+    let main_editor_a = cx_a.new_window_model(|window, cx| {
+        Editor::for_buffer(main_buffer_a, Some(project_a.clone()), window, cx)
+    });
+    let other_editor_a = cx_a.new_window_model(|window, cx| {
+        Editor::for_buffer(other_buffer_a, Some(project_a), window, cx)
+    });
     let mut main_editor_cx_a = EditorTestContext {
         cx: cx_a.clone(),
-        window: cx_a.handle(),
+        window: cx_a.window_handle(),
         editor: main_editor_a,
         assertion_cx: AssertionContextManager::new(),
     };
     let mut other_editor_cx_a = EditorTestContext {
         cx: cx_a.clone(),
-        window: cx_a.handle(),
+        window: cx_a.window_handle(),
         editor: other_editor_a,
         assertion_cx: AssertionContextManager::new(),
     };
@@ -2207,19 +2229,21 @@ async fn test_collaborating_with_editorconfig(
         .await
         .unwrap();
     let cx_b = cx_b.add_empty_window();
-    let main_editor_b =
-        cx_b.new_view(|cx| Editor::for_buffer(main_buffer_b, Some(project_b.clone()), cx));
-    let other_editor_b =
-        cx_b.new_view(|cx| Editor::for_buffer(other_buffer_b, Some(project_b.clone()), cx));
+    let main_editor_b = cx_b.new_window_model(|window, cx| {
+        Editor::for_buffer(main_buffer_b, Some(project_b.clone()), window, cx)
+    });
+    let other_editor_b = cx_b.new_window_model(|window, cx| {
+        Editor::for_buffer(other_buffer_b, Some(project_b.clone()), window, cx)
+    });
     let mut main_editor_cx_b = EditorTestContext {
         cx: cx_b.clone(),
-        window: cx_b.handle(),
+        window: cx_b.window_handle(),
         editor: main_editor_b,
         assertion_cx: AssertionContextManager::new(),
     };
     let mut other_editor_cx_b = EditorTestContext {
         cx: cx_b.clone(),
-        window: cx_b.handle(),
+        window: cx_b.window_handle(),
         editor: other_editor_b,
         assertion_cx: AssertionContextManager::new(),
     };
@@ -2383,12 +2407,12 @@ fn tab_undo_assert(
     cx_b.assert_editor_state(expected_initial);
 
     if a_tabs {
-        cx_a.update_editor(|editor, cx| {
-            editor.tab(&editor::actions::Tab, cx);
+        cx_a.update_editor(|editor, window, cx| {
+            editor.tab(&editor::actions::Tab, window, cx);
         });
     } else {
-        cx_b.update_editor(|editor, cx| {
-            editor.tab(&editor::actions::Tab, cx);
+        cx_b.update_editor(|editor, window, cx| {
+            editor.tab(&editor::actions::Tab, window, cx);
         });
     }
 
@@ -2399,12 +2423,12 @@ fn tab_undo_assert(
     cx_b.assert_editor_state(expected_tabbed);
 
     if a_tabs {
-        cx_a.update_editor(|editor, cx| {
-            editor.undo(&editor::actions::Undo, cx);
+        cx_a.update_editor(|editor, window, cx| {
+            editor.undo(&editor::actions::Undo, window, cx);
         });
     } else {
-        cx_b.update_editor(|editor, cx| {
-            editor.undo(&editor::actions::Undo, cx);
+        cx_b.update_editor(|editor, window, cx| {
+            editor.undo(&editor::actions::Undo, window, cx);
         });
     }
     cx_a.run_until_parked();

crates/collab/src/tests/following_tests.rs 🔗

@@ -8,8 +8,8 @@ use collab_ui::{
 };
 use editor::{Editor, ExcerptRange, MultiBuffer};
 use gpui::{
-    point, BackgroundExecutor, BorrowAppContext, Context, Entity, SharedString, TestAppContext,
-    View, VisualContext, VisualTestContext,
+    point, AppContext as _, BackgroundExecutor, BorrowAppContext, Entity, SharedString,
+    TestAppContext, VisualTestContext,
 };
 use language::Capability;
 use project::WorktreeSettings;
@@ -77,23 +77,23 @@ async fn test_basic_following(
     let (workspace_a, cx_a) = client_a.build_workspace(&project_a, cx_a);
     let (workspace_b, cx_b) = client_b.build_workspace(&project_b, cx_b);
 
-    cx_b.update(|cx| {
-        assert!(cx.is_window_active());
+    cx_b.update(|window, _| {
+        assert!(window.is_window_active());
     });
 
     // Client A opens some editors.
     let pane_a = workspace_a.update(cx_a, |workspace, _| workspace.active_pane().clone());
     let editor_a1 = workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
+        .update_in(cx_a, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "1.txt"), None, true, window, cx)
         })
         .await
         .unwrap()
         .downcast::<Editor>()
         .unwrap();
     let editor_a2 = workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
+        .update_in(cx_a, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "2.txt"), None, true, window, cx)
         })
         .await
         .unwrap()
@@ -102,8 +102,8 @@ async fn test_basic_following(
 
     // Client B opens an editor.
     let editor_b1 = workspace_b
-        .update(cx_b, |workspace, cx| {
-            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
+        .update_in(cx_b, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "1.txt"), None, true, window, cx)
         })
         .await
         .unwrap()
@@ -116,22 +116,24 @@ async fn test_basic_following(
     let peer_id_d = client_d.peer_id().unwrap();
 
     // Client A updates their selections in those editors
-    editor_a1.update(cx_a, |editor, cx| {
-        editor.handle_input("a", cx);
-        editor.handle_input("b", cx);
-        editor.handle_input("c", cx);
-        editor.select_left(&Default::default(), cx);
+    editor_a1.update_in(cx_a, |editor, window, cx| {
+        editor.handle_input("a", window, cx);
+        editor.handle_input("b", window, cx);
+        editor.handle_input("c", window, cx);
+        editor.select_left(&Default::default(), window, cx);
         assert_eq!(editor.selections.ranges(cx), vec![3..2]);
     });
-    editor_a2.update(cx_a, |editor, cx| {
-        editor.handle_input("d", cx);
-        editor.handle_input("e", cx);
-        editor.select_left(&Default::default(), cx);
+    editor_a2.update_in(cx_a, |editor, window, cx| {
+        editor.handle_input("d", window, cx);
+        editor.handle_input("e", window, cx);
+        editor.select_left(&Default::default(), window, cx);
         assert_eq!(editor.selections.ranges(cx), vec![2..1]);
     });
 
     // When client B starts following client A, only the active view state is replicated to client B.
-    workspace_b.update(cx_b, |workspace, cx| workspace.follow(peer_id_a, cx));
+    workspace_b.update_in(cx_b, |workspace, window, cx| {
+        workspace.follow(peer_id_a, window, cx)
+    });
 
     cx_c.executor().run_until_parked();
     let editor_b2 = workspace_b.update(cx_b, |workspace, cx| {
@@ -165,7 +167,9 @@ async fn test_basic_following(
     drop(project_c);
 
     // Client C also follows client A.
-    workspace_c.update(cx_c, |workspace, cx| workspace.follow(peer_id_a, cx));
+    workspace_c.update_in(cx_c, |workspace, window, cx| {
+        workspace.follow(peer_id_a, window, cx)
+    });
 
     cx_d.executor().run_until_parked();
     let active_call_d = cx_d.read(ActiveCall::global);
@@ -188,8 +192,8 @@ async fn test_basic_following(
     }
 
     // Client C unfollows client A.
-    workspace_c.update(cx_c, |workspace, cx| {
-        workspace.unfollow(peer_id_a, cx).unwrap();
+    workspace_c.update_in(cx_c, |workspace, window, cx| {
+        workspace.unfollow(peer_id_a, window, cx).unwrap();
     });
 
     // All clients see that clients B is following client A.
@@ -203,7 +207,9 @@ async fn test_basic_following(
     }
 
     // Client C re-follows client A.
-    workspace_c.update(cx_c, |workspace, cx| workspace.follow(peer_id_a, cx));
+    workspace_c.update_in(cx_c, |workspace, window, cx| {
+        workspace.follow(peer_id_a, window, cx)
+    });
 
     // All clients see that clients B and C are following client A.
     cx_c.executor().run_until_parked();
@@ -216,9 +222,13 @@ async fn test_basic_following(
     }
 
     // Client D follows client B, then switches to following client C.
-    workspace_d.update(cx_d, |workspace, cx| workspace.follow(peer_id_b, cx));
+    workspace_d.update_in(cx_d, |workspace, window, cx| {
+        workspace.follow(peer_id_b, window, cx)
+    });
     cx_a.executor().run_until_parked();
-    workspace_d.update(cx_d, |workspace, cx| workspace.follow(peer_id_c, cx));
+    workspace_d.update_in(cx_d, |workspace, window, cx| {
+        workspace.follow(peer_id_c, window, cx)
+    });
 
     // All clients see that D is following C
     cx_a.executor().run_until_parked();
@@ -235,8 +245,8 @@ async fn test_basic_following(
 
     // Client C closes the project.
     let weak_workspace_c = workspace_c.downgrade();
-    workspace_c.update(cx_c, |workspace, cx| {
-        workspace.close_window(&Default::default(), cx);
+    workspace_c.update_in(cx_c, |workspace, window, cx| {
+        workspace.close_window(&Default::default(), window, cx);
     });
     executor.run_until_parked();
     // are you sure you want to leave the call?
@@ -260,8 +270,8 @@ async fn test_basic_following(
     }
 
     // When client A activates a different editor, client B does so as well.
-    workspace_a.update(cx_a, |workspace, cx| {
-        workspace.activate_item(&editor_a1, true, true, cx)
+    workspace_a.update_in(cx_a, |workspace, window, cx| {
+        workspace.activate_item(&editor_a1, true, true, window, cx)
     });
     executor.run_until_parked();
     workspace_b.update(cx_b, |workspace, cx| {
@@ -272,7 +282,7 @@ async fn test_basic_following(
     });
 
     // When client A opens a multibuffer, client B does so as well.
-    let multibuffer_a = cx_a.new_model(|cx| {
+    let multibuffer_a = cx_a.new(|cx| {
         let buffer_a1 = project_a.update(cx, |project, cx| {
             project
                 .get_open_buffer(&(worktree_id, "1.txt").into(), cx)
@@ -302,11 +312,11 @@ async fn test_basic_following(
         );
         result
     });
-    let multibuffer_editor_a = workspace_a.update(cx_a, |workspace, cx| {
-        let editor = cx.new_view(|cx| {
-            Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), true, cx)
+    let multibuffer_editor_a = workspace_a.update_in(cx_a, |workspace, window, cx| {
+        let editor = cx.new(|cx| {
+            Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), true, window, cx)
         });
-        workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
+        workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
         editor
     });
     executor.run_until_parked();
@@ -324,8 +334,8 @@ async fn test_basic_following(
 
     // When client A navigates back and forth, client B does so as well.
     workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.go_back(workspace.active_pane().downgrade(), cx)
+        .update_in(cx_a, |workspace, window, cx| {
+            workspace.go_back(workspace.active_pane().downgrade(), window, cx)
         })
         .await
         .unwrap();
@@ -338,8 +348,8 @@ async fn test_basic_following(
     });
 
     workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.go_back(workspace.active_pane().downgrade(), cx)
+        .update_in(cx_a, |workspace, window, cx| {
+            workspace.go_back(workspace.active_pane().downgrade(), window, cx)
         })
         .await
         .unwrap();
@@ -352,8 +362,8 @@ async fn test_basic_following(
     });
 
     workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.go_forward(workspace.active_pane().downgrade(), cx)
+        .update_in(cx_a, |workspace, window, cx| {
+            workspace.go_forward(workspace.active_pane().downgrade(), window, cx)
         })
         .await
         .unwrap();
@@ -366,8 +376,8 @@ async fn test_basic_following(
     });
 
     // Changes to client A's editor are reflected on client B.
-    editor_a1.update(cx_a, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2]));
+    editor_a1.update_in(cx_a, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2]));
     });
     executor.advance_clock(workspace::item::LEADER_UPDATE_THROTTLE);
     executor.run_until_parked();
@@ -377,13 +387,15 @@ async fn test_basic_following(
         assert_eq!(editor.selections.ranges(cx), &[1..1, 2..2]);
     });
 
-    editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
+    editor_a1.update_in(cx_a, |editor, window, cx| {
+        editor.set_text("TWO", window, cx)
+    });
     executor.run_until_parked();
     editor_b1.update(cx_b, |editor, cx| assert_eq!(editor.text(cx), "TWO"));
 
-    editor_a1.update(cx_a, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
-        editor.set_scroll_position(point(0., 100.), cx);
+    editor_a1.update_in(cx_a, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
+        editor.set_scroll_position(point(0., 100.), window, cx);
     });
     executor.advance_clock(workspace::item::LEADER_UPDATE_THROTTLE);
     executor.run_until_parked();
@@ -392,11 +404,11 @@ async fn test_basic_following(
     });
 
     // After unfollowing, client B stops receiving updates from client A.
-    workspace_b.update(cx_b, |workspace, cx| {
-        workspace.unfollow(peer_id_a, cx).unwrap()
+    workspace_b.update_in(cx_b, |workspace, window, cx| {
+        workspace.unfollow(peer_id_a, window, cx).unwrap()
     });
-    workspace_a.update(cx_a, |workspace, cx| {
-        workspace.activate_item(&editor_a2, true, true, cx)
+    workspace_a.update_in(cx_a, |workspace, window, cx| {
+        workspace.activate_item(&editor_a2, true, true, window, cx)
     });
     executor.run_until_parked();
     assert_eq!(
@@ -408,14 +420,16 @@ async fn test_basic_following(
     );
 
     // Client A starts following client B.
-    workspace_a.update(cx_a, |workspace, cx| workspace.follow(peer_id_b, cx));
+    workspace_a.update_in(cx_a, |workspace, window, cx| {
+        workspace.follow(peer_id_b, window, cx)
+    });
     executor.run_until_parked();
     assert_eq!(
         workspace_a.update(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
         Some(peer_id_b)
     );
     assert_eq!(
-        workspace_a.update(cx_a, |workspace, cx| workspace
+        workspace_a.update_in(cx_a, |workspace, _, cx| workspace
             .active_item(cx)
             .unwrap()
             .item_id()),
@@ -471,8 +485,8 @@ async fn test_basic_following(
         });
 
         // Client B activates a multibuffer that was created by following client A. Client A returns to that multibuffer.
-        workspace_b.update(cx_b, |workspace, cx| {
-            workspace.activate_item(&multibuffer_editor_b, true, true, cx)
+        workspace_b.update_in(cx_b, |workspace, window, cx| {
+            workspace.activate_item(&multibuffer_editor_b, true, true, window, cx)
         });
         executor.run_until_parked();
         workspace_a.update(cx_a, |workspace, cx| {
@@ -483,10 +497,10 @@ async fn test_basic_following(
         });
 
         // Client B activates a panel, and the previously-opened screen-sharing item gets activated.
-        let panel = cx_b.new_view(|cx| TestPanel::new(DockPosition::Left, cx));
-        workspace_b.update(cx_b, |workspace, cx| {
-            workspace.add_panel(panel, cx);
-            workspace.toggle_panel_focus::<TestPanel>(cx);
+        let panel = cx_b.new(|cx| TestPanel::new(DockPosition::Left, cx));
+        workspace_b.update_in(cx_b, |workspace, window, cx| {
+            workspace.add_panel(panel, window, cx);
+            workspace.toggle_panel_focus::<TestPanel>(window, cx);
         });
         executor.run_until_parked();
         assert_eq!(
@@ -498,8 +512,8 @@ async fn test_basic_following(
         );
 
         // Toggling the focus back to the pane causes client A to return to the multibuffer.
-        workspace_b.update(cx_b, |workspace, cx| {
-            workspace.toggle_panel_focus::<TestPanel>(cx);
+        workspace_b.update_in(cx_b, |workspace, window, cx| {
+            workspace.toggle_panel_focus::<TestPanel>(window, cx);
         });
         executor.run_until_parked();
         workspace_a.update(cx_a, |workspace, cx| {
@@ -511,10 +525,10 @@ async fn test_basic_following(
 
         // Client B activates an item that doesn't implement following,
         // so the previously-opened screen-sharing item gets activated.
-        let unfollowable_item = cx_b.new_view(TestItem::new);
-        workspace_b.update(cx_b, |workspace, cx| {
+        let unfollowable_item = cx_b.new(TestItem::new);
+        workspace_b.update_in(cx_b, |workspace, window, cx| {
             workspace.active_pane().update(cx, |pane, cx| {
-                pane.add_item(Box::new(unfollowable_item), true, true, None, cx)
+                pane.add_item(Box::new(unfollowable_item), true, true, None, window, cx)
             })
         });
         executor.run_until_parked();
@@ -593,19 +607,19 @@ async fn test_following_tab_order(
 
     //Open 1, 3 in that order on client A
     workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
+        .update_in(cx_a, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "1.txt"), None, true, window, cx)
         })
         .await
         .unwrap();
     workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
+        .update_in(cx_a, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "3.txt"), None, true, window, cx)
         })
         .await
         .unwrap();
 
-    let pane_paths = |pane: &View<workspace::Pane>, cx: &mut VisualTestContext| {
+    let pane_paths = |pane: &Entity<workspace::Pane>, cx: &mut VisualTestContext| {
         pane.update(cx, |pane, cx| {
             pane.items()
                 .map(|item| {
@@ -624,13 +638,15 @@ async fn test_following_tab_order(
     assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt"]);
 
     //Follow client B as client A
-    workspace_a.update(cx_a, |workspace, cx| workspace.follow(client_b_id, cx));
+    workspace_a.update_in(cx_a, |workspace, window, cx| {
+        workspace.follow(client_b_id, window, cx)
+    });
     executor.run_until_parked();
 
     //Open just 2 on client B
     workspace_b
-        .update(cx_b, |workspace, cx| {
-            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
+        .update_in(cx_b, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "2.txt"), None, true, window, cx)
         })
         .await
         .unwrap();
@@ -641,8 +657,8 @@ async fn test_following_tab_order(
 
     //Open just 1 on client B
     workspace_b
-        .update(cx_b, |workspace, cx| {
-            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
+        .update_in(cx_b, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "1.txt"), None, true, window, cx)
         })
         .await
         .unwrap();
@@ -701,8 +717,8 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T
     // Client A opens a file.
     let (workspace_a, cx_a) = client_a.build_workspace(&project_a, cx_a);
     workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
+        .update_in(cx_a, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "1.txt"), None, true, window, cx)
         })
         .await
         .unwrap()
@@ -712,8 +728,8 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T
     // Client B opens a different file.
     let (workspace_b, cx_b) = client_b.build_workspace(&project_b, cx_b);
     workspace_b
-        .update(cx_b, |workspace, cx| {
-            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
+        .update_in(cx_b, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "2.txt"), None, true, window, cx)
         })
         .await
         .unwrap()
@@ -721,24 +737,38 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T
         .unwrap();
 
     // Clients A and B follow each other in split panes
-    workspace_a.update(cx_a, |workspace, cx| {
-        workspace.split_and_clone(workspace.active_pane().clone(), SplitDirection::Right, cx);
+    workspace_a.update_in(cx_a, |workspace, window, cx| {
+        workspace.split_and_clone(
+            workspace.active_pane().clone(),
+            SplitDirection::Right,
+            window,
+            cx,
+        );
     });
-    workspace_a.update(cx_a, |workspace, cx| {
-        workspace.follow(client_b.peer_id().unwrap(), cx)
+    workspace_a.update_in(cx_a, |workspace, window, cx| {
+        workspace.follow(client_b.peer_id().unwrap(), window, cx)
     });
     executor.run_until_parked();
-    workspace_b.update(cx_b, |workspace, cx| {
-        workspace.split_and_clone(workspace.active_pane().clone(), SplitDirection::Right, cx);
+    workspace_b.update_in(cx_b, |workspace, window, cx| {
+        workspace.split_and_clone(
+            workspace.active_pane().clone(),
+            SplitDirection::Right,
+            window,
+            cx,
+        );
     });
-    workspace_b.update(cx_b, |workspace, cx| {
-        workspace.follow(client_a.peer_id().unwrap(), cx)
+    workspace_b.update_in(cx_b, |workspace, window, cx| {
+        workspace.follow(client_a.peer_id().unwrap(), window, cx)
     });
     executor.run_until_parked();
 
     // Clients A and B return focus to the original files they had open
-    workspace_a.update(cx_a, |workspace, cx| workspace.activate_next_pane(cx));
-    workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
+    workspace_a.update_in(cx_a, |workspace, window, cx| {
+        workspace.activate_next_pane(window, cx)
+    });
+    workspace_b.update_in(cx_b, |workspace, window, cx| {
+        workspace.activate_next_pane(window, cx)
+    });
     executor.run_until_parked();
 
     // Both clients see the other client's focused file in their right pane.
@@ -775,15 +805,15 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T
 
     // Clients A and B each open a new file.
     workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
+        .update_in(cx_a, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "3.txt"), None, true, window, cx)
         })
         .await
         .unwrap();
 
     workspace_b
-        .update(cx_b, |workspace, cx| {
-            workspace.open_path((worktree_id, "4.txt"), None, true, cx)
+        .update_in(cx_b, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "4.txt"), None, true, window, cx)
         })
         .await
         .unwrap();
@@ -831,7 +861,9 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T
     );
 
     // Client A focuses their right pane, in which they're following client B.
-    workspace_a.update(cx_a, |workspace, cx| workspace.activate_next_pane(cx));
+    workspace_a.update_in(cx_a, |workspace, window, cx| {
+        workspace.activate_next_pane(window, cx)
+    });
     executor.run_until_parked();
 
     // Client B sees that client A is now looking at the same file as them.
@@ -877,7 +909,9 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T
 
     // Client B focuses their right pane, in which they're following client A,
     // who is following them.
-    workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
+    workspace_b.update_in(cx_b, |workspace, window, cx| {
+        workspace.activate_next_pane(window, cx)
+    });
     executor.run_until_parked();
 
     // Client A sees that client B is now looking at the same file as them.
@@ -923,9 +957,9 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T
 
     // Client B focuses a file that they previously followed A to, breaking
     // the follow.
-    workspace_b.update(cx_b, |workspace, cx| {
+    workspace_b.update_in(cx_b, |workspace, window, cx| {
         workspace.active_pane().update(cx, |pane, cx| {
-            pane.activate_prev_item(true, cx);
+            pane.activate_prev_item(true, window, cx);
         });
     });
     executor.run_until_parked();
@@ -974,9 +1008,9 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T
 
     // Client B closes tabs, some of which were originally opened by client A,
     // and some of which were originally opened by client B.
-    workspace_b.update(cx_b, |workspace, cx| {
+    workspace_b.update_in(cx_b, |workspace, window, cx| {
         workspace.active_pane().update(cx, |pane, cx| {
-            pane.close_inactive_items(&Default::default(), cx)
+            pane.close_inactive_items(&Default::default(), window, cx)
                 .unwrap()
                 .detach();
         });
@@ -1022,14 +1056,14 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T
     );
 
     // Client B follows client A again.
-    workspace_b.update(cx_b, |workspace, cx| {
-        workspace.follow(client_a.peer_id().unwrap(), cx)
+    workspace_b.update_in(cx_b, |workspace, window, cx| {
+        workspace.follow(client_a.peer_id().unwrap(), window, cx)
     });
     executor.run_until_parked();
     // Client A cycles through some tabs.
-    workspace_a.update(cx_a, |workspace, cx| {
+    workspace_a.update_in(cx_a, |workspace, window, cx| {
         workspace.active_pane().update(cx, |pane, cx| {
-            pane.activate_prev_item(true, cx);
+            pane.activate_prev_item(true, window, cx);
         });
     });
     executor.run_until_parked();
@@ -1071,9 +1105,9 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T
         ]
     );
 
-    workspace_a.update(cx_a, |workspace, cx| {
+    workspace_a.update_in(cx_a, |workspace, window, cx| {
         workspace.active_pane().update(cx, |pane, cx| {
-            pane.activate_prev_item(true, cx);
+            pane.activate_prev_item(true, window, cx);
         });
     });
     executor.run_until_parked();
@@ -1118,9 +1152,9 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T
         ]
     );
 
-    workspace_a.update(cx_a, |workspace, cx| {
+    workspace_a.update_in(cx_a, |workspace, window, cx| {
         workspace.active_pane().update(cx, |pane, cx| {
-            pane.activate_prev_item(true, cx);
+            pane.activate_prev_item(true, window, cx);
         });
     });
     executor.run_until_parked();
@@ -1215,8 +1249,8 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont
     let (workspace_b, cx_b) = client_b.build_workspace(&project_b, cx_b);
 
     let _editor_a1 = workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
+        .update_in(cx_a, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "1.txt"), None, true, window, cx)
         })
         .await
         .unwrap()
@@ -1228,7 +1262,9 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont
     let leader_id = project_b.update(cx_b, |project, _| {
         project.collaborators().values().next().unwrap().peer_id
     });
-    workspace_b.update(cx_b, |workspace, cx| workspace.follow(leader_id, cx));
+    workspace_b.update_in(cx_b, |workspace, window, cx| {
+        workspace.follow(leader_id, window, cx)
+    });
     executor.run_until_parked();
     assert_eq!(
         workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
@@ -1243,15 +1279,17 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont
     });
 
     // When client B moves, it automatically stops following client A.
-    editor_b2.update(cx_b, |editor, cx| {
-        editor.move_right(&editor::actions::MoveRight, cx)
+    editor_b2.update_in(cx_b, |editor, window, cx| {
+        editor.move_right(&editor::actions::MoveRight, window, cx)
     });
     assert_eq!(
         workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
         None
     );
 
-    workspace_b.update(cx_b, |workspace, cx| workspace.follow(leader_id, cx));
+    workspace_b.update_in(cx_b, |workspace, window, cx| {
+        workspace.follow(leader_id, window, cx)
+    });
     executor.run_until_parked();
     assert_eq!(
         workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
@@ -1259,13 +1297,15 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont
     );
 
     // When client B edits, it automatically stops following client A.
-    editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
+    editor_b2.update_in(cx_b, |editor, window, cx| editor.insert("X", window, cx));
     assert_eq!(
-        workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
+        workspace_b.update_in(cx_b, |workspace, _, _| workspace.leader_for_pane(&pane_b)),
         None
     );
 
-    workspace_b.update(cx_b, |workspace, cx| workspace.follow(leader_id, cx));
+    workspace_b.update_in(cx_b, |workspace, window, cx| {
+        workspace.follow(leader_id, window, cx)
+    });
     executor.run_until_parked();
     assert_eq!(
         workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
@@ -1273,15 +1313,17 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont
     );
 
     // When client B scrolls, it automatically stops following client A.
-    editor_b2.update(cx_b, |editor, cx| {
-        editor.set_scroll_position(point(0., 3.), cx)
+    editor_b2.update_in(cx_b, |editor, window, cx| {
+        editor.set_scroll_position(point(0., 3.), window, cx)
     });
     assert_eq!(
         workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
         None
     );
 
-    workspace_b.update(cx_b, |workspace, cx| workspace.follow(leader_id, cx));
+    workspace_b.update_in(cx_b, |workspace, window, cx| {
+        workspace.follow(leader_id, window, cx)
+    });
     executor.run_until_parked();
     assert_eq!(
         workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
@@ -1289,15 +1331,17 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont
     );
 
     // When client B activates a different pane, it continues following client A in the original pane.
-    workspace_b.update(cx_b, |workspace, cx| {
-        workspace.split_and_clone(pane_b.clone(), SplitDirection::Right, cx)
+    workspace_b.update_in(cx_b, |workspace, window, cx| {
+        workspace.split_and_clone(pane_b.clone(), SplitDirection::Right, window, cx)
     });
     assert_eq!(
         workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
         Some(leader_id)
     );
 
-    workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
+    workspace_b.update_in(cx_b, |workspace, window, cx| {
+        workspace.activate_next_pane(window, cx)
+    });
     assert_eq!(
         workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
         Some(leader_id)
@@ -1305,8 +1349,8 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont
 
     // When client B activates a different item in the original pane, it automatically stops following client A.
     workspace_b
-        .update(cx_b, |workspace, cx| {
-            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
+        .update_in(cx_b, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "2.txt"), None, true, window, cx)
         })
         .await
         .unwrap();
@@ -1352,8 +1396,12 @@ async fn test_peers_simultaneously_following_each_other(
         project.collaborators().values().next().unwrap().peer_id
     });
 
-    workspace_a.update(cx_a, |workspace, cx| workspace.follow(client_b_id, cx));
-    workspace_b.update(cx_b, |workspace, cx| workspace.follow(client_a_id, cx));
+    workspace_a.update_in(cx_a, |workspace, window, cx| {
+        workspace.follow(client_b_id, window, cx)
+    });
+    workspace_b.update_in(cx_b, |workspace, window, cx| {
+        workspace.follow(client_a_id, window, cx)
+    });
     executor.run_until_parked();
 
     workspace_a.update(cx_a, |workspace, _| {
@@ -1434,8 +1482,8 @@ async fn test_following_across_workspaces(cx_a: &mut TestAppContext, cx_b: &mut
         .unwrap();
 
     workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.open_path((worktree_id_a, "w.rs"), None, true, cx)
+        .update_in(cx_a, |workspace, window, cx| {
+            workspace.open_path((worktree_id_a, "w.rs"), None, true, window, cx)
         })
         .await
         .unwrap();
@@ -1443,8 +1491,8 @@ async fn test_following_across_workspaces(cx_a: &mut TestAppContext, cx_b: &mut
     executor.run_until_parked();
     assert_eq!(visible_push_notifications(cx_b).len(), 1);
 
-    workspace_b.update(cx_b, |workspace, cx| {
-        workspace.follow(client_a.peer_id().unwrap(), cx)
+    workspace_b.update_in(cx_b, |workspace, window, cx| {
+        workspace.follow(client_a.peer_id().unwrap(), window, cx)
     });
 
     executor.run_until_parked();
@@ -1490,8 +1538,8 @@ async fn test_following_across_workspaces(cx_a: &mut TestAppContext, cx_b: &mut
 
     // b moves to x.rs in a's project, and a follows
     workspace_b_project_a
-        .update(&mut cx_b2, |workspace, cx| {
-            workspace.open_path((worktree_id_a, "x.rs"), None, true, cx)
+        .update_in(&mut cx_b2, |workspace, window, cx| {
+            workspace.open_path((worktree_id_a, "x.rs"), None, true, window, cx)
         })
         .await
         .unwrap();
@@ -1505,8 +1553,8 @@ async fn test_following_across_workspaces(cx_a: &mut TestAppContext, cx_b: &mut
         );
     });
 
-    workspace_a.update(cx_a, |workspace, cx| {
-        workspace.follow(client_b.peer_id().unwrap(), cx)
+    workspace_a.update_in(cx_a, |workspace, window, cx| {
+        workspace.follow(client_b.peer_id().unwrap(), window, cx)
     });
 
     executor.run_until_parked();
@@ -1522,8 +1570,8 @@ async fn test_following_across_workspaces(cx_a: &mut TestAppContext, cx_b: &mut
 
     // b moves to y.rs in b's project, a is still following but can't yet see
     workspace_b
-        .update(cx_b, |workspace, cx| {
-            workspace.open_path((worktree_id_b, "y.rs"), None, true, cx)
+        .update_in(cx_b, |workspace, window, cx| {
+            workspace.open_path((worktree_id_b, "y.rs"), None, true, window, cx)
         })
         .await
         .unwrap();
@@ -1544,7 +1592,7 @@ async fn test_following_across_workspaces(cx_a: &mut TestAppContext, cx_b: &mut
 
     executor.run_until_parked();
     assert_eq!(visible_push_notifications(cx_a).len(), 1);
-    cx_a.update(|cx| {
+    cx_a.update(|_, cx| {
         workspace::join_in_room_project(
             project_b_id,
             client_b.user_id().unwrap(),
@@ -1607,8 +1655,8 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T
     });
 
     // b should follow a to position 1
-    editor_a.update(cx_a, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges([1..1]))
+    editor_a.update_in(cx_a, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]))
     });
     cx_a.executor()
         .advance_clock(workspace::item::LEADER_UPDATE_THROTTLE);
@@ -1618,7 +1666,7 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T
     });
 
     // a unshares the project
-    cx_a.update(|cx| {
+    cx_a.update(|_, cx| {
         let project = workspace_a.read(cx).project().clone();
         ActiveCall::global(cx).update(cx, |call, cx| {
             call.unshare_project(project, cx).unwrap();
@@ -1627,8 +1675,8 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T
     cx_a.run_until_parked();
 
     // b should not follow a to position 2
-    editor_a.update(cx_a, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges([2..2]))
+    editor_a.update_in(cx_a, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]))
     });
     cx_a.executor()
         .advance_clock(workspace::item::LEADER_UPDATE_THROTTLE);
@@ -1636,7 +1684,7 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T
     editor_b.update(cx_b, |editor, cx| {
         assert_eq!(editor.selections.ranges(cx), vec![1..1])
     });
-    cx_b.update(|cx| {
+    cx_b.update(|_, cx| {
         let room = ActiveCall::global(cx).read(cx).room().unwrap().read(cx);
         let participant = room.remote_participants().get(&client_a.id()).unwrap();
         assert_eq!(participant.location, ParticipantLocation::UnsharedProject)
@@ -1703,16 +1751,16 @@ async fn test_following_into_excluded_file(
 
     // Client A opens editors for a regular file and an excluded file.
     let editor_for_regular = workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
+        .update_in(cx_a, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "1.txt"), None, true, window, cx)
         })
         .await
         .unwrap()
         .downcast::<Editor>()
         .unwrap();
     let editor_for_excluded_a = workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.open_path((worktree_id, ".git/COMMIT_EDITMSG"), None, true, cx)
+        .update_in(cx_a, |workspace, window, cx| {
+            workspace.open_path((worktree_id, ".git/COMMIT_EDITMSG"), None, true, window, cx)
         })
         .await
         .unwrap()
@@ -1720,22 +1768,24 @@ async fn test_following_into_excluded_file(
         .unwrap();
 
     // Client A updates their selections in those editors
-    editor_for_regular.update(cx_a, |editor, cx| {
-        editor.handle_input("a", cx);
-        editor.handle_input("b", cx);
-        editor.handle_input("c", cx);
-        editor.select_left(&Default::default(), cx);
+    editor_for_regular.update_in(cx_a, |editor, window, cx| {
+        editor.handle_input("a", window, cx);
+        editor.handle_input("b", window, cx);
+        editor.handle_input("c", window, cx);
+        editor.select_left(&Default::default(), window, cx);
         assert_eq!(editor.selections.ranges(cx), vec![3..2]);
     });
-    editor_for_excluded_a.update(cx_a, |editor, cx| {
-        editor.select_all(&Default::default(), cx);
-        editor.handle_input("new commit message", cx);
-        editor.select_left(&Default::default(), cx);
+    editor_for_excluded_a.update_in(cx_a, |editor, window, cx| {
+        editor.select_all(&Default::default(), window, cx);
+        editor.handle_input("new commit message", window, cx);
+        editor.select_left(&Default::default(), window, cx);
         assert_eq!(editor.selections.ranges(cx), vec![18..17]);
     });
 
     // When client B starts following client A, currently visible file is replicated
-    workspace_b.update(cx_b, |workspace, cx| workspace.follow(peer_id_a, cx));
+    workspace_b.update_in(cx_b, |workspace, window, cx| {
+        workspace.follow(peer_id_a, window, cx)
+    });
     executor.advance_clock(workspace::item::LEADER_UPDATE_THROTTLE);
     executor.run_until_parked();
 
@@ -1755,15 +1805,15 @@ async fn test_following_into_excluded_file(
         vec![18..17]
     );
 
-    editor_for_excluded_a.update(cx_a, |editor, cx| {
-        editor.select_right(&Default::default(), cx);
+    editor_for_excluded_a.update_in(cx_a, |editor, window, cx| {
+        editor.select_right(&Default::default(), window, cx);
     });
     executor.advance_clock(workspace::item::LEADER_UPDATE_THROTTLE);
     executor.run_until_parked();
 
     // Changes from B to the excluded file are replicated in A's editor
-    editor_for_excluded_b.update(cx_b, |editor, cx| {
-        editor.handle_input("\nCo-Authored-By: B <b@b.b>", cx);
+    editor_for_excluded_b.update_in(cx_b, |editor, window, cx| {
+        editor.handle_input("\nCo-Authored-By: B <b@b.b>", window, cx);
     });
     executor.run_until_parked();
     editor_for_excluded_a.update(cx_a, |editor, cx| {
@@ -1774,13 +1824,11 @@ async fn test_following_into_excluded_file(
     });
 }
 
-fn visible_push_notifications(
-    cx: &mut TestAppContext,
-) -> Vec<gpui::View<ProjectSharedNotification>> {
+fn visible_push_notifications(cx: &mut TestAppContext) -> Vec<Entity<ProjectSharedNotification>> {
     let mut ret = Vec::new();
     for window in cx.windows() {
         window
-            .update(cx, |window, _| {
+            .update(cx, |window, _, _| {
                 if let Ok(handle) = window.downcast::<ProjectSharedNotification>() {
                     ret.push(handle)
                 }
@@ -1821,7 +1869,7 @@ fn followers_by_leader(project_id: u64, cx: &TestAppContext) -> Vec<(PeerId, Vec
     })
 }
 
-fn pane_summaries(workspace: &View<Workspace>, cx: &mut VisualTestContext) -> Vec<PaneSummary> {
+fn pane_summaries(workspace: &Entity<Workspace>, cx: &mut VisualTestContext) -> Vec<PaneSummary> {
     workspace.update(cx, |workspace, cx| {
         let active_pane = workspace.active_pane();
         workspace
@@ -1924,14 +1972,14 @@ async fn test_following_to_channel_notes_without_a_shared_project(
 
     // Client A opens the notes for channel 1.
     let channel_notes_1_a = cx_a
-        .update(|cx| ChannelView::open(channel_1_id, None, workspace_a.clone(), cx))
+        .update(|window, cx| ChannelView::open(channel_1_id, None, workspace_a.clone(), window, cx))
         .await
         .unwrap();
-    channel_notes_1_a.update(cx_a, |notes, cx| {
+    channel_notes_1_a.update_in(cx_a, |notes, window, cx| {
         assert_eq!(notes.channel(cx).unwrap().name, "channel-1");
         notes.editor.update(cx, |editor, cx| {
-            editor.insert("Hello from A.", cx);
-            editor.change_selections(None, cx, |selections| {
+            editor.insert("Hello from A.", window, cx);
+            editor.change_selections(None, window, cx, |selections| {
                 selections.select_ranges(vec![3..4]);
             });
         });
@@ -1939,9 +1987,9 @@ async fn test_following_to_channel_notes_without_a_shared_project(
 
     // Client B follows client A.
     workspace_b
-        .update(cx_b, |workspace, cx| {
+        .update_in(cx_b, |workspace, window, cx| {
             workspace
-                .start_following(client_a.peer_id().unwrap(), cx)
+                .start_following(client_a.peer_id().unwrap(), window, cx)
                 .unwrap()
         })
         .await
@@ -1971,7 +2019,7 @@ async fn test_following_to_channel_notes_without_a_shared_project(
 
     //  Client A opens the notes for channel 2.
     let channel_notes_2_a = cx_a
-        .update(|cx| ChannelView::open(channel_2_id, None, workspace_a.clone(), cx))
+        .update(|window, cx| ChannelView::open(channel_2_id, None, workspace_a.clone(), window, cx))
         .await
         .unwrap();
     channel_notes_2_a.update(cx_a, |notes, cx| {
@@ -1997,8 +2045,8 @@ async fn test_following_to_channel_notes_without_a_shared_project(
 
     // Client A opens a local buffer in their unshared project.
     let _unshared_editor_a1 = workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
+        .update_in(cx_a, |workspace, window, cx| {
+            workspace.open_path((worktree_id, "1.txt"), None, true, window, cx)
         })
         .await
         .unwrap()
@@ -2027,7 +2075,7 @@ pub(crate) async fn join_channel(
 }
 
 async fn share_workspace(
-    workspace: &View<Workspace>,
+    workspace: &Entity<Workspace>,
     cx: &mut VisualTestContext,
 ) -> anyhow::Result<u64> {
     let project = workspace.update(cx, |workspace, _| workspace.project().clone());
@@ -2069,9 +2117,9 @@ async fn test_following_to_channel_notes_other_workspace(
 
     // a opens a second workspace and the channel notes
     let (workspace_a2, cx_a2) = client_a.build_test_workspace(&mut cx_a2).await;
-    cx_a2.update(|cx| cx.activate_window());
+    cx_a2.update(|window, _| window.activate_window());
     cx_a2
-        .update(|cx| ChannelView::open(channel, None, workspace_a2, cx))
+        .update(|window, cx| ChannelView::open(channel, None, workspace_a2, window, cx))
         .await
         .unwrap();
     cx_a2.run_until_parked();
@@ -2083,7 +2131,7 @@ async fn test_following_to_channel_notes_other_workspace(
     });
 
     // a returns to the shared project
-    cx_a.update(|cx| cx.activate_window());
+    cx_a.update(|window, _| window.activate_window());
     cx_a.run_until_parked();
 
     workspace_a.update(cx_a, |workspace, cx| {
@@ -2141,7 +2189,7 @@ async fn test_following_while_deactivated(cx_a: &mut TestAppContext, cx_b: &mut
 
     // a opens a file in a new window
     let (_, cx_a2) = client_a.build_test_workspace(&mut cx_a2).await;
-    cx_a2.update(|cx| cx.activate_window());
+    cx_a2.update(|window, _| window.activate_window());
     cx_a2.simulate_keystrokes("cmd-p");
     cx_a2.run_until_parked();
     cx_a2.simulate_keystrokes("3 enter");
@@ -2152,7 +2200,7 @@ async fn test_following_while_deactivated(cx_a: &mut TestAppContext, cx_b: &mut
     cx_a.run_until_parked();
 
     // a returns to the shared project
-    cx_a.update(|cx| cx.activate_window());
+    cx_a.update(|window, _| window.activate_window());
     cx_a.run_until_parked();
 
     workspace_a.update(cx_a, |workspace, cx| {

crates/collab/src/tests/integration_tests.rs 🔗

@@ -18,7 +18,7 @@ use prompt_library::PromptBuilder;
 
 use git::status::{FileStatus, StatusCode, TrackedStatus, UnmergedStatus, UnmergedStatusCode};
 use gpui::{
-    px, size, AppContext, BackgroundExecutor, Model, Modifiers, MouseButton, MouseDownEvent,
+    px, size, App, BackgroundExecutor, Entity, Modifiers, MouseButton, MouseDownEvent,
     TestAppContext, UpdateGlobal,
 };
 use language::{
@@ -2073,7 +2073,7 @@ async fn test_mute_deafen(
     }
 
     fn participant_audio_state(
-        room: &Model<Room>,
+        room: &Entity<Room>,
         cx: &TestAppContext,
     ) -> Vec<ParticipantAudioState> {
         room.read_with(cx, |room, _| {
@@ -2252,7 +2252,7 @@ async fn test_room_location(
     );
 
     fn participant_locations(
-        room: &Model<Room>,
+        room: &Entity<Room>,
         cx: &TestAppContext,
     ) -> Vec<(String, ParticipantLocation)> {
         room.read_with(cx, |room, _| {
@@ -2821,7 +2821,7 @@ async fn test_git_branch_name(
     executor.run_until_parked();
 
     #[track_caller]
-    fn assert_branch(branch_name: Option<impl Into<String>>, project: &Project, cx: &AppContext) {
+    fn assert_branch(branch_name: Option<impl Into<String>>, project: &Project, cx: &App) {
         let branch_name = branch_name.map(Into::into);
         let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
         assert_eq!(worktrees.len(), 1);
@@ -2931,7 +2931,7 @@ async fn test_git_status_sync(
         file: &impl AsRef<Path>,
         status: Option<FileStatus>,
         project: &Project,
-        cx: &AppContext,
+        cx: &App,
     ) {
         let file = file.as_ref();
         let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
@@ -6167,7 +6167,7 @@ async fn test_right_click_menu_behind_collab_panel(cx: &mut TestAppContext) {
     cx.simulate_resize(size(px(300.), px(300.)));
 
     cx.simulate_keystrokes("cmd-n cmd-n cmd-n");
-    cx.update(|cx| cx.refresh());
+    cx.update(|window, _cx| window.refresh());
 
     let tab_bounds = cx.debug_bounds("TAB-2").unwrap();
     let new_tab_button_bounds = cx.debug_bounds("ICON-Plus").unwrap();
@@ -6260,14 +6260,14 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
 
     let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
 
-    let get_path = |pane: &Pane, idx: usize, cx: &AppContext| {
+    let get_path = |pane: &Pane, idx: usize, cx: &App| {
         pane.item_for_index(idx).unwrap().project_path(cx).unwrap()
     };
 
     // Opening item 3 as a "permanent" tab
     workspace
-        .update(cx, |workspace, cx| {
-            workspace.open_path(path_3.clone(), None, false, cx)
+        .update_in(cx, |workspace, window, cx| {
+            workspace.open_path(path_3.clone(), None, false, window, cx)
         })
         .await
         .unwrap();
@@ -6283,8 +6283,8 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
 
     // Open item 1 as preview
     workspace
-        .update(cx, |workspace, cx| {
-            workspace.open_path_preview(path_1.clone(), None, true, true, cx)
+        .update_in(cx, |workspace, window, cx| {
+            workspace.open_path_preview(path_1.clone(), None, true, true, window, cx)
         })
         .await
         .unwrap();
@@ -6304,8 +6304,8 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
 
     // Open item 2 as preview
     workspace
-        .update(cx, |workspace, cx| {
-            workspace.open_path_preview(path_2.clone(), None, true, true, cx)
+        .update_in(cx, |workspace, window, cx| {
+            workspace.open_path_preview(path_2.clone(), None, true, true, window, cx)
         })
         .await
         .unwrap();
@@ -6325,7 +6325,9 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
 
     // Going back should show item 1 as preview
     workspace
-        .update(cx, |workspace, cx| workspace.go_back(pane.downgrade(), cx))
+        .update_in(cx, |workspace, window, cx| {
+            workspace.go_back(pane.downgrade(), window, cx)
+        })
         .await
         .unwrap();
 
@@ -6343,10 +6345,11 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
     });
 
     // Closing item 1
-    pane.update(cx, |pane, cx| {
+    pane.update_in(cx, |pane, window, cx| {
         pane.close_item_by_id(
             pane.active_item().unwrap().item_id(),
             workspace::SaveIntent::Skip,
+            window,
             cx,
         )
     })
@@ -6364,7 +6367,9 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
 
     // Going back should show item 1 as preview
     workspace
-        .update(cx, |workspace, cx| workspace.go_back(pane.downgrade(), cx))
+        .update_in(cx, |workspace, window, cx| {
+            workspace.go_back(pane.downgrade(), window, cx)
+        })
         .await
         .unwrap();
 
@@ -6382,9 +6387,9 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
     });
 
     // Close permanent tab
-    pane.update(cx, |pane, cx| {
+    pane.update_in(cx, |pane, window, cx| {
         let id = pane.items().next().unwrap().item_id();
-        pane.close_item_by_id(id, workspace::SaveIntent::Skip, cx)
+        pane.close_item_by_id(id, workspace::SaveIntent::Skip, window, cx)
     })
     .await
     .unwrap();
@@ -6431,8 +6436,8 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
 
     // Open item 2 as preview in right pane
     workspace
-        .update(cx, |workspace, cx| {
-            workspace.open_path_preview(path_2.clone(), None, true, true, cx)
+        .update_in(cx, |workspace, window, cx| {
+            workspace.open_path_preview(path_2.clone(), None, true, true, window, cx)
         })
         .await
         .unwrap();
@@ -6463,14 +6468,14 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
     });
 
     // Focus left pane
-    workspace.update(cx, |workspace, cx| {
-        workspace.activate_pane_in_direction(workspace::SplitDirection::Left, cx)
+    workspace.update_in(cx, |workspace, window, cx| {
+        workspace.activate_pane_in_direction(workspace::SplitDirection::Left, window, cx)
     });
 
     // Open item 2 as preview in left pane
     workspace
-        .update(cx, |workspace, cx| {
-            workspace.open_path_preview(path_2.clone(), None, true, true, cx)
+        .update_in(cx, |workspace, window, cx| {
+            workspace.open_path_preview(path_2.clone(), None, true, true, window, cx)
         })
         .await
         .unwrap();

crates/collab/src/tests/notification_tests.rs 🔗

@@ -21,14 +21,14 @@ async fn test_notifications(
     let notification_events_b = Arc::new(Mutex::new(Vec::new()));
     client_a.notification_store().update(cx_a, |_, cx| {
         let events = notification_events_a.clone();
-        cx.subscribe(&cx.handle(), move |_, _, event, _| {
+        cx.subscribe(&cx.model(), move |_, _, event, _| {
             events.lock().push(event.clone());
         })
         .detach()
     });
     client_b.notification_store().update(cx_b, |_, cx| {
         let events = notification_events_b.clone();
-        cx.subscribe(&cx.handle(), move |_, _, event, _| {
+        cx.subscribe(&cx.model(), move |_, _, event, _| {
             events.lock().push(event.clone());
         })
         .detach()

crates/collab/src/tests/random_project_collaboration_tests.rs 🔗

@@ -7,7 +7,7 @@ use collections::{BTreeMap, HashMap};
 use editor::Bias;
 use fs::{FakeFs, Fs as _};
 use git::status::{FileStatus, StatusCode, TrackedStatus, UnmergedStatus, UnmergedStatusCode};
-use gpui::{BackgroundExecutor, Model, TestAppContext};
+use gpui::{BackgroundExecutor, Entity, TestAppContext};
 use language::{
     range_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageMatcher, PointUtf16,
 };
@@ -1475,10 +1475,10 @@ fn generate_git_operation(rng: &mut StdRng, client: &TestClient) -> GitOperation
 
 fn buffer_for_full_path(
     client: &TestClient,
-    project: &Model<Project>,
+    project: &Entity<Project>,
     full_path: &PathBuf,
     cx: &TestAppContext,
-) -> Option<Model<language::Buffer>> {
+) -> Option<Entity<language::Buffer>> {
     client
         .buffers_for_project(project)
         .iter()
@@ -1494,7 +1494,7 @@ fn project_for_root_name(
     client: &TestClient,
     root_name: &str,
     cx: &TestAppContext,
-) -> Option<Model<Project>> {
+) -> Option<Entity<Project>> {
     if let Some(ix) = project_ix_for_root_name(client.local_projects().deref(), root_name, cx) {
         return Some(client.local_projects()[ix].clone());
     }
@@ -1506,7 +1506,7 @@ fn project_for_root_name(
 }
 
 fn project_ix_for_root_name(
-    projects: &[Model<Project>],
+    projects: &[Entity<Project>],
     root_name: &str,
     cx: &TestAppContext,
 ) -> Option<usize> {
@@ -1518,7 +1518,7 @@ fn project_ix_for_root_name(
     })
 }
 
-fn root_name_for_project(project: &Model<Project>, cx: &TestAppContext) -> String {
+fn root_name_for_project(project: &Entity<Project>, cx: &TestAppContext) -> String {
     project.read_with(cx, |project, cx| {
         project
             .visible_worktrees(cx)
@@ -1531,7 +1531,7 @@ fn root_name_for_project(project: &Model<Project>, cx: &TestAppContext) -> Strin
 }
 
 fn project_path_for_full_path(
-    project: &Model<Project>,
+    project: &Entity<Project>,
     full_path: &Path,
     cx: &TestAppContext,
 ) -> Option<ProjectPath> {
@@ -1552,7 +1552,7 @@ fn project_path_for_full_path(
 }
 
 async fn ensure_project_shared(
-    project: &Model<Project>,
+    project: &Entity<Project>,
     client: &TestClient,
     cx: &mut TestAppContext,
 ) {
@@ -1585,7 +1585,7 @@ async fn ensure_project_shared(
     }
 }
 
-fn choose_random_project(client: &TestClient, rng: &mut StdRng) -> Option<Model<Project>> {
+fn choose_random_project(client: &TestClient, rng: &mut StdRng) -> Option<Entity<Project>> {
     client
         .local_projects()
         .deref()

crates/collab/src/tests/remote_editing_collaboration_tests.rs 🔗

@@ -4,7 +4,9 @@ use collections::HashSet;
 use extension::ExtensionHostProxy;
 use fs::{FakeFs, Fs as _};
 use futures::StreamExt as _;
-use gpui::{BackgroundExecutor, Context as _, SemanticVersion, TestAppContext, UpdateGlobal as _};
+use gpui::{
+    AppContext as _, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal as _,
+};
 use http_client::BlockedHttpClient;
 use language::{
     language_settings::{
@@ -73,7 +75,7 @@ async fn test_sharing_an_ssh_remote_project(
     let remote_http_client = Arc::new(BlockedHttpClient);
     let node = NodeRuntime::unavailable();
     let languages = Arc::new(LanguageRegistry::new(server_cx.executor()));
-    let _headless_project = server_cx.new_model(|cx| {
+    let _headless_project = server_cx.new(|cx| {
         client::init_settings(cx);
         HeadlessProject::new(
             HeadlessAppState {
@@ -240,7 +242,7 @@ async fn test_ssh_collaboration_git_branches(
     let remote_http_client = Arc::new(BlockedHttpClient);
     let node = NodeRuntime::unavailable();
     let languages = Arc::new(LanguageRegistry::new(server_cx.executor()));
-    let headless_project = server_cx.new_model(|cx| {
+    let headless_project = server_cx.new(|cx| {
         client::init_settings(cx);
         HeadlessProject::new(
             HeadlessAppState {
@@ -398,7 +400,7 @@ async fn test_ssh_collaboration_formatting_with_prettier(
     // User A connects to the remote project via SSH.
     server_cx.update(HeadlessProject::init);
     let remote_http_client = Arc::new(BlockedHttpClient);
-    let _headless_project = server_cx.new_model(|cx| {
+    let _headless_project = server_cx.new(|cx| {
         client::init_settings(cx);
         HeadlessProject::new(
             HeadlessAppState {

crates/collab/src/tests/test_server.rs 🔗

@@ -17,7 +17,7 @@ use collections::{HashMap, HashSet};
 use fs::FakeFs;
 use futures::{channel::oneshot, StreamExt as _};
 use git::GitHostingProviderRegistry;
-use gpui::{BackgroundExecutor, Context, Model, Task, TestAppContext, View, VisualTestContext};
+use gpui::{AppContext as _, BackgroundExecutor, Entity, Task, TestAppContext, VisualTestContext};
 use http_client::FakeHttpClient;
 use language::LanguageRegistry;
 use node_runtime::NodeRuntime;
@@ -64,17 +64,17 @@ pub struct TestServer {
 pub struct TestClient {
     pub username: String,
     pub app_state: Arc<workspace::AppState>,
-    channel_store: Model<ChannelStore>,
-    notification_store: Model<NotificationStore>,
+    channel_store: Entity<ChannelStore>,
+    notification_store: Entity<NotificationStore>,
     state: RefCell<TestClientState>,
 }
 
 #[derive(Default)]
 struct TestClientState {
-    local_projects: Vec<Model<Project>>,
-    dev_server_projects: Vec<Model<Project>>,
-    buffers: HashMap<Model<Project>, HashSet<Model<language::Buffer>>>,
-    channel_buffers: HashSet<Model<ChannelBuffer>>,
+    local_projects: Vec<Entity<Project>>,
+    dev_server_projects: Vec<Entity<Project>>,
+    buffers: HashMap<Entity<Project>, HashSet<Entity<language::Buffer>>>,
+    channel_buffers: HashSet<Entity<ChannelBuffer>>,
 }
 
 pub struct ContactsSummary {
@@ -274,10 +274,10 @@ impl TestServer {
         git_hosting_provider_registry
             .register_hosting_provider(Arc::new(git_hosting_providers::Github));
 
-        let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
-        let workspace_store = cx.new_model(|cx| WorkspaceStore::new(client.clone(), cx));
+        let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
+        let workspace_store = cx.new(|cx| WorkspaceStore::new(client.clone(), cx));
         let language_registry = Arc::new(LanguageRegistry::test(cx.executor()));
-        let session = cx.new_model(|cx| AppSession::new(Session::test(), cx));
+        let session = cx.new(|cx| AppSession::new(Session::test(), cx));
         let app_state = Arc::new(workspace::AppState {
             client: client.clone(),
             user_store: user_store.clone(),
@@ -596,15 +596,15 @@ impl TestClient {
         self.app_state.fs.as_fake()
     }
 
-    pub fn channel_store(&self) -> &Model<ChannelStore> {
+    pub fn channel_store(&self) -> &Entity<ChannelStore> {
         &self.channel_store
     }
 
-    pub fn notification_store(&self) -> &Model<NotificationStore> {
+    pub fn notification_store(&self) -> &Entity<NotificationStore> {
         &self.notification_store
     }
 
-    pub fn user_store(&self) -> &Model<UserStore> {
+    pub fn user_store(&self) -> &Entity<UserStore> {
         &self.app_state.user_store
     }
 
@@ -639,19 +639,19 @@ impl TestClient {
             .await;
     }
 
-    pub fn local_projects(&self) -> impl Deref<Target = Vec<Model<Project>>> + '_ {
+    pub fn local_projects(&self) -> impl Deref<Target = Vec<Entity<Project>>> + '_ {
         Ref::map(self.state.borrow(), |state| &state.local_projects)
     }
 
-    pub fn dev_server_projects(&self) -> impl Deref<Target = Vec<Model<Project>>> + '_ {
+    pub fn dev_server_projects(&self) -> impl Deref<Target = Vec<Entity<Project>>> + '_ {
         Ref::map(self.state.borrow(), |state| &state.dev_server_projects)
     }
 
-    pub fn local_projects_mut(&self) -> impl DerefMut<Target = Vec<Model<Project>>> + '_ {
+    pub fn local_projects_mut(&self) -> impl DerefMut<Target = Vec<Entity<Project>>> + '_ {
         RefMut::map(self.state.borrow_mut(), |state| &mut state.local_projects)
     }
 
-    pub fn dev_server_projects_mut(&self) -> impl DerefMut<Target = Vec<Model<Project>>> + '_ {
+    pub fn dev_server_projects_mut(&self) -> impl DerefMut<Target = Vec<Entity<Project>>> + '_ {
         RefMut::map(self.state.borrow_mut(), |state| {
             &mut state.dev_server_projects
         })
@@ -659,8 +659,8 @@ impl TestClient {
 
     pub fn buffers_for_project<'a>(
         &'a self,
-        project: &Model<Project>,
-    ) -> impl DerefMut<Target = HashSet<Model<language::Buffer>>> + 'a {
+        project: &Entity<Project>,
+    ) -> impl DerefMut<Target = HashSet<Entity<language::Buffer>>> + 'a {
         RefMut::map(self.state.borrow_mut(), |state| {
             state.buffers.entry(project.clone()).or_default()
         })
@@ -668,12 +668,12 @@ impl TestClient {
 
     pub fn buffers(
         &self,
-    ) -> impl DerefMut<Target = HashMap<Model<Project>, HashSet<Model<language::Buffer>>>> + '_
+    ) -> impl DerefMut<Target = HashMap<Entity<Project>, HashSet<Entity<language::Buffer>>>> + '_
     {
         RefMut::map(self.state.borrow_mut(), |state| &mut state.buffers)
     }
 
-    pub fn channel_buffers(&self) -> impl DerefMut<Target = HashSet<Model<ChannelBuffer>>> + '_ {
+    pub fn channel_buffers(&self) -> impl DerefMut<Target = HashSet<Entity<ChannelBuffer>>> + '_ {
         RefMut::map(self.state.borrow_mut(), |state| &mut state.channel_buffers)
     }
 
@@ -703,7 +703,7 @@ impl TestClient {
         &self,
         root_path: impl AsRef<Path>,
         cx: &mut TestAppContext,
-    ) -> (Model<Project>, WorktreeId) {
+    ) -> (Entity<Project>, WorktreeId) {
         let project = self.build_empty_local_project(cx);
         let (worktree, _) = project
             .update(cx, |p, cx| p.find_or_create_worktree(root_path, true, cx))
@@ -718,9 +718,9 @@ impl TestClient {
     pub async fn build_ssh_project(
         &self,
         root_path: impl AsRef<Path>,
-        ssh: Model<SshRemoteClient>,
+        ssh: Entity<SshRemoteClient>,
         cx: &mut TestAppContext,
-    ) -> (Model<Project>, WorktreeId) {
+    ) -> (Entity<Project>, WorktreeId) {
         let project = cx.update(|cx| {
             Project::ssh(
                 ssh,
@@ -739,7 +739,7 @@ impl TestClient {
         (project, worktree.read_with(cx, |tree, _| tree.id()))
     }
 
-    pub async fn build_test_project(&self, cx: &mut TestAppContext) -> Model<Project> {
+    pub async fn build_test_project(&self, cx: &mut TestAppContext) -> Entity<Project> {
         self.fs()
             .insert_tree(
                 "/a",
@@ -755,17 +755,17 @@ impl TestClient {
 
     pub async fn host_workspace(
         &self,
-        workspace: &View<Workspace>,
+        workspace: &Entity<Workspace>,
         channel_id: ChannelId,
         cx: &mut VisualTestContext,
     ) {
-        cx.update(|cx| {
+        cx.update(|_, cx| {
             let active_call = ActiveCall::global(cx);
             active_call.update(cx, |call, cx| call.join_channel(channel_id, cx))
         })
         .await
         .unwrap();
-        cx.update(|cx| {
+        cx.update(|_, cx| {
             let active_call = ActiveCall::global(cx);
             let project = workspace.read(cx).project().clone();
             active_call.update(cx, |call, cx| call.share_project(project, cx))
@@ -779,7 +779,7 @@ impl TestClient {
         &'a self,
         channel_id: ChannelId,
         cx: &'a mut TestAppContext,
-    ) -> (View<Workspace>, &'a mut VisualTestContext) {
+    ) -> (Entity<Workspace>, &'a mut VisualTestContext) {
         cx.update(|cx| workspace::join_channel(channel_id, self.app_state.clone(), None, cx))
             .await
             .unwrap();
@@ -788,7 +788,7 @@ impl TestClient {
         self.active_workspace(cx)
     }
 
-    pub fn build_empty_local_project(&self, cx: &mut TestAppContext) -> Model<Project> {
+    pub fn build_empty_local_project(&self, cx: &mut TestAppContext) -> Entity<Project> {
         cx.update(|cx| {
             Project::local(
                 self.client().clone(),
@@ -806,7 +806,7 @@ impl TestClient {
         &self,
         host_project_id: u64,
         guest_cx: &mut TestAppContext,
-    ) -> Model<Project> {
+    ) -> Entity<Project> {
         let active_call = guest_cx.read(ActiveCall::global);
         let room = active_call.read_with(guest_cx, |call, _| call.room().unwrap().clone());
         room.update(guest_cx, |room, cx| {
@@ -823,47 +823,47 @@ impl TestClient {
 
     pub fn build_workspace<'a>(
         &'a self,
-        project: &Model<Project>,
+        project: &Entity<Project>,
         cx: &'a mut TestAppContext,
-    ) -> (View<Workspace>, &'a mut VisualTestContext) {
-        cx.add_window_view(|cx| {
-            cx.activate_window();
-            Workspace::new(None, project.clone(), self.app_state.clone(), cx)
+    ) -> (Entity<Workspace>, &'a mut VisualTestContext) {
+        cx.add_window_view(|window, cx| {
+            window.activate_window();
+            Workspace::new(None, project.clone(), self.app_state.clone(), window, cx)
         })
     }
 
     pub async fn build_test_workspace<'a>(
         &'a self,
         cx: &'a mut TestAppContext,
-    ) -> (View<Workspace>, &'a mut VisualTestContext) {
+    ) -> (Entity<Workspace>, &'a mut VisualTestContext) {
         let project = self.build_test_project(cx).await;
-        cx.add_window_view(|cx| {
-            cx.activate_window();
-            Workspace::new(None, project.clone(), self.app_state.clone(), cx)
+        cx.add_window_view(|window, cx| {
+            window.activate_window();
+            Workspace::new(None, project.clone(), self.app_state.clone(), window, cx)
         })
     }
 
     pub fn active_workspace<'a>(
         &'a self,
         cx: &'a mut TestAppContext,
-    ) -> (View<Workspace>, &'a mut VisualTestContext) {
+    ) -> (Entity<Workspace>, &'a mut VisualTestContext) {
         let window = cx.update(|cx| cx.active_window().unwrap().downcast::<Workspace>().unwrap());
 
-        let view = window.root_view(cx).unwrap();
+        let model = window.root_model(cx).unwrap();
         let cx = VisualTestContext::from_window(*window.deref(), cx).as_mut();
         // it might be nice to try and cleanup these at the end of each test.
-        (view, cx)
+        (model, cx)
     }
 }
 
 pub fn open_channel_notes(
     channel_id: ChannelId,
     cx: &mut VisualTestContext,
-) -> Task<anyhow::Result<View<ChannelView>>> {
-    let window = cx.update(|cx| cx.active_window().unwrap().downcast::<Workspace>().unwrap());
-    let view = window.root_view(cx).unwrap();
+) -> Task<anyhow::Result<Entity<ChannelView>>> {
+    let window = cx.update(|_, cx| cx.active_window().unwrap().downcast::<Workspace>().unwrap());
+    let model = window.root_model(cx).unwrap();
 
-    cx.update(|cx| ChannelView::open(channel_id, None, view.clone(), cx))
+    cx.update(|window, cx| ChannelView::open(channel_id, None, model.clone(), window, cx))
 }
 
 impl Drop for TestClient {

crates/collab/src/user_backfiller.rs 🔗

@@ -1,6 +1,6 @@
 use std::sync::Arc;
 
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use chrono::{DateTime, Utc};
 use util::ResultExt;
 

crates/collab_ui/src/channel_view.rs 🔗

@@ -11,9 +11,8 @@ use editor::{
     EditorEvent,
 };
 use gpui::{
-    actions, AnyView, AppContext, ClipboardItem, Entity as _, EventEmitter, FocusableView, Model,
-    Pixels, Point, Render, Subscription, Task, View, ViewContext, VisualContext as _, WeakView,
-    WindowContext,
+    actions, AnyView, App, ClipboardItem, Context, Entity, EventEmitter, Focusable, Pixels, Point,
+    Render, Subscription, Task, VisualContext as _, WeakEntity, Window,
 };
 use project::Project;
 use rpc::proto::ChannelVisibility;
@@ -33,16 +32,16 @@ use workspace::{
 
 actions!(collab, [CopyLink]);
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     workspace::FollowableViewRegistry::register::<ChannelView>(cx)
 }
 
 pub struct ChannelView {
-    pub editor: View<Editor>,
-    workspace: WeakView<Workspace>,
-    project: Model<Project>,
-    channel_store: Model<ChannelStore>,
-    channel_buffer: Model<ChannelBuffer>,
+    pub editor: Entity<Editor>,
+    workspace: WeakEntity<Workspace>,
+    project: Entity<Project>,
+    channel_store: Entity<ChannelStore>,
+    channel_buffer: Entity<ChannelBuffer>,
     remote_id: Option<ViewId>,
     _editor_event_subscription: Subscription,
     _reparse_subscription: Option<Subscription>,
@@ -52,20 +51,22 @@ impl ChannelView {
     pub fn open(
         channel_id: ChannelId,
         link_position: Option<String>,
-        workspace: View<Workspace>,
-        cx: &mut WindowContext,
-    ) -> Task<Result<View<Self>>> {
+        workspace: Entity<Workspace>,
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Task<Result<Entity<Self>>> {
         let pane = workspace.read(cx).active_pane().clone();
         let channel_view = Self::open_in_pane(
             channel_id,
             link_position,
             pane.clone(),
             workspace.clone(),
+            window,
             cx,
         );
-        cx.spawn(|mut cx| async move {
+        window.spawn(cx, |mut cx| async move {
             let channel_view = channel_view.await?;
-            pane.update(&mut cx, |pane, cx| {
+            pane.update_in(&mut cx, |pane, window, cx| {
                 telemetry::event!(
                     "Channel Notes Opened",
                     channel_id,
@@ -74,7 +75,7 @@ impl ChannelView {
                         .room()
                         .map(|r| r.read(cx).id())
                 );
-                pane.add_item(Box::new(channel_view.clone()), true, true, None, cx);
+                pane.add_item(Box::new(channel_view.clone()), true, true, None, window, cx);
             })?;
             anyhow::Ok(channel_view)
         })
@@ -83,15 +84,16 @@ impl ChannelView {
     pub fn open_in_pane(
         channel_id: ChannelId,
         link_position: Option<String>,
-        pane: View<Pane>,
-        workspace: View<Workspace>,
-        cx: &mut WindowContext,
-    ) -> Task<Result<View<Self>>> {
-        let channel_view = Self::load(channel_id, workspace, cx);
-        cx.spawn(|mut cx| async move {
+        pane: Entity<Pane>,
+        workspace: Entity<Workspace>,
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Task<Result<Entity<Self>>> {
+        let channel_view = Self::load(channel_id, workspace, window, cx);
+        window.spawn(cx, |mut cx| async move {
             let channel_view = channel_view.await?;
 
-            pane.update(&mut cx, |pane, cx| {
+            pane.update_in(&mut cx, |pane, window, cx| {
                 let buffer_id = channel_view.read(cx).channel_buffer.read(cx).remote_id(cx);
 
                 let existing_view = pane
@@ -104,7 +106,12 @@ impl ChannelView {
                     {
                         if let Some(link_position) = link_position {
                             existing_view.update(cx, |channel_view, cx| {
-                                channel_view.focus_position_from_link(link_position, true, cx)
+                                channel_view.focus_position_from_link(
+                                    link_position,
+                                    true,
+                                    window,
+                                    cx,
+                                )
                             });
                         }
                         return existing_view;
@@ -115,15 +122,27 @@ impl ChannelView {
                 // replace that.
                 if let Some(existing_item) = existing_view {
                     if let Some(ix) = pane.index_for_item(&existing_item) {
-                        pane.close_item_by_id(existing_item.entity_id(), SaveIntent::Skip, cx)
-                            .detach();
-                        pane.add_item(Box::new(channel_view.clone()), true, true, Some(ix), cx);
+                        pane.close_item_by_id(
+                            existing_item.entity_id(),
+                            SaveIntent::Skip,
+                            window,
+                            cx,
+                        )
+                        .detach();
+                        pane.add_item(
+                            Box::new(channel_view.clone()),
+                            true,
+                            true,
+                            Some(ix),
+                            window,
+                            cx,
+                        );
                     }
                 }
 
                 if let Some(link_position) = link_position {
                     channel_view.update(cx, |channel_view, cx| {
-                        channel_view.focus_position_from_link(link_position, true, cx)
+                        channel_view.focus_position_from_link(link_position, true, window, cx)
                     });
                 }
 
@@ -134,9 +153,10 @@ impl ChannelView {
 
     pub fn load(
         channel_id: ChannelId,
-        workspace: View<Workspace>,
-        cx: &mut WindowContext,
-    ) -> Task<Result<View<Self>>> {
+        workspace: Entity<Workspace>,
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Task<Result<Entity<Self>>> {
         let weak_workspace = workspace.downgrade();
         let workspace = workspace.read(cx);
         let project = workspace.project().to_owned();
@@ -146,7 +166,7 @@ impl ChannelView {
         let channel_buffer =
             channel_store.update(cx, |store, cx| store.open_channel_buffer(channel_id, cx));
 
-        cx.spawn(|mut cx| async move {
+        window.spawn(cx, |mut cx| async move {
             let channel_buffer = channel_buffer.await?;
             let markdown = markdown.await.log_err();
 
@@ -160,9 +180,15 @@ impl ChannelView {
                 })
             })?;
 
-            cx.new_view(|cx| {
-                let mut this =
-                    Self::new(project, weak_workspace, channel_store, channel_buffer, cx);
+            cx.new_window_model(|window, cx| {
+                let mut this = Self::new(
+                    project,
+                    weak_workspace,
+                    channel_store,
+                    channel_buffer,
+                    window,
+                    cx,
+                );
                 this.acknowledge_buffer_version(cx);
                 this
             })
@@ -170,25 +196,28 @@ impl ChannelView {
     }
 
     pub fn new(
-        project: Model<Project>,
-        workspace: WeakView<Workspace>,
-        channel_store: Model<ChannelStore>,
-        channel_buffer: Model<ChannelBuffer>,
-        cx: &mut ViewContext<Self>,
+        project: Entity<Project>,
+        workspace: WeakEntity<Workspace>,
+        channel_store: Entity<ChannelStore>,
+        channel_buffer: Entity<ChannelBuffer>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let buffer = channel_buffer.read(cx).buffer();
-        let this = cx.view().downgrade();
-        let editor = cx.new_view(|cx| {
-            let mut editor = Editor::for_buffer(buffer, None, cx);
+        let this = cx.model().downgrade();
+        let editor = cx.new(|cx| {
+            let mut editor = Editor::for_buffer(buffer, None, window, cx);
             editor.set_collaboration_hub(Box::new(ChannelBufferCollaborationHub(
                 channel_buffer.clone(),
             )));
-            editor.set_custom_context_menu(move |_, position, cx| {
+            editor.set_custom_context_menu(move |_, position, window, cx| {
                 let this = this.clone();
-                Some(ui::ContextMenu::build(cx, move |menu, _| {
-                    menu.entry("Copy link to section", None, move |cx| {
-                        this.update(cx, |this, cx| this.copy_link_for_position(position, cx))
-                            .ok();
+                Some(ui::ContextMenu::build(window, cx, move |menu, _, _| {
+                    menu.entry("Copy link to section", None, move |window, cx| {
+                        this.update(cx, |this, cx| {
+                            this.copy_link_for_position(position, window, cx)
+                        })
+                        .ok();
                     })
                 }))
             });
@@ -197,7 +226,7 @@ impl ChannelView {
         let _editor_event_subscription =
             cx.subscribe(&editor, |_, _, e: &EditorEvent, cx| cx.emit(e.clone()));
 
-        cx.subscribe(&channel_buffer, Self::handle_channel_buffer_event)
+        cx.subscribe_in(&channel_buffer, window, Self::handle_channel_buffer_event)
             .detach();
 
         Self {
@@ -216,10 +245,13 @@ impl ChannelView {
         &mut self,
         position: String,
         first_attempt: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let position = Channel::slug(&position).to_lowercase();
-        let snapshot = self.editor.update(cx, |editor, cx| editor.snapshot(cx));
+        let snapshot = self
+            .editor
+            .update(cx, |editor, cx| editor.snapshot(window, cx));
 
         if let Some(outline) = snapshot.buffer_snapshot.outline(None) {
             if let Some(item) = outline
@@ -228,7 +260,7 @@ impl ChannelView {
                 .find(|item| &Channel::slug(&item.text).to_lowercase() == &position)
             {
                 self.editor.update(cx, |editor, cx| {
-                    editor.change_selections(Some(Autoscroll::focused()), cx, |s| {
+                    editor.change_selections(Some(Autoscroll::focused()), window, cx, |s| {
                         s.replace_cursors_with(|map| vec![item.range.start.to_display_point(map)])
                     })
                 });
@@ -239,12 +271,13 @@ impl ChannelView {
         if !first_attempt {
             return;
         }
-        self._reparse_subscription = Some(cx.subscribe(
+        self._reparse_subscription = Some(cx.subscribe_in(
             &self.editor,
-            move |this, _, e: &EditorEvent, cx| {
+            window,
+            move |this, _, e: &EditorEvent, window, cx| {
                 match e {
                     EditorEvent::Reparsed(_) => {
-                        this.focus_position_from_link(position.clone(), false, cx);
+                        this.focus_position_from_link(position.clone(), false, window, cx);
                         this._reparse_subscription.take();
                     }
                     EditorEvent::Edited { .. } | EditorEvent::SelectionsChanged { local: true } => {
@@ -256,15 +289,22 @@ impl ChannelView {
         ));
     }
 
-    fn copy_link(&mut self, _: &CopyLink, cx: &mut ViewContext<Self>) {
+    fn copy_link(&mut self, _: &CopyLink, window: &mut Window, cx: &mut Context<Self>) {
         let position = self
             .editor
             .update(cx, |editor, cx| editor.selections.newest_display(cx).start);
-        self.copy_link_for_position(position, cx)
+        self.copy_link_for_position(position, window, cx)
     }
 
-    fn copy_link_for_position(&self, position: DisplayPoint, cx: &mut ViewContext<Self>) {
-        let snapshot = self.editor.update(cx, |editor, cx| editor.snapshot(cx));
+    fn copy_link_for_position(
+        &self,
+        position: DisplayPoint,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        let snapshot = self
+            .editor
+            .update(cx, |editor, cx| editor.snapshot(window, cx));
 
         let mut closest_heading = None;
 
@@ -298,15 +338,16 @@ impl ChannelView {
             .ok();
     }
 
-    pub fn channel(&self, cx: &AppContext) -> Option<Arc<Channel>> {
+    pub fn channel(&self, cx: &App) -> Option<Arc<Channel>> {
         self.channel_buffer.read(cx).channel(cx)
     }
 
     fn handle_channel_buffer_event(
         &mut self,
-        _: Model<ChannelBuffer>,
+        _: &Entity<ChannelBuffer>,
         event: &ChannelBufferEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match event {
             ChannelBufferEvent::Disconnected => self.editor.update(cx, |editor, cx| {
@@ -320,7 +361,7 @@ impl ChannelView {
                 });
             }
             ChannelBufferEvent::BufferEdited => {
-                if self.editor.read(cx).is_focused(cx) {
+                if self.editor.read(cx).is_focused(window) {
                     self.acknowledge_buffer_version(cx);
                 } else {
                     self.channel_store.update(cx, |store, cx| {
@@ -338,7 +379,7 @@ impl ChannelView {
         }
     }
 
-    fn acknowledge_buffer_version(&mut self, cx: &mut ViewContext<ChannelView>) {
+    fn acknowledge_buffer_version(&mut self, cx: &mut Context<ChannelView>) {
         self.channel_store.update(cx, |store, cx| {
             let channel_buffer = self.channel_buffer.read(cx);
             store.acknowledge_notes_version(
@@ -357,7 +398,7 @@ impl ChannelView {
 impl EventEmitter<EditorEvent> for ChannelView {}
 
 impl Render for ChannelView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         div()
             .size_full()
             .on_action(cx.listener(Self::copy_link))
@@ -365,8 +406,8 @@ impl Render for ChannelView {
     }
 }
 
-impl FocusableView for ChannelView {
-    fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
+impl Focusable for ChannelView {
+    fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
         self.editor.read(cx).focus_handle(cx)
     }
 }
@@ -377,8 +418,8 @@ impl Item for ChannelView {
     fn act_as_type<'a>(
         &'a self,
         type_id: TypeId,
-        self_handle: &'a View<Self>,
-        _: &'a AppContext,
+        self_handle: &'a Entity<Self>,
+        _: &'a App,
     ) -> Option<AnyView> {
         if type_id == TypeId::of::<Self>() {
             Some(self_handle.to_any())
@@ -389,7 +430,7 @@ impl Item for ChannelView {
         }
     }
 
-    fn tab_icon(&self, cx: &WindowContext) -> Option<Icon> {
+    fn tab_icon(&self, _: &Window, cx: &App) -> Option<Icon> {
         let channel = self.channel(cx)?;
         let icon = match channel.visibility {
             ChannelVisibility::Public => IconName::Public,
@@ -399,7 +440,7 @@ impl Item for ChannelView {
         Some(Icon::new(icon))
     }
 
-    fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> gpui::AnyElement {
+    fn tab_content(&self, params: TabContentParams, _: &Window, cx: &App) -> gpui::AnyElement {
         let (channel_name, status) = if let Some(channel) = self.channel(cx) {
             let status = match (
                 self.channel_buffer.read(cx).buffer().read(cx).read_only(),
@@ -439,38 +480,52 @@ impl Item for ChannelView {
     fn clone_on_split(
         &self,
         _: Option<WorkspaceId>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<View<Self>> {
-        Some(cx.new_view(|cx| {
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Entity<Self>> {
+        Some(cx.new(|cx| {
             Self::new(
                 self.project.clone(),
                 self.workspace.clone(),
                 self.channel_store.clone(),
                 self.channel_buffer.clone(),
+                window,
                 cx,
             )
         }))
     }
 
-    fn is_singleton(&self, _cx: &AppContext) -> bool {
+    fn is_singleton(&self, _cx: &App) -> bool {
         false
     }
 
-    fn navigate(&mut self, data: Box<dyn Any>, cx: &mut ViewContext<Self>) -> bool {
+    fn navigate(
+        &mut self,
+        data: Box<dyn Any>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> bool {
         self.editor
-            .update(cx, |editor, cx| editor.navigate(data, cx))
+            .update(cx, |editor, cx| editor.navigate(data, window, cx))
     }
 
-    fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
-        self.editor.update(cx, Item::deactivated)
+    fn deactivated(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        self.editor
+            .update(cx, |item, cx| item.deactivated(window, cx))
     }
 
-    fn set_nav_history(&mut self, history: ItemNavHistory, cx: &mut ViewContext<Self>) {
-        self.editor
-            .update(cx, |editor, cx| Item::set_nav_history(editor, history, cx))
+    fn set_nav_history(
+        &mut self,
+        history: ItemNavHistory,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.editor.update(cx, |editor, cx| {
+            Item::set_nav_history(editor, history, window, cx)
+        })
     }
 
-    fn as_searchable(&self, _: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
+    fn as_searchable(&self, _: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {
         Some(Box::new(self.editor.clone()))
     }
 
@@ -478,7 +533,7 @@ impl Item for ChannelView {
         true
     }
 
-    fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Point<Pixels>> {
+    fn pixel_position_of_cursor(&self, cx: &App) -> Option<Point<Pixels>> {
         self.editor.read(cx).pixel_position_of_cursor(cx)
     }
 
@@ -492,7 +547,7 @@ impl FollowableItem for ChannelView {
         self.remote_id
     }
 
-    fn to_state_proto(&self, cx: &WindowContext) -> Option<proto::view::Variant> {
+    fn to_state_proto(&self, window: &Window, cx: &App) -> Option<proto::view::Variant> {
         let channel_buffer = self.channel_buffer.read(cx);
         if !channel_buffer.is_connected() {
             return None;
@@ -502,7 +557,7 @@ impl FollowableItem for ChannelView {
             proto::view::ChannelView {
                 channel_id: channel_buffer.channel_id.0,
                 editor: if let Some(proto::view::Variant::Editor(proto)) =
-                    self.editor.read(cx).to_state_proto(cx)
+                    self.editor.read(cx).to_state_proto(window, cx)
                 {
                     Some(proto)
                 } else {
@@ -513,11 +568,12 @@ impl FollowableItem for ChannelView {
     }
 
     fn from_state_proto(
-        workspace: View<workspace::Workspace>,
+        workspace: Entity<workspace::Workspace>,
         remote_id: workspace::ViewId,
         state: &mut Option<proto::view::Variant>,
-        cx: &mut WindowContext,
-    ) -> Option<gpui::Task<anyhow::Result<View<Self>>>> {
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Option<gpui::Task<anyhow::Result<Entity<Self>>>> {
         let Some(proto::view::Variant::ChannelView(_)) = state else {
             return None;
         };
@@ -525,12 +581,12 @@ impl FollowableItem for ChannelView {
             unreachable!()
         };
 
-        let open = ChannelView::load(ChannelId(state.channel_id), workspace, cx);
+        let open = ChannelView::load(ChannelId(state.channel_id), workspace, window, cx);
 
-        Some(cx.spawn(|mut cx| async move {
+        Some(window.spawn(cx, |mut cx| async move {
             let this = open.await?;
 
-            let task = this.update(&mut cx, |this, cx| {
+            let task = this.update_in(&mut cx, |this, window, cx| {
                 this.remote_id = Some(remote_id);
 
                 if let Some(state) = state.editor {
@@ -545,6 +601,7 @@ impl FollowableItem for ChannelView {
                                 scroll_y: state.scroll_y,
                                 ..Default::default()
                             }),
+                            window,
                             cx,
                         )
                     }))
@@ -565,31 +622,38 @@ impl FollowableItem for ChannelView {
         &self,
         event: &EditorEvent,
         update: &mut Option<proto::update_view::Variant>,
-        cx: &WindowContext,
+        window: &Window,
+        cx: &App,
     ) -> bool {
         self.editor
             .read(cx)
-            .add_event_to_update_proto(event, update, cx)
+            .add_event_to_update_proto(event, update, window, cx)
     }
 
     fn apply_update_proto(
         &mut self,
-        project: &Model<Project>,
+        project: &Entity<Project>,
         message: proto::update_view::Variant,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> gpui::Task<anyhow::Result<()>> {
         self.editor.update(cx, |editor, cx| {
-            editor.apply_update_proto(project, message, cx)
+            editor.apply_update_proto(project, message, window, cx)
         })
     }
 
-    fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>) {
+    fn set_leader_peer_id(
+        &mut self,
+        leader_peer_id: Option<PeerId>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.editor.update(cx, |editor, cx| {
-            editor.set_leader_peer_id(leader_peer_id, cx)
+            editor.set_leader_peer_id(leader_peer_id, window, cx)
         })
     }
 
-    fn is_project_item(&self, _cx: &WindowContext) -> bool {
+    fn is_project_item(&self, _window: &Window, _cx: &App) -> bool {
         false
     }
 
@@ -597,7 +661,7 @@ impl FollowableItem for ChannelView {
         Editor::to_follow_event(event)
     }
 
-    fn dedup(&self, existing: &Self, cx: &WindowContext) -> Option<Dedup> {
+    fn dedup(&self, existing: &Self, _: &Window, cx: &App) -> Option<Dedup> {
         let existing = existing.channel_buffer.read(cx);
         if self.channel_buffer.read(cx).channel_id == existing.channel_id {
             if existing.is_connected() {
@@ -611,21 +675,18 @@ impl FollowableItem for ChannelView {
     }
 }
 
-struct ChannelBufferCollaborationHub(Model<ChannelBuffer>);
+struct ChannelBufferCollaborationHub(Entity<ChannelBuffer>);
 
 impl CollaborationHub for ChannelBufferCollaborationHub {
-    fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
+    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
         self.0.read(cx).collaborators()
     }
 
-    fn user_participant_indices<'a>(
-        &self,
-        cx: &'a AppContext,
-    ) -> &'a HashMap<u64, ParticipantIndex> {
+    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
         self.0.read(cx).user_store().read(cx).participant_indices()
     }
 
-    fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
+    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
         let user_ids = self.collaborators(cx).values().map(|c| c.user_id);
         self.0
             .read(cx)

crates/collab_ui/src/chat_panel.rs 🔗

@@ -7,10 +7,10 @@ use collections::HashMap;
 use db::kvp::KEY_VALUE_STORE;
 use editor::{actions, Editor};
 use gpui::{
-    actions, div, list, prelude::*, px, Action, AppContext, AsyncWindowContext, ClipboardItem,
-    CursorStyle, DismissEvent, ElementId, EventEmitter, FocusHandle, FocusableView, FontWeight,
-    HighlightStyle, ListOffset, ListScrollEvent, ListState, Model, Render, Stateful, Subscription,
-    Task, View, ViewContext, VisualContext, WeakView,
+    actions, div, list, prelude::*, px, Action, App, AsyncWindowContext, ClipboardItem, Context,
+    CursorStyle, DismissEvent, ElementId, Entity, EventEmitter, FocusHandle, Focusable, FontWeight,
+    HighlightStyle, ListOffset, ListScrollEvent, ListState, Render, Stateful, Subscription, Task,
+    WeakEntity, Window,
 };
 use language::LanguageRegistry;
 use menu::Confirm;
@@ -36,10 +36,10 @@ mod message_editor;
 const MESSAGE_LOADING_THRESHOLD: usize = 50;
 const CHAT_PANEL_KEY: &str = "ChatPanel";
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(|workspace: &mut Workspace, _| {
-        workspace.register_action(|workspace, _: &ToggleFocus, cx| {
-            workspace.toggle_panel_focus::<ChatPanel>(cx);
+pub fn init(cx: &mut App) {
+    cx.observe_new(|workspace: &mut Workspace, _, _| {
+        workspace.register_action(|workspace, _: &ToggleFocus, window, cx| {
+            workspace.toggle_panel_focus::<ChatPanel>(window, cx);
         });
     })
     .detach();
@@ -47,11 +47,11 @@ pub fn init(cx: &mut AppContext) {
 
 pub struct ChatPanel {
     client: Arc<Client>,
-    channel_store: Model<ChannelStore>,
+    channel_store: Entity<ChannelStore>,
     languages: Arc<LanguageRegistry>,
     message_list: ListState,
-    active_chat: Option<(Model<ChannelChat>, Subscription)>,
-    message_editor: View<MessageEditor>,
+    active_chat: Option<(Entity<ChannelChat>, Subscription)>,
+    message_editor: Entity<MessageEditor>,
     local_timezone: UtcOffset,
     fs: Arc<dyn Fs>,
     width: Option<Pixels>,
@@ -74,37 +74,46 @@ struct SerializedChatPanel {
 actions!(chat_panel, [ToggleFocus]);
 
 impl ChatPanel {
-    pub fn new(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) -> View<Self> {
+    pub fn new(
+        workspace: &mut Workspace,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Entity<Self> {
         let fs = workspace.app_state().fs.clone();
         let client = workspace.app_state().client.clone();
         let channel_store = ChannelStore::global(cx);
         let user_store = workspace.app_state().user_store.clone();
         let languages = workspace.app_state().languages.clone();
 
-        let input_editor = cx.new_view(|cx| {
+        let input_editor = cx.new(|cx| {
             MessageEditor::new(
                 languages.clone(),
                 user_store.clone(),
                 None,
-                cx.new_view(|cx| Editor::auto_height(4, cx)),
+                cx.new(|cx| Editor::auto_height(4, window, cx)),
+                window,
                 cx,
             )
         });
 
-        cx.new_view(|cx: &mut ViewContext<Self>| {
-            let view = cx.view().downgrade();
-            let message_list =
-                ListState::new(0, gpui::ListAlignment::Bottom, px(1000.), move |ix, cx| {
-                    if let Some(view) = view.upgrade() {
-                        view.update(cx, |view, cx| {
-                            view.render_message(ix, cx).into_any_element()
+        cx.new(|cx| {
+            let model = cx.model().downgrade();
+            let message_list = ListState::new(
+                0,
+                gpui::ListAlignment::Bottom,
+                px(1000.),
+                move |ix, window, cx| {
+                    if let Some(model) = model.upgrade() {
+                        model.update(cx, |this: &mut Self, cx| {
+                            this.render_message(ix, window, cx).into_any_element()
                         })
                     } else {
                         div().into_any()
                     }
-                });
+                },
+            );
 
-            message_list.set_scroll_handler(cx.listener(|this, event: &ListScrollEvent, cx| {
+            message_list.set_scroll_handler(cx.listener(|this, event: &ListScrollEvent, _, cx| {
                 if event.visible_range.start < MESSAGE_LOADING_THRESHOLD {
                     this.load_more_messages(cx);
                 }
@@ -172,7 +181,7 @@ impl ChatPanel {
         })
     }
 
-    pub fn channel_id(&self, cx: &AppContext) -> Option<ChannelId> {
+    pub fn channel_id(&self, cx: &App) -> Option<ChannelId> {
         self.active_chat
             .as_ref()
             .map(|(chat, _)| chat.read(cx).channel_id)
@@ -182,14 +191,14 @@ impl ChatPanel {
         self.is_scrolled_to_bottom
     }
 
-    pub fn active_chat(&self) -> Option<Model<ChannelChat>> {
+    pub fn active_chat(&self) -> Option<Entity<ChannelChat>> {
         self.active_chat.as_ref().map(|(chat, _)| chat.clone())
     }
 
     pub fn load(
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         cx: AsyncWindowContext,
-    ) -> Task<Result<View<Self>>> {
+    ) -> Task<Result<Entity<Self>>> {
         cx.spawn(|mut cx| async move {
             let serialized_panel = if let Some(panel) = cx
                 .background_executor()
@@ -203,8 +212,8 @@ impl ChatPanel {
                 None
             };
 
-            workspace.update(&mut cx, |workspace, cx| {
-                let panel = Self::new(workspace, cx);
+            workspace.update_in(&mut cx, |workspace, window, cx| {
+                let panel = Self::new(workspace, window, cx);
                 if let Some(serialized_panel) = serialized_panel {
                     panel.update(cx, |panel, cx| {
                         panel.width = serialized_panel.width.map(|r| r.round());
@@ -216,7 +225,7 @@ impl ChatPanel {
         })
     }
 
-    fn serialize(&mut self, cx: &mut ViewContext<Self>) {
+    fn serialize(&mut self, cx: &mut Context<Self>) {
         let width = self.width;
         self.pending_serialization = cx.background_executor().spawn(
             async move {
@@ -232,7 +241,7 @@ impl ChatPanel {
         );
     }
 
-    fn set_active_chat(&mut self, chat: Model<ChannelChat>, cx: &mut ViewContext<Self>) {
+    fn set_active_chat(&mut self, chat: Entity<ChannelChat>, cx: &mut Context<Self>) {
         if self.active_chat.as_ref().map(|e| &e.0) != Some(&chat) {
             self.markdown_data.clear();
             self.message_list.reset(chat.read(cx).message_count());
@@ -249,9 +258,9 @@ impl ChatPanel {
 
     fn channel_did_change(
         &mut self,
-        _: Model<ChannelChat>,
+        _: Entity<ChannelChat>,
         event: &ChannelChatEvent,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match event {
             ChannelChatEvent::MessagesUpdated {
@@ -284,7 +293,7 @@ impl ChatPanel {
         cx.notify();
     }
 
-    fn acknowledge_last_message(&mut self, cx: &mut ViewContext<Self>) {
+    fn acknowledge_last_message(&mut self, cx: &mut Context<Self>) {
         if self.active && self.is_scrolled_to_bottom {
             if let Some((chat, _)) = &self.active_chat {
                 if let Some(channel_id) = self.channel_id(cx) {
@@ -305,7 +314,7 @@ impl ChatPanel {
         &mut self,
         message_id: Option<ChannelMessageId>,
         reply_to_message: &Option<ChannelMessage>,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> impl IntoElement {
         let reply_to_message = match reply_to_message {
             None => {
@@ -369,8 +378,8 @@ impl ChatPanel {
                     ),
                 )
                 .cursor(CursorStyle::PointingHand)
-                .tooltip(|cx| Tooltip::text("Go to message", cx))
-                .on_click(cx.listener(move |chat_panel, _, cx| {
+                .tooltip(Tooltip::text("Go to message"))
+                .on_click(cx.listener(move |chat_panel, _, _, cx| {
                     if let Some(channel_id) = current_channel_id {
                         chat_panel
                             .select_channel(channel_id, reply_to_message_id.into(), cx)
@@ -380,7 +389,12 @@ impl ChatPanel {
         )
     }
 
-    fn render_message(&mut self, ix: usize, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render_message(
+        &mut self,
+        ix: usize,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> impl IntoElement {
         let active_chat = &self.active_chat.as_ref().unwrap().0;
         let (message, is_continuation_from_previous, is_admin) =
             active_chat.update(cx, |active_chat, cx| {
@@ -530,7 +544,7 @@ impl ChatPanel {
                                 .w_full()
                                 .text_ui_sm(cx)
                                 .id(element_id)
-                                .child(text.element("body".into(), cx)),
+                                .child(text.element("body".into(), window, cx)),
                         )
                         .when(self.has_open_menu(message_id), |el| {
                             el.bg(cx.theme().colors().element_selected)
@@ -560,7 +574,7 @@ impl ChatPanel {
                 },
             )
             .child(
-                self.render_popover_buttons(cx, message_id, can_delete_message, can_edit_message)
+                self.render_popover_buttons(message_id, can_delete_message, can_edit_message, cx)
                     .mt_neg_2p5(),
             )
     }
@@ -572,7 +586,7 @@ impl ChatPanel {
         }
     }
 
-    fn render_popover_button(&self, cx: &ViewContext<Self>, child: Stateful<Div>) -> Div {
+    fn render_popover_button(&self, cx: &mut Context<Self>, child: Stateful<Div>) -> Div {
         div()
             .w_6()
             .bg(cx.theme().colors().element_background)
@@ -582,10 +596,10 @@ impl ChatPanel {
 
     fn render_popover_buttons(
         &self,
-        cx: &ViewContext<Self>,
         message_id: Option<u64>,
         can_delete_message: bool,
         can_edit_message: bool,
+        cx: &mut Context<Self>,
     ) -> Div {
         h_flex()
             .absolute()
@@ -606,16 +620,16 @@ impl ChatPanel {
                             .id("reply")
                             .child(
                                 IconButton::new(("reply", message_id), IconName::ReplyArrowRight)
-                                    .on_click(cx.listener(move |this, _, cx| {
+                                    .on_click(cx.listener(move |this, _, window, cx| {
                                         this.cancel_edit_message(cx);
 
                                         this.message_editor.update(cx, |editor, cx| {
                                             editor.set_reply_to_message_id(message_id);
-                                            editor.focus_handle(cx).focus(cx);
+                                            window.focus(&editor.focus_handle(cx));
                                         })
                                     })),
                             )
-                            .tooltip(|cx| Tooltip::text("Reply", cx)),
+                            .tooltip(Tooltip::text("Reply")),
                     ),
                 )
             })
@@ -628,7 +642,7 @@ impl ChatPanel {
                                 .id("edit")
                                 .child(
                                     IconButton::new(("edit", message_id), IconName::Pencil)
-                                        .on_click(cx.listener(move |this, _, cx| {
+                                        .on_click(cx.listener(move |this, _, window, cx| {
                                             this.message_editor.update(cx, |editor, cx| {
                                                 editor.clear_reply_to_message_id();
 
@@ -655,18 +669,18 @@ impl ChatPanel {
                                                     });
 
                                                     editor.set_edit_message_id(message_id);
-                                                    editor.focus_handle(cx).focus(cx);
+                                                    editor.focus_handle(cx).focus(window);
                                                 }
                                             })
                                         })),
                                 )
-                                .tooltip(|cx| Tooltip::text("Edit", cx)),
+                                .tooltip(Tooltip::text("Edit")),
                         ),
                     )
                 })
             })
             .when_some(message_id, |el, message_id| {
-                let this = cx.view().clone();
+                let this = cx.model().clone();
 
                 el.child(
                     self.render_popover_button(
@@ -678,34 +692,36 @@ impl ChatPanel {
                                         ("trigger", message_id),
                                         IconName::Ellipsis,
                                     ))
-                                    .menu(move |cx| {
+                                    .menu(move |window, cx| {
                                         Some(Self::render_message_menu(
                                             &this,
                                             message_id,
                                             can_delete_message,
+                                            window,
                                             cx,
                                         ))
                                     }),
                             )
                             .id("more")
-                            .tooltip(|cx| Tooltip::text("More", cx)),
+                            .tooltip(Tooltip::text("More")),
                     ),
                 )
             })
     }
 
     fn render_message_menu(
-        this: &View<Self>,
+        this: &Entity<Self>,
         message_id: u64,
         can_delete_message: bool,
-        cx: &mut WindowContext,
-    ) -> View<ContextMenu> {
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Entity<ContextMenu> {
         let menu = {
-            ContextMenu::build(cx, move |menu, cx| {
+            ContextMenu::build(window, cx, move |menu, window, _| {
                 menu.entry(
                     "Copy message text",
                     None,
-                    cx.handler_for(this, move |this, cx| {
+                    window.handler_for(this, move |this, _, cx| {
                         if let Some(message) = this.active_chat().and_then(|active_chat| {
                             active_chat.read(cx).find_loaded_message(message_id)
                         }) {
@@ -718,15 +734,21 @@ impl ChatPanel {
                     menu.entry(
                         "Delete message",
                         None,
-                        cx.handler_for(this, move |this, cx| this.remove_message(message_id, cx)),
+                        window.handler_for(this, move |this, _, cx| {
+                            this.remove_message(message_id, cx)
+                        }),
                     )
                 })
             })
         };
         this.update(cx, |this, cx| {
-            let subscription = cx.subscribe(&menu, |this: &mut Self, _, _: &DismissEvent, _| {
-                this.open_context_menu = None;
-            });
+            let subscription = cx.subscribe_in(
+                &menu,
+                window,
+                |this: &mut Self, _, _: &DismissEvent, _, _| {
+                    this.open_context_menu = None;
+                },
+            );
             this.open_context_menu = Some((message_id, subscription));
         });
         menu
@@ -737,7 +759,7 @@ impl ChatPanel {
         current_user_id: u64,
         message: &channel::ChannelMessage,
         local_timezone: UtcOffset,
-        cx: &AppContext,
+        cx: &App,
     ) -> RichText {
         let mentions = message
             .mentions
@@ -777,19 +799,19 @@ impl ChatPanel {
                 );
 
                 rich_text.custom_ranges.push(range);
-                rich_text.set_tooltip_builder_for_custom_ranges(move |_, _, cx| {
-                    Some(Tooltip::text(edit_timestamp_text.clone(), cx))
+                rich_text.set_tooltip_builder_for_custom_ranges(move |_, _, _, cx| {
+                    Some(Tooltip::simple(edit_timestamp_text.clone(), cx))
                 })
             }
         }
         rich_text
     }
 
-    fn send(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
+    fn send(&mut self, _: &Confirm, window: &mut Window, cx: &mut Context<Self>) {
         if let Some((chat, _)) = self.active_chat.as_ref() {
             let message = self
                 .message_editor
-                .update(cx, |editor, cx| editor.take_message(cx));
+                .update(cx, |editor, cx| editor.take_message(window, cx));
 
             if let Some(id) = self.message_editor.read(cx).edit_message_id() {
                 self.message_editor.update(cx, |editor, _| {
@@ -811,13 +833,13 @@ impl ChatPanel {
         }
     }
 
-    fn remove_message(&mut self, id: u64, cx: &mut ViewContext<Self>) {
+    fn remove_message(&mut self, id: u64, cx: &mut Context<Self>) {
         if let Some((chat, _)) = self.active_chat.as_ref() {
             chat.update(cx, |chat, cx| chat.remove_message(id, cx).detach())
         }
     }
 
-    fn load_more_messages(&mut self, cx: &mut ViewContext<Self>) {
+    fn load_more_messages(&mut self, cx: &mut Context<Self>) {
         if let Some((chat, _)) = self.active_chat.as_ref() {
             chat.update(cx, |channel, cx| {
                 if let Some(task) = channel.load_more_messages(cx) {
@@ -831,7 +853,7 @@ impl ChatPanel {
         &mut self,
         selected_channel_id: ChannelId,
         scroll_to_message_id: Option<u64>,
-        cx: &mut ViewContext<ChatPanel>,
+        cx: &mut Context<ChatPanel>,
     ) -> Task<Result<()>> {
         let open_chat = self
             .active_chat
@@ -857,20 +879,18 @@ impl ChatPanel {
 
             if let Some(message_id) = scroll_to_message_id {
                 if let Some(item_ix) =
-                    ChannelChat::load_history_since_message(chat.clone(), message_id, (*cx).clone())
+                    ChannelChat::load_history_since_message(chat.clone(), message_id, cx.clone())
                         .await
                 {
                     this.update(&mut cx, |this, cx| {
                         if let Some(highlight_message_id) = highlight_message_id {
-                            let task = cx.spawn({
-                                |this, mut cx| async move {
-                                    cx.background_executor().timer(Duration::from_secs(2)).await;
-                                    this.update(&mut cx, |this, cx| {
-                                        this.highlighted_message.take();
-                                        cx.notify();
-                                    })
-                                    .ok();
-                                }
+                            let task = cx.spawn(|this, mut cx| async move {
+                                cx.background_executor().timer(Duration::from_secs(2)).await;
+                                this.update(&mut cx, |this, cx| {
+                                    this.highlighted_message.take();
+                                    cx.notify();
+                                })
+                                .ok();
                             });
 
                             this.highlighted_message = Some((highlight_message_id, task));
@@ -891,12 +911,12 @@ impl ChatPanel {
         })
     }
 
-    fn close_reply_preview(&mut self, cx: &mut ViewContext<Self>) {
+    fn close_reply_preview(&mut self, cx: &mut Context<Self>) {
         self.message_editor
             .update(cx, |editor, _| editor.clear_reply_to_message_id());
     }
 
-    fn cancel_edit_message(&mut self, cx: &mut ViewContext<Self>) {
+    fn cancel_edit_message(&mut self, cx: &mut Context<Self>) {
         self.message_editor.update(cx, |editor, cx| {
             // only clear the editor input if we were editing a message
             if editor.edit_message_id().is_none() {
@@ -919,7 +939,7 @@ impl ChatPanel {
 }
 
 impl Render for ChatPanel {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let channel_id = self
             .active_chat
             .as_ref()
@@ -971,11 +991,12 @@ impl Render for ChatPanel {
                                         .full_width()
                                         .key_binding(KeyBinding::for_action(
                                             &collab_panel::ToggleFocus,
-                                            cx,
+                                            window,
                                         ))
-                                        .on_click(|_, cx| {
-                                            cx.dispatch_action(
+                                        .on_click(|_, window, cx| {
+                                            window.dispatch_action(
                                                 collab_panel::ToggleFocus.boxed_clone(),
+                                                cx,
                                             )
                                         }),
                                 ),
@@ -999,8 +1020,8 @@ impl Render for ChatPanel {
                         .child(
                             IconButton::new("cancel-edit-message", IconName::Close)
                                 .shape(ui::IconButtonShape::Square)
-                                .tooltip(|cx| Tooltip::text("Cancel edit message", cx))
-                                .on_click(cx.listener(move |this, _, cx| {
+                                .tooltip(Tooltip::text("Cancel edit message"))
+                                .on_click(cx.listener(move |this, _, _, cx| {
                                     this.cancel_edit_message(cx);
                                 })),
                         ),
@@ -1045,7 +1066,7 @@ impl Render for ChatPanel {
                                         )
                                         .when_some(channel_id, |this, channel_id| {
                                             this.cursor_pointer().on_click(cx.listener(
-                                                move |chat_panel, _, cx| {
+                                                move |chat_panel, _, _, cx| {
                                                     chat_panel
                                                         .select_channel(
                                                             channel_id,
@@ -1061,8 +1082,8 @@ impl Render for ChatPanel {
                             .child(
                                 IconButton::new("close-reply-preview", IconName::Close)
                                     .shape(ui::IconButtonShape::Square)
-                                    .tooltip(|cx| Tooltip::text("Close reply", cx))
-                                    .on_click(cx.listener(move |this, _, cx| {
+                                    .tooltip(Tooltip::text("Close reply"))
+                                    .on_click(cx.listener(move |this, _, _, cx| {
                                         this.close_reply_preview(cx);
                                     })),
                             ),
@@ -1073,7 +1094,7 @@ impl Render for ChatPanel {
                 Some(
                     h_flex()
                         .p_2()
-                        .on_action(cx.listener(|this, _: &actions::Cancel, cx| {
+                        .on_action(cx.listener(|this, _: &actions::Cancel, _, cx| {
                             this.cancel_edit_message(cx);
                             this.close_reply_preview(cx);
                         }))
@@ -1085,8 +1106,8 @@ impl Render for ChatPanel {
     }
 }
 
-impl FocusableView for ChatPanel {
-    fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
+impl Focusable for ChatPanel {
+    fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
         if self.active_chat.is_some() {
             self.message_editor.read(cx).focus_handle(cx)
         } else {
@@ -1096,7 +1117,7 @@ impl FocusableView for ChatPanel {
 }
 
 impl Panel for ChatPanel {
-    fn position(&self, cx: &WindowContext) -> DockPosition {
+    fn position(&self, _: &Window, cx: &App) -> DockPosition {
         ChatPanelSettings::get_global(cx).dock
     }
 
@@ -1104,7 +1125,7 @@ impl Panel for ChatPanel {
         matches!(position, DockPosition::Left | DockPosition::Right)
     }
 
-    fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) {
+    fn set_position(&mut self, position: DockPosition, _: &mut Window, cx: &mut Context<Self>) {
         settings::update_settings_file::<ChatPanelSettings>(
             self.fs.clone(),
             cx,
@@ -1112,18 +1133,18 @@ impl Panel for ChatPanel {
         );
     }
 
-    fn size(&self, cx: &WindowContext) -> Pixels {
+    fn size(&self, _: &Window, cx: &App) -> Pixels {
         self.width
             .unwrap_or_else(|| ChatPanelSettings::get_global(cx).default_width)
     }
 
-    fn set_size(&mut self, size: Option<Pixels>, cx: &mut ViewContext<Self>) {
+    fn set_size(&mut self, size: Option<Pixels>, _: &mut Window, cx: &mut Context<Self>) {
         self.width = size;
         self.serialize(cx);
         cx.notify();
     }
 
-    fn set_active(&mut self, active: bool, cx: &mut ViewContext<Self>) {
+    fn set_active(&mut self, active: bool, _: &mut Window, cx: &mut Context<Self>) {
         self.active = active;
         if active {
             self.acknowledge_last_message(cx);
@@ -1134,7 +1155,7 @@ impl Panel for ChatPanel {
         "ChatPanel"
     }
 
-    fn icon(&self, cx: &WindowContext) -> Option<ui::IconName> {
+    fn icon(&self, _window: &Window, cx: &App) -> Option<ui::IconName> {
         let show_icon = match ChatPanelSettings::get_global(cx).button {
             ChatPanelButton::Never => false,
             ChatPanelButton::Always => true,
@@ -1151,7 +1172,7 @@ impl Panel for ChatPanel {
         show_icon.then(|| ui::IconName::MessageBubbles)
     }
 
-    fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> {
+    fn icon_tooltip(&self, _: &Window, _: &App) -> Option<&'static str> {
         Some("Chat Panel")
     }
 
@@ -1159,7 +1180,7 @@ impl Panel for ChatPanel {
         Box::new(ToggleFocus)
     }
 
-    fn starts_open(&self, cx: &WindowContext) -> bool {
+    fn starts_open(&self, _: &Window, cx: &App) -> bool {
         ActiveCall::global(cx)
             .read(cx)
             .room()
@@ -1183,7 +1204,7 @@ mod tests {
     use util::test::marked_text_ranges;
 
     #[gpui::test]
-    fn test_render_markdown_with_mentions(cx: &mut AppContext) {
+    fn test_render_markdown_with_mentions(cx: &mut App) {
         let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
         let (body, ranges) = marked_text_ranges("*hi*, «@abc», let's **call** «@fgh»", false);
         let message = channel::ChannelMessage {
@@ -1240,7 +1261,7 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_render_markdown_with_auto_detect_links(cx: &mut AppContext) {
+    fn test_render_markdown_with_auto_detect_links(cx: &mut App) {
         let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
         let message = channel::ChannelMessage {
             id: ChannelMessageId::Saved(0),
@@ -1289,7 +1310,7 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_render_markdown_with_auto_detect_links_and_additional_formatting(cx: &mut AppContext) {
+    fn test_render_markdown_with_auto_detect_links_and_additional_formatting(cx: &mut App) {
         let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
         let message = channel::ChannelMessage {
             id: ChannelMessageId::Saved(0),

crates/collab_ui/src/chat_panel/message_editor.rs 🔗

@@ -1,12 +1,12 @@
-use anyhow::{Context, Result};
+use anyhow::{Context as _, Result};
 use channel::{ChannelChat, ChannelStore, MessageParams};
 use client::{UserId, UserStore};
 use collections::HashSet;
 use editor::{AnchorRangeExt, CompletionProvider, Editor, EditorElement, EditorStyle};
 use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{
-    AsyncWindowContext, FocusableView, FontStyle, FontWeight, HighlightStyle, IntoElement, Model,
-    Render, Task, TextStyle, View, ViewContext, WeakView,
+    AsyncAppContext, AsyncWindowContext, Context, Entity, Focusable, FontStyle, FontWeight,
+    HighlightStyle, IntoElement, Render, Task, TextStyle, WeakEntity, Window,
 };
 use language::{
     language_settings::SoftWrap, Anchor, Buffer, BufferSnapshot, CodeLabel, LanguageRegistry,
@@ -42,24 +42,25 @@ static MENTIONS_SEARCH: LazyLock<SearchQuery> = LazyLock::new(|| {
 });
 
 pub struct MessageEditor {
-    pub editor: View<Editor>,
-    user_store: Model<UserStore>,
-    channel_chat: Option<Model<ChannelChat>>,
+    pub editor: Entity<Editor>,
+    user_store: Entity<UserStore>,
+    channel_chat: Option<Entity<ChannelChat>>,
     mentions: Vec<UserId>,
     mentions_task: Option<Task<()>>,
     reply_to_message_id: Option<u64>,
     edit_message_id: Option<u64>,
 }
 
-struct MessageEditorCompletionProvider(WeakView<MessageEditor>);
+struct MessageEditorCompletionProvider(WeakEntity<MessageEditor>);
 
 impl CompletionProvider for MessageEditorCompletionProvider {
     fn completions(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         buffer_position: language::Anchor,
         _: editor::CompletionContext,
-        cx: &mut ViewContext<Editor>,
+        _window: &mut Window,
+        cx: &mut Context<Editor>,
     ) -> Task<anyhow::Result<Vec<Completion>>> {
         let Some(handle) = self.0.upgrade() else {
             return Task::ready(Ok(Vec::new()));
@@ -71,21 +72,21 @@ impl CompletionProvider for MessageEditorCompletionProvider {
 
     fn resolve_completions(
         &self,
-        _buffer: Model<Buffer>,
+        _buffer: Entity<Buffer>,
         _completion_indices: Vec<usize>,
         _completions: Rc<RefCell<Box<[Completion]>>>,
-        _cx: &mut ViewContext<Editor>,
+        _cx: &mut Context<Editor>,
     ) -> Task<anyhow::Result<bool>> {
         Task::ready(Ok(false))
     }
 
     fn is_completion_trigger(
         &self,
-        _buffer: &Model<Buffer>,
+        _buffer: &Entity<Buffer>,
         _position: language::Anchor,
         text: &str,
         _trigger_in_words: bool,
-        _cx: &mut ViewContext<Editor>,
+        _cx: &mut Context<Editor>,
     ) -> bool {
         text == "@"
     }
@@ -94,12 +95,13 @@ impl CompletionProvider for MessageEditorCompletionProvider {
 impl MessageEditor {
     pub fn new(
         language_registry: Arc<LanguageRegistry>,
-        user_store: Model<UserStore>,
-        channel_chat: Option<Model<ChannelChat>>,
-        editor: View<Editor>,
-        cx: &mut ViewContext<Self>,
+        user_store: Entity<UserStore>,
+        channel_chat: Option<Entity<ChannelChat>>,
+        editor: Entity<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
-        let this = cx.view().downgrade();
+        let this = cx.model().downgrade();
         editor.update(cx, |editor, cx| {
             editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
             editor.set_use_autoclose(false);
@@ -121,9 +123,10 @@ impl MessageEditor {
             .as_singleton()
             .expect("message editor must be singleton");
 
-        cx.subscribe(&buffer, Self::on_buffer_event).detach();
-        cx.observe_global::<settings::SettingsStore>(|view, cx| {
-            view.editor.update(cx, |editor, cx| {
+        cx.subscribe_in(&buffer, window, Self::on_buffer_event)
+            .detach();
+        cx.observe_global::<settings::SettingsStore>(|this, cx| {
+            this.editor.update(cx, |editor, cx| {
                 editor.set_auto_replace_emoji_shortcode(
                     MessageEditorSettings::get_global(cx)
                         .auto_replace_emoji_shortcode
@@ -134,7 +137,7 @@ impl MessageEditor {
         .detach();
 
         let markdown = language_registry.language_for_name("Markdown");
-        cx.spawn(|_, mut cx| async move {
+        cx.spawn_in(window, |_, mut cx| async move {
             let markdown = markdown.await.context("failed to load Markdown language")?;
             buffer.update(&mut cx, |buffer, cx| {
                 buffer.set_language(Some(markdown), cx)
@@ -177,7 +180,7 @@ impl MessageEditor {
         self.edit_message_id = None;
     }
 
-    pub fn set_channel_chat(&mut self, chat: Model<ChannelChat>, cx: &mut ViewContext<Self>) {
+    pub fn set_channel_chat(&mut self, chat: Entity<ChannelChat>, cx: &mut Context<Self>) {
         let channel_id = chat.read(cx).channel_id;
         self.channel_chat = Some(chat);
         let channel_name = ChannelStore::global(cx)
@@ -193,7 +196,7 @@ impl MessageEditor {
         });
     }
 
-    pub fn take_message(&mut self, cx: &mut ViewContext<Self>) -> MessageParams {
+    pub fn take_message(&mut self, window: &mut Window, cx: &mut Context<Self>) -> MessageParams {
         self.editor.update(cx, |editor, cx| {
             let highlights = editor.text_highlights::<Self>(cx);
             let text = editor.text(cx);
@@ -208,7 +211,7 @@ impl MessageEditor {
                 Vec::new()
             };
 
-            editor.clear(cx);
+            editor.clear(window, cx);
             self.mentions.clear();
             let reply_to_message_id = std::mem::take(&mut self.reply_to_message_id);
 
@@ -222,13 +225,14 @@ impl MessageEditor {
 
     fn on_buffer_event(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: &Entity<Buffer>,
         event: &language::BufferEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         if let language::BufferEvent::Reparsed | language::BufferEvent::Edited = event {
             let buffer = buffer.read(cx).snapshot();
-            self.mentions_task = Some(cx.spawn(|this, cx| async move {
+            self.mentions_task = Some(cx.spawn_in(window, |this, cx| async move {
                 cx.background_executor()
                     .timer(MENTIONS_DEBOUNCE_INTERVAL)
                     .await;
@@ -239,9 +243,9 @@ impl MessageEditor {
 
     fn completions(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         end_anchor: Anchor,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<Vec<Completion>>> {
         if let Some((start_anchor, query, candidates)) =
             self.collect_mention_candidates(buffer, end_anchor, cx)
@@ -281,7 +285,7 @@ impl MessageEditor {
     }
 
     async fn resolve_completions_for_candidates(
-        cx: &AsyncWindowContext,
+        cx: &AsyncAppContext,
         query: &str,
         candidates: &[StringMatchCandidate],
         range: Range<Anchor>,
@@ -336,9 +340,9 @@ impl MessageEditor {
 
     fn collect_mention_candidates(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         end_anchor: Anchor,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<(Anchor, String, Vec<StringMatchCandidate>)> {
         let end_offset = end_anchor.to_offset(buffer.read(cx));
 
@@ -385,9 +389,9 @@ impl MessageEditor {
 
     fn collect_emoji_candidates(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         end_anchor: Anchor,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<(Anchor, String, &'static [StringMatchCandidate])> {
         static EMOJI_FUZZY_MATCH_CANDIDATES: LazyLock<Vec<StringMatchCandidate>> =
             LazyLock::new(|| {
@@ -445,7 +449,7 @@ impl MessageEditor {
     }
 
     async fn find_mentions(
-        this: WeakView<MessageEditor>,
+        this: WeakEntity<MessageEditor>,
         buffer: BufferSnapshot,
         mut cx: AsyncWindowContext,
     ) {
@@ -499,13 +503,13 @@ impl MessageEditor {
         .ok();
     }
 
-    pub(crate) fn focus_handle(&self, cx: &gpui::AppContext) -> gpui::FocusHandle {
+    pub(crate) fn focus_handle(&self, cx: &gpui::App) -> gpui::FocusHandle {
         self.editor.read(cx).focus_handle(cx)
     }
 }
 
 impl Render for MessageEditor {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let settings = ThemeSettings::get_global(cx);
         let text_style = TextStyle {
             color: if self.editor.read(cx).read_only(cx) {

crates/collab_ui/src/collab_panel.rs 🔗

@@ -11,12 +11,11 @@ use db::kvp::KEY_VALUE_STORE;
 use editor::{Editor, EditorElement, EditorStyle};
 use fuzzy::{match_strings, StringMatchCandidate};
 use gpui::{
-    actions, anchored, canvas, deferred, div, fill, list, point, prelude::*, px, AnyElement,
-    AppContext, AsyncWindowContext, Bounds, ClickEvent, ClipboardItem, DismissEvent, Div,
-    EventEmitter, FocusHandle, FocusableView, FontStyle, InteractiveElement, IntoElement,
-    ListOffset, ListState, Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel,
-    Render, SharedString, Styled, Subscription, Task, TextStyle, View, ViewContext, VisualContext,
-    WeakView,
+    actions, anchored, canvas, deferred, div, fill, list, point, prelude::*, px, AnyElement, App,
+    AsyncWindowContext, Bounds, ClickEvent, ClipboardItem, Context, DismissEvent, Div, Entity,
+    EventEmitter, FocusHandle, Focusable, FontStyle, InteractiveElement, IntoElement, ListOffset,
+    ListState, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render, SharedString,
+    Styled, Subscription, Task, TextStyle, WeakEntity, Window,
 };
 use menu::{Cancel, Confirm, SecondaryConfirm, SelectNext, SelectPrev};
 use project::{Fs, Project};
@@ -62,21 +61,22 @@ struct ChannelMoveClipboard {
 
 const COLLABORATION_PANEL_KEY: &str = "CollaborationPanel";
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(|workspace: &mut Workspace, _| {
-        workspace.register_action(|workspace, _: &ToggleFocus, cx| {
-            workspace.toggle_panel_focus::<CollabPanel>(cx);
+pub fn init(cx: &mut App) {
+    cx.observe_new(|workspace: &mut Workspace, _, _| {
+        workspace.register_action(|workspace, _: &ToggleFocus, window, cx| {
+            workspace.toggle_panel_focus::<CollabPanel>(window, cx);
         });
-        workspace.register_action(|_, _: &OpenChannelNotes, cx| {
+        workspace.register_action(|_, _: &OpenChannelNotes, window, cx| {
             let channel_id = ActiveCall::global(cx)
                 .read(cx)
                 .room()
                 .and_then(|room| room.read(cx).channel_id());
 
             if let Some(channel_id) = channel_id {
-                let workspace = cx.view().clone();
-                cx.window_context().defer(move |cx| {
-                    ChannelView::open(channel_id, None, workspace, cx).detach_and_log_err(cx)
+                let workspace = cx.model().clone();
+                window.defer(cx, move |window, cx| {
+                    ChannelView::open(channel_id, None, workspace, window, cx)
+                        .detach_and_log_err(cx)
                 });
             }
         });
@@ -111,22 +111,22 @@ pub struct CollabPanel {
     focus_handle: FocusHandle,
     channel_clipboard: Option<ChannelMoveClipboard>,
     pending_serialization: Task<Option<()>>,
-    context_menu: Option<(View<ContextMenu>, Point<Pixels>, Subscription)>,
+    context_menu: Option<(Entity<ContextMenu>, Point<Pixels>, Subscription)>,
     list_state: ListState,
-    filter_editor: View<Editor>,
-    channel_name_editor: View<Editor>,
+    filter_editor: Entity<Editor>,
+    channel_name_editor: Entity<Editor>,
     channel_editing_state: Option<ChannelEditingState>,
     entries: Vec<ListEntry>,
     selection: Option<usize>,
-    channel_store: Model<ChannelStore>,
-    user_store: Model<UserStore>,
+    channel_store: Entity<ChannelStore>,
+    user_store: Entity<UserStore>,
     client: Arc<Client>,
-    project: Model<Project>,
+    project: Entity<Project>,
     match_candidates: Vec<StringMatchCandidate>,
     subscriptions: Vec<Subscription>,
     collapsed_sections: Vec<Section>,
     collapsed_channels: Vec<ChannelId>,
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
 }
 
 #[derive(Serialize, Deserialize)]
@@ -190,10 +190,14 @@ enum ListEntry {
 }
 
 impl CollabPanel {
-    pub fn new(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) -> View<Self> {
-        cx.new_view(|cx| {
-            let filter_editor = cx.new_view(|cx| {
-                let mut editor = Editor::single_line(cx);
+    pub fn new(
+        workspace: &mut Workspace,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Entity<Self> {
+        cx.new(|cx| {
+            let filter_editor = cx.new(|cx| {
+                let mut editor = Editor::single_line(window, cx);
                 editor.set_placeholder_text("Filter...", cx);
                 editor
             });
@@ -215,31 +219,39 @@ impl CollabPanel {
             })
             .detach();
 
-            let channel_name_editor = cx.new_view(Editor::single_line);
-
-            cx.subscribe(&channel_name_editor, |this: &mut Self, _, event, cx| {
-                if let editor::EditorEvent::Blurred = event {
-                    if let Some(state) = &this.channel_editing_state {
-                        if state.pending_name().is_some() {
-                            return;
+            let channel_name_editor = cx.new(|cx| Editor::single_line(window, cx));
+
+            cx.subscribe_in(
+                &channel_name_editor,
+                window,
+                |this: &mut Self, _, event, window, cx| {
+                    if let editor::EditorEvent::Blurred = event {
+                        if let Some(state) = &this.channel_editing_state {
+                            if state.pending_name().is_some() {
+                                return;
+                            }
                         }
+                        this.take_editing_state(window, cx);
+                        this.update_entries(false, cx);
+                        cx.notify();
                     }
-                    this.take_editing_state(cx);
-                    this.update_entries(false, cx);
-                    cx.notify();
-                }
-            })
+                },
+            )
             .detach();
 
-            let view = cx.view().downgrade();
-            let list_state =
-                ListState::new(0, gpui::ListAlignment::Top, px(1000.), move |ix, cx| {
-                    if let Some(view) = view.upgrade() {
-                        view.update(cx, |view, cx| view.render_list_entry(ix, cx))
+            let model = cx.model().downgrade();
+            let list_state = ListState::new(
+                0,
+                gpui::ListAlignment::Top,
+                px(1000.),
+                move |ix, window, cx| {
+                    if let Some(model) = model.upgrade() {
+                        model.update(cx, |this, cx| this.render_list_entry(ix, window, cx))
                     } else {
                         div().into_any()
                     }
-                });
+                },
+            );
 
             let mut this = Self {
                 width: None,
@@ -278,12 +290,13 @@ impl CollabPanel {
                 }));
             this.subscriptions
                 .push(cx.observe(&active_call, |this, _, cx| this.update_entries(true, cx)));
-            this.subscriptions.push(cx.subscribe(
+            this.subscriptions.push(cx.subscribe_in(
                 &this.channel_store,
-                |this, _channel_store, e, cx| match e {
+                window,
+                |this, _channel_store, e, window, cx| match e {
                     ChannelEvent::ChannelCreated(channel_id)
                     | ChannelEvent::ChannelRenamed(channel_id) => {
-                        if this.take_editing_state(cx) {
+                        if this.take_editing_state(window, cx) {
                             this.update_entries(false, cx);
                             this.selection = this.entries.iter().position(|entry| {
                                 if let ListEntry::Channel { channel, .. } = entry {
@@ -302,9 +315,9 @@ impl CollabPanel {
     }
 
     pub async fn load(
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         mut cx: AsyncWindowContext,
-    ) -> anyhow::Result<View<Self>> {
+    ) -> anyhow::Result<Entity<Self>> {
         let serialized_panel = cx
             .background_executor()
             .spawn(async move { KEY_VALUE_STORE.read_kvp(COLLABORATION_PANEL_KEY) })
@@ -317,8 +330,8 @@ impl CollabPanel {
             .log_err()
             .flatten();
 
-        workspace.update(&mut cx, |workspace, cx| {
-            let panel = CollabPanel::new(workspace, cx);
+        workspace.update_in(&mut cx, |workspace, window, cx| {
+            let panel = CollabPanel::new(workspace, window, cx);
             if let Some(serialized_panel) = serialized_panel {
                 panel.update(cx, |panel, cx| {
                     panel.width = serialized_panel.width.map(|w| w.round());
@@ -335,7 +348,7 @@ impl CollabPanel {
         })
     }
 
-    fn serialize(&mut self, cx: &mut ViewContext<Self>) {
+    fn serialize(&mut self, cx: &mut Context<Self>) {
         let width = self.width;
         let collapsed_channels = self.collapsed_channels.clone();
         self.pending_serialization = cx.background_executor().spawn(
@@ -361,7 +374,7 @@ impl CollabPanel {
         self.list_state.scroll_to_reveal_item(ix)
     }
 
-    fn update_entries(&mut self, select_same_item: bool, cx: &mut ViewContext<Self>) {
+    fn update_entries(&mut self, select_same_item: bool, cx: &mut Context<Self>) {
         let channel_store = self.channel_store.read(cx);
         let user_store = self.user_store.read(cx);
         let query = self.filter_editor.read(cx).text(cx);
@@ -799,7 +812,7 @@ impl CollabPanel {
         is_pending: bool,
         role: proto::ChannelRole,
         is_selected: bool,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> ListItem {
         let user_id = user.id;
         let is_current_user =
@@ -819,8 +832,8 @@ impl CollabPanel {
             } else if is_current_user {
                 IconButton::new("leave-call", IconName::Exit)
                     .style(ButtonStyle::Subtle)
-                    .on_click(move |_, cx| Self::leave_call(cx))
-                    .tooltip(|cx| Tooltip::text("Leave Call", cx))
+                    .on_click(move |_, window, cx| Self::leave_call(window, cx))
+                    .tooltip(Tooltip::text("Leave Call"))
                     .into_any_element()
             } else if role == proto::ChannelRole::Guest {
                 Label::new("Guest").color(Color::Muted).into_any_element()
@@ -835,20 +848,29 @@ impl CollabPanel {
                 if role == proto::ChannelRole::Guest {
                     return el;
                 }
-                el.tooltip(move |cx| Tooltip::text(tooltip.clone(), cx))
-                    .on_click(cx.listener(move |this, _, cx| {
+                el.tooltip(Tooltip::text(tooltip.clone()))
+                    .on_click(cx.listener(move |this, _, window, cx| {
                         this.workspace
-                            .update(cx, |workspace, cx| workspace.follow(peer_id, cx))
+                            .update(cx, |workspace, cx| workspace.follow(peer_id, window, cx))
                             .ok();
                     }))
             })
             .when(is_call_admin, |el| {
-                el.on_secondary_mouse_down(cx.listener(move |this, event: &MouseDownEvent, cx| {
-                    this.deploy_participant_context_menu(event.position, user_id, role, cx)
-                }))
+                el.on_secondary_mouse_down(cx.listener(
+                    move |this, event: &MouseDownEvent, window, cx| {
+                        this.deploy_participant_context_menu(
+                            event.position,
+                            user_id,
+                            role,
+                            window,
+                            cx,
+                        )
+                    },
+                ))
             })
     }
 
+    #[allow(clippy::too_many_arguments)]
     fn render_participant_project(
         &self,
         project_id: u64,
@@ -856,7 +878,8 @@ impl CollabPanel {
         host_user_id: u64,
         is_last: bool,
         is_selected: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> impl IntoElement {
         let project_name: SharedString = if worktree_root_names.is_empty() {
             "untitled".to_string()
@@ -867,23 +890,28 @@ impl CollabPanel {
 
         ListItem::new(project_id as usize)
             .toggle_state(is_selected)
-            .on_click(cx.listener(move |this, _, cx| {
+            .on_click(cx.listener(move |this, _, window, cx| {
                 this.workspace
                     .update(cx, |workspace, cx| {
                         let app_state = workspace.app_state().clone();
                         workspace::join_in_room_project(project_id, host_user_id, app_state, cx)
-                            .detach_and_prompt_err("Failed to join project", cx, |_, _| None);
+                            .detach_and_prompt_err(
+                                "Failed to join project",
+                                window,
+                                cx,
+                                |_, _, _| None,
+                            );
                     })
                     .ok();
             }))
             .start_slot(
                 h_flex()
                     .gap_1()
-                    .child(render_tree_branch(is_last, false, cx))
+                    .child(render_tree_branch(is_last, false, window, cx))
                     .child(IconButton::new(0, IconName::Folder)),
             )
             .child(Label::new(project_name.clone()))
-            .tooltip(move |cx| Tooltip::text(format!("Open {}", project_name), cx))
+            .tooltip(Tooltip::text(format!("Open {}", project_name)))
     }
 
     fn render_participant_screen(
@@ -891,7 +919,8 @@ impl CollabPanel {
         peer_id: Option<PeerId>,
         is_last: bool,
         is_selected: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> impl IntoElement {
         let id = peer_id.map_or(usize::MAX, |id| id.as_u64() as usize);
 
@@ -900,26 +929,26 @@ impl CollabPanel {
             .start_slot(
                 h_flex()
                     .gap_1()
-                    .child(render_tree_branch(is_last, false, cx))
+                    .child(render_tree_branch(is_last, false, window, cx))
                     .child(IconButton::new(0, IconName::Screen)),
             )
             .child(Label::new("Screen"))
             .when_some(peer_id, |this, _| {
-                this.on_click(cx.listener(move |this, _, cx| {
+                this.on_click(cx.listener(move |this, _, window, cx| {
                     this.workspace
                         .update(cx, |workspace, cx| {
-                            workspace.open_shared_screen(peer_id.unwrap(), cx)
+                            workspace.open_shared_screen(peer_id.unwrap(), window, cx)
                         })
                         .ok();
                 }))
-                .tooltip(move |cx| Tooltip::text("Open shared screen", cx))
+                .tooltip(Tooltip::text("Open shared screen"))
             })
     }
 
-    fn take_editing_state(&mut self, cx: &mut ViewContext<Self>) -> bool {
+    fn take_editing_state(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
         if self.channel_editing_state.take().is_some() {
             self.channel_name_editor.update(cx, |editor, cx| {
-                editor.set_text("", cx);
+                editor.set_text("", window, cx);
             });
             true
         } else {
@@ -931,20 +960,21 @@ impl CollabPanel {
         &self,
         channel_id: ChannelId,
         is_selected: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> impl IntoElement {
         let channel_store = self.channel_store.read(cx);
         let has_channel_buffer_changed = channel_store.has_channel_buffer_changed(channel_id);
         ListItem::new("channel-notes")
             .toggle_state(is_selected)
-            .on_click(cx.listener(move |this, _, cx| {
-                this.open_channel_notes(channel_id, cx);
+            .on_click(cx.listener(move |this, _, window, cx| {
+                this.open_channel_notes(channel_id, window, cx);
             }))
             .start_slot(
                 h_flex()
                     .relative()
                     .gap_1()
-                    .child(render_tree_branch(false, true, cx))
+                    .child(render_tree_branch(false, true, window, cx))
                     .child(IconButton::new(0, IconName::File))
                     .children(has_channel_buffer_changed.then(|| {
                         div()
@@ -956,27 +986,28 @@ impl CollabPanel {
                     })),
             )
             .child(Label::new("notes"))
-            .tooltip(move |cx| Tooltip::text("Open Channel Notes", cx))
+            .tooltip(Tooltip::text("Open Channel Notes"))
     }
 
     fn render_channel_chat(
         &self,
         channel_id: ChannelId,
         is_selected: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> impl IntoElement {
         let channel_store = self.channel_store.read(cx);
         let has_messages_notification = channel_store.has_new_messages(channel_id);
         ListItem::new("channel-chat")
             .toggle_state(is_selected)
-            .on_click(cx.listener(move |this, _, cx| {
-                this.join_channel_chat(channel_id, cx);
+            .on_click(cx.listener(move |this, _, window, cx| {
+                this.join_channel_chat(channel_id, window, cx);
             }))
             .start_slot(
                 h_flex()
                     .relative()
                     .gap_1()
-                    .child(render_tree_branch(false, false, cx))
+                    .child(render_tree_branch(false, false, window, cx))
                     .child(IconButton::new(0, IconName::MessageBubbles))
                     .children(has_messages_notification.then(|| {
                         div()
@@ -988,7 +1019,7 @@ impl CollabPanel {
                     })),
             )
             .child(Label::new("chat"))
-            .tooltip(move |cx| Tooltip::text("Open Chat", cx))
+            .tooltip(Tooltip::text("Open Chat"))
     }
 
     fn has_subchannels(&self, ix: usize) -> bool {
@@ -1006,9 +1037,10 @@ impl CollabPanel {
         position: Point<Pixels>,
         user_id: u64,
         role: proto::ChannelRole,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
-        let this = cx.view().clone();
+        let this = cx.model().clone();
         if !(role == proto::ChannelRole::Guest
             || role == proto::ChannelRole::Talker
             || role == proto::ChannelRole::Member)
@@ -1016,12 +1048,12 @@ impl CollabPanel {
             return;
         }
 
-        let context_menu = ContextMenu::build(cx, |mut context_menu, cx| {
+        let context_menu = ContextMenu::build(window, cx, |mut context_menu, window, _| {
             if role == proto::ChannelRole::Guest {
                 context_menu = context_menu.entry(
                     "Grant Mic Access",
                     None,
-                    cx.handler_for(&this, move |_, cx| {
+                    window.handler_for(&this, move |_, window, cx| {
                         ActiveCall::global(cx)
                             .update(cx, |call, cx| {
                                 let Some(room) = call.room() else {
@@ -1035,7 +1067,12 @@ impl CollabPanel {
                                     )
                                 })
                             })
-                            .detach_and_prompt_err("Failed to grant mic access", cx, |_, _| None)
+                            .detach_and_prompt_err(
+                                "Failed to grant mic access",
+                                window,
+                                cx,
+                                |_, _, _| None,
+                            )
                     }),
                 );
             }
@@ -1043,7 +1080,7 @@ impl CollabPanel {
                 context_menu = context_menu.entry(
                     "Grant Write Access",
                     None,
-                    cx.handler_for(&this, move |_, cx| {
+                    window.handler_for(&this, move |_, window, cx| {
                         ActiveCall::global(cx)
                             .update(cx, |call, cx| {
                                 let Some(room) = call.room() else {
@@ -1057,7 +1094,7 @@ impl CollabPanel {
                                     )
                                 })
                             })
-                            .detach_and_prompt_err("Failed to grant write access", cx, |e, _| {
+                            .detach_and_prompt_err("Failed to grant write access", window, cx, |e, _, _| {
                                 match e.error_code() {
                                     ErrorCode::NeedsCla => Some("This user has not yet signed the CLA at https://zed.dev/cla.".into()),
                                     _ => None,
@@ -1075,7 +1112,7 @@ impl CollabPanel {
                 context_menu = context_menu.entry(
                     label,
                     None,
-                    cx.handler_for(&this, move |_, cx| {
+                    window.handler_for(&this, move |_, window, cx| {
                         ActiveCall::global(cx)
                             .update(cx, |call, cx| {
                                 let Some(room) = call.room() else {
@@ -1089,7 +1126,12 @@ impl CollabPanel {
                                     )
                                 })
                             })
-                            .detach_and_prompt_err("Failed to revoke access", cx, |_, _| None)
+                            .detach_and_prompt_err(
+                                "Failed to revoke access",
+                                window,
+                                cx,
+                                |_, _, _| None,
+                            )
                     }),
                 );
             }
@@ -1097,17 +1139,20 @@ impl CollabPanel {
             context_menu
         });
 
-        cx.focus_view(&context_menu);
-        let subscription =
-            cx.subscribe(&context_menu, |this, _, _: &DismissEvent, cx| {
+        window.focus(&context_menu.focus_handle(cx));
+        let subscription = cx.subscribe_in(
+            &context_menu,
+            window,
+            |this, _, _: &DismissEvent, window, cx| {
                 if this.context_menu.as_ref().is_some_and(|context_menu| {
-                    context_menu.0.focus_handle(cx).contains_focused(cx)
+                    context_menu.0.focus_handle(cx).contains_focused(window, cx)
                 }) {
-                    cx.focus_self();
+                    cx.focus_self(window);
                 }
                 this.context_menu.take();
                 cx.notify();
-            });
+            },
+        );
         self.context_menu = Some((context_menu, position, subscription));
     }
 
@@ -1116,7 +1161,8 @@ impl CollabPanel {
         position: Point<Pixels>,
         channel_id: ChannelId,
         ix: usize,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let clipboard_channel_name = self.channel_clipboard.as_ref().and_then(|clipboard| {
             self.channel_store
@@ -1124,9 +1170,9 @@ impl CollabPanel {
                 .channel_for_id(clipboard.channel_id)
                 .map(|channel| channel.name.clone())
         });
-        let this = cx.view().clone();
+        let this = cx.model().clone();
 
-        let context_menu = ContextMenu::build(cx, |mut context_menu, cx| {
+        let context_menu = ContextMenu::build(window, cx, |mut context_menu, window, cx| {
             if self.has_subchannels(ix) {
                 let expand_action_name = if self.is_channel_collapsed(channel_id) {
                     "Expand Subchannels"
@@ -1136,8 +1182,8 @@ impl CollabPanel {
                 context_menu = context_menu.entry(
                     expand_action_name,
                     None,
-                    cx.handler_for(&this, move |this, cx| {
-                        this.toggle_channel_collapsed(channel_id, cx)
+                    window.handler_for(&this, move |this, window, cx| {
+                        this.toggle_channel_collapsed(channel_id, window, cx)
                     }),
                 );
             }
@@ -1146,21 +1192,21 @@ impl CollabPanel {
                 .entry(
                     "Open Notes",
                     None,
-                    cx.handler_for(&this, move |this, cx| {
-                        this.open_channel_notes(channel_id, cx)
+                    window.handler_for(&this, move |this, window, cx| {
+                        this.open_channel_notes(channel_id, window, cx)
                     }),
                 )
                 .entry(
                     "Open Chat",
                     None,
-                    cx.handler_for(&this, move |this, cx| {
-                        this.join_channel_chat(channel_id, cx)
+                    window.handler_for(&this, move |this, window, cx| {
+                        this.join_channel_chat(channel_id, window, cx)
                     }),
                 )
                 .entry(
                     "Copy Channel Link",
                     None,
-                    cx.handler_for(&this, move |this, cx| {
+                    window.handler_for(&this, move |this, _, cx| {
                         this.copy_channel_link(channel_id, cx)
                     }),
                 );
@@ -1173,20 +1219,24 @@ impl CollabPanel {
                     .entry(
                         "New Subchannel",
                         None,
-                        cx.handler_for(&this, move |this, cx| this.new_subchannel(channel_id, cx)),
+                        window.handler_for(&this, move |this, window, cx| {
+                            this.new_subchannel(channel_id, window, cx)
+                        }),
                     )
                     .entry(
                         "Rename",
                         Some(Box::new(SecondaryConfirm)),
-                        cx.handler_for(&this, move |this, cx| this.rename_channel(channel_id, cx)),
+                        window.handler_for(&this, move |this, window, cx| {
+                            this.rename_channel(channel_id, window, cx)
+                        }),
                     );
 
                 if let Some(channel_name) = clipboard_channel_name {
                     context_menu = context_menu.separator().entry(
                         format!("Move '#{}' here", channel_name),
                         None,
-                        cx.handler_for(&this, move |this, cx| {
-                            this.move_channel_on_clipboard(channel_id, cx)
+                        window.handler_for(&this, move |this, window, cx| {
+                            this.move_channel_on_clipboard(channel_id, window, cx)
                         }),
                     );
                 }
@@ -1195,24 +1245,27 @@ impl CollabPanel {
                     context_menu = context_menu.separator().entry(
                         "Manage Members",
                         None,
-                        cx.handler_for(&this, move |this, cx| this.manage_members(channel_id, cx)),
+                        window.handler_for(&this, move |this, window, cx| {
+                            this.manage_members(channel_id, window, cx)
+                        }),
                     )
                 } else {
                     context_menu = context_menu.entry(
                         "Move this channel",
                         None,
-                        cx.handler_for(&this, move |this, cx| {
-                            this.start_move_channel(channel_id, cx)
+                        window.handler_for(&this, move |this, window, cx| {
+                            this.start_move_channel(channel_id, window, cx)
                         }),
                     );
                     if self.channel_store.read(cx).is_public_channel(channel_id) {
                         context_menu = context_menu.separator().entry(
                             "Make Channel Private",
                             None,
-                            cx.handler_for(&this, move |this, cx| {
+                            window.handler_for(&this, move |this, window, cx| {
                                 this.set_channel_visibility(
                                     channel_id,
                                     ChannelVisibility::Members,
+                                    window,
                                     cx,
                                 )
                             }),
@@ -1221,10 +1274,11 @@ impl CollabPanel {
                         context_menu = context_menu.separator().entry(
                             "Make Channel Public",
                             None,
-                            cx.handler_for(&this, move |this, cx| {
+                            window.handler_for(&this, move |this, window, cx| {
                                 this.set_channel_visibility(
                                     channel_id,
                                     ChannelVisibility::Public,
+                                    window,
                                     cx,
                                 )
                             }),
@@ -1235,7 +1289,9 @@ impl CollabPanel {
                 context_menu = context_menu.entry(
                     "Delete",
                     None,
-                    cx.handler_for(&this, move |this, cx| this.remove_channel(channel_id, cx)),
+                    window.handler_for(&this, move |this, window, cx| {
+                        this.remove_channel(channel_id, window, cx)
+                    }),
                 );
             }
 
@@ -1246,24 +1302,29 @@ impl CollabPanel {
                 context_menu = context_menu.entry(
                     "Leave Channel",
                     None,
-                    cx.handler_for(&this, move |this, cx| this.leave_channel(channel_id, cx)),
+                    window.handler_for(&this, move |this, window, cx| {
+                        this.leave_channel(channel_id, window, cx)
+                    }),
                 );
             }
 
             context_menu
         });
 
-        cx.focus_view(&context_menu);
-        let subscription =
-            cx.subscribe(&context_menu, |this, _, _: &DismissEvent, cx| {
+        window.focus(&context_menu.focus_handle(cx));
+        let subscription = cx.subscribe_in(
+            &context_menu,
+            window,
+            |this, _, _: &DismissEvent, window, cx| {
                 if this.context_menu.as_ref().is_some_and(|context_menu| {
-                    context_menu.0.focus_handle(cx).contains_focused(cx)
+                    context_menu.0.focus_handle(cx).contains_focused(window, cx)
                 }) {
-                    cx.focus_self();
+                    cx.focus_self(window);
                 }
                 this.context_menu.take();
                 cx.notify();
-            });
+            },
+        );
         self.context_menu = Some((context_menu, position, subscription));
 
         cx.notify();
@@ -1273,12 +1334,13 @@ impl CollabPanel {
         &mut self,
         position: Point<Pixels>,
         contact: Arc<Contact>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
-        let this = cx.view().clone();
+        let this = cx.model().clone();
         let in_room = ActiveCall::global(cx).read(cx).room().is_some();
 
-        let context_menu = ContextMenu::build(cx, |mut context_menu, _| {
+        let context_menu = ContextMenu::build(window, cx, |mut context_menu, _, _| {
             let user_id = contact.user.id;
 
             if contact.online && !contact.busy {
@@ -1289,9 +1351,9 @@ impl CollabPanel {
                 };
                 context_menu = context_menu.entry(label, None, {
                     let this = this.clone();
-                    move |cx| {
+                    move |window, cx| {
                         this.update(cx, |this, cx| {
-                            this.call(user_id, cx);
+                            this.call(user_id, window, cx);
                         });
                     }
                 });
@@ -1299,34 +1361,42 @@ impl CollabPanel {
 
             context_menu.entry("Remove Contact", None, {
                 let this = this.clone();
-                move |cx| {
+                move |window, cx| {
                     this.update(cx, |this, cx| {
-                        this.remove_contact(contact.user.id, &contact.user.github_login, cx);
+                        this.remove_contact(
+                            contact.user.id,
+                            &contact.user.github_login,
+                            window,
+                            cx,
+                        );
                     });
                 }
             })
         });
 
-        cx.focus_view(&context_menu);
-        let subscription =
-            cx.subscribe(&context_menu, |this, _, _: &DismissEvent, cx| {
+        window.focus(&context_menu.focus_handle(cx));
+        let subscription = cx.subscribe_in(
+            &context_menu,
+            window,
+            |this, _, _: &DismissEvent, window, cx| {
                 if this.context_menu.as_ref().is_some_and(|context_menu| {
-                    context_menu.0.focus_handle(cx).contains_focused(cx)
+                    context_menu.0.focus_handle(cx).contains_focused(window, cx)
                 }) {
-                    cx.focus_self();
+                    cx.focus_self(window);
                 }
                 this.context_menu.take();
                 cx.notify();
-            });
+            },
+        );
         self.context_menu = Some((context_menu, position, subscription));
 
         cx.notify();
     }
 
-    fn reset_filter_editor_text(&mut self, cx: &mut ViewContext<Self>) -> bool {
+    fn reset_filter_editor_text(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
         self.filter_editor.update(cx, |editor, cx| {
             if editor.buffer().read(cx).len(cx) > 0 {
-                editor.set_text("", cx);
+                editor.set_text("", window, cx);
                 true
             } else {
                 false
@@ -1334,11 +1404,11 @@ impl CollabPanel {
         })
     }
 
-    fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
-        if self.take_editing_state(cx) {
-            cx.focus_view(&self.filter_editor);
-        } else if !self.reset_filter_editor_text(cx) {
-            self.focus_handle.focus(cx);
+    fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
+        if self.take_editing_state(window, cx) {
+            window.focus(&self.filter_editor.focus_handle(cx));
+        } else if !self.reset_filter_editor_text(window, cx) {
+            self.focus_handle.focus(window);
         }
 
         if self.context_menu.is_some() {
@@ -1349,7 +1419,7 @@ impl CollabPanel {
         self.update_entries(false, cx);
     }
 
-    fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
+    fn select_next(&mut self, _: &SelectNext, _: &mut Window, cx: &mut Context<Self>) {
         let ix = self.selection.map_or(0, |ix| ix + 1);
         if ix < self.entries.len() {
             self.selection = Some(ix);
@@ -1361,7 +1431,7 @@ impl CollabPanel {
         cx.notify();
     }
 
-    fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext<Self>) {
+    fn select_prev(&mut self, _: &SelectPrev, _: &mut Window, cx: &mut Context<Self>) {
         let ix = self.selection.take().unwrap_or(0);
         if ix > 0 {
             self.selection = Some(ix - 1);
@@ -1373,8 +1443,8 @@ impl CollabPanel {
         cx.notify();
     }
 
-    fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
-        if self.confirm_channel_edit(cx) {
+    fn confirm(&mut self, _: &Confirm, window: &mut Window, cx: &mut Context<Self>) {
+        if self.confirm_channel_edit(window, cx) {
             return;
         }
 
@@ -1382,9 +1452,9 @@ impl CollabPanel {
             if let Some(entry) = self.entries.get(selection) {
                 match entry {
                     ListEntry::Header(section) => match section {
-                        Section::ActiveCall => Self::leave_call(cx),
-                        Section::Channels => self.new_root_channel(cx),
-                        Section::Contacts => self.toggle_contact_finder(cx),
+                        Section::ActiveCall => Self::leave_call(window, cx),
+                        Section::Channels => self.new_root_channel(window, cx),
+                        Section::Contacts => self.toggle_contact_finder(window, cx),
                         Section::ContactRequests
                         | Section::Online
                         | Section::Offline
@@ -1394,7 +1464,7 @@ impl CollabPanel {
                     },
                     ListEntry::Contact { contact, calling } => {
                         if contact.online && !contact.busy && !calling {
-                            self.call(contact.user.id, cx);
+                            self.call(contact.user.id, window, cx);
                         }
                     }
                     ListEntry::ParticipantProject {
@@ -1412,8 +1482,9 @@ impl CollabPanel {
                             )
                             .detach_and_prompt_err(
                                 "Failed to join project",
+                                window,
                                 cx,
-                                |_, _| None,
+                                |_, _, _| None,
                             );
                         }
                     }
@@ -1423,7 +1494,7 @@ impl CollabPanel {
                         };
                         if let Some(workspace) = self.workspace.upgrade() {
                             workspace.update(cx, |workspace, cx| {
-                                workspace.open_shared_screen(*peer_id, cx)
+                                workspace.open_shared_screen(*peer_id, window, cx)
                             });
                         }
                     }
@@ -1439,32 +1510,32 @@ impl CollabPanel {
                         })
                         .unwrap_or(false);
                         if is_active {
-                            self.open_channel_notes(channel.id, cx)
+                            self.open_channel_notes(channel.id, window, cx)
                         } else {
-                            self.join_channel(channel.id, cx)
+                            self.join_channel(channel.id, window, cx)
                         }
                     }
-                    ListEntry::ContactPlaceholder => self.toggle_contact_finder(cx),
+                    ListEntry::ContactPlaceholder => self.toggle_contact_finder(window, cx),
                     ListEntry::CallParticipant { user, peer_id, .. } => {
                         if Some(user) == self.user_store.read(cx).current_user().as_ref() {
-                            Self::leave_call(cx);
+                            Self::leave_call(window, cx);
                         } else if let Some(peer_id) = peer_id {
                             self.workspace
-                                .update(cx, |workspace, cx| workspace.follow(*peer_id, cx))
+                                .update(cx, |workspace, cx| workspace.follow(*peer_id, window, cx))
                                 .ok();
                         }
                     }
                     ListEntry::IncomingRequest(user) => {
-                        self.respond_to_contact_request(user.id, true, cx)
+                        self.respond_to_contact_request(user.id, true, window, cx)
                     }
                     ListEntry::ChannelInvite(channel) => {
                         self.respond_to_channel_invite(channel.id, true, cx)
                     }
                     ListEntry::ChannelNotes { channel_id } => {
-                        self.open_channel_notes(*channel_id, cx)
+                        self.open_channel_notes(*channel_id, window, cx)
                     }
                     ListEntry::ChannelChat { channel_id } => {
-                        self.join_channel_chat(*channel_id, cx)
+                        self.join_channel_chat(*channel_id, window, cx)
                     }
                     ListEntry::OutgoingRequest(_) => {}
                     ListEntry::ChannelEditor { .. } => {}
@@ -1473,15 +1544,15 @@ impl CollabPanel {
         }
     }
 
-    fn insert_space(&mut self, _: &InsertSpace, cx: &mut ViewContext<Self>) {
+    fn insert_space(&mut self, _: &InsertSpace, window: &mut Window, cx: &mut Context<Self>) {
         if self.channel_editing_state.is_some() {
             self.channel_name_editor.update(cx, |editor, cx| {
-                editor.insert(" ", cx);
+                editor.insert(" ", window, cx);
             });
         }
     }
 
-    fn confirm_channel_edit(&mut self, cx: &mut ViewContext<CollabPanel>) -> bool {
+    fn confirm_channel_edit(&mut self, window: &mut Window, cx: &mut Context<CollabPanel>) -> bool {
         if let Some(editing_state) = &mut self.channel_editing_state {
             match editing_state {
                 ChannelEditingState::Create {
@@ -1500,23 +1571,30 @@ impl CollabPanel {
                         channel_store.create_channel(&channel_name, *location, cx)
                     });
                     if location.is_none() {
-                        cx.spawn(|this, mut cx| async move {
+                        cx.spawn_in(window, |this, mut cx| async move {
                             let channel_id = create.await?;
-                            this.update(&mut cx, |this, cx| {
+                            this.update_in(&mut cx, |this, window, cx| {
                                 this.show_channel_modal(
                                     channel_id,
                                     channel_modal::Mode::InviteMembers,
+                                    window,
                                     cx,
                                 )
                             })
                         })
                         .detach_and_prompt_err(
                             "Failed to create channel",
+                            window,
                             cx,
-                            |_, _| None,
+                            |_, _, _| None,
                         );
                     } else {
-                        create.detach_and_prompt_err("Failed to create channel", cx, |_, _| None);
+                        create.detach_and_prompt_err(
+                            "Failed to create channel",
+                            window,
+                            cx,
+                            |_, _, _| None,
+                        );
                     }
                     cx.notify();
                 }
@@ -1538,14 +1616,14 @@ impl CollabPanel {
                     cx.notify();
                 }
             }
-            cx.focus_self();
+            cx.focus_self(window);
             true
         } else {
             false
         }
     }
 
-    fn toggle_section_expanded(&mut self, section: Section, cx: &mut ViewContext<Self>) {
+    fn toggle_section_expanded(&mut self, section: Section, cx: &mut Context<Self>) {
         if let Some(ix) = self.collapsed_sections.iter().position(|s| *s == section) {
             self.collapsed_sections.remove(ix);
         } else {
@@ -1557,7 +1635,8 @@ impl CollabPanel {
     fn collapse_selected_channel(
         &mut self,
         _: &CollapseSelectedChannel,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let Some(channel_id) = self.selected_channel().map(|channel| channel.id) else {
             return;

crates/collab_ui/src/collab_panel/channel_modal.rs 🔗

@@ -5,9 +5,8 @@ use client::{
 };
 use fuzzy::{match_strings, StringMatchCandidate};
 use gpui::{
-    actions, anchored, deferred, div, AppContext, ClipboardItem, DismissEvent, EventEmitter,
-    FocusableView, Model, ParentElement, Render, Styled, Subscription, Task, View, ViewContext,
-    VisualContext, WeakView,
+    actions, anchored, deferred, div, App, ClipboardItem, Context, DismissEvent, Entity,
+    EventEmitter, Focusable, ParentElement, Render, Styled, Subscription, Task, WeakEntity, Window,
 };
 use picker::{Picker, PickerDelegate};
 use std::sync::Arc;
@@ -26,22 +25,23 @@ actions!(
 );
 
 pub struct ChannelModal {
-    picker: View<Picker<ChannelModalDelegate>>,
-    channel_store: Model<ChannelStore>,
+    picker: Entity<Picker<ChannelModalDelegate>>,
+    channel_store: Entity<ChannelStore>,
     channel_id: ChannelId,
 }
 
 impl ChannelModal {
     pub fn new(
-        user_store: Model<UserStore>,
-        channel_store: Model<ChannelStore>,
+        user_store: Entity<UserStore>,
+        channel_store: Entity<ChannelStore>,
         channel_id: ChannelId,
         mode: Mode,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         cx.observe(&channel_store, |_, _, cx| cx.notify()).detach();
-        let channel_modal = cx.view().downgrade();
-        let picker = cx.new_view(|cx| {
+        let channel_modal = cx.model().downgrade();
+        let picker = cx.new(|cx| {
             Picker::uniform_list(
                 ChannelModalDelegate {
                     channel_modal,
@@ -57,6 +57,7 @@ impl ChannelModal {
                     has_all_members: false,
                     mode,
                 },
+                window,
                 cx,
             )
             .modal(false)
@@ -69,27 +70,32 @@ impl ChannelModal {
         }
     }
 
-    fn toggle_mode(&mut self, _: &ToggleMode, cx: &mut ViewContext<Self>) {
+    fn toggle_mode(&mut self, _: &ToggleMode, window: &mut Window, cx: &mut Context<Self>) {
         let mode = match self.picker.read(cx).delegate.mode {
             Mode::ManageMembers => Mode::InviteMembers,
             Mode::InviteMembers => Mode::ManageMembers,
         };
-        self.set_mode(mode, cx);
+        self.set_mode(mode, window, cx);
     }
 
-    fn set_mode(&mut self, mode: Mode, cx: &mut ViewContext<Self>) {
+    fn set_mode(&mut self, mode: Mode, window: &mut Window, cx: &mut Context<Self>) {
         self.picker.update(cx, |picker, cx| {
             let delegate = &mut picker.delegate;
             delegate.mode = mode;
             delegate.selected_index = 0;
-            picker.set_query("", cx);
-            picker.update_matches(picker.query(cx), cx);
+            picker.set_query("", window, cx);
+            picker.update_matches(picker.query(cx), window, cx);
             cx.notify()
         });
         cx.notify()
     }
 
-    fn set_channel_visibility(&mut self, selection: &ToggleState, cx: &mut ViewContext<Self>) {
+    fn set_channel_visibility(
+        &mut self,
+        selection: &ToggleState,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.channel_store.update(cx, |channel_store, cx| {
             channel_store
                 .set_channel_visibility(
@@ -105,7 +111,7 @@ impl ChannelModal {
         });
     }
 
-    fn dismiss(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
+    fn dismiss(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {
         cx.emit(DismissEvent);
     }
 }
@@ -113,14 +119,14 @@ impl ChannelModal {
 impl EventEmitter<DismissEvent> for ChannelModal {}
 impl ModalView for ChannelModal {}
 
-impl FocusableView for ChannelModal {
-    fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
+impl Focusable for ChannelModal {
+    fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
 
 impl Render for ChannelModal {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let channel_store = self.channel_store.read(cx);
         let Some(channel) = channel_store.channel_for_id(self.channel_id) else {
             return div();
@@ -169,7 +175,7 @@ impl Render for ChannelModal {
                                 Some(
                                     Button::new("copy-link", "Copy Link")
                                         .label_size(LabelSize::Small)
-                                        .on_click(cx.listener(move |this, _, cx| {
+                                        .on_click(cx.listener(move |this, _, _, cx| {
                                             if let Some(channel) = this
                                                 .channel_store
                                                 .read(cx)
@@ -197,8 +203,8 @@ impl Render for ChannelModal {
                                         this.border_color(cx.theme().colors().border)
                                     })
                                     .child(Label::new("Manage Members"))
-                                    .on_click(cx.listener(|this, _, cx| {
-                                        this.set_mode(Mode::ManageMembers, cx);
+                                    .on_click(cx.listener(|this, _, window, cx| {
+                                        this.set_mode(Mode::ManageMembers, window, cx);
                                     })),
                             )
                             .child(
@@ -212,8 +218,8 @@ impl Render for ChannelModal {
                                         this.border_color(cx.theme().colors().border)
                                     })
                                     .child(Label::new("Invite Members"))
-                                    .on_click(cx.listener(|this, _, cx| {
-                                        this.set_mode(Mode::InviteMembers, cx);
+                                    .on_click(cx.listener(|this, _, window, cx| {
+                                        this.set_mode(Mode::InviteMembers, window, cx);
                                     })),
                             ),
                     ),
@@ -229,24 +235,24 @@ pub enum Mode {
 }
 
 pub struct ChannelModalDelegate {
-    channel_modal: WeakView<ChannelModal>,
+    channel_modal: WeakEntity<ChannelModal>,
     matching_users: Vec<Arc<User>>,
     matching_member_indices: Vec<usize>,
-    user_store: Model<UserStore>,
-    channel_store: Model<ChannelStore>,
+    user_store: Entity<UserStore>,
+    channel_store: Entity<ChannelStore>,
     channel_id: ChannelId,
     selected_index: usize,
     mode: Mode,
     match_candidates: Vec<StringMatchCandidate>,
     members: Vec<ChannelMembership>,
     has_all_members: bool,
-    context_menu: Option<(View<ContextMenu>, Subscription)>,
+    context_menu: Option<(Entity<ContextMenu>, Subscription)>,
 }
 
 impl PickerDelegate for ChannelModalDelegate {
     type ListItem = ListItem;
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Search collaborator by username...".into()
     }
 
@@ -261,11 +267,21 @@ impl PickerDelegate for ChannelModalDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        _window: &mut Window,
+        _: &mut Context<Picker<Self>>,
+    ) {
         self.selected_index = ix;
     }
 
-    fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
+    fn update_matches(
+        &mut self,
+        query: String,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Task<()> {
         match self.mode {
             Mode::ManageMembers => {
                 if self.has_all_members {
@@ -284,7 +300,7 @@ impl PickerDelegate for ChannelModalDelegate {
                         cx.background_executor().clone(),
                     ));
 
-                    cx.spawn(|picker, mut cx| async move {
+                    cx.spawn_in(window, |picker, mut cx| async move {
                         picker
                             .update(&mut cx, |picker, cx| {
                                 let delegate = &mut picker.delegate;
@@ -300,7 +316,7 @@ impl PickerDelegate for ChannelModalDelegate {
                     let search_members = self.channel_store.update(cx, |store, cx| {
                         store.fuzzy_search_members(self.channel_id, query.clone(), 100, cx)
                     });
-                    cx.spawn(|picker, mut cx| async move {
+                    cx.spawn_in(window, |picker, mut cx| async move {
                         async {
                             let members = search_members.await?;
                             picker.update(&mut cx, |picker, cx| {
@@ -322,7 +338,7 @@ impl PickerDelegate for ChannelModalDelegate {
                 let search_users = self
                     .user_store
                     .update(cx, |store, cx| store.fuzzy_search_users(query, cx));
-                cx.spawn(|picker, mut cx| async move {
+                cx.spawn_in(window, |picker, mut cx| async move {
                     async {
                         let users = search_users.await?;
                         picker.update(&mut cx, |picker, cx| {
@@ -338,26 +354,26 @@ impl PickerDelegate for ChannelModalDelegate {
         }
     }
 
-    fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
         if let Some(selected_user) = self.user_at_index(self.selected_index) {
             if Some(selected_user.id) == self.user_store.read(cx).current_user().map(|user| user.id)
             {
                 return;
             }
             match self.mode {
-                Mode::ManageMembers => self.show_context_menu(self.selected_index, cx),
+                Mode::ManageMembers => self.show_context_menu(self.selected_index, window, cx),
                 Mode::InviteMembers => match self.member_status(selected_user.id, cx) {
                     Some(proto::channel_member::Kind::Invitee) => {
-                        self.remove_member(selected_user.id, cx);
+                        self.remove_member(selected_user.id, window, cx);
                     }
                     Some(proto::channel_member::Kind::Member) => {}
-                    None => self.invite_member(selected_user, cx),
+                    None => self.invite_member(selected_user, window, cx),
                 },
             }
         }
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+    fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         if self.context_menu.is_none() {
             self.channel_modal
                 .update(cx, |_, cx| {
@@ -371,7 +387,8 @@ impl PickerDelegate for ChannelModalDelegate {
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let user = self.user_at_index(ix)?;
         let membership = self.member_at_index(ix);
@@ -434,11 +451,7 @@ impl PickerDelegate for ChannelModalDelegate {
 }
 
 impl ChannelModalDelegate {
-    fn member_status(
-        &self,
-        user_id: UserId,
-        cx: &AppContext,
-    ) -> Option<proto::channel_member::Kind> {
+    fn member_status(&self, user_id: UserId, cx: &App) -> Option<proto::channel_member::Kind> {
         self.members
             .iter()
             .find_map(|membership| (membership.user.id == user_id).then_some(membership.kind))
@@ -470,33 +483,39 @@ impl ChannelModalDelegate {
         &mut self,
         user_id: UserId,
         new_role: ChannelRole,
-        cx: &mut ViewContext<Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<()> {
         let update = self.channel_store.update(cx, |store, cx| {
             store.set_member_role(self.channel_id, user_id, new_role, cx)
         });
-        cx.spawn(|picker, mut cx| async move {
+        cx.spawn_in(window, |picker, mut cx| async move {
             update.await?;
-            picker.update(&mut cx, |picker, cx| {
+            picker.update_in(&mut cx, |picker, window, cx| {
                 let this = &mut picker.delegate;
                 if let Some(member) = this.members.iter_mut().find(|m| m.user.id == user_id) {
                     member.role = new_role;
                 }
-                cx.focus_self();
+                cx.focus_self(window);
                 cx.notify();
             })
         })
-        .detach_and_prompt_err("Failed to update role", cx, |_, _| None);
+        .detach_and_prompt_err("Failed to update role", window, cx, |_, _, _| None);
         Some(())
     }
 
-    fn remove_member(&mut self, user_id: UserId, cx: &mut ViewContext<Picker<Self>>) -> Option<()> {
+    fn remove_member(
+        &mut self,
+        user_id: UserId,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Option<()> {
         let update = self.channel_store.update(cx, |store, cx| {
             store.remove_member(self.channel_id, user_id, cx)
         });
-        cx.spawn(|picker, mut cx| async move {
+        cx.spawn_in(window, |picker, mut cx| async move {
             update.await?;
-            picker.update(&mut cx, |picker, cx| {
+            picker.update_in(&mut cx, |picker, window, cx| {
                 let this = &mut picker.delegate;
                 if let Some(ix) = this.members.iter_mut().position(|m| m.user.id == user_id) {
                     this.members.remove(ix);
@@ -514,20 +533,25 @@ impl ChannelModalDelegate {
                     .selected_index
                     .min(this.matching_member_indices.len().saturating_sub(1));
 
-                picker.focus(cx);
+                picker.focus(window, cx);
                 cx.notify();
             })
         })
-        .detach_and_prompt_err("Failed to remove member", cx, |_, _| None);
+        .detach_and_prompt_err("Failed to remove member", window, cx, |_, _, _| None);
         Some(())
     }
 
-    fn invite_member(&mut self, user: Arc<User>, cx: &mut ViewContext<Picker<Self>>) {
+    fn invite_member(
+        &mut self,
+        user: Arc<User>,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) {
         let invite_member = self.channel_store.update(cx, |store, cx| {
             store.invite_member(self.channel_id, user.id, ChannelRole::Member, cx)
         });
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             invite_member.await?;
 
             this.update(&mut cx, |this, cx| {
@@ -544,25 +568,30 @@ impl ChannelModalDelegate {
                 cx.notify();
             })
         })
-        .detach_and_prompt_err("Failed to invite member", cx, |_, _| None);
+        .detach_and_prompt_err("Failed to invite member", window, cx, |_, _, _| None);
     }
 
-    fn show_context_menu(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
+    fn show_context_menu(
+        &mut self,
+        ix: usize,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) {
         let Some(membership) = self.member_at_index(ix) else {
             return;
         };
         let user_id = membership.user.id;
-        let picker = cx.view().clone();
-        let context_menu = ContextMenu::build(cx, |mut menu, _cx| {
+        let picker = cx.model().clone();
+        let context_menu = ContextMenu::build(window, cx, |mut menu, _window, _cx| {
             let role = membership.role;
 
             if role == ChannelRole::Admin || role == ChannelRole::Member {
                 let picker = picker.clone();
-                menu = menu.entry("Demote to Guest", None, move |cx| {
+                menu = menu.entry("Demote to Guest", None, move |window, cx| {
                     picker.update(cx, |picker, cx| {
                         picker
                             .delegate
-                            .set_user_role(user_id, ChannelRole::Guest, cx);
+                            .set_user_role(user_id, ChannelRole::Guest, window, cx);
                     })
                 });
             }
@@ -575,22 +604,22 @@ impl ChannelModalDelegate {
                     "Demote to Member"
                 };
 
-                menu = menu.entry(label, None, move |cx| {
+                menu = menu.entry(label, None, move |window, cx| {
                     picker.update(cx, |picker, cx| {
                         picker
                             .delegate
-                            .set_user_role(user_id, ChannelRole::Member, cx);
+                            .set_user_role(user_id, ChannelRole::Member, window, cx);
                     })
                 });
             }
 
             if role == ChannelRole::Member || role == ChannelRole::Guest {
                 let picker = picker.clone();
-                menu = menu.entry("Promote to Admin", None, move |cx| {
+                menu = menu.entry("Promote to Admin", None, move |window, cx| {
                     picker.update(cx, |picker, cx| {
                         picker
                             .delegate
-                            .set_user_role(user_id, ChannelRole::Admin, cx);
+                            .set_user_role(user_id, ChannelRole::Admin, window, cx);
                     })
                 });
             };
@@ -598,20 +627,24 @@ impl ChannelModalDelegate {
             menu = menu.separator();
             menu = menu.entry("Remove from Channel", None, {
                 let picker = picker.clone();
-                move |cx| {
+                move |window, cx| {
                     picker.update(cx, |picker, cx| {
-                        picker.delegate.remove_member(user_id, cx);
+                        picker.delegate.remove_member(user_id, window, cx);
                     })
                 }
             });
             menu
         });
-        cx.focus_view(&context_menu);
-        let subscription = cx.subscribe(&context_menu, |picker, _, _: &DismissEvent, cx| {
-            picker.delegate.context_menu = None;
-            picker.focus(cx);
-            cx.notify();
-        });
+        window.focus(&context_menu.focus_handle(cx));
+        let subscription = cx.subscribe_in(
+            &context_menu,
+            window,
+            |picker, _, _: &DismissEvent, window, cx| {
+                picker.delegate.context_menu = None;
+                picker.focus(window, cx);
+                cx.notify();
+            },
+        );
         self.context_menu = Some((context_menu, subscription));
     }
 }

crates/collab_ui/src/collab_panel/contact_finder.rs 🔗

@@ -1,7 +1,7 @@
 use client::{ContactRequestStatus, User, UserStore};
 use gpui::{
-    AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Model, ParentElement as _,
-    Render, Styled, Task, View, ViewContext, VisualContext, WeakView,
+    App, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, ParentElement as _,
+    Render, Styled, Task, WeakEntity, Window,
 };
 use picker::{Picker, PickerDelegate};
 use std::sync::Arc;
@@ -10,31 +10,31 @@ use util::{ResultExt as _, TryFutureExt};
 use workspace::ModalView;
 
 pub struct ContactFinder {
-    picker: View<Picker<ContactFinderDelegate>>,
+    picker: Entity<Picker<ContactFinderDelegate>>,
 }
 
 impl ContactFinder {
-    pub fn new(user_store: Model<UserStore>, cx: &mut ViewContext<Self>) -> Self {
+    pub fn new(user_store: Entity<UserStore>, window: &mut Window, cx: &mut Context<Self>) -> Self {
         let delegate = ContactFinderDelegate {
-            parent: cx.view().downgrade(),
+            parent: cx.model().downgrade(),
             user_store,
             potential_contacts: Arc::from([]),
             selected_index: 0,
         };
-        let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx).modal(false));
+        let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx).modal(false));
 
         Self { picker }
     }
 
-    pub fn set_query(&mut self, query: String, cx: &mut ViewContext<Self>) {
+    pub fn set_query(&mut self, query: String, window: &mut Window, cx: &mut Context<Self>) {
         self.picker.update(cx, |picker, cx| {
-            picker.set_query(query, cx);
+            picker.set_query(query, window, cx);
         });
     }
 }
 
 impl Render for ContactFinder {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         v_flex()
             .elevation_3(cx)
             .child(
@@ -53,17 +53,17 @@ impl Render for ContactFinder {
 }
 
 pub struct ContactFinderDelegate {
-    parent: WeakView<ContactFinder>,
+    parent: WeakEntity<ContactFinder>,
     potential_contacts: Arc<[Arc<User>]>,
-    user_store: Model<UserStore>,
+    user_store: Entity<UserStore>,
     selected_index: usize,
 }
 
 impl EventEmitter<DismissEvent> for ContactFinder {}
 impl ModalView for ContactFinder {}
 
-impl FocusableView for ContactFinder {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for ContactFinder {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
@@ -79,20 +79,30 @@ impl PickerDelegate for ContactFinderDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        _window: &mut Window,
+        _: &mut Context<Picker<Self>>,
+    ) {
         self.selected_index = ix;
     }
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Search collaborator by username...".into()
     }
 
-    fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
+    fn update_matches(
+        &mut self,
+        query: String,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Task<()> {
         let search_users = self
             .user_store
             .update(cx, |store, cx| store.fuzzy_search_users(query, cx));
 
-        cx.spawn(|picker, mut cx| async move {
+        cx.spawn_in(window, |picker, mut cx| async move {
             async {
                 let potential_contacts = search_users.await?;
                 picker.update(&mut cx, |picker, cx| {
@@ -106,7 +116,7 @@ impl PickerDelegate for ContactFinderDelegate {
         })
     }
 
-    fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _: bool, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         if let Some(user) = self.potential_contacts.get(self.selected_index) {
             let user_store = self.user_store.read(cx);
             match user_store.contact_request_status(user) {
@@ -125,7 +135,7 @@ impl PickerDelegate for ContactFinderDelegate {
         }
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+    fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.parent
             .update(cx, |_, cx| cx.emit(DismissEvent))
             .log_err();
@@ -135,7 +145,8 @@ impl PickerDelegate for ContactFinderDelegate {
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let user = &self.potential_contacts[ix];
         let request_status = self.user_store.read(cx).contact_request_status(user);

crates/collab_ui/src/collab_ui.rs 🔗

@@ -9,7 +9,7 @@ use std::{rc::Rc, sync::Arc};
 
 pub use collab_panel::CollabPanel;
 use gpui::{
-    point, AppContext, Pixels, PlatformDisplay, Size, WindowBackgroundAppearance, WindowBounds,
+    point, App, Pixels, PlatformDisplay, Size, WindowBackgroundAppearance, WindowBounds,
     WindowDecorations, WindowKind, WindowOptions,
 };
 use panel_settings::MessageEditorSettings;
@@ -21,7 +21,7 @@ use settings::Settings;
 use ui::px;
 use workspace::AppState;
 
-pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
+pub fn init(app_state: &Arc<AppState>, cx: &mut App) {
     CollaborationPanelSettings::register(cx);
     ChatPanelSettings::register(cx);
     NotificationPanelSettings::register(cx);
@@ -38,7 +38,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
 fn notification_window_options(
     screen: Rc<dyn PlatformDisplay>,
     size: Size<Pixels>,
-    cx: &AppContext,
+    cx: &App,
 ) -> WindowOptions {
     let notification_margin_width = px(16.);
     let notification_margin_height = px(-48.);

crates/collab_ui/src/notification_panel.rs 🔗

@@ -6,11 +6,10 @@ use collections::HashMap;
 use db::kvp::KEY_VALUE_STORE;
 use futures::StreamExt;
 use gpui::{
-    actions, div, img, list, px, AnyElement, AppContext, AsyncWindowContext, CursorStyle,
-    DismissEvent, Element, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
-    IntoElement, ListAlignment, ListScrollEvent, ListState, Model, ParentElement, Render,
-    StatefulInteractiveElement, Styled, Task, View, ViewContext, VisualContext, WeakView,
-    WindowContext,
+    actions, div, img, list, px, AnyElement, App, AsyncWindowContext, Context, CursorStyle,
+    DismissEvent, Element, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement,
+    IntoElement, ListAlignment, ListScrollEvent, ListState, ParentElement, Render,
+    StatefulInteractiveElement, Styled, Task, WeakEntity, Window,
 };
 use notifications::{NotificationEntry, NotificationEvent, NotificationStore};
 use project::Fs;
@@ -36,16 +35,16 @@ const NOTIFICATION_PANEL_KEY: &str = "NotificationPanel";
 
 pub struct NotificationPanel {
     client: Arc<Client>,
-    user_store: Model<UserStore>,
-    channel_store: Model<ChannelStore>,
-    notification_store: Model<NotificationStore>,
+    user_store: Entity<UserStore>,
+    channel_store: Entity<ChannelStore>,
+    notification_store: Entity<NotificationStore>,
     fs: Arc<dyn Fs>,
     width: Option<Pixels>,
     active: bool,
     notification_list: ListState,
     pending_serialization: Task<Option<()>>,
     subscriptions: Vec<gpui::Subscription>,
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     current_notification_toast: Option<(u64, Task<()>)>,
     local_timezone: UtcOffset,
     focus_handle: FocusHandle,
@@ -75,28 +74,32 @@ pub struct NotificationPresenter {
 
 actions!(notification_panel, [ToggleFocus]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(|workspace: &mut Workspace, _| {
-        workspace.register_action(|workspace, _: &ToggleFocus, cx| {
-            workspace.toggle_panel_focus::<NotificationPanel>(cx);
+pub fn init(cx: &mut App) {
+    cx.observe_new(|workspace: &mut Workspace, _, _| {
+        workspace.register_action(|workspace, _: &ToggleFocus, window, cx| {
+            workspace.toggle_panel_focus::<NotificationPanel>(window, cx);
         });
     })
     .detach();
 }
 
 impl NotificationPanel {
-    pub fn new(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) -> View<Self> {
+    pub fn new(
+        workspace: &mut Workspace,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Entity<Self> {
         let fs = workspace.app_state().fs.clone();
         let client = workspace.app_state().client.clone();
         let user_store = workspace.app_state().user_store.clone();
         let workspace_handle = workspace.weak_handle();
 
-        cx.new_view(|cx: &mut ViewContext<Self>| {
+        cx.new(|cx| {
             let mut status = client.status();
-            cx.spawn(|this, mut cx| async move {
+            cx.spawn_in(window, |this, mut cx| async move {
                 while (status.next().await).is_some() {
                     if this
-                        .update(&mut cx, |_, cx| {
+                        .update(&mut cx, |_: &mut Self, cx| {
                             cx.notify();
                         })
                         .is_err()
@@ -107,17 +110,18 @@ impl NotificationPanel {
             })
             .detach();
 
-            let view = cx.view().downgrade();
+            let model = cx.model().downgrade();
             let notification_list =
-                ListState::new(0, ListAlignment::Top, px(1000.), move |ix, cx| {
-                    view.upgrade()
-                        .and_then(|view| {
-                            view.update(cx, |this, cx| this.render_notification(ix, cx))
+                ListState::new(0, ListAlignment::Top, px(1000.), move |ix, window, cx| {
+                    model
+                        .upgrade()
+                        .and_then(|model| {
+                            model.update(cx, |this, cx| this.render_notification(ix, window, cx))
                         })
                         .unwrap_or_else(|| div().into_any())
                 });
             notification_list.set_scroll_handler(cx.listener(
-                |this, event: &ListScrollEvent, cx| {
+                |this, event: &ListScrollEvent, _, cx| {
                     if event.count.saturating_sub(event.visible_range.end) < LOADING_THRESHOLD {
                         if let Some(task) = this
                             .notification_store
@@ -149,27 +153,34 @@ impl NotificationPanel {
                 unseen_notifications: Vec::new(),
             };
 
-            let mut old_dock_position = this.position(cx);
+            let mut old_dock_position = this.position(window, cx);
             this.subscriptions.extend([
                 cx.observe(&this.notification_store, |_, _, cx| cx.notify()),
-                cx.subscribe(&this.notification_store, Self::on_notification_event),
-                cx.observe_global::<SettingsStore>(move |this: &mut Self, cx| {
-                    let new_dock_position = this.position(cx);
-                    if new_dock_position != old_dock_position {
-                        old_dock_position = new_dock_position;
-                        cx.emit(Event::DockPositionChanged);
-                    }
-                    cx.notify();
-                }),
+                cx.subscribe_in(
+                    &this.notification_store,
+                    window,
+                    Self::on_notification_event,
+                ),
+                cx.observe_global_in::<SettingsStore>(
+                    window,
+                    move |this: &mut Self, window, cx| {
+                        let new_dock_position = this.position(window, cx);
+                        if new_dock_position != old_dock_position {
+                            old_dock_position = new_dock_position;
+                            cx.emit(Event::DockPositionChanged);
+                        }
+                        cx.notify();
+                    },
+                ),
             ]);
             this
         })
     }
 
     pub fn load(
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         cx: AsyncWindowContext,
-    ) -> Task<Result<View<Self>>> {
+    ) -> Task<Result<Entity<Self>>> {
         cx.spawn(|mut cx| async move {
             let serialized_panel = if let Some(panel) = cx
                 .background_executor()
@@ -183,8 +194,8 @@ impl NotificationPanel {
                 None
             };
 
-            workspace.update(&mut cx, |workspace, cx| {
-                let panel = Self::new(workspace, cx);
+            workspace.update_in(&mut cx, |workspace, window, cx| {
+                let panel = Self::new(workspace, window, cx);
                 if let Some(serialized_panel) = serialized_panel {
                     panel.update(cx, |panel, cx| {
                         panel.width = serialized_panel.width.map(|w| w.round());
@@ -196,7 +207,7 @@ impl NotificationPanel {
         })
     }
 
-    fn serialize(&mut self, cx: &mut ViewContext<Self>) {
+    fn serialize(&mut self, cx: &mut Context<Self>) {
         let width = self.width;
         self.pending_serialization = cx.background_executor().spawn(
             async move {
@@ -212,7 +223,12 @@ impl NotificationPanel {
         );
     }
 
-    fn render_notification(&mut self, ix: usize, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
+    fn render_notification(
+        &mut self,
+        ix: usize,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<AnyElement> {
         let entry = self.notification_store.read(cx).notification_at(ix)?;
         let notification_id = entry.id;
         let now = OffsetDateTime::now_utc();
@@ -229,7 +245,7 @@ impl NotificationPanel {
         let notification = entry.notification.clone();
 
         if self.active && !entry.is_read {
-            self.did_render_notification(notification_id, &notification, cx);
+            self.did_render_notification(notification_id, &notification, window, cx);
         }
 
         let relative_timestamp = time_format::format_localized_timestamp(
@@ -259,8 +275,8 @@ impl NotificationPanel {
                 .when(can_navigate, |el| {
                     el.cursor(CursorStyle::PointingHand).on_click({
                         let notification = notification.clone();
-                        cx.listener(move |this, _, cx| {
-                            this.did_click_notification(&notification, cx)
+                        cx.listener(move |this, _, window, cx| {
+                            this.did_click_notification(&notification, window, cx)
                         })
                     })
                 })
@@ -288,8 +304,8 @@ impl NotificationPanel {
                                                 .rounded_md()
                                         })
                                         .child(Label::new(relative_timestamp).color(Color::Muted))
-                                        .tooltip(move |cx| {
-                                            Tooltip::text(absolute_timestamp.clone(), cx)
+                                        .tooltip(move |_, cx| {
+                                            Tooltip::simple(absolute_timestamp.clone(), cx)
                                         }),
                                 )
                                 .children(if let Some(is_accepted) = response {
@@ -307,9 +323,9 @@ impl NotificationPanel {
                                             .justify_end()
                                             .child(Button::new("decline", "Decline").on_click({
                                                 let notification = notification.clone();
-                                                let view = cx.view().clone();
-                                                move |_, cx| {
-                                                    view.update(cx, |this, cx| {
+                                                let model = cx.model().clone();
+                                                move |_, _, cx| {
+                                                    model.update(cx, |this, cx| {
                                                         this.respond_to_notification(
                                                             notification.clone(),
                                                             false,
@@ -320,9 +336,9 @@ impl NotificationPanel {
                                             }))
                                             .child(Button::new("accept", "Accept").on_click({
                                                 let notification = notification.clone();
-                                                let view = cx.view().clone();
-                                                move |_, cx| {
-                                                    view.update(cx, |this, cx| {
+                                                let model = cx.model().clone();
+                                                move |_, _, cx| {
+                                                    model.update(cx, |this, cx| {
                                                         this.respond_to_notification(
                                                             notification.clone(),
                                                             true,
@@ -344,7 +360,7 @@ impl NotificationPanel {
     fn present_notification(
         &self,
         entry: &NotificationEntry,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<NotificationPresenter> {
         let user_store = self.user_store.read(cx);
         let channel_store = self.channel_store.read(cx);
@@ -415,7 +431,8 @@ impl NotificationPanel {
         &mut self,
         notification_id: u64,
         notification: &Notification,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let should_mark_as_read = match notification {
             Notification::ContactRequestAccepted { .. } => true,
@@ -429,7 +446,7 @@ impl NotificationPanel {
                 .entry(notification_id)
                 .or_insert_with(|| {
                     let client = self.client.clone();
-                    cx.spawn(|this, mut cx| async move {
+                    cx.spawn_in(window, |this, mut cx| async move {
                         cx.background_executor().timer(MARK_AS_READ_DELAY).await;
                         client
                             .request(proto::MarkNotificationRead { notification_id })
@@ -443,7 +460,12 @@ impl NotificationPanel {
         }
     }
 
-    fn did_click_notification(&mut self, notification: &Notification, cx: &mut ViewContext<Self>) {
+    fn did_click_notification(
+        &mut self,
+        notification: &Notification,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Notification::ChannelMessageMention {
             message_id,
             channel_id,
@@ -451,9 +473,9 @@ impl NotificationPanel {
         } = notification.clone()
         {
             if let Some(workspace) = self.workspace.upgrade() {
-                cx.window_context().defer(move |cx| {
+                window.defer(cx, move |window, cx| {
                     workspace.update(cx, |workspace, cx| {
-                        if let Some(panel) = workspace.focus_panel::<ChatPanel>(cx) {
+                        if let Some(panel) = workspace.focus_panel::<ChatPanel>(window, cx) {
                             panel.update(cx, |panel, cx| {
                                 panel
                                     .select_channel(ChannelId(channel_id), Some(message_id), cx)
@@ -466,7 +488,7 @@ impl NotificationPanel {
         }
     }
 
-    fn is_showing_notification(&self, notification: &Notification, cx: &ViewContext<Self>) -> bool {
+    fn is_showing_notification(&self, notification: &Notification, cx: &mut Context<Self>) -> bool {
         if !self.active {
             return false;
         }
@@ -490,16 +512,17 @@ impl NotificationPanel {
 
     fn on_notification_event(
         &mut self,
-        _: Model<NotificationStore>,
+        _: &Entity<NotificationStore>,
         event: &NotificationEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match event {
             NotificationEvent::NewNotification { entry } => {
                 if !self.is_showing_notification(&entry.notification, cx) {
                     self.unseen_notifications.push(entry.clone());
                 }
-                self.add_toast(entry, cx);
+                self.add_toast(entry, window, cx);
             }
             NotificationEvent::NotificationRemoved { entry }
             | NotificationEvent::NotificationRead { entry } => {
@@ -516,7 +539,12 @@ impl NotificationPanel {
         }
     }
 
-    fn add_toast(&mut self, entry: &NotificationEntry, cx: &mut ViewContext<Self>) {
+    fn add_toast(
+        &mut self,
+        entry: &NotificationEntry,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if self.is_showing_notification(&entry.notification, cx) {
             return;
         }
@@ -529,7 +557,7 @@ impl NotificationPanel {
         let notification_id = entry.id;
         self.current_notification_toast = Some((
             notification_id,
-            cx.spawn(|this, mut cx| async move {
+            cx.spawn_in(window, |this, mut cx| async move {
                 cx.background_executor().timer(TOAST_DURATION).await;
                 this.update(&mut cx, |this, cx| this.remove_toast(notification_id, cx))
                     .ok();
@@ -542,8 +570,8 @@ impl NotificationPanel {
 
                 workspace.dismiss_notification(&id, cx);
                 workspace.show_notification(id, cx, |cx| {
-                    let workspace = cx.view().downgrade();
-                    cx.new_view(|_| NotificationToast {
+                    let workspace = cx.model().downgrade();
+                    cx.new(|_| NotificationToast {
                         notification_id,
                         actor,
                         text,
@@ -554,7 +582,7 @@ impl NotificationPanel {
             .ok();
     }
 
-    fn remove_toast(&mut self, notification_id: u64, cx: &mut ViewContext<Self>) {
+    fn remove_toast(&mut self, notification_id: u64, cx: &mut Context<Self>) {
         if let Some((current_id, _)) = &self.current_notification_toast {
             if *current_id == notification_id {
                 self.current_notification_toast.take();
@@ -572,7 +600,8 @@ impl NotificationPanel {
         &mut self,
         notification: Notification,
         response: bool,
-        cx: &mut ViewContext<Self>,
+
+        cx: &mut Context<Self>,
     ) {
         self.notification_store.update(cx, |store, cx| {
             store.respond_to_notification(notification, response, cx);
@@ -581,7 +610,7 @@ impl NotificationPanel {
 }
 
 impl Render for NotificationPanel {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         v_flex()
             .size_full()
             .child(
@@ -611,15 +640,16 @@ impl Render for NotificationPanel {
                                     .full_width()
                                     .on_click({
                                         let client = self.client.clone();
-                                        move |_, cx| {
+                                        move |_, window, cx| {
                                             let client = client.clone();
-                                            cx.spawn(move |cx| async move {
-                                                client
-                                                    .authenticate_and_connect(true, &cx)
-                                                    .log_err()
-                                                    .await;
-                                            })
-                                            .detach()
+                                            window
+                                                .spawn(cx, move |cx| async move {
+                                                    client
+                                                        .authenticate_and_connect(true, &cx)
+                                                        .log_err()
+                                                        .await;
+                                                })
+                                                .detach()
                                         }
                                     }),
                             )
@@ -648,8 +678,8 @@ impl Render for NotificationPanel {
     }
 }
 
-impl FocusableView for NotificationPanel {
-    fn focus_handle(&self, _: &AppContext) -> FocusHandle {
+impl Focusable for NotificationPanel {
+    fn focus_handle(&self, _: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -662,7 +692,7 @@ impl Panel for NotificationPanel {
         "NotificationPanel"
     }
 
-    fn position(&self, cx: &WindowContext) -> DockPosition {
+    fn position(&self, _: &Window, cx: &App) -> DockPosition {
         NotificationPanelSettings::get_global(cx).dock
     }
 
@@ -670,7 +700,7 @@ impl Panel for NotificationPanel {
         matches!(position, DockPosition::Left | DockPosition::Right)
     }
 
-    fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) {
+    fn set_position(&mut self, position: DockPosition, _: &mut Window, cx: &mut Context<Self>) {
         settings::update_settings_file::<NotificationPanelSettings>(
             self.fs.clone(),
             cx,
@@ -678,18 +708,18 @@ impl Panel for NotificationPanel {
         );
     }
 
-    fn size(&self, cx: &WindowContext) -> Pixels {
+    fn size(&self, _: &Window, cx: &App) -> Pixels {
         self.width
             .unwrap_or_else(|| NotificationPanelSettings::get_global(cx).default_width)
     }
 
-    fn set_size(&mut self, size: Option<Pixels>, cx: &mut ViewContext<Self>) {
+    fn set_size(&mut self, size: Option<Pixels>, _: &mut Window, cx: &mut Context<Self>) {
         self.width = size;
         self.serialize(cx);
         cx.notify();
     }
 
-    fn set_active(&mut self, active: bool, cx: &mut ViewContext<Self>) {
+    fn set_active(&mut self, active: bool, _: &mut Window, cx: &mut Context<Self>) {
         self.active = active;
 
         if self.active {
@@ -702,7 +732,7 @@ impl Panel for NotificationPanel {
         }
     }
 
-    fn icon(&self, cx: &WindowContext) -> Option<IconName> {
+    fn icon(&self, _: &Window, cx: &App) -> Option<IconName> {
         let show_button = NotificationPanelSettings::get_global(cx).button;
         if !show_button {
             return None;
@@ -715,11 +745,11 @@ impl Panel for NotificationPanel {
         Some(IconName::BellDot)
     }
 
-    fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> {
+    fn icon_tooltip(&self, _window: &Window, _cx: &App) -> Option<&'static str> {
         Some("Notification Panel")
     }
 
-    fn icon_label(&self, cx: &WindowContext) -> Option<String> {
+    fn icon_label(&self, _window: &Window, cx: &App) -> Option<String> {
         let count = self.notification_store.read(cx).unread_notification_count();
         if count == 0 {
             None
@@ -741,21 +771,25 @@ pub struct NotificationToast {
     notification_id: u64,
     actor: Option<Arc<User>>,
     text: String,
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
 }
 
 impl NotificationToast {
-    fn focus_notification_panel(&self, cx: &mut ViewContext<Self>) {
+    fn focus_notification_panel(&self, window: &mut Window, cx: &mut Context<Self>) {
         let workspace = self.workspace.clone();
         let notification_id = self.notification_id;
-        cx.window_context().defer(move |cx| {
+        window.defer(cx, move |window, cx| {
             workspace
                 .update(cx, |workspace, cx| {
-                    if let Some(panel) = workspace.focus_panel::<NotificationPanel>(cx) {
+                    if let Some(panel) = workspace.focus_panel::<NotificationPanel>(window, cx) {
                         panel.update(cx, |panel, cx| {
                             let store = panel.notification_store.read(cx);
                             if let Some(entry) = store.notification_for_id(notification_id) {
-                                panel.did_click_notification(&entry.clone().notification, cx);
+                                panel.did_click_notification(
+                                    &entry.clone().notification,
+                                    window,
+                                    cx,
+                                );
                             }
                         });
                     }
@@ -766,7 +800,7 @@ impl NotificationToast {
 }
 
 impl Render for NotificationToast {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let user = self.actor.clone();
 
         h_flex()
@@ -778,10 +812,10 @@ impl Render for NotificationToast {
             .child(Label::new(self.text.clone()))
             .child(
                 IconButton::new("close", IconName::Close)
-                    .on_click(cx.listener(|_, _, cx| cx.emit(DismissEvent))),
+                    .on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))),
             )
-            .on_click(cx.listener(|this, _, cx| {
-                this.focus_notification_panel(cx);
+            .on_click(cx.listener(|this, _, window, cx| {
+                this.focus_notification_panel(window, cx);
                 cx.emit(DismissEvent);
             }))
     }

crates/collab_ui/src/notifications.rs 🔗

@@ -5,14 +5,14 @@ pub mod project_shared_notification;
 #[cfg(feature = "stories")]
 mod stories;
 
-use gpui::AppContext;
+use gpui::App;
 use std::sync::Arc;
 use workspace::AppState;
 
 #[cfg(feature = "stories")]
 pub use stories::*;
 
-pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
+pub fn init(app_state: &Arc<AppState>, cx: &mut App) {
     incoming_call_notification::init(app_state, cx);
     project_shared_notification::init(app_state, cx);
 }

crates/collab_ui/src/notifications/collab_notification.rs 🔗

@@ -32,7 +32,7 @@ impl ParentElement for CollabNotification {
 }
 
 impl RenderOnce for CollabNotification {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
         h_flex()
             .text_ui(cx)
             .justify_between()

crates/collab_ui/src/notifications/incoming_call_notification.rs 🔗

@@ -2,14 +2,14 @@ use crate::notification_window_options;
 use crate::notifications::collab_notification::CollabNotification;
 use call::{ActiveCall, IncomingCall};
 use futures::StreamExt;
-use gpui::{prelude::*, AppContext, WindowHandle};
+use gpui::{prelude::*, App, WindowHandle};
 
 use std::sync::{Arc, Weak};
 use ui::{prelude::*, Button, Label};
 use util::ResultExt;
 use workspace::AppState;
 
-pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
+pub fn init(app_state: &Arc<AppState>, cx: &mut App) {
     let app_state = Arc::downgrade(app_state);
     let mut incoming_call = ActiveCall::global(cx).read(cx).incoming();
     cx.spawn(|mut cx| async move {
@@ -17,8 +17,8 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
         while let Some(incoming_call) = incoming_call.next().await {
             for window in notification_windows.drain(..) {
                 window
-                    .update(&mut cx, |_, cx| {
-                        cx.remove_window();
+                    .update(&mut cx, |_, window, _| {
+                        window.remove_window();
                     })
                     .log_err();
             }
@@ -36,8 +36,8 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
                         .log_err()
                     {
                         let window = cx
-                            .open_window(options, |cx| {
-                                cx.new_view(|_| {
+                            .open_window(options, |_, cx| {
+                                cx.new(|_| {
                                     IncomingCallNotification::new(
                                         incoming_call.clone(),
                                         app_state.clone(),
@@ -67,14 +67,14 @@ impl IncomingCallNotificationState {
         Self { call, app_state }
     }
 
-    fn respond(&self, accept: bool, cx: &mut AppContext) {
+    fn respond(&self, accept: bool, cx: &mut App) {
         let active_call = ActiveCall::global(cx);
         if accept {
             let join = active_call.update(cx, |active_call, cx| active_call.accept_incoming(cx));
             let caller_user_id = self.call.calling_user.id;
             let initial_project_id = self.call.initial_project.as_ref().map(|project| project.id);
             let app_state = self.app_state.clone();
-            let cx: &mut AppContext = cx;
+            let cx: &mut App = cx;
             cx.spawn(|cx| async move {
                 join.await?;
                 if let Some(project_id) = initial_project_id {
@@ -111,19 +111,19 @@ impl IncomingCallNotification {
 }
 
 impl Render for IncomingCallNotification {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let ui_font = theme::setup_ui_font(cx);
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+        let ui_font = theme::setup_ui_font(window, cx);
 
         div().size_full().font(ui_font).child(
             CollabNotification::new(
                 self.state.call.calling_user.avatar_uri.clone(),
                 Button::new("accept", "Accept").on_click({
                     let state = self.state.clone();
-                    move |_, cx| state.respond(true, cx)
+                    move |_, _, cx| state.respond(true, cx)
                 }),
                 Button::new("decline", "Decline").on_click({
                     let state = self.state.clone();
-                    move |_, cx| state.respond(false, cx)
+                    move |_, _, cx| state.respond(false, cx)
                 }),
             )
             .child(v_flex().overflow_hidden().child(Label::new(format!(

crates/collab_ui/src/notifications/project_shared_notification.rs 🔗

@@ -3,14 +3,14 @@ use crate::notifications::collab_notification::CollabNotification;
 use call::{room, ActiveCall};
 use client::User;
 use collections::HashMap;
-use gpui::{AppContext, Size};
+use gpui::{App, Size};
 use std::sync::{Arc, Weak};
 
 use ui::{prelude::*, Button, Label};
 use util::ResultExt;
 use workspace::AppState;
 
-pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
+pub fn init(app_state: &Arc<AppState>, cx: &mut App) {
     let app_state = Arc::downgrade(app_state);
     let active_call = ActiveCall::global(cx);
     let mut notification_windows = HashMap::default();
@@ -28,8 +28,8 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
             for screen in cx.displays() {
                 let options = notification_window_options(screen, window_size, cx);
                 let Some(window) = cx
-                    .open_window(options, |cx| {
-                        cx.new_view(|_| {
+                    .open_window(options, |_, cx| {
+                        cx.new(|_| {
                             ProjectSharedNotification::new(
                                 owner.clone(),
                                 *project_id,
@@ -55,8 +55,8 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
             if let Some(windows) = notification_windows.remove(project_id) {
                 for window in windows {
                     window
-                        .update(cx, |_, cx| {
-                            cx.remove_window();
+                        .update(cx, |_, window, _| {
+                            window.remove_window();
                         })
                         .ok();
                 }
@@ -67,8 +67,8 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
             for (_, windows) in notification_windows.drain() {
                 for window in windows {
                     window
-                        .update(cx, |_, cx| {
-                            cx.remove_window();
+                        .update(cx, |_, window, _| {
+                            window.remove_window();
                         })
                         .ok();
                 }
@@ -101,14 +101,14 @@ impl ProjectSharedNotification {
         }
     }
 
-    fn join(&mut self, cx: &mut ViewContext<Self>) {
+    fn join(&mut self, cx: &mut Context<Self>) {
         if let Some(app_state) = self.app_state.upgrade() {
             workspace::join_in_room_project(self.project_id, self.owner.id, app_state, cx)
                 .detach_and_log_err(cx);
         }
     }
 
-    fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
+    fn dismiss(&mut self, cx: &mut Context<Self>) {
         if let Some(active_room) =
             ActiveCall::global(cx).read_with(cx, |call, _| call.room().cloned())
         {
@@ -122,18 +122,20 @@ impl ProjectSharedNotification {
 }
 
 impl Render for ProjectSharedNotification {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let ui_font = theme::setup_ui_font(cx);
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+        let ui_font = theme::setup_ui_font(window, cx);
 
         div().size_full().font(ui_font).child(
             CollabNotification::new(
                 self.owner.avatar_uri.clone(),
-                Button::new("open", "Open").on_click(cx.listener(move |this, _event, cx| {
+                Button::new("open", "Open").on_click(cx.listener(move |this, _event, _, cx| {
                     this.join(cx);
                 })),
-                Button::new("dismiss", "Dismiss").on_click(cx.listener(move |this, _event, cx| {
-                    this.dismiss(cx);
-                })),
+                Button::new("dismiss", "Dismiss").on_click(cx.listener(
+                    move |this, _event, _, cx| {
+                        this.dismiss(cx);
+                    },
+                )),
             )
             .child(Label::new(self.owner.github_login.clone()))
             .child(Label::new(format!(

crates/collab_ui/src/notifications/stories/collab_notification.rs 🔗

@@ -7,7 +7,7 @@ use crate::notifications::collab_notification::CollabNotification;
 pub struct CollabNotificationStory;
 
 impl Render for CollabNotificationStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         let window_container = |width, height| div().w(px(width)).h(px(height));
 
         Story::container()

crates/collab_ui/src/panel_settings.rs 🔗

@@ -126,7 +126,7 @@ impl Settings for CollaborationPanelSettings {
 
     fn load(
         sources: SettingsSources<Self::FileContent>,
-        _: &mut gpui::AppContext,
+        _: &mut gpui::App,
     ) -> anyhow::Result<Self> {
         sources.json_merge()
     }
@@ -139,7 +139,7 @@ impl Settings for ChatPanelSettings {
 
     fn load(
         sources: SettingsSources<Self::FileContent>,
-        _: &mut gpui::AppContext,
+        _: &mut gpui::App,
     ) -> anyhow::Result<Self> {
         sources.json_merge()
     }
@@ -152,7 +152,7 @@ impl Settings for NotificationPanelSettings {
 
     fn load(
         sources: SettingsSources<Self::FileContent>,
-        _: &mut gpui::AppContext,
+        _: &mut gpui::App,
     ) -> anyhow::Result<Self> {
         sources.json_merge()
     }
@@ -165,7 +165,7 @@ impl Settings for MessageEditorSettings {
 
     fn load(
         sources: SettingsSources<Self::FileContent>,
-        _: &mut gpui::AppContext,
+        _: &mut gpui::App,
     ) -> anyhow::Result<Self> {
         sources.json_merge()
     }

crates/command_palette/src/command_palette.rs 🔗

@@ -11,8 +11,8 @@ use command_palette_hooks::{
 };
 use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{
-    Action, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Global,
-    ParentElement, Render, Styled, Task, UpdateGlobal, View, ViewContext, VisualContext, WeakView,
+    Action, App, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Global,
+    ParentElement, Render, Styled, Task, UpdateGlobal, WeakEntity, Window,
 };
 use picker::{Picker, PickerDelegate};
 use postage::{sink::Sink, stream::Stream};
@@ -22,17 +22,17 @@ use util::ResultExt;
 use workspace::{ModalView, Workspace, WorkspaceSettings};
 use zed_actions::{command_palette::Toggle, OpenZedUrl};
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     client::init_settings(cx);
     cx.set_global(HitCounts::default());
     command_palette_hooks::init(cx);
-    cx.observe_new_views(CommandPalette::register).detach();
+    cx.observe_new(CommandPalette::register).detach();
 }
 
 impl ModalView for CommandPalette {}
 
 pub struct CommandPalette {
-    picker: View<Picker<CommandPaletteDelegate>>,
+    picker: Entity<Picker<CommandPaletteDelegate>>,
 }
 
 /// Removes subsequent whitespace characters and double colons from the query.
@@ -59,24 +59,40 @@ fn normalize_query(input: &str) -> String {
 }
 
 impl CommandPalette {
-    fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-        workspace.register_action(|workspace, _: &Toggle, cx| Self::toggle(workspace, "", cx));
+    fn register(
+        workspace: &mut Workspace,
+        _window: Option<&mut Window>,
+        _: &mut Context<Workspace>,
+    ) {
+        workspace.register_action(|workspace, _: &Toggle, window, cx| {
+            Self::toggle(workspace, "", window, cx)
+        });
     }
 
-    pub fn toggle(workspace: &mut Workspace, query: &str, cx: &mut ViewContext<Workspace>) {
-        let Some(previous_focus_handle) = cx.focused() else {
+    pub fn toggle(
+        workspace: &mut Workspace,
+        query: &str,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) {
+        let Some(previous_focus_handle) = window.focused(cx) else {
             return;
         };
-        workspace.toggle_modal(cx, move |cx| {
-            CommandPalette::new(previous_focus_handle, query, cx)
+        workspace.toggle_modal(window, cx, move |window, cx| {
+            CommandPalette::new(previous_focus_handle, query, window, cx)
         });
     }
 
-    fn new(previous_focus_handle: FocusHandle, query: &str, cx: &mut ViewContext<Self>) -> Self {
+    fn new(
+        previous_focus_handle: FocusHandle,
+        query: &str,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Self {
         let filter = CommandPaletteFilter::try_global(cx);
 
-        let commands = cx
-            .available_actions()
+        let commands = window
+            .available_actions(cx)
             .into_iter()
             .filter_map(|action| {
                 if filter.is_some_and(|filter| filter.is_hidden(&*action)) {
@@ -91,38 +107,38 @@ impl CommandPalette {
             .collect();
 
         let delegate =
-            CommandPaletteDelegate::new(cx.view().downgrade(), commands, previous_focus_handle);
+            CommandPaletteDelegate::new(cx.model().downgrade(), commands, previous_focus_handle);
 
-        let picker = cx.new_view(|cx| {
-            let picker = Picker::uniform_list(delegate, cx);
-            picker.set_query(query, cx);
+        let picker = cx.new(|cx| {
+            let picker = Picker::uniform_list(delegate, window, cx);
+            picker.set_query(query, window, cx);
             picker
         });
         Self { picker }
     }
 
-    pub fn set_query(&mut self, query: &str, cx: &mut ViewContext<Self>) {
+    pub fn set_query(&mut self, query: &str, window: &mut Window, cx: &mut Context<Self>) {
         self.picker
-            .update(cx, |picker, cx| picker.set_query(query, cx))
+            .update(cx, |picker, cx| picker.set_query(query, window, cx))
     }
 }
 
 impl EventEmitter<DismissEvent> for CommandPalette {}
 
-impl FocusableView for CommandPalette {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for CommandPalette {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
 
 impl Render for CommandPalette {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         v_flex().w(rems(34.)).child(self.picker.clone())
     }
 }
 
 pub struct CommandPaletteDelegate {
-    command_palette: WeakView<CommandPalette>,
+    command_palette: WeakEntity<CommandPalette>,
     all_commands: Vec<Command>,
     commands: Vec<Command>,
     matches: Vec<StringMatch>,
@@ -158,7 +174,7 @@ impl Global for HitCounts {}
 
 impl CommandPaletteDelegate {
     fn new(
-        command_palette: WeakView<CommandPalette>,
+        command_palette: WeakEntity<CommandPalette>,
         commands: Vec<Command>,
         previous_focus_handle: FocusHandle,
     ) -> Self {
@@ -178,7 +194,7 @@ impl CommandPaletteDelegate {
         query: String,
         mut commands: Vec<Command>,
         mut matches: Vec<StringMatch>,
-        cx: &mut ViewContext<Picker<Self>>,
+        cx: &mut Context<Picker<Self>>,
     ) {
         self.updating_matches.take();
 
@@ -232,7 +248,7 @@ impl CommandPaletteDelegate {
 impl PickerDelegate for CommandPaletteDelegate {
     type ListItem = ListItem;
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Execute a command...".into()
     }
 
@@ -244,14 +260,20 @@ impl PickerDelegate for CommandPaletteDelegate {
         self.selected_ix
     }
 
-    fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        _window: &mut Window,
+        _: &mut Context<Picker<Self>>,
+    ) {
         self.selected_ix = ix;
     }
 
     fn update_matches(
         &mut self,
         mut query: String,
-        cx: &mut ViewContext<Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> gpui::Task<()> {
         let settings = WorkspaceSettings::get_global(cx);
         if let Some(alias) = settings.command_aliases.get(&query) {
@@ -304,7 +326,7 @@ impl PickerDelegate for CommandPaletteDelegate {
         });
         self.updating_matches = Some((task, rx.clone()));
 
-        cx.spawn(move |picker, mut cx| async move {
+        cx.spawn_in(window, move |picker, mut cx| async move {
             let Some((commands, matches)) = rx.recv().await else {
                 return;
             };
@@ -323,7 +345,8 @@ impl PickerDelegate for CommandPaletteDelegate {
         &mut self,
         query: String,
         duration: Duration,
-        cx: &mut ViewContext<Picker<Self>>,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> bool {
         let Some((task, rx)) = self.updating_matches.take() else {
             return true;
@@ -344,20 +367,19 @@ impl PickerDelegate for CommandPaletteDelegate {
         }
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+    fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.command_palette
             .update(cx, |_, cx| cx.emit(DismissEvent))
             .log_err();
     }
 
-    fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
         if self.matches.is_empty() {
-            self.dismissed(cx);
+            self.dismissed(window, cx);
             return;
         }
         let action_ix = self.matches[self.selected_ix].candidate_id;
         let command = self.commands.swap_remove(action_ix);
-
         telemetry::event!(
             "Action Invoked",
             source = "command palette",
@@ -369,16 +391,17 @@ impl PickerDelegate for CommandPaletteDelegate {
             *hit_counts.0.entry(command.name).or_default() += 1;
         });
         let action = command.action;
-        cx.focus(&self.previous_focus_handle);
-        self.dismissed(cx);
-        cx.dispatch_action(action);
+        window.focus(&self.previous_focus_handle);
+        self.dismissed(window, cx);
+        window.dispatch_action(action, cx);
     }
 
     fn render_match(
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        window: &mut Window,
+        _: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let r#match = self.matches.get(ix)?;
         let command = self.commands.get(r#match.candidate_id)?;
@@ -399,7 +422,7 @@ impl PickerDelegate for CommandPaletteDelegate {
                         .children(KeyBinding::for_action_in(
                             &*command.action,
                             &self.previous_focus_handle,
-                            cx,
+                            window,
                         )),
                 ),
         )
@@ -490,17 +513,18 @@ mod tests {
     async fn test_command_palette(cx: &mut TestAppContext) {
         let app_state = init_test(cx);
         let project = Project::test(app_state.fs.clone(), [], cx).await;
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let (workspace, cx) =
+            cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
-        let editor = cx.new_view(|cx| {
-            let mut editor = Editor::single_line(cx);
-            editor.set_text("abc", cx);
+        let editor = cx.new_window_model(|window, cx| {
+            let mut editor = Editor::single_line(window, cx);
+            editor.set_text("abc", window, cx);
             editor
         });
 
-        workspace.update(cx, |workspace, cx| {
-            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
-            editor.update(cx, |editor, cx| editor.focus(cx))
+        workspace.update_in(cx, |workspace, window, cx| {
+            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
+            editor.update(cx, |editor, cx| window.focus(&editor.focus_handle(cx)))
         });
 
         cx.simulate_keystrokes("cmd-shift-p");
@@ -535,7 +559,7 @@ mod tests {
         });
 
         // Add namespace filter, and redeploy the palette
-        cx.update(|cx| {
+        cx.update(|_window, cx| {
             CommandPaletteFilter::update_global(cx, |filter, _| {
                 filter.hide_namespace("editor");
             });
@@ -560,17 +584,18 @@ mod tests {
     async fn test_normalized_matches(cx: &mut TestAppContext) {
         let app_state = init_test(cx);
         let project = Project::test(app_state.fs.clone(), [], cx).await;
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let (workspace, cx) =
+            cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
-        let editor = cx.new_view(|cx| {
-            let mut editor = Editor::single_line(cx);
-            editor.set_text("abc", cx);
+        let editor = cx.new_window_model(|window, cx| {
+            let mut editor = Editor::single_line(window, cx);
+            editor.set_text("abc", window, cx);
             editor
         });
 
-        workspace.update(cx, |workspace, cx| {
-            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
-            editor.update(cx, |editor, cx| editor.focus(cx))
+        workspace.update_in(cx, |workspace, window, cx| {
+            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
+            editor.update(cx, |editor, cx| window.focus(&editor.focus_handle(cx)))
         });
 
         // Test normalize (trimming whitespace and double colons)
@@ -595,14 +620,17 @@ mod tests {
     async fn test_go_to_line(cx: &mut TestAppContext) {
         let app_state = init_test(cx);
         let project = Project::test(app_state.fs.clone(), [], cx).await;
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let (workspace, cx) =
+            cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
         cx.simulate_keystrokes("cmd-n");
 
         let editor = workspace.update(cx, |workspace, cx| {
             workspace.active_item_as::<Editor>(cx).unwrap()
         });
-        editor.update(cx, |editor, cx| editor.set_text("1\n2\n3\n4\n5\n6\n", cx));
+        editor.update_in(cx, |editor, window, cx| {
+            editor.set_text("1\n2\n3\n4\n5\n6\n", window, cx)
+        });
 
         cx.simulate_keystrokes("cmd-shift-p");
         cx.simulate_input("go to line: Toggle");
@@ -614,8 +642,8 @@ mod tests {
 
         cx.simulate_keystrokes("3 enter");
 
-        editor.update(cx, |editor, cx| {
-            assert!(editor.focus_handle(cx).is_focused(cx));
+        editor.update_in(cx, |editor, window, cx| {
+            assert!(editor.focus_handle(cx).is_focused(window));
             assert_eq!(
                 editor.selections.last::<Point>(cx).range().start,
                 Point::new(2, 0)

crates/command_palette_hooks/src/command_palette_hooks.rs 🔗

@@ -6,10 +6,10 @@ use std::any::TypeId;
 
 use collections::HashSet;
 use derive_more::{Deref, DerefMut};
-use gpui::{Action, AppContext, BorrowAppContext, Global};
+use gpui::{Action, App, BorrowAppContext, Global};
 
 /// Initializes the command palette hooks.
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     cx.set_global(GlobalCommandPaletteFilter::default());
     cx.set_global(GlobalCommandPaletteInterceptor::default());
 }
@@ -31,20 +31,20 @@ impl Global for GlobalCommandPaletteFilter {}
 
 impl CommandPaletteFilter {
     /// Returns the global [`CommandPaletteFilter`], if one is set.
-    pub fn try_global(cx: &AppContext) -> Option<&CommandPaletteFilter> {
+    pub fn try_global(cx: &App) -> Option<&CommandPaletteFilter> {
         cx.try_global::<GlobalCommandPaletteFilter>()
             .map(|filter| &filter.0)
     }
 
     /// Returns a mutable reference to the global [`CommandPaletteFilter`].
-    pub fn global_mut(cx: &mut AppContext) -> &mut Self {
+    pub fn global_mut(cx: &mut App) -> &mut Self {
         cx.global_mut::<GlobalCommandPaletteFilter>()
     }
 
     /// Updates the global [`CommandPaletteFilter`] using the given closure.
-    pub fn update_global<F>(cx: &mut AppContext, update: F)
+    pub fn update_global<F>(cx: &mut App, update: F)
     where
-        F: FnOnce(&mut Self, &mut AppContext),
+        F: FnOnce(&mut Self, &mut App),
     {
         if cx.has_global::<GlobalCommandPaletteFilter>() {
             cx.update_global(|this: &mut GlobalCommandPaletteFilter, cx| update(&mut this.0, cx))
@@ -93,6 +93,7 @@ impl CommandPaletteFilter {
 }
 
 /// The result of intercepting a command palette command.
+#[derive(Debug)]
 pub struct CommandInterceptResult {
     /// The action produced as a result of the interception.
     pub action: Box<dyn Action>,
@@ -107,7 +108,7 @@ pub struct CommandInterceptResult {
 /// An interceptor for the command palette.
 #[derive(Default)]
 pub struct CommandPaletteInterceptor(
-    Option<Box<dyn Fn(&str, &AppContext) -> Option<CommandInterceptResult>>>,
+    Option<Box<dyn Fn(&str, &App) -> Option<CommandInterceptResult>>>,
 );
 
 #[derive(Default)]
@@ -117,21 +118,21 @@ impl Global for GlobalCommandPaletteInterceptor {}
 
 impl CommandPaletteInterceptor {
     /// Returns the global [`CommandPaletteInterceptor`], if one is set.
-    pub fn try_global(cx: &AppContext) -> Option<&CommandPaletteInterceptor> {
+    pub fn try_global(cx: &App) -> Option<&CommandPaletteInterceptor> {
         cx.try_global::<GlobalCommandPaletteInterceptor>()
             .map(|interceptor| &interceptor.0)
     }
 
     /// Updates the global [`CommandPaletteInterceptor`] using the given closure.
-    pub fn update_global<F, R>(cx: &mut AppContext, update: F) -> R
+    pub fn update_global<F, R>(cx: &mut App, update: F) -> R
     where
-        F: FnOnce(&mut Self, &mut AppContext) -> R,
+        F: FnOnce(&mut Self, &mut App) -> R,
     {
         cx.update_global(|this: &mut GlobalCommandPaletteInterceptor, cx| update(&mut this.0, cx))
     }
 
     /// Intercepts the given query from the command palette.
-    pub fn intercept(&self, query: &str, cx: &AppContext) -> Option<CommandInterceptResult> {
+    pub fn intercept(&self, query: &str, cx: &App) -> Option<CommandInterceptResult> {
         let handler = self.0.as_ref()?;
 
         (handler)(query, cx)
@@ -145,10 +146,7 @@ impl CommandPaletteInterceptor {
     /// Sets the global interceptor.
     ///
     /// This will override the previous interceptor, if it exists.
-    pub fn set(
-        &mut self,
-        handler: Box<dyn Fn(&str, &AppContext) -> Option<CommandInterceptResult>>,
-    ) {
+    pub fn set(&mut self, handler: Box<dyn Fn(&str, &App) -> Option<CommandInterceptResult>>) {
         self.0 = Some(handler);
     }
 }

crates/context_server/src/client.rs 🔗

@@ -1,4 +1,4 @@
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use collections::HashMap;
 use futures::{channel::oneshot, io::BufWriter, select, AsyncRead, AsyncWrite, FutureExt};
 use gpui::{AsyncAppContext, BackgroundExecutor, Task};

crates/context_server/src/context_server.rs 🔗

@@ -8,7 +8,7 @@ pub mod types;
 
 use command_palette_hooks::CommandPaletteFilter;
 pub use context_server_settings::{ContextServerSettings, ServerCommand, ServerConfig};
-use gpui::{actions, AppContext};
+use gpui::{actions, App};
 
 pub use crate::context_server_tool::ContextServerTool;
 pub use crate::registry::ContextServerFactoryRegistry;
@@ -18,7 +18,7 @@ actions!(context_servers, [Restart]);
 /// The namespace for the context servers actions.
 pub const CONTEXT_SERVERS_NAMESPACE: &'static str = "context_servers";
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     context_server_settings::init(cx);
     ContextServerFactoryRegistry::default_global(cx);
     extension_context_server::init(cx);

crates/context_server/src/context_server_tool.rs 🔗

@@ -2,20 +2,20 @@ use std::sync::Arc;
 
 use anyhow::{anyhow, bail};
 use assistant_tool::Tool;
-use gpui::{Model, Task, WindowContext};
+use gpui::{App, Entity, Task, Window};
 
 use crate::manager::ContextServerManager;
 use crate::types;
 
 pub struct ContextServerTool {
-    server_manager: Model<ContextServerManager>,
+    server_manager: Entity<ContextServerManager>,
     server_id: Arc<str>,
     tool: types::Tool,
 }
 
 impl ContextServerTool {
     pub fn new(
-        server_manager: Model<ContextServerManager>,
+        server_manager: Entity<ContextServerManager>,
         server_id: impl Into<Arc<str>>,
         tool: types::Tool,
     ) -> Self {
@@ -51,8 +51,9 @@ impl Tool for ContextServerTool {
     fn run(
         self: std::sync::Arc<Self>,
         input: serde_json::Value,
-        _workspace: gpui::WeakView<workspace::Workspace>,
-        cx: &mut WindowContext,
+        _workspace: gpui::WeakEntity<workspace::Workspace>,
+        _: &mut Window,
+        cx: &mut App,
     ) -> gpui::Task<gpui::Result<String>> {
         if let Some(server) = self.server_manager.read(cx).get_server(&self.server_id) {
             cx.foreground_executor().spawn({

crates/context_server/src/extension_context_server.rs 🔗

@@ -1,7 +1,7 @@
 use std::sync::Arc;
 
 use extension::{Extension, ExtensionContextServerProxy, ExtensionHostProxy, ProjectDelegate};
-use gpui::{AppContext, Model};
+use gpui::{App, Entity};
 
 use crate::{ContextServerFactoryRegistry, ServerCommand};
 
@@ -15,7 +15,7 @@ impl ProjectDelegate for ExtensionProject {
     }
 }
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     let proxy = ExtensionHostProxy::default_global(cx);
     proxy.register_context_server_proxy(ContextServerFactoryRegistryProxy {
         context_server_factory_registry: ContextServerFactoryRegistry::global(cx),
@@ -23,16 +23,11 @@ pub fn init(cx: &mut AppContext) {
 }
 
 struct ContextServerFactoryRegistryProxy {
-    context_server_factory_registry: Model<ContextServerFactoryRegistry>,
+    context_server_factory_registry: Entity<ContextServerFactoryRegistry>,
 }
 
 impl ExtensionContextServerProxy for ContextServerFactoryRegistryProxy {
-    fn register_context_server(
-        &self,
-        extension: Arc<dyn Extension>,
-        id: Arc<str>,
-        cx: &mut AppContext,
-    ) {
+    fn register_context_server(&self, extension: Arc<dyn Extension>, id: Arc<str>, cx: &mut App) {
         self.context_server_factory_registry
             .update(cx, |registry, _| {
                 registry.register_server_factory(

crates/context_server/src/manager.rs 🔗

@@ -20,7 +20,7 @@ use std::sync::Arc;
 use anyhow::{bail, Result};
 use collections::HashMap;
 use command_palette_hooks::CommandPaletteFilter;
-use gpui::{AsyncAppContext, EventEmitter, Model, ModelContext, Subscription, Task, WeakModel};
+use gpui::{AsyncAppContext, Context, Entity, EventEmitter, Subscription, Task, WeakEntity};
 use log;
 use parking_lot::RwLock;
 use project::Project;
@@ -104,8 +104,8 @@ impl ContextServer {
 
 pub struct ContextServerManager {
     servers: HashMap<Arc<str>, Arc<ContextServer>>,
-    project: Model<Project>,
-    registry: Model<ContextServerFactoryRegistry>,
+    project: Entity<Project>,
+    registry: Entity<ContextServerFactoryRegistry>,
     update_servers_task: Option<Task<Result<()>>>,
     needs_server_update: bool,
     _subscriptions: Vec<Subscription>,
@@ -120,9 +120,9 @@ impl EventEmitter<Event> for ContextServerManager {}
 
 impl ContextServerManager {
     pub fn new(
-        registry: Model<ContextServerFactoryRegistry>,
-        project: Model<Project>,
-        cx: &mut ModelContext<Self>,
+        registry: Entity<ContextServerFactoryRegistry>,
+        project: Entity<Project>,
+        cx: &mut Context<Self>,
     ) -> Self {
         let mut this = Self {
             _subscriptions: vec![
@@ -143,7 +143,7 @@ impl ContextServerManager {
         this
     }
 
-    fn available_context_servers_changed(&mut self, cx: &mut ModelContext<Self>) {
+    fn available_context_servers_changed(&mut self, cx: &mut Context<Self>) {
         if self.update_servers_task.is_some() {
             self.needs_server_update = true;
         } else {
@@ -183,7 +183,7 @@ impl ContextServerManager {
     pub fn restart_server(
         &mut self,
         id: &Arc<str>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<anyhow::Result<()>> {
         let id = id.clone();
         cx.spawn(|this, mut cx| async move {
@@ -214,7 +214,7 @@ impl ContextServerManager {
             .collect()
     }
 
-    async fn maintain_servers(this: WeakModel<Self>, mut cx: AsyncAppContext) -> Result<()> {
+    async fn maintain_servers(this: WeakEntity<Self>, mut cx: AsyncAppContext) -> Result<()> {
         let mut desired_servers = HashMap::default();
 
         let (registry, project) = this.update(&mut cx, |this, cx| {

crates/context_server/src/registry.rs 🔗

@@ -2,16 +2,19 @@ use std::sync::Arc;
 
 use anyhow::Result;
 use collections::HashMap;
-use gpui::{AppContext, AsyncAppContext, Context, Global, Model, ReadGlobal, Task};
+use gpui::{App, AppContext as _, AsyncAppContext, Entity, Global, ReadGlobal, Task};
 use project::Project;
 
 use crate::ServerCommand;
 
 pub type ContextServerFactory = Arc<
-    dyn Fn(Model<Project>, &AsyncAppContext) -> Task<Result<ServerCommand>> + Send + Sync + 'static,
+    dyn Fn(Entity<Project>, &AsyncAppContext) -> Task<Result<ServerCommand>>
+        + Send
+        + Sync
+        + 'static,
 >;
 
-struct GlobalContextServerFactoryRegistry(Model<ContextServerFactoryRegistry>);
+struct GlobalContextServerFactoryRegistry(Entity<ContextServerFactoryRegistry>);
 
 impl Global for GlobalContextServerFactoryRegistry {}
 
@@ -22,16 +25,16 @@ pub struct ContextServerFactoryRegistry {
 
 impl ContextServerFactoryRegistry {
     /// Returns the global [`ContextServerFactoryRegistry`].
-    pub fn global(cx: &AppContext) -> Model<Self> {
+    pub fn global(cx: &App) -> Entity<Self> {
         GlobalContextServerFactoryRegistry::global(cx).0.clone()
     }
 
     /// Returns the global [`ContextServerFactoryRegistry`].
     ///
     /// Inserts a default [`ContextServerFactoryRegistry`] if one does not yet exist.
-    pub fn default_global(cx: &mut AppContext) -> Model<Self> {
+    pub fn default_global(cx: &mut App) -> Entity<Self> {
         if !cx.has_global::<GlobalContextServerFactoryRegistry>() {
-            let registry = cx.new_model(|_| Self::new());
+            let registry = cx.new(|_| Self::new());
             cx.set_global(GlobalContextServerFactoryRegistry(registry));
         }
         cx.global::<GlobalContextServerFactoryRegistry>().0.clone()

crates/context_server_settings/src/context_server_settings.rs 🔗

@@ -1,14 +1,14 @@
 use std::sync::Arc;
 
 use collections::HashMap;
-use gpui::AppContext;
+use gpui::App;
 use schemars::gen::SchemaGenerator;
 use schemars::schema::{InstanceType, Schema, SchemaObject};
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsSources};
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     ContextServerSettings::register(cx);
 }
 
@@ -54,7 +54,7 @@ impl Settings for ContextServerSettings {
 
     fn load(
         sources: SettingsSources<Self::FileContent>,
-        _: &mut gpui::AppContext,
+        _: &mut gpui::App,
     ) -> anyhow::Result<Self> {
         sources.json_merge()
     }

crates/copilot/src/copilot.rs 🔗

@@ -11,8 +11,8 @@ use collections::{HashMap, HashSet};
 use command_palette_hooks::CommandPaletteFilter;
 use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt};
 use gpui::{
-    actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Global, Model,
-    ModelContext, Task, WeakModel,
+    actions, App, AppContext as _, AsyncAppContext, Context, Entity, EntityId, EventEmitter,
+    Global, Task, WeakEntity,
 };
 use http_client::github::get_release_by_tag_name;
 use http_client::HttpClient;
@@ -58,11 +58,11 @@ pub fn init(
     fs: Arc<dyn Fs>,
     http: Arc<dyn HttpClient>,
     node_runtime: NodeRuntime,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) {
     copilot_chat::init(fs, http.clone(), cx);
 
-    let copilot = cx.new_model({
+    let copilot = cx.new({
         let node_runtime = node_runtime.clone();
         move |cx| Copilot::start(new_server_id, http, node_runtime, cx)
     });
@@ -209,8 +209,8 @@ struct RegisteredBuffer {
 impl RegisteredBuffer {
     fn report_changes(
         &mut self,
-        buffer: &Model<Buffer>,
-        cx: &mut ModelContext<Copilot>,
+        buffer: &Entity<Buffer>,
+        cx: &mut Context<Copilot>,
     ) -> oneshot::Receiver<(i32, BufferSnapshot)> {
         let (done_tx, done_rx) = oneshot::channel();
 
@@ -304,7 +304,7 @@ pub struct Copilot {
     http: Arc<dyn HttpClient>,
     node_runtime: NodeRuntime,
     server: CopilotServer,
-    buffers: HashSet<WeakModel<Buffer>>,
+    buffers: HashSet<WeakEntity<Buffer>>,
     server_id: LanguageServerId,
     _subscription: gpui::Subscription,
 }
@@ -317,17 +317,17 @@ pub enum Event {
 
 impl EventEmitter<Event> for Copilot {}
 
-struct GlobalCopilot(Model<Copilot>);
+struct GlobalCopilot(Entity<Copilot>);
 
 impl Global for GlobalCopilot {}
 
 impl Copilot {
-    pub fn global(cx: &AppContext) -> Option<Model<Self>> {
+    pub fn global(cx: &App) -> Option<Entity<Self>> {
         cx.try_global::<GlobalCopilot>()
             .map(|model| model.0.clone())
     }
 
-    pub fn set_global(copilot: Model<Self>, cx: &mut AppContext) {
+    pub fn set_global(copilot: Entity<Self>, cx: &mut App) {
         cx.set_global(GlobalCopilot(copilot));
     }
 
@@ -335,7 +335,7 @@ impl Copilot {
         new_server_id: LanguageServerId,
         http: Arc<dyn HttpClient>,
         node_runtime: NodeRuntime,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Self {
         let mut this = Self {
             server_id: new_server_id,
@@ -351,10 +351,7 @@ impl Copilot {
         this
     }
 
-    fn shutdown_language_server(
-        &mut self,
-        _cx: &mut ModelContext<Self>,
-    ) -> impl Future<Output = ()> {
+    fn shutdown_language_server(&mut self, _cx: &mut Context<Self>) -> impl Future<Output = ()> {
         let shutdown = match mem::replace(&mut self.server, CopilotServer::Disabled) {
             CopilotServer::Running(server) => Some(Box::pin(async move { server.lsp.shutdown() })),
             _ => None,
@@ -367,7 +364,7 @@ impl Copilot {
         }
     }
 
-    fn enable_or_disable_copilot(&mut self, cx: &mut ModelContext<Self>) {
+    fn enable_or_disable_copilot(&mut self, cx: &mut Context<Self>) {
         let server_id = self.server_id;
         let http = self.http.clone();
         let node_runtime = self.node_runtime.clone();
@@ -390,7 +387,7 @@ impl Copilot {
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn fake(cx: &mut gpui::TestAppContext) -> (Model<Self>, lsp::FakeLanguageServer) {
+    pub fn fake(cx: &mut gpui::TestAppContext) -> (Entity<Self>, lsp::FakeLanguageServer) {
         use lsp::FakeLanguageServer;
         use node_runtime::NodeRuntime;
 
@@ -407,7 +404,7 @@ impl Copilot {
         );
         let http = http_client::FakeHttpClient::create(|_| async { unreachable!() });
         let node_runtime = NodeRuntime::unavailable();
-        let this = cx.new_model(|cx| Self {
+        let this = cx.new(|cx| Self {
             server_id: LanguageServerId(0),
             http: http.clone(),
             node_runtime,
@@ -426,7 +423,7 @@ impl Copilot {
         new_server_id: LanguageServerId,
         http: Arc<dyn HttpClient>,
         node_runtime: NodeRuntime,
-        this: WeakModel<Self>,
+        this: WeakEntity<Self>,
         mut cx: AsyncAppContext,
     ) {
         let start_language_server = async {
@@ -513,7 +510,7 @@ impl Copilot {
         .ok();
     }
 
-    pub fn sign_in(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    pub fn sign_in(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         if let CopilotServer::Running(server) = &mut self.server {
             let task = match &server.sign_in_status {
                 SignInStatus::Authorized { .. } => Task::ready(Ok(())).shared(),
@@ -598,7 +595,7 @@ impl Copilot {
         }
     }
 
-    pub fn sign_out(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    pub fn sign_out(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         self.update_sign_in_status(request::SignInStatus::NotSignedIn, cx);
         if let CopilotServer::Running(RunningCopilotServer { lsp: server, .. }) = &self.server {
             let server = server.clone();
@@ -613,7 +610,7 @@ impl Copilot {
         }
     }
 
-    pub fn reinstall(&mut self, cx: &mut ModelContext<Self>) -> Task<()> {
+    pub fn reinstall(&mut self, cx: &mut Context<Self>) -> Task<()> {
         let start_task = cx
             .spawn({
                 let http = self.http.clone();
@@ -643,7 +640,7 @@ impl Copilot {
         }
     }
 
-    pub fn register_buffer(&mut self, buffer: &Model<Buffer>, cx: &mut ModelContext<Self>) {
+    pub fn register_buffer(&mut self, buffer: &Entity<Buffer>, cx: &mut Context<Self>) {
         let weak_buffer = buffer.downgrade();
         self.buffers.insert(weak_buffer.clone());
 
@@ -699,9 +696,9 @@ impl Copilot {
 
     fn handle_buffer_event(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         event: &language::BufferEvent,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Result<()> {
         if let Ok(server) = self.server.as_running() {
             if let Some(registered_buffer) = server.registered_buffers.get_mut(&buffer.entity_id())
@@ -760,7 +757,7 @@ impl Copilot {
         Ok(())
     }
 
-    fn unregister_buffer(&mut self, buffer: &WeakModel<Buffer>) {
+    fn unregister_buffer(&mut self, buffer: &WeakEntity<Buffer>) {
         if let Ok(server) = self.server.as_running() {
             if let Some(buffer) = server.registered_buffers.remove(&buffer.entity_id()) {
                 server
@@ -777,9 +774,9 @@ impl Copilot {
 
     pub fn completions<T>(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         position: T,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<Vec<Completion>>>
     where
         T: ToPointUtf16,
@@ -789,9 +786,9 @@ impl Copilot {
 
     pub fn completions_cycling<T>(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         position: T,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<Vec<Completion>>>
     where
         T: ToPointUtf16,
@@ -802,7 +799,7 @@ impl Copilot {
     pub fn accept_completion(
         &mut self,
         completion: &Completion,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let server = match self.server.as_authenticated() {
             Ok(server) => server,
@@ -823,7 +820,7 @@ impl Copilot {
     pub fn discard_completions(
         &mut self,
         completions: &[Completion],
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let server = match self.server.as_authenticated() {
             Ok(server) => server,
@@ -846,9 +843,9 @@ impl Copilot {
 
     fn request_completions<R, T>(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         position: T,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<Vec<Completion>>>
     where
         R: 'static
@@ -937,11 +934,7 @@ impl Copilot {
         }
     }
 
-    fn update_sign_in_status(
-        &mut self,
-        lsp_status: request::SignInStatus,
-        cx: &mut ModelContext<Self>,
-    ) {
+    fn update_sign_in_status(&mut self, lsp_status: request::SignInStatus, cx: &mut Context<Self>) {
         self.buffers.retain(|buffer| buffer.is_upgradable());
 
         if let Ok(server) = self.server.as_running() {
@@ -983,7 +976,7 @@ fn id_for_language(language: Option<&Arc<Language>>) -> String {
         .unwrap_or_else(|| "plaintext".to_string())
 }
 
-fn uri_for_buffer(buffer: &Model<Buffer>, cx: &AppContext) -> lsp::Url {
+fn uri_for_buffer(buffer: &Entity<Buffer>, cx: &App) -> lsp::Url {
     if let Some(file) = buffer.read(cx).file().and_then(|file| file.as_local()) {
         lsp::Url::from_file_path(file.abs_path(cx)).unwrap()
     } else {
@@ -1073,7 +1066,7 @@ mod tests {
     async fn test_buffer_management(cx: &mut TestAppContext) {
         let (copilot, mut lsp) = Copilot::fake(cx);
 
-        let buffer_1 = cx.new_model(|cx| Buffer::local("Hello", cx));
+        let buffer_1 = cx.new(|cx| Buffer::local("Hello", cx));
         let buffer_1_uri: lsp::Url = format!("buffer://{}", buffer_1.entity_id().as_u64())
             .parse()
             .unwrap();
@@ -1091,7 +1084,7 @@ mod tests {
             }
         );
 
-        let buffer_2 = cx.new_model(|cx| Buffer::local("Goodbye", cx));
+        let buffer_2 = cx.new(|cx| Buffer::local("Goodbye", cx));
         let buffer_2_uri: lsp::Url = format!("buffer://{}", buffer_2.entity_id().as_u64())
             .parse()
             .unwrap();
@@ -1246,11 +1239,11 @@ mod tests {
             &self.path
         }
 
-        fn full_path(&self, _: &AppContext) -> PathBuf {
+        fn full_path(&self, _: &App) -> PathBuf {
             unimplemented!()
         }
 
-        fn file_name<'a>(&'a self, _: &'a AppContext) -> &'a std::ffi::OsStr {
+        fn file_name<'a>(&'a self, _: &'a App) -> &'a std::ffi::OsStr {
             unimplemented!()
         }
 
@@ -1258,11 +1251,11 @@ mod tests {
             unimplemented!()
         }
 
-        fn to_proto(&self, _: &AppContext) -> rpc::proto::File {
+        fn to_proto(&self, _: &App) -> rpc::proto::File {
             unimplemented!()
         }
 
-        fn worktree_id(&self, _: &AppContext) -> settings::WorktreeId {
+        fn worktree_id(&self, _: &App) -> settings::WorktreeId {
             settings::WorktreeId::from_usize(0)
         }
 
@@ -1272,15 +1265,15 @@ mod tests {
     }
 
     impl language::LocalFile for File {
-        fn abs_path(&self, _: &AppContext) -> PathBuf {
+        fn abs_path(&self, _: &App) -> PathBuf {
             self.abs_path.clone()
         }
 
-        fn load(&self, _: &AppContext) -> Task<Result<String>> {
+        fn load(&self, _: &App) -> Task<Result<String>> {
             unimplemented!()
         }
 
-        fn load_bytes(&self, _cx: &AppContext) -> Task<Result<Vec<u8>>> {
+        fn load_bytes(&self, _cx: &App) -> Task<Result<Vec<u8>>> {
             unimplemented!()
         }
     }

crates/copilot/src/copilot_chat.rs 🔗

@@ -6,7 +6,7 @@ use anyhow::{anyhow, Result};
 use chrono::DateTime;
 use fs::Fs;
 use futures::{io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, StreamExt};
-use gpui::{prelude::*, AppContext, AsyncAppContext, Global};
+use gpui::{prelude::*, App, AsyncAppContext, Global};
 use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest};
 use paths::home_dir;
 use serde::{Deserialize, Serialize};
@@ -181,7 +181,7 @@ impl TryFrom<ApiTokenResponse> for ApiToken {
     }
 }
 
-struct GlobalCopilotChat(gpui::Model<CopilotChat>);
+struct GlobalCopilotChat(gpui::Entity<CopilotChat>);
 
 impl Global for GlobalCopilotChat {}
 
@@ -191,8 +191,8 @@ pub struct CopilotChat {
     client: Arc<dyn HttpClient>,
 }
 
-pub fn init(fs: Arc<dyn Fs>, client: Arc<dyn HttpClient>, cx: &mut AppContext) {
-    let copilot_chat = cx.new_model(|cx| CopilotChat::new(fs, client, cx));
+pub fn init(fs: Arc<dyn Fs>, client: Arc<dyn HttpClient>, cx: &mut App) {
+    let copilot_chat = cx.new(|cx| CopilotChat::new(fs, client, cx));
     cx.set_global(GlobalCopilotChat(copilot_chat));
 }
 
@@ -215,12 +215,12 @@ fn copilot_chat_config_paths() -> [PathBuf; 2] {
 }
 
 impl CopilotChat {
-    pub fn global(cx: &AppContext) -> Option<gpui::Model<Self>> {
+    pub fn global(cx: &App) -> Option<gpui::Entity<Self>> {
         cx.try_global::<GlobalCopilotChat>()
             .map(|model| model.0.clone())
     }
 
-    pub fn new(fs: Arc<dyn Fs>, client: Arc<dyn HttpClient>, cx: &AppContext) -> Self {
+    pub fn new(fs: Arc<dyn Fs>, client: Arc<dyn HttpClient>, cx: &App) -> Self {
         let config_paths = copilot_chat_config_paths();
 
         let resolve_config_path = {

crates/copilot/src/copilot_completion_provider.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{Completion, Copilot};
 use anyhow::Result;
-use gpui::{AppContext, EntityId, Model, ModelContext, Task};
+use gpui::{App, Context, Entity, EntityId, Task};
 use inline_completion::{Direction, InlineCompletion, InlineCompletionProvider};
 use language::{
     language_settings::{all_language_settings, AllLanguageSettings},
@@ -19,11 +19,11 @@ pub struct CopilotCompletionProvider {
     file_extension: Option<String>,
     pending_refresh: Option<Task<Result<()>>>,
     pending_cycling_refresh: Option<Task<Result<()>>>,
-    copilot: Model<Copilot>,
+    copilot: Entity<Copilot>,
 }
 
 impl CopilotCompletionProvider {
-    pub fn new(copilot: Model<Copilot>) -> Self {
+    pub fn new(copilot: Entity<Copilot>) -> Self {
         Self {
             cycled: false,
             buffer_id: None,
@@ -73,9 +73,9 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
 
     fn is_enabled(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         cursor_position: language::Anchor,
-        cx: &AppContext,
+        cx: &App,
     ) -> bool {
         if !self.copilot.read(cx).status().is_authorized() {
             return false;
@@ -90,10 +90,10 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
 
     fn refresh(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         cursor_position: language::Anchor,
         debounce: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let copilot = self.copilot.clone();
         self.pending_refresh = Some(cx.spawn(|this, mut cx| async move {
@@ -139,10 +139,10 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
 
     fn cycle(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         cursor_position: language::Anchor,
         direction: Direction,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if self.cycled {
             match direction {
@@ -194,7 +194,7 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
         }
     }
 
-    fn accept(&mut self, cx: &mut ModelContext<Self>) {
+    fn accept(&mut self, cx: &mut Context<Self>) {
         if let Some(completion) = self.active_completion() {
             self.copilot
                 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
@@ -202,7 +202,7 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
         }
     }
 
-    fn discard(&mut self, cx: &mut ModelContext<Self>) {
+    fn discard(&mut self, cx: &mut Context<Self>) {
         let settings = AllLanguageSettings::get_global(cx);
 
         let copilot_enabled = settings.inline_completions_enabled(None, None, cx);
@@ -220,9 +220,9 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
 
     fn suggest(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         cursor_position: language::Anchor,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<InlineCompletion> {
         let buffer_id = buffer.entity_id();
         let buffer = buffer.read(cx);
@@ -280,7 +280,7 @@ mod tests {
     };
     use fs::FakeFs;
     use futures::StreamExt;
-    use gpui::{BackgroundExecutor, Context, TestAppContext, UpdateGlobal};
+    use gpui::{AppContext as _, BackgroundExecutor, TestAppContext, UpdateGlobal};
     use indoc::indoc;
     use language::{
         language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
@@ -309,9 +309,9 @@ mod tests {
             cx,
         )
         .await;
-        let copilot_provider = cx.new_model(|_| CopilotCompletionProvider::new(copilot));
-        cx.update_editor(|editor, cx| {
-            editor.set_inline_completion_provider(Some(copilot_provider), cx)
+        let copilot_provider = cx.new(|_| CopilotCompletionProvider::new(copilot));
+        cx.update_editor(|editor, window, cx| {
+            editor.set_inline_completion_provider(Some(copilot_provider), window, cx)
         });
 
         cx.set_state(indoc! {"
@@ -339,7 +339,7 @@ mod tests {
             vec![],
         );
         executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, window, cx| {
             assert!(editor.context_menu_visible());
             assert!(!editor.context_menu_contains_inline_completion());
             assert!(!editor.has_active_inline_completion());
@@ -350,7 +350,7 @@ mod tests {
             // Confirming a non-copilot completion inserts it and hides the context menu, without showing
             // the copilot suggestion afterwards.
             editor
-                .confirm_completion(&Default::default(), cx)
+                .confirm_completion(&Default::default(), window, cx)
                 .unwrap()
                 .detach();
             assert!(!editor.context_menu_visible());
@@ -386,7 +386,7 @@ mod tests {
             vec![],
         );
         executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, _, cx| {
             assert!(!editor.context_menu_visible());
             assert!(editor.has_active_inline_completion());
             // Since only the copilot is available, it's shown inline
@@ -397,7 +397,7 @@ mod tests {
         // Ensure existing inline completion is interpolated when inserting again.
         cx.simulate_keystroke("c");
         executor.run_until_parked();
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, _, cx| {
             assert!(!editor.context_menu_visible());
             assert!(!editor.context_menu_contains_inline_completion());
             assert!(editor.has_active_inline_completion());
@@ -416,7 +416,7 @@ mod tests {
             vec![],
         );
         executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, window, cx| {
             assert!(!editor.context_menu_visible());
             assert!(editor.has_active_inline_completion());
             assert!(!editor.context_menu_contains_inline_completion());
@@ -424,19 +424,19 @@ mod tests {
             assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
 
             // Canceling should remove the active Copilot suggestion.
-            editor.cancel(&Default::default(), cx);
+            editor.cancel(&Default::default(), window, cx);
             assert!(!editor.has_active_inline_completion());
             assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
             assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
 
             // After canceling, tabbing shouldn't insert the previously shown suggestion.
-            editor.tab(&Default::default(), cx);
+            editor.tab(&Default::default(), window, cx);
             assert!(!editor.has_active_inline_completion());
             assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
             assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
 
             // When undoing the previously active suggestion is shown again.
-            editor.undo(&Default::default(), cx);
+            editor.undo(&Default::default(), window, cx);
             assert!(editor.has_active_inline_completion());
             assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
             assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
@@ -444,25 +444,25 @@ mod tests {
 
         // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
         cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, window, cx| {
             assert!(editor.has_active_inline_completion());
             assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
             assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
 
             // AcceptInlineCompletion when there is an active suggestion inserts it.
-            editor.accept_inline_completion(&Default::default(), cx);
+            editor.accept_inline_completion(&Default::default(), window, cx);
             assert!(!editor.has_active_inline_completion());
             assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
             assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
 
             // When undoing the previously active suggestion is shown again.
-            editor.undo(&Default::default(), cx);
+            editor.undo(&Default::default(), window, cx);
             assert!(editor.has_active_inline_completion());
             assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
             assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
 
             // Hide suggestion.
-            editor.cancel(&Default::default(), cx);
+            editor.cancel(&Default::default(), window, cx);
             assert!(!editor.has_active_inline_completion());
             assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
             assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
@@ -471,16 +471,16 @@ mod tests {
         // If an edit occurs outside of this editor but no suggestion is being shown,
         // we won't make it visible.
         cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, _, cx| {
             assert!(!editor.has_active_inline_completion());
             assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
             assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
         });
 
         // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
-        cx.update_editor(|editor, cx| {
-            editor.set_text("fn foo() {\n  \n}", cx);
-            editor.change_selections(None, cx, |s| {
+        cx.update_editor(|editor, window, cx| {
+            editor.set_text("fn foo() {\n  \n}", window, cx);
+            editor.change_selections(None, window, cx, |s| {
                 s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
             });
         });
@@ -494,21 +494,23 @@ mod tests {
             vec![],
         );
 
-        cx.update_editor(|editor, cx| editor.next_inline_completion(&Default::default(), cx));
+        cx.update_editor(|editor, window, cx| {
+            editor.next_inline_completion(&Default::default(), window, cx)
+        });
         executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, window, cx| {
             assert!(editor.has_active_inline_completion());
             assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
             assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
 
             // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
-            editor.tab(&Default::default(), cx);
+            editor.tab(&Default::default(), window, cx);
             assert!(editor.has_active_inline_completion());
             assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
             assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
 
             // Using AcceptInlineCompletion again accepts the suggestion.
-            editor.accept_inline_completion(&Default::default(), cx);
+            editor.accept_inline_completion(&Default::default(), window, cx);
             assert!(!editor.has_active_inline_completion());
             assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
             assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
@@ -535,9 +537,9 @@ mod tests {
             cx,
         )
         .await;
-        let copilot_provider = cx.new_model(|_| CopilotCompletionProvider::new(copilot));
-        cx.update_editor(|editor, cx| {
-            editor.set_inline_completion_provider(Some(copilot_provider), cx)
+        let copilot_provider = cx.new(|_| CopilotCompletionProvider::new(copilot));
+        cx.update_editor(|editor, window, cx| {
+            editor.set_inline_completion_provider(Some(copilot_provider), window, cx)
         });
 
         // Setup the editor with a completion request.
@@ -566,17 +568,17 @@ mod tests {
             vec![],
         );
         executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, window, cx| {
             assert!(editor.has_active_inline_completion());
 
             // Accepting the first word of the suggestion should only accept the first word and still show the rest.
-            editor.accept_partial_inline_completion(&Default::default(), cx);
+            editor.accept_partial_inline_completion(&Default::default(), window, cx);
             assert!(editor.has_active_inline_completion());
             assert_eq!(editor.text(cx), "one.copilot\ntwo\nthree\n");
             assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
 
             // Accepting next word should accept the non-word and copilot suggestion should be gone
-            editor.accept_partial_inline_completion(&Default::default(), cx);
+            editor.accept_partial_inline_completion(&Default::default(), window, cx);
             assert!(!editor.has_active_inline_completion());
             assert_eq!(editor.text(cx), "one.copilot1\ntwo\nthree\n");
             assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
@@ -608,11 +610,11 @@ mod tests {
             vec![],
         );
         executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, window, cx| {
             assert!(editor.has_active_inline_completion());
 
             // Accepting the first word (non-word) of the suggestion should only accept the first word and still show the rest.
-            editor.accept_partial_inline_completion(&Default::default(), cx);
+            editor.accept_partial_inline_completion(&Default::default(), window, cx);
             assert!(editor.has_active_inline_completion());
             assert_eq!(editor.text(cx), "one.123. \ntwo\nthree\n");
             assert_eq!(
@@ -621,7 +623,7 @@ mod tests {
             );
 
             // Accepting next word should accept the next word and copilot suggestion should still exist
-            editor.accept_partial_inline_completion(&Default::default(), cx);
+            editor.accept_partial_inline_completion(&Default::default(), window, cx);
             assert!(editor.has_active_inline_completion());
             assert_eq!(editor.text(cx), "one.123. copilot\ntwo\nthree\n");
             assert_eq!(
@@ -630,7 +632,7 @@ mod tests {
             );
 
             // Accepting the whitespace should accept the non-word/whitespaces with newline and copilot suggestion should be gone
-            editor.accept_partial_inline_completion(&Default::default(), cx);
+            editor.accept_partial_inline_completion(&Default::default(), window, cx);
             assert!(!editor.has_active_inline_completion());
             assert_eq!(editor.text(cx), "one.123. copilot\n 456\ntwo\nthree\n");
             assert_eq!(
@@ -659,9 +661,9 @@ mod tests {
             cx,
         )
         .await;
-        let copilot_provider = cx.new_model(|_| CopilotCompletionProvider::new(copilot));
-        cx.update_editor(|editor, cx| {
-            editor.set_inline_completion_provider(Some(copilot_provider), cx)
+        let copilot_provider = cx.new(|_| CopilotCompletionProvider::new(copilot));
+        cx.update_editor(|editor, window, cx| {
+            editor.set_inline_completion_provider(Some(copilot_provider), window, cx)
         });
 
         cx.set_state(indoc! {"
@@ -679,31 +681,33 @@ mod tests {
             }],
             vec![],
         );
-        cx.update_editor(|editor, cx| editor.next_inline_completion(&Default::default(), cx));
+        cx.update_editor(|editor, window, cx| {
+            editor.next_inline_completion(&Default::default(), window, cx)
+        });
         executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, window, cx| {
             assert!(editor.has_active_inline_completion());
             assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
             assert_eq!(editor.text(cx), "one\ntw\nthree\n");
 
-            editor.backspace(&Default::default(), cx);
+            editor.backspace(&Default::default(), window, cx);
             assert!(editor.has_active_inline_completion());
             assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
             assert_eq!(editor.text(cx), "one\nt\nthree\n");
 
-            editor.backspace(&Default::default(), cx);
+            editor.backspace(&Default::default(), window, cx);
             assert!(editor.has_active_inline_completion());
             assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
             assert_eq!(editor.text(cx), "one\n\nthree\n");
 
             // Deleting across the original suggestion range invalidates it.
-            editor.backspace(&Default::default(), cx);
+            editor.backspace(&Default::default(), window, cx);
             assert!(!editor.has_active_inline_completion());
             assert_eq!(editor.display_text(cx), "one\nthree\n");
             assert_eq!(editor.text(cx), "one\nthree\n");
 
             // Undoing the deletion restores the suggestion.
-            editor.undo(&Default::default(), cx);
+            editor.undo(&Default::default(), window, cx);
             assert!(editor.has_active_inline_completion());
             assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
             assert_eq!(editor.text(cx), "one\n\nthree\n");
@@ -716,9 +720,9 @@ mod tests {
 
         let (copilot, copilot_lsp) = Copilot::fake(cx);
 
-        let buffer_1 = cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx));
-        let buffer_2 = cx.new_model(|cx| Buffer::local("c = 3\nd = 4\n", cx));
-        let multibuffer = cx.new_model(|cx| {
+        let buffer_1 = cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx));
+        let buffer_2 = cx.new(|cx| Buffer::local("c = 3\nd = 4\n", cx));
+        let multibuffer = cx.new(|cx| {
             let mut multibuffer = MultiBuffer::new(language::Capability::ReadWrite);
             multibuffer.push_excerpts(
                 buffer_1.clone(),
@@ -738,12 +742,18 @@ mod tests {
             );
             multibuffer
         });
-        let editor = cx.add_window(|cx| Editor::for_multibuffer(multibuffer, None, true, cx));
-        editor.update(cx, |editor, cx| editor.focus(cx)).unwrap();
-        let copilot_provider = cx.new_model(|_| CopilotCompletionProvider::new(copilot));
+        let editor = cx
+            .add_window(|window, cx| Editor::for_multibuffer(multibuffer, None, true, window, cx));
+        editor
+            .update(cx, |editor, window, cx| {
+                use gpui::Focusable;
+                window.focus(&editor.focus_handle(cx));
+            })
+            .unwrap();
+        let copilot_provider = cx.new(|_| CopilotCompletionProvider::new(copilot));
         editor
-            .update(cx, |editor, cx| {
-                editor.set_inline_completion_provider(Some(copilot_provider), cx)
+            .update(cx, |editor, window, cx| {
+                editor.set_inline_completion_provider(Some(copilot_provider), window, cx)
             })
             .unwrap();
 
@@ -756,15 +766,15 @@ mod tests {
             }],
             vec![],
         );
-        _ = editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, window, cx| {
             // Ensure copilot suggestions are shown for the first excerpt.
-            editor.change_selections(None, cx, |s| {
+            editor.change_selections(None, window, cx, |s| {
                 s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
             });
-            editor.next_inline_completion(&Default::default(), cx);
+            editor.next_inline_completion(&Default::default(), window, cx);
         });
         executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-        _ = editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, _, cx| {
             assert!(editor.has_active_inline_completion());
             assert_eq!(
                 editor.display_text(cx),
@@ -782,9 +792,9 @@ mod tests {
             }],
             vec![],
         );
-        _ = editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, window, cx| {
             // Move to another excerpt, ensuring the suggestion gets cleared.
-            editor.change_selections(None, cx, |s| {
+            editor.change_selections(None, window, cx, |s| {
                 s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
             });
             assert!(!editor.has_active_inline_completion());
@@ -795,7 +805,7 @@ mod tests {
             assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
 
             // Type a character, ensuring we don't even try to interpolate the previous suggestion.
-            editor.handle_input(" ", cx);
+            editor.handle_input(" ", window, cx);
             assert!(!editor.has_active_inline_completion());
             assert_eq!(
                 editor.display_text(cx),
@@ -806,7 +816,7 @@ mod tests {
 
         // Ensure the new suggestion is displayed when the debounce timeout expires.
         executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-        _ = editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, _, cx| {
             assert!(editor.has_active_inline_completion());
             assert_eq!(
                 editor.display_text(cx),
@@ -835,9 +845,9 @@ mod tests {
             cx,
         )
         .await;
-        let copilot_provider = cx.new_model(|_| CopilotCompletionProvider::new(copilot));
-        cx.update_editor(|editor, cx| {
-            editor.set_inline_completion_provider(Some(copilot_provider), cx)
+        let copilot_provider = cx.new(|_| CopilotCompletionProvider::new(copilot));
+        cx.update_editor(|editor, window, cx| {
+            editor.set_inline_completion_provider(Some(copilot_provider), window, cx)
         });
 
         cx.set_state(indoc! {"
@@ -864,9 +874,11 @@ mod tests {
             }],
             vec![],
         );
-        cx.update_editor(|editor, cx| editor.next_inline_completion(&Default::default(), cx));
+        cx.update_editor(|editor, window, cx| {
+            editor.next_inline_completion(&Default::default(), window, cx)
+        });
         executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, _, cx| {
             assert!(!editor.context_menu_visible());
             assert!(editor.has_active_inline_completion());
             assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
@@ -893,7 +905,7 @@ mod tests {
             vec![],
         );
         executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, _, cx| {
             assert!(!editor.context_menu_visible());
             assert!(editor.has_active_inline_completion());
             assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
@@ -920,7 +932,7 @@ mod tests {
             vec![],
         );
         executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, _, cx| {
             assert!(editor.context_menu_visible());
             assert!(!editor.context_menu_contains_inline_completion());
             assert!(!editor.has_active_inline_completion(),);
@@ -963,7 +975,7 @@ mod tests {
             .await
             .unwrap();
 
-        let multibuffer = cx.new_model(|cx| {
+        let multibuffer = cx.new(|cx| {
             let mut multibuffer = MultiBuffer::new(language::Capability::ReadWrite);
             multibuffer.push_excerpts(
                 private_buffer.clone(),
@@ -983,12 +995,18 @@ mod tests {
             );
             multibuffer
         });
-        let editor = cx.add_window(|cx| Editor::for_multibuffer(multibuffer, None, true, cx));
-        editor.update(cx, |editor, cx| editor.focus(cx)).unwrap();
-        let copilot_provider = cx.new_model(|_| CopilotCompletionProvider::new(copilot));
+        let editor = cx
+            .add_window(|window, cx| Editor::for_multibuffer(multibuffer, None, true, window, cx));
+        editor
+            .update(cx, |editor, window, cx| {
+                use gpui::Focusable;
+                window.focus(&editor.focus_handle(cx))
+            })
+            .unwrap();
+        let copilot_provider = cx.new(|_| CopilotCompletionProvider::new(copilot));
         editor
-            .update(cx, |editor, cx| {
-                editor.set_inline_completion_provider(Some(copilot_provider), cx)
+            .update(cx, |editor, window, cx| {
+                editor.set_inline_completion_provider(Some(copilot_provider), window, cx)
             })
             .unwrap();
 
@@ -1008,21 +1026,21 @@ mod tests {
                 },
             );
 
-        _ = editor.update(cx, |editor, cx| {
-            editor.change_selections(None, cx, |selections| {
+        _ = editor.update(cx, |editor, window, cx| {
+            editor.change_selections(None, window, cx, |selections| {
                 selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
             });
-            editor.refresh_inline_completion(true, false, cx);
+            editor.refresh_inline_completion(true, false, window, cx);
         });
 
         executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
         assert!(copilot_requests.try_next().is_err());
 
-        _ = editor.update(cx, |editor, cx| {
-            editor.change_selections(None, cx, |s| {
+        _ = editor.update(cx, |editor, window, cx| {
+            editor.change_selections(None, window, cx, |s| {
                 s.select_ranges([Point::new(5, 0)..Point::new(5, 0)])
             });
-            editor.refresh_inline_completion(true, false, cx);
+            editor.refresh_inline_completion(true, false, window, cx);
         });
 
         executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);

crates/copilot/src/sign_in.rs 🔗

@@ -1,8 +1,8 @@
 use crate::{request::PromptUserDeviceFlow, Copilot, Status};
 use gpui::{
-    div, AppContext, ClipboardItem, DismissEvent, Element, EventEmitter, FocusHandle,
-    FocusableView, InteractiveElement, IntoElement, Model, MouseDownEvent, ParentElement, Render,
-    Styled, Subscription, ViewContext,
+    div, App, ClipboardItem, Context, DismissEvent, Element, Entity, EventEmitter, FocusHandle,
+    Focusable, InteractiveElement, IntoElement, MouseDownEvent, ParentElement, Render, Styled,
+    Subscription, Window,
 };
 use ui::{prelude::*, Button, Label, Vector, VectorName};
 use util::ResultExt as _;
@@ -13,21 +13,21 @@ const COPILOT_SIGN_UP_URL: &str = "https://github.com/features/copilot";
 
 struct CopilotStartingToast;
 
-pub fn initiate_sign_in(cx: &mut WindowContext) {
+pub fn initiate_sign_in(window: &mut Window, cx: &mut App) {
     let Some(copilot) = Copilot::global(cx) else {
         return;
     };
     let status = copilot.read(cx).status();
-    let Some(workspace) = cx.window_handle().downcast::<Workspace>() else {
+    let Some(workspace) = window.window_handle().downcast::<Workspace>() else {
         return;
     };
     match status {
         Status::Starting { task } => {
-            let Some(workspace) = cx.window_handle().downcast::<Workspace>() else {
+            let Some(workspace) = window.window_handle().downcast::<Workspace>() else {
                 return;
             };
 
-            let Ok(workspace) = workspace.update(cx, |workspace, cx| {
+            let Ok(workspace) = workspace.update(cx, |workspace, _window, cx| {
                 workspace.show_toast(
                     Toast::new(
                         NotificationId::unique::<CopilotStartingToast>(),
@@ -70,8 +70,10 @@ pub fn initiate_sign_in(cx: &mut WindowContext) {
         _ => {
             copilot.update(cx, |this, cx| this.sign_in(cx)).detach();
             workspace
-                .update(cx, |this, cx| {
-                    this.toggle_modal(cx, |cx| CopilotCodeVerification::new(&copilot, cx));
+                .update(cx, |this, window, cx| {
+                    this.toggle_modal(window, cx, |_, cx| {
+                        CopilotCodeVerification::new(&copilot, cx)
+                    });
                 })
                 .ok();
         }
@@ -85,8 +87,8 @@ pub struct CopilotCodeVerification {
     _subscription: Subscription,
 }
 
-impl FocusableView for CopilotCodeVerification {
-    fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
+impl Focusable for CopilotCodeVerification {
+    fn focus_handle(&self, _: &App) -> gpui::FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -95,7 +97,7 @@ impl EventEmitter<DismissEvent> for CopilotCodeVerification {}
 impl ModalView for CopilotCodeVerification {}
 
 impl CopilotCodeVerification {
-    pub fn new(copilot: &Model<Copilot>, cx: &mut ViewContext<Self>) -> Self {
+    pub fn new(copilot: &Entity<Copilot>, cx: &mut Context<Self>) -> Self {
         let status = copilot.read(cx).status();
         Self {
             status,
@@ -113,15 +115,12 @@ impl CopilotCodeVerification {
         }
     }
 
-    pub fn set_status(&mut self, status: Status, cx: &mut ViewContext<Self>) {
+    pub fn set_status(&mut self, status: Status, cx: &mut Context<Self>) {
         self.status = status;
         cx.notify();
     }
 
-    fn render_device_code(
-        data: &PromptUserDeviceFlow,
-        cx: &mut ViewContext<Self>,
-    ) -> impl IntoElement {
+    fn render_device_code(data: &PromptUserDeviceFlow, cx: &mut Context<Self>) -> impl IntoElement {
         let copied = cx
             .read_from_clipboard()
             .map(|item| item.text().as_ref() == Some(&data.user_code))
@@ -136,9 +135,9 @@ impl CopilotCodeVerification {
             .justify_between()
             .on_mouse_down(gpui::MouseButton::Left, {
                 let user_code = data.user_code.clone();
-                move |_, cx| {
+                move |_, window, cx| {
                     cx.write_to_clipboard(ClipboardItem::new_string(user_code.clone()));
-                    cx.refresh();
+                    window.refresh();
                 }
             })
             .child(div().flex_1().child(Label::new(data.user_code.clone())))
@@ -152,7 +151,8 @@ impl CopilotCodeVerification {
     fn render_prompting_modal(
         connect_clicked: bool,
         data: &PromptUserDeviceFlow,
-        cx: &mut ViewContext<Self>,
+
+        cx: &mut Context<Self>,
     ) -> impl Element {
         let connect_button_label = if connect_clicked {
             "Waiting for connection..."
@@ -177,7 +177,7 @@ impl CopilotCodeVerification {
                 Button::new("connect-button", connect_button_label)
                     .on_click({
                         let verification_uri = data.verification_uri.clone();
-                        cx.listener(move |this, _, cx| {
+                        cx.listener(move |this, _, _window, cx| {
                             cx.open_url(&verification_uri);
                             this.connect_clicked = true;
                         })
@@ -188,10 +188,10 @@ impl CopilotCodeVerification {
             .child(
                 Button::new("copilot-enable-cancel-button", "Cancel")
                     .full_width()
-                    .on_click(cx.listener(|_, _, cx| cx.emit(DismissEvent))),
+                    .on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))),
             )
     }
-    fn render_enabled_modal(cx: &mut ViewContext<Self>) -> impl Element {
+    fn render_enabled_modal(cx: &mut Context<Self>) -> impl Element {
         v_flex()
             .gap_2()
             .child(Headline::new("Copilot Enabled!").size(HeadlineSize::Large))
@@ -201,11 +201,11 @@ impl CopilotCodeVerification {
             .child(
                 Button::new("copilot-enabled-done-button", "Done")
                     .full_width()
-                    .on_click(cx.listener(|_, _, cx| cx.emit(DismissEvent))),
+                    .on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))),
             )
     }
 
-    fn render_unauthorized_modal(cx: &mut ViewContext<Self>) -> impl Element {
+    fn render_unauthorized_modal(cx: &mut Context<Self>) -> impl Element {
         v_flex()
             .child(Headline::new("You must have an active GitHub Copilot subscription.").size(HeadlineSize::Large))
 
@@ -215,12 +215,12 @@ impl CopilotCodeVerification {
             .child(
                 Button::new("copilot-subscribe-button", "Subscribe on GitHub")
                     .full_width()
-                    .on_click(|_, cx| cx.open_url(COPILOT_SIGN_UP_URL)),
+                    .on_click(|_, _, cx| cx.open_url(COPILOT_SIGN_UP_URL)),
             )
             .child(
                 Button::new("copilot-subscribe-cancel-button", "Cancel")
                     .full_width()
-                    .on_click(cx.listener(|_, _, cx| cx.emit(DismissEvent))),
+                    .on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))),
             )
     }
 
@@ -232,7 +232,7 @@ impl CopilotCodeVerification {
 }
 
 impl Render for CopilotCodeVerification {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let prompt = match &self.status {
             Status::SigningIn {
                 prompt: Some(prompt),
@@ -260,11 +260,11 @@ impl Render for CopilotCodeVerification {
             .items_center()
             .p_4()
             .gap_2()
-            .on_action(cx.listener(|_, _: &menu::Cancel, cx| {
+            .on_action(cx.listener(|_, _: &menu::Cancel, _, cx| {
                 cx.emit(DismissEvent);
             }))
-            .on_any_mouse_down(cx.listener(|this, _: &MouseDownEvent, cx| {
-                cx.focus(&this.focus_handle);
+            .on_any_mouse_down(cx.listener(|this, _: &MouseDownEvent, window, _| {
+                window.focus(&this.focus_handle);
             }))
             .child(
                 Vector::new(VectorName::ZedXCopilot, rems(8.), rems(4.))

crates/db/src/db.rs 🔗

@@ -3,8 +3,8 @@ pub mod query;
 
 // Re-export
 pub use anyhow;
-use anyhow::Context;
-use gpui::AppContext;
+use anyhow::Context as _;
+use gpui::App;
 pub use indoc::indoc;
 pub use paths::database_dir;
 pub use smol;
@@ -188,7 +188,7 @@ macro_rules! define_connection {
     };
 }
 
-pub fn write_and_log<F>(cx: &AppContext, db_write: impl FnOnce() -> F + Send + 'static)
+pub fn write_and_log<F>(cx: &App, db_write: impl FnOnce() -> F + Send + 'static)
 where
     F: Future<Output = anyhow::Result<()>> + Send,
 {

crates/diagnostics/src/diagnostics.rs 🔗

@@ -15,10 +15,9 @@ use editor::{
     Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
 };
 use gpui::{
-    actions, div, svg, AnyElement, AnyView, AppContext, Context, EventEmitter, FocusHandle,
-    FocusableView, Global, HighlightStyle, InteractiveElement, IntoElement, Model, ParentElement,
-    Render, SharedString, Styled, StyledText, Subscription, Task, View, ViewContext, VisualContext,
-    WeakView, WindowContext,
+    actions, div, svg, AnyElement, AnyView, App, Context, Entity, EventEmitter, FocusHandle,
+    Focusable, Global, HighlightStyle, InteractiveElement, IntoElement, ParentElement, Render,
+    SharedString, Styled, StyledText, Subscription, Task, WeakEntity, Window,
 };
 use language::{
     Bias, Buffer, BufferRow, BufferSnapshot, Diagnostic, DiagnosticEntry, DiagnosticSeverity,
@@ -52,19 +51,18 @@ actions!(diagnostics, [Deploy, ToggleWarnings]);
 struct IncludeWarnings(bool);
 impl Global for IncludeWarnings {}
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     ProjectDiagnosticsSettings::register(cx);
-    cx.observe_new_views(ProjectDiagnosticsEditor::register)
-        .detach();
+    cx.observe_new(ProjectDiagnosticsEditor::register).detach();
 }
 
 struct ProjectDiagnosticsEditor {
-    project: Model<Project>,
-    workspace: WeakView<Workspace>,
+    project: Entity<Project>,
+    workspace: WeakEntity<Workspace>,
     focus_handle: FocusHandle,
-    editor: View<Editor>,
+    editor: Entity<Editor>,
     summary: DiagnosticSummary,
-    excerpts: Model<MultiBuffer>,
+    excerpts: Entity<MultiBuffer>,
     path_states: Vec<PathState>,
     paths_to_update: BTreeSet<(ProjectPath, Option<LanguageServerId>)>,
     include_warnings: bool,
@@ -92,7 +90,7 @@ impl EventEmitter<EditorEvent> for ProjectDiagnosticsEditor {}
 const DIAGNOSTICS_UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
 
 impl Render for ProjectDiagnosticsEditor {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let child = if self.path_states.is_empty() {
             div()
                 .key_context("EmptyPane")
@@ -116,25 +114,30 @@ impl Render for ProjectDiagnosticsEditor {
 }
 
 impl ProjectDiagnosticsEditor {
-    fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+    fn register(
+        workspace: &mut Workspace,
+        _window: Option<&mut Window>,
+        _: &mut Context<Workspace>,
+    ) {
         workspace.register_action(Self::deploy);
     }
 
     fn new_with_context(
         context: u32,
         include_warnings: bool,
-        project_handle: Model<Project>,
-        workspace: WeakView<Workspace>,
-        cx: &mut ViewContext<Self>,
+        project_handle: Entity<Project>,
+        workspace: WeakEntity<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let project_event_subscription =
-            cx.subscribe(&project_handle, |this, project, event, cx| match event {
+            cx.subscribe_in(&project_handle, window, |this, project, event, window, cx| match event {
                 project::Event::DiskBasedDiagnosticsStarted { .. } => {
                     cx.notify();
                 }
                 project::Event::DiskBasedDiagnosticsFinished { language_server_id } => {
                     log::debug!("disk based diagnostics finished for server {language_server_id}");
-                    this.update_stale_excerpts(cx);
+                    this.update_stale_excerpts(window, cx);
                 }
                 project::Event::DiagnosticsUpdated {
                     language_server_id,
@@ -145,45 +148,58 @@ impl ProjectDiagnosticsEditor {
                     this.summary = project.read(cx).diagnostic_summary(false, cx);
                     cx.emit(EditorEvent::TitleChanged);
 
-                    if this.editor.focus_handle(cx).contains_focused(cx) || this.focus_handle.contains_focused(cx) {
+                    if this.editor.focus_handle(cx).contains_focused(window, cx) || this.focus_handle.contains_focused(window, cx) {
                         log::debug!("diagnostics updated for server {language_server_id}, path {path:?}. recording change");
                     } else {
                         log::debug!("diagnostics updated for server {language_server_id}, path {path:?}. updating excerpts");
-                        this.update_stale_excerpts(cx);
+                        this.update_stale_excerpts(window, cx);
                     }
                 }
                 _ => {}
             });
 
         let focus_handle = cx.focus_handle();
-        cx.on_focus_in(&focus_handle, |this, cx| this.focus_in(cx))
-            .detach();
-        cx.on_focus_out(&focus_handle, |this, _event, cx| this.focus_out(cx))
-            .detach();
-
-        let excerpts = cx.new_model(|cx| MultiBuffer::new(project_handle.read(cx).capability()));
-        let editor = cx.new_view(|cx| {
-            let mut editor =
-                Editor::for_multibuffer(excerpts.clone(), Some(project_handle.clone()), true, cx);
+        cx.on_focus_in(&focus_handle, window, |this, window, cx| {
+            this.focus_in(window, cx)
+        })
+        .detach();
+        cx.on_focus_out(&focus_handle, window, |this, _event, window, cx| {
+            this.focus_out(window, cx)
+        })
+        .detach();
+
+        let excerpts = cx.new(|cx| MultiBuffer::new(project_handle.read(cx).capability()));
+        let editor = cx.new(|cx| {
+            let mut editor = Editor::for_multibuffer(
+                excerpts.clone(),
+                Some(project_handle.clone()),
+                true,
+                window,
+                cx,
+            );
             editor.set_vertical_scroll_margin(5, cx);
             editor
         });
-        cx.subscribe(&editor, |this, _editor, event: &EditorEvent, cx| {
-            cx.emit(event.clone());
-            match event {
-                EditorEvent::Focused => {
-                    if this.path_states.is_empty() {
-                        cx.focus(&this.focus_handle);
+        cx.subscribe_in(
+            &editor,
+            window,
+            |this, _editor, event: &EditorEvent, window, cx| {
+                cx.emit(event.clone());
+                match event {
+                    EditorEvent::Focused => {
+                        if this.path_states.is_empty() {
+                            window.focus(&this.focus_handle);
+                        }
                     }
+                    EditorEvent::Blurred => this.update_stale_excerpts(window, cx),
+                    _ => {}
                 }
-                EditorEvent::Blurred => this.update_stale_excerpts(cx),
-                _ => {}
-            }
-        })
+            },
+        )
         .detach();
-        cx.observe_global::<IncludeWarnings>(|this, cx| {
+        cx.observe_global_in::<IncludeWarnings>(window, |this, window, cx| {
             this.include_warnings = cx.global::<IncludeWarnings>().0;
-            this.update_all_excerpts(cx);
+            this.update_all_excerpts(window, cx);
         })
         .detach();
 
@@ -202,16 +218,16 @@ impl ProjectDiagnosticsEditor {
             update_excerpts_task: None,
             _subscription: project_event_subscription,
         };
-        this.update_all_excerpts(cx);
+        this.update_all_excerpts(window, cx);
         this
     }
 
-    fn update_stale_excerpts(&mut self, cx: &mut ViewContext<Self>) {
+    fn update_stale_excerpts(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if self.update_excerpts_task.is_some() {
             return;
         }
         let project_handle = self.project.clone();
-        self.update_excerpts_task = Some(cx.spawn(|this, mut cx| async move {
+        self.update_excerpts_task = Some(cx.spawn_in(window, |this, mut cx| async move {
             cx.background_executor()
                 .timer(DIAGNOSTICS_UPDATE_DEBOUNCE)
                 .await;
@@ -232,8 +248,8 @@ impl ProjectDiagnosticsEditor {
                     .await
                     .log_err()
                 {
-                    this.update(&mut cx, |this, cx| {
-                        this.update_excerpts(path, language_server_id, buffer, cx);
+                    this.update_in(&mut cx, |this, window, cx| {
+                        this.update_excerpts(path, language_server_id, buffer, window, cx);
                     })?;
                 }
             }
@@ -242,65 +258,74 @@ impl ProjectDiagnosticsEditor {
     }
 
     fn new(
-        project_handle: Model<Project>,
+        project_handle: Entity<Project>,
         include_warnings: bool,
-        workspace: WeakView<Workspace>,
-        cx: &mut ViewContext<Self>,
+        workspace: WeakEntity<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         Self::new_with_context(
             editor::DEFAULT_MULTIBUFFER_CONTEXT,
             include_warnings,
             project_handle,
             workspace,
+            window,
             cx,
         )
     }
 
-    fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext<Workspace>) {
+    fn deploy(
+        workspace: &mut Workspace,
+        _: &Deploy,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) {
         if let Some(existing) = workspace.item_of_type::<ProjectDiagnosticsEditor>(cx) {
-            workspace.activate_item(&existing, true, true, cx);
+            workspace.activate_item(&existing, true, true, window, cx);
         } else {
-            let workspace_handle = cx.view().downgrade();
+            let workspace_handle = cx.model().downgrade();
 
             let include_warnings = match cx.try_global::<IncludeWarnings>() {
                 Some(include_warnings) => include_warnings.0,
                 None => ProjectDiagnosticsSettings::get_global(cx).include_warnings,
             };
 
-            let diagnostics = cx.new_view(|cx| {
+            let diagnostics = cx.new(|cx| {
                 ProjectDiagnosticsEditor::new(
                     workspace.project().clone(),
                     include_warnings,
                     workspace_handle,
+                    window,
                     cx,
                 )
             });
-            workspace.add_item_to_active_pane(Box::new(diagnostics), None, true, cx);
+            workspace.add_item_to_active_pane(Box::new(diagnostics), None, true, window, cx);
         }
     }
 
-    fn toggle_warnings(&mut self, _: &ToggleWarnings, cx: &mut ViewContext<Self>) {
+    fn toggle_warnings(&mut self, _: &ToggleWarnings, window: &mut Window, cx: &mut Context<Self>) {
         self.include_warnings = !self.include_warnings;
         cx.set_global(IncludeWarnings(self.include_warnings));
-        self.update_all_excerpts(cx);
+        self.update_all_excerpts(window, cx);
         cx.notify();
     }
 
-    fn focus_in(&mut self, cx: &mut ViewContext<Self>) {
-        if self.focus_handle.is_focused(cx) && !self.path_states.is_empty() {
-            self.editor.focus_handle(cx).focus(cx)
+    fn focus_in(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        if self.focus_handle.is_focused(window) && !self.path_states.is_empty() {
+            self.editor.focus_handle(cx).focus(window)
         }
     }
 
-    fn focus_out(&mut self, cx: &mut ViewContext<Self>) {
-        if !self.focus_handle.is_focused(cx) && !self.editor.focus_handle(cx).is_focused(cx) {
-            self.update_stale_excerpts(cx);
+    fn focus_out(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        if !self.focus_handle.is_focused(window) && !self.editor.focus_handle(cx).is_focused(window)
+        {
+            self.update_stale_excerpts(window, cx);
         }
     }
 
     /// Enqueue an update of all excerpts. Updates all paths that either
     /// currently have diagnostics or are currently present in this view.
-    fn update_all_excerpts(&mut self, cx: &mut ViewContext<Self>) {
+    fn update_all_excerpts(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.project.update(cx, |project, cx| {
             let mut paths = project
                 .diagnostic_summaries(false, cx)
@@ -315,15 +340,16 @@ impl ProjectDiagnosticsEditor {
             paths.extend(paths_to_update.into_iter().map(|(path, _)| (path, None)));
             self.paths_to_update = paths;
         });
-        self.update_stale_excerpts(cx);
+        self.update_stale_excerpts(window, cx);
     }
 
     fn update_excerpts(
         &mut self,
         path_to_update: ProjectPath,
         server_to_update: Option<LanguageServerId>,
-        buffer: Model<Buffer>,
-        cx: &mut ViewContext<Self>,
+        buffer: Entity<Buffer>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let was_empty = self.path_states.is_empty();
         let snapshot = buffer.read(cx).snapshot();
@@ -579,12 +605,12 @@ impl ProjectDiagnosticsEditor {
             } else {
                 groups = self.path_states.get(path_ix)?.diagnostic_groups.as_slice();
                 new_excerpt_ids_by_selection_id =
-                    editor.change_selections(Some(Autoscroll::fit()), cx, |s| s.refresh());
+                    editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.refresh());
                 selections = editor.selections.all::<usize>(cx);
             }
 
             // If any selection has lost its position, move it to start of the next primary diagnostic.
-            let snapshot = editor.snapshot(cx);
+            let snapshot = editor.snapshot(window, cx);
             for selection in &mut selections {
                 if let Some(new_excerpt_id) = new_excerpt_ids_by_selection_id.get(&selection.id) {
                     let group_ix = match groups.binary_search_by(|probe| {
@@ -610,19 +636,19 @@ impl ProjectDiagnosticsEditor {
                     }
                 }
             }
-            editor.change_selections(None, cx, |s| {
+            editor.change_selections(None, window, cx, |s| {
                 s.select(selections);
             });
             Some(())
         });
 
         if self.path_states.is_empty() {
-            if self.editor.focus_handle(cx).is_focused(cx) {
-                cx.focus(&self.focus_handle);
+            if self.editor.focus_handle(cx).is_focused(window) {
+                window.focus(&self.focus_handle);
             }
-        } else if self.focus_handle.is_focused(cx) {
+        } else if self.focus_handle.is_focused(window) {
             let focus_handle = self.editor.focus_handle(cx);
-            cx.focus(&focus_handle);
+            window.focus(&focus_handle);
         }
 
         #[cfg(test)]
@@ -632,7 +658,7 @@ impl ProjectDiagnosticsEditor {
     }
 
     #[cfg(test)]
-    fn check_invariants(&self, cx: &mut ViewContext<Self>) {
+    fn check_invariants(&self, cx: &mut Context<Self>) {
         let mut excerpts = Vec::new();
         for (id, buffer, _) in self.excerpts.read(cx).snapshot(cx).excerpts() {
             if let Some(file) = buffer.file() {
@@ -652,8 +678,8 @@ impl ProjectDiagnosticsEditor {
     }
 }
 
-impl FocusableView for ProjectDiagnosticsEditor {
-    fn focus_handle(&self, _: &AppContext) -> FocusHandle {
+impl Focusable for ProjectDiagnosticsEditor {
+    fn focus_handle(&self, _: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -665,20 +691,26 @@ impl Item for ProjectDiagnosticsEditor {
         Editor::to_item_events(event, f)
     }
 
-    fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
-        self.editor.update(cx, |editor, cx| editor.deactivated(cx));
+    fn deactivated(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        self.editor
+            .update(cx, |editor, cx| editor.deactivated(window, cx));
     }
 
-    fn navigate(&mut self, data: Box<dyn Any>, cx: &mut ViewContext<Self>) -> bool {
+    fn navigate(
+        &mut self,
+        data: Box<dyn Any>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> bool {
         self.editor
-            .update(cx, |editor, cx| editor.navigate(data, cx))
+            .update(cx, |editor, cx| editor.navigate(data, window, cx))
     }
 
-    fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
+    fn tab_tooltip_text(&self, _: &App) -> Option<SharedString> {
         Some("Project Diagnostics".into())
     }
 
-    fn tab_content(&self, params: TabContentParams, _: &WindowContext) -> AnyElement {
+    fn tab_content(&self, params: TabContentParams, _window: &Window, _: &App) -> AnyElement {
         h_flex()
             .gap_1()
             .when(
@@ -723,17 +755,22 @@ impl Item for ProjectDiagnosticsEditor {
 
     fn for_each_project_item(
         &self,
-        cx: &AppContext,
+        cx: &App,
         f: &mut dyn FnMut(gpui::EntityId, &dyn project::ProjectItem),
     ) {
         self.editor.for_each_project_item(cx, f)
     }
 
-    fn is_singleton(&self, _: &AppContext) -> bool {
+    fn is_singleton(&self, _: &App) -> bool {
         false
     }
 
-    fn set_nav_history(&mut self, nav_history: ItemNavHistory, cx: &mut ViewContext<Self>) {
+    fn set_nav_history(
+        &mut self,
+        nav_history: ItemNavHistory,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.editor.update(cx, |editor, _| {
             editor.set_nav_history(Some(nav_history));
         });
@@ -742,64 +779,73 @@ impl Item for ProjectDiagnosticsEditor {
     fn clone_on_split(
         &self,
         _workspace_id: Option<workspace::WorkspaceId>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<View<Self>>
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Entity<Self>>
     where
         Self: Sized,
     {
-        Some(cx.new_view(|cx| {
+        Some(cx.new(|cx| {
             ProjectDiagnosticsEditor::new(
                 self.project.clone(),
                 self.include_warnings,
                 self.workspace.clone(),
+                window,
                 cx,
             )
         }))
     }
 
-    fn is_dirty(&self, cx: &AppContext) -> bool {
+    fn is_dirty(&self, cx: &App) -> bool {
         self.excerpts.read(cx).is_dirty(cx)
     }
 
-    fn has_deleted_file(&self, cx: &AppContext) -> bool {
+    fn has_deleted_file(&self, cx: &App) -> bool {
         self.excerpts.read(cx).has_deleted_file(cx)
     }
 
-    fn has_conflict(&self, cx: &AppContext) -> bool {
+    fn has_conflict(&self, cx: &App) -> bool {
         self.excerpts.read(cx).has_conflict(cx)
     }
 
-    fn can_save(&self, _: &AppContext) -> bool {
+    fn can_save(&self, _: &App) -> bool {
         true
     }
 
     fn save(
         &mut self,
         format: bool,
-        project: Model<Project>,
-        cx: &mut ViewContext<Self>,
+        project: Entity<Project>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
-        self.editor.save(format, project, cx)
+        self.editor.save(format, project, window, cx)
     }
 
     fn save_as(
         &mut self,
-        _: Model<Project>,
+        _: Entity<Project>,
         _: ProjectPath,
-        _: &mut ViewContext<Self>,
+        _window: &mut Window,
+        _: &mut Context<Self>,
     ) -> Task<Result<()>> {
         unreachable!()
     }
 
-    fn reload(&mut self, project: Model<Project>, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
-        self.editor.reload(project, cx)
+    fn reload(
+        &mut self,
+        project: Entity<Project>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Task<Result<()>> {
+        self.editor.reload(project, window, cx)
     }
 
     fn act_as_type<'a>(
         &'a self,
         type_id: TypeId,
-        self_handle: &'a View<Self>,
-        _: &'a AppContext,
+        self_handle: &'a Entity<Self>,
+        _: &'a App,
     ) -> Option<AnyView> {
         if type_id == TypeId::of::<Self>() {
             Some(self_handle.to_any())
@@ -810,21 +856,27 @@ impl Item for ProjectDiagnosticsEditor {
         }
     }
 
-    fn as_searchable(&self, _: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
+    fn as_searchable(&self, _: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {
         Some(Box::new(self.editor.clone()))
     }
 
-    fn breadcrumb_location(&self, _: &AppContext) -> ToolbarItemLocation {
+    fn breadcrumb_location(&self, _: &App) -> ToolbarItemLocation {
         ToolbarItemLocation::PrimaryLeft
     }
 
-    fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
+    fn breadcrumbs(&self, theme: &theme::Theme, cx: &App) -> Option<Vec<BreadcrumbText>> {
         self.editor.breadcrumbs(theme, cx)
     }
 
-    fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
-        self.editor
-            .update(cx, |editor, cx| editor.added_to_workspace(workspace, cx));
+    fn added_to_workspace(
+        &mut self,
+        workspace: &mut Workspace,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.editor.update(cx, |editor, cx| {
+            editor.added_to_workspace(workspace, window, cx)
+        });
     }
 }
 
@@ -840,7 +892,7 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
         h_flex()
             .id(DIAGNOSTIC_HEADER)
             .block_mouse_down()
-            .h(2. * cx.line_height())
+            .h(2. * cx.window.line_height())
             .w_full()
             .px_9()
             .justify_between()
@@ -854,7 +906,7 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
                     .map(|stack| {
                         stack.child(
                             svg()
-                                .size(cx.text_style().font_size)
+                                .size(cx.window.text_style().font_size)
                                 .flex_none()
                                 .map(|icon| {
                                     if diagnostic.severity == DiagnosticSeverity::ERROR {
@@ -872,7 +924,7 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
                             .gap_1()
                             .child(
                                 StyledText::new(message.clone()).with_highlights(
-                                    &cx.text_style(),
+                                    &cx.window.text_style(),
                                     code_ranges
                                         .iter()
                                         .map(|range| (range.clone(), highlight_style)),
@@ -929,7 +981,7 @@ fn context_range_for_entry(
     entry: &DiagnosticEntry<Point>,
     context: u32,
     snapshot: &BufferSnapshot,
-    cx: &AppContext,
+    cx: &App,
 ) -> Range<Point> {
     if let Some(rows) = heuristic_syntactic_expand(
         entry.range.clone(),
@@ -960,7 +1012,7 @@ fn heuristic_syntactic_expand<'a>(
     input_range: Range<Point>,
     max_row_count: u32,
     snapshot: &'a BufferSnapshot,
-    cx: &'a AppContext,
+    cx: &'a App,
 ) -> Option<RangeInclusive<BufferRow>> {
     let input_row_count = input_range.end.row - input_range.start.row;
     if input_row_count > max_row_count {

crates/diagnostics/src/diagnostics_tests.rs 🔗

@@ -61,7 +61,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
     let language_server_id = LanguageServerId(0);
     let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
     let lsp_store = project.read_with(cx, |project, _| project.lsp_store());
-    let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
+    let window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
     let cx = &mut VisualTestContext::from_window(*window, cx);
     let workspace = window.root(cx).unwrap();
 
@@ -150,18 +150,20 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
     });
 
     // Open the project diagnostics view while there are already diagnostics.
-    let view = window.build_view(cx, |cx| {
+    let diagnostics = window.build_model(cx, |window, cx| {
         ProjectDiagnosticsEditor::new_with_context(
             1,
             true,
             project.clone(),
             workspace.downgrade(),
+            window,
             cx,
         )
     });
-    let editor = view.update(cx, |view, _| view.editor.clone());
+    let editor = diagnostics.update(cx, |diagnostics, _| diagnostics.editor.clone());
 
-    view.next_notification(DIAGNOSTICS_UPDATE_DEBOUNCE + Duration::from_millis(10), cx)
+    diagnostics
+        .next_notification(DIAGNOSTICS_UPDATE_DEBOUNCE + Duration::from_millis(10), cx)
         .await;
     assert_eq!(
         editor_blocks(&editor, cx),
@@ -251,7 +253,8 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
         lsp_store.disk_based_diagnostics_finished(language_server_id, cx);
     });
 
-    view.next_notification(DIAGNOSTICS_UPDATE_DEBOUNCE + Duration::from_millis(10), cx)
+    diagnostics
+        .next_notification(DIAGNOSTICS_UPDATE_DEBOUNCE + Duration::from_millis(10), cx)
         .await;
     assert_eq!(
         editor_blocks(&editor, cx),
@@ -370,7 +373,8 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
         lsp_store.disk_based_diagnostics_finished(language_server_id, cx);
     });
 
-    view.next_notification(DIAGNOSTICS_UPDATE_DEBOUNCE + Duration::from_millis(10), cx)
+    diagnostics
+        .next_notification(DIAGNOSTICS_UPDATE_DEBOUNCE + Duration::from_millis(10), cx)
         .await;
     assert_eq!(
         editor_blocks(&editor, cx),
@@ -477,20 +481,21 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
     let server_id_2 = LanguageServerId(101);
     let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
     let lsp_store = project.read_with(cx, |project, _| project.lsp_store());
-    let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
+    let window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
     let cx = &mut VisualTestContext::from_window(*window, cx);
     let workspace = window.root(cx).unwrap();
 
-    let view = window.build_view(cx, |cx| {
+    let diagnostics = window.build_model(cx, |window, cx| {
         ProjectDiagnosticsEditor::new_with_context(
             1,
             true,
             project.clone(),
             workspace.downgrade(),
+            window,
             cx,
         )
     });
-    let editor = view.update(cx, |view, _| view.editor.clone());
+    let editor = diagnostics.update(cx, |diagnostics, _| diagnostics.editor.clone());
 
     // Two language servers start updating diagnostics
     lsp_store.update(cx, |lsp_store, cx| {
@@ -754,25 +759,26 @@ async fn test_random_diagnostics(cx: &mut TestAppContext, mut rng: StdRng) {
 
     let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
     let lsp_store = project.read_with(cx, |project, _| project.lsp_store());
-    let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
+    let window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
     let cx = &mut VisualTestContext::from_window(*window, cx);
     let workspace = window.root(cx).unwrap();
 
-    let mutated_view = window.build_view(cx, |cx| {
+    let mutated_diagnostics = window.build_model(cx, |window, cx| {
         ProjectDiagnosticsEditor::new_with_context(
             1,
             true,
             project.clone(),
             workspace.downgrade(),
+            window,
             cx,
         )
     });
 
-    workspace.update(cx, |workspace, cx| {
-        workspace.add_item_to_center(Box::new(mutated_view.clone()), cx);
+    workspace.update_in(cx, |workspace, window, cx| {
+        workspace.add_item_to_center(Box::new(mutated_diagnostics.clone()), window, cx);
     });
-    mutated_view.update(cx, |view, cx| {
-        assert!(view.focus_handle.is_focused(cx));
+    mutated_diagnostics.update_in(cx, |diagnostics, window, _cx| {
+        assert!(diagnostics.focus_handle.is_focused(window));
     });
 
     let mut next_group_id = 0;
@@ -858,16 +864,19 @@ async fn test_random_diagnostics(cx: &mut TestAppContext, mut rng: StdRng) {
     }
 
     log::info!("updating mutated diagnostics view");
-    mutated_view.update(cx, |view, cx| view.update_stale_excerpts(cx));
+    mutated_diagnostics.update_in(cx, |diagnostics, window, cx| {
+        diagnostics.update_stale_excerpts(window, cx)
+    });
     cx.run_until_parked();
 
     log::info!("constructing reference diagnostics view");
-    let reference_view = window.build_view(cx, |cx| {
+    let reference_diagnostics = window.build_model(cx, |window, cx| {
         ProjectDiagnosticsEditor::new_with_context(
             1,
             true,
             project.clone(),
             workspace.downgrade(),
+            window,
             cx,
         )
     });
@@ -875,8 +884,8 @@ async fn test_random_diagnostics(cx: &mut TestAppContext, mut rng: StdRng) {
         .advance_clock(DIAGNOSTICS_UPDATE_DEBOUNCE + Duration::from_millis(10));
     cx.run_until_parked();
 
-    let mutated_excerpts = get_diagnostics_excerpts(&mutated_view, cx);
-    let reference_excerpts = get_diagnostics_excerpts(&reference_view, cx);
+    let mutated_excerpts = get_diagnostics_excerpts(&mutated_diagnostics, cx);
+    let reference_excerpts = get_diagnostics_excerpts(&reference_diagnostics, cx);
 
     for ((path, language_server_id), diagnostics) in current_diagnostics {
         for diagnostic in diagnostics {
@@ -917,13 +926,13 @@ struct ExcerptInfo {
 }
 
 fn get_diagnostics_excerpts(
-    view: &View<ProjectDiagnosticsEditor>,
+    diagnostics: &Entity<ProjectDiagnosticsEditor>,
     cx: &mut VisualTestContext,
 ) -> Vec<ExcerptInfo> {
-    view.update(cx, |view, cx| {
+    diagnostics.update(cx, |diagnostics, cx| {
         let mut result = vec![];
         let mut excerpt_indices_by_id = HashMap::default();
-        view.excerpts.update(cx, |multibuffer, cx| {
+        diagnostics.excerpts.update(cx, |multibuffer, cx| {
             let snapshot = multibuffer.snapshot(cx);
             for (id, buffer, range) in snapshot.excerpts() {
                 excerpt_indices_by_id.insert(id, result.len());
@@ -940,7 +949,7 @@ fn get_diagnostics_excerpts(
             }
         });
 
-        for state in &view.path_states {
+        for state in &diagnostics.path_states {
             for group in &state.diagnostic_groups {
                 for (ix, excerpt_id) in group.excerpts.iter().enumerate() {
                     let excerpt_ix = excerpt_indices_by_id[excerpt_id];
@@ -1043,58 +1052,63 @@ const FILE_HEADER: &str = "file header";
 const EXCERPT_HEADER: &str = "excerpt header";
 
 fn editor_blocks(
-    editor: &View<Editor>,
+    editor: &Entity<Editor>,
     cx: &mut VisualTestContext,
 ) -> Vec<(DisplayRow, SharedString)> {
     let mut blocks = Vec::new();
-    cx.draw(gpui::Point::default(), AvailableSpace::min_size(), |cx| {
-        editor.update(cx, |editor, cx| {
-            let snapshot = editor.snapshot(cx);
-            blocks.extend(
-                snapshot
-                    .blocks_in_range(DisplayRow(0)..snapshot.max_point().row())
-                    .filter_map(|(row, block)| {
-                        let block_id = block.id();
-                        let name: SharedString = match block {
-                            Block::Custom(block) => {
-                                let mut element = block.render(&mut BlockContext {
-                                    context: cx,
-                                    anchor_x: px(0.),
-                                    gutter_dimensions: &GutterDimensions::default(),
-                                    line_height: px(0.),
-                                    em_width: px(0.),
-                                    max_width: px(0.),
-                                    block_id,
-                                    selected: false,
-                                    editor_style: &editor::EditorStyle::default(),
-                                });
-                                let element = element.downcast_mut::<Stateful<Div>>().unwrap();
-                                element
-                                    .interactivity()
-                                    .element_id
-                                    .clone()?
-                                    .try_into()
-                                    .ok()?
-                            }
-
-                            Block::FoldedBuffer { .. } => FILE_HEADER.into(),
-                            Block::ExcerptBoundary {
-                                starts_new_buffer, ..
-                            } => {
-                                if *starts_new_buffer {
-                                    FILE_HEADER.into()
-                                } else {
-                                    EXCERPT_HEADER.into()
+    cx.draw(
+        gpui::Point::default(),
+        AvailableSpace::min_size(),
+        |window, cx| {
+            editor.update(cx, |editor, cx| {
+                let snapshot = editor.snapshot(window, cx);
+                blocks.extend(
+                    snapshot
+                        .blocks_in_range(DisplayRow(0)..snapshot.max_point().row())
+                        .filter_map(|(row, block)| {
+                            let block_id = block.id();
+                            let name: SharedString = match block {
+                                Block::Custom(block) => {
+                                    let mut element = block.render(&mut BlockContext {
+                                        app: cx,
+                                        window,
+                                        anchor_x: px(0.),
+                                        gutter_dimensions: &GutterDimensions::default(),
+                                        line_height: px(0.),
+                                        em_width: px(0.),
+                                        max_width: px(0.),
+                                        block_id,
+                                        selected: false,
+                                        editor_style: &editor::EditorStyle::default(),
+                                    });
+                                    let element = element.downcast_mut::<Stateful<Div>>().unwrap();
+                                    element
+                                        .interactivity()
+                                        .element_id
+                                        .clone()?
+                                        .try_into()
+                                        .ok()?
                                 }
-                            }
-                        };
 
-                        Some((row, name))
-                    }),
-            )
-        });
+                                Block::FoldedBuffer { .. } => FILE_HEADER.into(),
+                                Block::ExcerptBoundary {
+                                    starts_new_buffer, ..
+                                } => {
+                                    if *starts_new_buffer {
+                                        FILE_HEADER.into()
+                                    } else {
+                                        EXCERPT_HEADER.into()
+                                    }
+                                }
+                            };
 
-        div().into_any()
-    });
+                            Some((row, name))
+                        }),
+                )
+            });
+
+            div().into_any()
+        },
+    );
     blocks
 }

crates/diagnostics/src/items.rs 🔗

@@ -2,8 +2,8 @@ use std::time::Duration;
 
 use editor::Editor;
 use gpui::{
-    EventEmitter, IntoElement, ParentElement, Render, Styled, Subscription, Task, View,
-    ViewContext, WeakView,
+    Context, Entity, EventEmitter, IntoElement, ParentElement, Render, Styled, Subscription, Task,
+    WeakEntity, Window,
 };
 use language::Diagnostic;
 use ui::{h_flex, prelude::*, Button, ButtonLike, Color, Icon, IconName, Label, Tooltip};
@@ -13,15 +13,15 @@ use crate::{Deploy, ProjectDiagnosticsEditor};
 
 pub struct DiagnosticIndicator {
     summary: project::DiagnosticSummary,
-    active_editor: Option<WeakView<Editor>>,
-    workspace: WeakView<Workspace>,
+    active_editor: Option<WeakEntity<Editor>>,
+    workspace: WeakEntity<Workspace>,
     current_diagnostic: Option<Diagnostic>,
     _observe_active_editor: Option<Subscription>,
     diagnostics_update: Task<()>,
 }
 
 impl Render for DiagnosticIndicator {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let diagnostic_indicator = match (self.summary.error_count, self.summary.warning_count) {
             (0, 0) => h_flex().map(|this| {
                 this.child(
@@ -67,11 +67,16 @@ impl Render for DiagnosticIndicator {
             Some(
                 Button::new("diagnostic_message", message)
                     .label_size(LabelSize::Small)
-                    .tooltip(|cx| {
-                        Tooltip::for_action("Next Diagnostic", &editor::actions::GoToDiagnostic, cx)
+                    .tooltip(|window, cx| {
+                        Tooltip::for_action(
+                            "Next Diagnostic",
+                            &editor::actions::GoToDiagnostic,
+                            window,
+                            cx,
+                        )
                     })
-                    .on_click(cx.listener(|this, _, cx| {
-                        this.go_to_next_diagnostic(cx);
+                    .on_click(cx.listener(|this, _, window, cx| {
+                        this.go_to_next_diagnostic(window, cx);
                     }))
                     .into_any_element(),
             )
@@ -87,11 +92,18 @@ impl Render for DiagnosticIndicator {
             .child(
                 ButtonLike::new("diagnostic-indicator")
                     .child(diagnostic_indicator)
-                    .tooltip(|cx| Tooltip::for_action("Project Diagnostics", &Deploy, cx))
-                    .on_click(cx.listener(|this, _, cx| {
+                    .tooltip(|window, cx| {
+                        Tooltip::for_action("Project Diagnostics", &Deploy, window, cx)
+                    })
+                    .on_click(cx.listener(|this, _, window, cx| {
                         if let Some(workspace) = this.workspace.upgrade() {
                             workspace.update(cx, |workspace, cx| {
-                                ProjectDiagnosticsEditor::deploy(workspace, &Default::default(), cx)
+                                ProjectDiagnosticsEditor::deploy(
+                                    workspace,
+                                    &Default::default(),
+                                    window,
+                                    cx,
+                                )
                             })
                         }
                     })),
@@ -101,7 +113,7 @@ impl Render for DiagnosticIndicator {
 }
 
 impl DiagnosticIndicator {
-    pub fn new(workspace: &Workspace, cx: &mut ViewContext<Self>) -> Self {
+    pub fn new(workspace: &Workspace, cx: &mut Context<Self>) -> Self {
         let project = workspace.project();
         cx.subscribe(project, |this, project, event, cx| match event {
             project::Event::DiskBasedDiagnosticsStarted { .. } => {
@@ -133,15 +145,15 @@ impl DiagnosticIndicator {
         }
     }
 
-    fn go_to_next_diagnostic(&mut self, cx: &mut ViewContext<Self>) {
+    fn go_to_next_diagnostic(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(editor) = self.active_editor.as_ref().and_then(|e| e.upgrade()) {
             editor.update(cx, |editor, cx| {
-                editor.go_to_diagnostic_impl(editor::Direction::Next, cx);
+                editor.go_to_diagnostic_impl(editor::Direction::Next, window, cx);
             })
         }
     }
 
-    fn update(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
+    fn update(&mut self, editor: Entity<Editor>, window: &mut Window, cx: &mut Context<Self>) {
         let (buffer, cursor_position) = editor.update(cx, |editor, cx| {
             let buffer = editor.buffer().read(cx).snapshot(cx);
             let cursor_position = editor.selections.newest::<usize>(cx).head();
@@ -153,17 +165,18 @@ impl DiagnosticIndicator {
             .min_by_key(|entry| (entry.diagnostic.severity, entry.range.len()))
             .map(|entry| entry.diagnostic);
         if new_diagnostic != self.current_diagnostic {
-            self.diagnostics_update = cx.spawn(|diagnostics_indicator, mut cx| async move {
-                cx.background_executor()
-                    .timer(Duration::from_millis(50))
-                    .await;
-                diagnostics_indicator
-                    .update(&mut cx, |diagnostics_indicator, cx| {
-                        diagnostics_indicator.current_diagnostic = new_diagnostic;
-                        cx.notify();
-                    })
-                    .ok();
-            });
+            self.diagnostics_update =
+                cx.spawn_in(window, |diagnostics_indicator, mut cx| async move {
+                    cx.background_executor()
+                        .timer(Duration::from_millis(50))
+                        .await;
+                    diagnostics_indicator
+                        .update(&mut cx, |diagnostics_indicator, cx| {
+                            diagnostics_indicator.current_diagnostic = new_diagnostic;
+                            cx.notify();
+                        })
+                        .ok();
+                });
         }
     }
 }
@@ -174,12 +187,13 @@ impl StatusItemView for DiagnosticIndicator {
     fn set_active_pane_item(
         &mut self,
         active_pane_item: Option<&dyn ItemHandle>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         if let Some(editor) = active_pane_item.and_then(|item| item.downcast::<Editor>()) {
             self.active_editor = Some(editor.downgrade());
-            self._observe_active_editor = Some(cx.observe(&editor, Self::update));
-            self.update(editor, cx);
+            self._observe_active_editor = Some(cx.observe_in(&editor, window, Self::update));
+            self.update(editor, window, cx);
         } else {
             self.active_editor = None;
             self.current_diagnostic = None;

crates/diagnostics/src/project_diagnostics_settings.rs 🔗

@@ -1,5 +1,5 @@
 use anyhow::Result;
-use gpui::AppContext;
+use gpui::App;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsSources};
@@ -22,7 +22,7 @@ impl Settings for ProjectDiagnosticsSettings {
     const KEY: Option<&'static str> = Some("diagnostics");
     type FileContent = ProjectDiagnosticsSettingsContent;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
         sources.json_merge()
     }
 }

crates/diagnostics/src/toolbar_controls.rs 🔗

@@ -1,15 +1,15 @@
 use crate::ProjectDiagnosticsEditor;
-use gpui::{EventEmitter, ParentElement, Render, View, ViewContext, WeakView};
+use gpui::{Context, Entity, EventEmitter, ParentElement, Render, WeakEntity, Window};
 use ui::prelude::*;
 use ui::{IconButton, IconButtonShape, IconName, Tooltip};
 use workspace::{item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
 
 pub struct ToolbarControls {
-    editor: Option<WeakView<ProjectDiagnosticsEditor>>,
+    editor: Option<WeakEntity<ProjectDiagnosticsEditor>>,
 }
 
 impl Render for ToolbarControls {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let mut include_warnings = false;
         let mut has_stale_excerpts = false;
         let mut is_updating = false;
@@ -47,11 +47,11 @@ impl Render for ToolbarControls {
                         .icon_color(Color::Info)
                         .shape(IconButtonShape::Square)
                         .disabled(is_updating)
-                        .tooltip(move |cx| Tooltip::text("Update excerpts", cx))
-                        .on_click(cx.listener(|this, _, cx| {
+                        .tooltip(Tooltip::text("Update excerpts"))
+                        .on_click(cx.listener(|this, _, window, cx| {
                             if let Some(diagnostics) = this.diagnostics() {
                                 diagnostics.update(cx, |diagnostics, cx| {
-                                    diagnostics.update_all_excerpts(cx);
+                                    diagnostics.update_all_excerpts(window, cx);
                                 });
                             }
                         })),
@@ -61,11 +61,11 @@ impl Render for ToolbarControls {
                 IconButton::new("toggle-warnings", IconName::Warning)
                     .icon_color(warning_color)
                     .shape(IconButtonShape::Square)
-                    .tooltip(move |cx| Tooltip::text(tooltip, cx))
-                    .on_click(cx.listener(|this, _, cx| {
+                    .tooltip(Tooltip::text(tooltip))
+                    .on_click(cx.listener(|this, _, window, cx| {
                         if let Some(editor) = this.diagnostics() {
                             editor.update(cx, |editor, cx| {
-                                editor.toggle_warnings(&Default::default(), cx);
+                                editor.toggle_warnings(&Default::default(), window, cx);
                             });
                         }
                     })),
@@ -79,7 +79,8 @@ impl ToolbarItemView for ToolbarControls {
     fn set_active_pane_item(
         &mut self,
         active_pane_item: Option<&dyn ItemHandle>,
-        _: &mut ViewContext<Self>,
+        _window: &mut Window,
+        _: &mut Context<Self>,
     ) -> ToolbarItemLocation {
         if let Some(pane_item) = active_pane_item.as_ref() {
             if let Some(editor) = pane_item.downcast::<ProjectDiagnosticsEditor>() {
@@ -105,7 +106,7 @@ impl ToolbarControls {
         ToolbarControls { editor: None }
     }
 
-    fn diagnostics(&self) -> Option<View<ProjectDiagnosticsEditor>> {
+    fn diagnostics(&self) -> Option<Entity<ProjectDiagnosticsEditor>> {
         self.editor.as_ref()?.upgrade()
     }
 }

crates/docs_preprocessor/src/main.rs 🔗

@@ -1,4 +1,4 @@
-use anyhow::{Context, Result};
+use anyhow::{Context as _, Result};
 use clap::{Arg, ArgMatches, Command};
 use docs_preprocessor::ZedDocsPreprocessor;
 use mdbook::preprocess::{CmdPreprocessor, Preprocessor};

crates/editor/src/blame_entry_tooltip.rs 🔗

@@ -2,8 +2,8 @@ use futures::Future;
 use git::blame::BlameEntry;
 use git::Oid;
 use gpui::{
-    AppContext, Asset, ClipboardItem, Element, ParentElement, Render, ScrollHandle,
-    StatefulInteractiveElement, WeakView,
+    App, Asset, ClipboardItem, Element, ParentElement, Render, ScrollHandle,
+    StatefulInteractiveElement, WeakEntity,
 };
 use settings::Settings;
 use std::hash::Hash;
@@ -27,7 +27,11 @@ impl<'a> CommitAvatar<'a> {
 }
 
 impl<'a> CommitAvatar<'a> {
-    fn render(&'a self, cx: &mut ViewContext<BlameEntryTooltip>) -> Option<impl IntoElement> {
+    fn render(
+        &'a self,
+        window: &mut Window,
+        cx: &mut Context<BlameEntryTooltip>,
+    ) -> Option<impl IntoElement> {
         let remote = self
             .details
             .and_then(|details| details.remote.as_ref())
@@ -35,7 +39,7 @@ impl<'a> CommitAvatar<'a> {
 
         let avatar_url = CommitAvatarAsset::new(remote.clone(), self.sha);
 
-        let element = match cx.use_asset::<CommitAvatarAsset>(&avatar_url) {
+        let element = match window.use_asset::<CommitAvatarAsset>(&avatar_url, cx) {
             // Loading or no avatar found
             None | Some(None) => Icon::new(IconName::Person)
                 .color(Color::Muted)
@@ -73,7 +77,7 @@ impl Asset for CommitAvatarAsset {
 
     fn load(
         source: Self::Source,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> impl Future<Output = Self::Output> + Send + 'static {
         let client = cx.http_client();
 
@@ -91,7 +95,7 @@ pub(crate) struct BlameEntryTooltip {
     blame_entry: BlameEntry,
     details: Option<CommitDetails>,
     editor_style: EditorStyle,
-    workspace: Option<WeakView<Workspace>>,
+    workspace: Option<WeakEntity<Workspace>>,
     scroll_handle: ScrollHandle,
 }
 
@@ -100,7 +104,7 @@ impl BlameEntryTooltip {
         blame_entry: BlameEntry,
         details: Option<CommitDetails>,
         style: &EditorStyle,
-        workspace: Option<WeakView<Workspace>>,
+        workspace: Option<WeakEntity<Workspace>>,
     ) -> Self {
         Self {
             editor_style: style.clone(),
@@ -113,8 +117,9 @@ impl BlameEntryTooltip {
 }
 
 impl Render for BlameEntryTooltip {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let avatar = CommitAvatar::new(self.details.as_ref(), self.blame_entry.sha).render(cx);
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+        let avatar =
+            CommitAvatar::new(self.details.as_ref(), self.blame_entry.sha).render(window, cx);
 
         let author = self
             .blame_entry
@@ -149,11 +154,11 @@ impl Render for BlameEntryTooltip {
             .and_then(|details| details.pull_request.clone());
 
         let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
-        let message_max_height = cx.line_height() * 12 + (ui_font_size / 0.4);
+        let message_max_height = window.line_height() * 12 + (ui_font_size / 0.4);
 
-        tooltip_container(cx, move |this, cx| {
+        tooltip_container(window, cx, move |this, _, cx| {
             this.occlude()
-                .on_mouse_move(|_, cx| cx.stop_propagation())
+                .on_mouse_move(|_, _, cx| cx.stop_propagation())
                 .child(
                     v_flex()
                         .w(gpui::rems(30.))
@@ -208,7 +213,7 @@ impl Render for BlameEntryTooltip {
                                                 .icon_color(Color::Muted)
                                                 .icon_position(IconPosition::Start)
                                                 .style(ButtonStyle::Subtle)
-                                                .on_click(move |_, cx| {
+                                                .on_click(move |_, _, cx| {
                                                     cx.stop_propagation();
                                                     cx.open_url(pr.url.as_str())
                                                 }),
@@ -235,7 +240,7 @@ impl Render for BlameEntryTooltip {
                                                     .as_ref()
                                                     .and_then(|details| details.permalink.clone()),
                                                 |this, url| {
-                                                    this.on_click(move |_, cx| {
+                                                    this.on_click(move |_, _, cx| {
                                                         cx.stop_propagation();
                                                         cx.open_url(url.as_str())
                                                     })
@@ -247,7 +252,7 @@ impl Render for BlameEntryTooltip {
                                                 .shape(IconButtonShape::Square)
                                                 .icon_size(IconSize::Small)
                                                 .icon_color(Color::Muted)
-                                                .on_click(move |_, cx| {
+                                                .on_click(move |_, _, cx| {
                                                     cx.stop_propagation();
                                                     cx.write_to_clipboard(
                                                         ClipboardItem::new_string(full_sha.clone()),
@@ -1,5 +1,5 @@
 use crate::EditorSettings;
-use gpui::ModelContext;
+use gpui::Context;
 use settings::Settings;
 use settings::SettingsStore;
 use smol::Timer;
@@ -15,7 +15,7 @@ pub struct BlinkManager {
 }
 
 impl BlinkManager {
-    pub fn new(blink_interval: Duration, cx: &mut ModelContext<Self>) -> Self {
+    pub fn new(blink_interval: Duration, cx: &mut Context<Self>) -> Self {
         // Make sure we blink the cursors if the setting is re-enabled
         cx.observe_global::<SettingsStore>(move |this, cx| {
             this.blink_cursors(this.blink_epoch, cx)
@@ -37,7 +37,7 @@ impl BlinkManager {
         self.blink_epoch
     }
 
-    pub fn pause_blinking(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn pause_blinking(&mut self, cx: &mut Context<Self>) {
         self.show_cursor(cx);
 
         let epoch = self.next_blink_epoch();
@@ -49,14 +49,14 @@ impl BlinkManager {
         .detach();
     }
 
-    fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ModelContext<Self>) {
+    fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut Context<Self>) {
         if epoch == self.blink_epoch {
             self.blinking_paused = false;
             self.blink_cursors(epoch, cx);
         }
     }
 
-    fn blink_cursors(&mut self, epoch: usize, cx: &mut ModelContext<Self>) {
+    fn blink_cursors(&mut self, epoch: usize, cx: &mut Context<Self>) {
         if EditorSettings::get_global(cx).cursor_blink {
             if epoch == self.blink_epoch && self.enabled && !self.blinking_paused {
                 self.visible = !self.visible;
@@ -78,14 +78,14 @@ impl BlinkManager {
         }
     }
 
-    pub fn show_cursor(&mut self, cx: &mut ModelContext<'_, BlinkManager>) {
+    pub fn show_cursor(&mut self, cx: &mut Context<'_, BlinkManager>) {
         if !self.visible {
             self.visible = true;
             cx.notify();
         }
     }
 
-    pub fn enable(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn enable(&mut self, cx: &mut Context<Self>) {
         if self.enabled {
             return;
         }
@@ -97,7 +97,7 @@ impl BlinkManager {
         self.blink_cursors(self.blink_epoch, cx);
     }
 
-    pub fn disable(&mut self, _cx: &mut ModelContext<Self>) {
+    pub fn disable(&mut self, _cx: &mut Context<Self>) {
         self.visible = false;
         self.enabled = false;
     }

crates/editor/src/clangd_ext.rs 🔗

@@ -1,5 +1,5 @@
 use anyhow::Context as _;
-use gpui::{View, ViewContext, WindowContext};
+use gpui::{App, Context, Entity, Window};
 use language::Language;
 use url::Url;
 
@@ -16,7 +16,8 @@ fn is_c_language(language: &Language) -> bool {
 pub fn switch_source_header(
     editor: &mut Editor,
     _: &SwitchSourceHeader,
-    cx: &mut ViewContext<Editor>,
+    window: &mut Window,
+    cx: &mut Context<Editor>,
 ) {
     let Some(project) = &editor.project else {
         return;
@@ -49,7 +50,7 @@ pub fn switch_source_header(
             cx,
         )
     });
-    cx.spawn(|_editor, mut cx| async move {
+    cx.spawn_in(window, |_editor, mut cx| async move {
         let switch_source_header = switch_source_header_task
             .await
             .with_context(|| format!("Switch source/header LSP request for path \"{source_file}\" failed"))?;
@@ -70,8 +71,8 @@ pub fn switch_source_header(
         })?;
 
         workspace
-            .update(&mut cx, |workspace, view_cx| {
-                workspace.open_abs_path(path, false, view_cx)
+            .update_in(&mut cx, |workspace, window, cx| {
+                workspace.open_abs_path(path, false, window, cx)
             })
             .with_context(|| {
                 format!(
@@ -84,11 +85,11 @@ pub fn switch_source_header(
     .detach_and_log_err(cx);
 }
 
-pub fn apply_related_actions(editor: &View<Editor>, cx: &mut WindowContext) {
+pub fn apply_related_actions(editor: &Entity<Editor>, window: &mut Window, cx: &mut App) {
     if editor.update(cx, |e, cx| {
         find_specific_language_server_in_selection(e, cx, is_c_language, CLANGD_SERVER_NAME)
             .is_some()
     }) {
-        register_action(editor, cx, switch_source_header);
+        register_action(editor, window, switch_source_header);
     }
 }

crates/editor/src/code_context_menus.rs 🔗

@@ -1,8 +1,8 @@
 use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{
     div, pulsating_between, px, uniform_list, Animation, AnimationExt, AnyElement,
-    BackgroundExecutor, Div, FontWeight, ListSizingBehavior, Model, ScrollStrategy, SharedString,
-    Size, StrikethroughStyle, StyledText, UniformListScrollHandle, ViewContext, WeakView,
+    BackgroundExecutor, Div, Entity, FontWeight, ListSizingBehavior, ScrollStrategy, SharedString,
+    Size, StrikethroughStyle, StyledText, UniformListScrollHandle, WeakEntity,
 };
 use language::Buffer;
 use language::{CodeLabel, Documentation};
@@ -46,7 +46,7 @@ impl CodeContextMenu {
     pub fn select_first(
         &mut self,
         provider: Option<&dyn CompletionProvider>,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) -> bool {
         if self.visible() {
             match self {
@@ -62,7 +62,7 @@ impl CodeContextMenu {
     pub fn select_prev(
         &mut self,
         provider: Option<&dyn CompletionProvider>,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) -> bool {
         if self.visible() {
             match self {
@@ -78,7 +78,7 @@ impl CodeContextMenu {
     pub fn select_next(
         &mut self,
         provider: Option<&dyn CompletionProvider>,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) -> bool {
         if self.visible() {
             match self {
@@ -94,7 +94,7 @@ impl CodeContextMenu {
     pub fn select_last(
         &mut self,
         provider: Option<&dyn CompletionProvider>,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) -> bool {
         if self.visible() {
             match self {
@@ -126,14 +126,15 @@ impl CodeContextMenu {
         style: &EditorStyle,
         max_height_in_lines: u32,
         y_flipped: bool,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) -> AnyElement {
         match self {
             CodeContextMenu::Completions(menu) => {
-                menu.render(style, max_height_in_lines, y_flipped, cx)
+                menu.render(style, max_height_in_lines, y_flipped, window, cx)
             }
             CodeContextMenu::CodeActions(menu) => {
-                menu.render(style, max_height_in_lines, y_flipped, cx)
+                menu.render(style, max_height_in_lines, y_flipped, window, cx)
             }
         }
     }
@@ -142,8 +143,8 @@ impl CodeContextMenu {
         &self,
         style: &EditorStyle,
         max_size: Size<Pixels>,
-        workspace: Option<WeakView<Workspace>>,
-        cx: &mut ViewContext<Editor>,
+        workspace: Option<WeakEntity<Workspace>>,
+        cx: &mut Context<Editor>,
     ) -> Option<AnyElement> {
         match self {
             CodeContextMenu::Completions(menu) => menu.render_aside(style, max_size, workspace, cx),
@@ -162,7 +163,7 @@ pub struct CompletionsMenu {
     pub id: CompletionId,
     sort_completions: bool,
     pub initial_position: Anchor,
-    pub buffer: Model<Buffer>,
+    pub buffer: Entity<Buffer>,
     pub completions: Rc<RefCell<Box<[Completion]>>>,
     match_candidates: Rc<[StringMatchCandidate]>,
     pub entries: Rc<RefCell<Vec<CompletionEntry>>>,
@@ -185,7 +186,7 @@ impl CompletionsMenu {
         sort_completions: bool,
         show_completion_documentation: bool,
         initial_position: Anchor,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         completions: Box<[Completion]>,
     ) -> Self {
         let match_candidates = completions
@@ -215,7 +216,7 @@ impl CompletionsMenu {
         sort_completions: bool,
         choices: &Vec<String>,
         selection: Range<Anchor>,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
     ) -> Self {
         let completions = choices
             .iter()
@@ -271,7 +272,7 @@ impl CompletionsMenu {
     fn select_first(
         &mut self,
         provider: Option<&dyn CompletionProvider>,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) {
         let index = if self.scroll_handle.y_flipped() {
             self.entries.borrow().len() - 1
@@ -281,11 +282,7 @@ impl CompletionsMenu {
         self.update_selection_index(index, provider, cx);
     }
 
-    fn select_last(
-        &mut self,
-        provider: Option<&dyn CompletionProvider>,
-        cx: &mut ViewContext<Editor>,
-    ) {
+    fn select_last(&mut self, provider: Option<&dyn CompletionProvider>, cx: &mut Context<Editor>) {
         let index = if self.scroll_handle.y_flipped() {
             0
         } else {
@@ -294,11 +291,7 @@ impl CompletionsMenu {
         self.update_selection_index(index, provider, cx);
     }
 
-    fn select_prev(
-        &mut self,
-        provider: Option<&dyn CompletionProvider>,
-        cx: &mut ViewContext<Editor>,
-    ) {
+    fn select_prev(&mut self, provider: Option<&dyn CompletionProvider>, cx: &mut Context<Editor>) {
         let index = if self.scroll_handle.y_flipped() {
             self.next_match_index()
         } else {
@@ -307,11 +300,7 @@ impl CompletionsMenu {
         self.update_selection_index(index, provider, cx);
     }
 
-    fn select_next(
-        &mut self,
-        provider: Option<&dyn CompletionProvider>,
-        cx: &mut ViewContext<Editor>,
-    ) {
+    fn select_next(&mut self, provider: Option<&dyn CompletionProvider>, cx: &mut Context<Editor>) {
         let index = if self.scroll_handle.y_flipped() {
             self.prev_match_index()
         } else {
@@ -324,7 +313,7 @@ impl CompletionsMenu {
         &mut self,
         match_index: usize,
         provider: Option<&dyn CompletionProvider>,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) {
         if self.selected_item != match_index {
             self.selected_item = match_index;
@@ -372,7 +361,7 @@ impl CompletionsMenu {
     pub fn resolve_visible_completions(
         &mut self,
         provider: Option<&dyn CompletionProvider>,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) {
         if !self.resolve_completions {
             return;
@@ -469,7 +458,8 @@ impl CompletionsMenu {
         style: &EditorStyle,
         max_height_in_lines: u32,
         y_flipped: bool,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) -> AnyElement {
         let completions = self.completions.borrow_mut();
         let show_completion_documentation = self.show_completion_documentation;
@@ -505,10 +495,10 @@ impl CompletionsMenu {
         let last_rendered_range = self.last_rendered_range.clone();
         let style = style.clone();
         let list = uniform_list(
-            cx.view().clone(),
+            cx.model().clone(),
             "completions",
             self.entries.borrow().len(),
-            move |_editor, range, cx| {
+            move |_editor, range, _window, cx| {
                 last_rendered_range.borrow_mut().replace(range.clone());
                 let start_ix = range.start;
                 let completions_guard = completions.borrow_mut();
@@ -591,12 +581,13 @@ impl CompletionsMenu {
                                     ListItem::new(mat.candidate_id)
                                         .inset(true)
                                         .toggle_state(item_ix == selected_item)
-                                        .on_click(cx.listener(move |editor, _event, cx| {
+                                        .on_click(cx.listener(move |editor, _event, window, cx| {
                                             cx.stop_propagation();
                                             if let Some(task) = editor.confirm_completion(
                                                 &ConfirmCompletion {
                                                     item_ix: Some(item_ix),
                                                 },
+                                                window,
                                                 cx,
                                             ) {
                                                 task.detach_and_log_err(cx)
@@ -659,9 +650,9 @@ impl CompletionsMenu {
                                                 .with_highlights(&style.text, None),
                                         ),
                                     )
-                                    .on_click(cx.listener(move |editor, _event, cx| {
+                                    .on_click(cx.listener(move |editor, _event, window, cx| {
                                         cx.stop_propagation();
-                                        editor.toggle_zed_predict_tos(cx);
+                                        editor.toggle_zed_predict_tos(window, cx);
                                     })),
                             ),
 
@@ -678,10 +669,11 @@ impl CompletionsMenu {
                                                 .with_highlights(&style.text, None),
                                         ),
                                     )
-                                    .on_click(cx.listener(move |editor, _event, cx| {
+                                    .on_click(cx.listener(move |editor, _event, window, cx| {
                                         cx.stop_propagation();
                                         editor.accept_inline_completion(
                                             &AcceptInlineCompletion {},
+                                            window,
                                             cx,
                                         );
                                     })),
@@ -692,7 +684,7 @@ impl CompletionsMenu {
             },
         )
         .occlude()
-        .max_h(max_height_in_lines as f32 * cx.line_height())
+        .max_h(max_height_in_lines as f32 * window.line_height())
         .track_scroll(self.scroll_handle.clone())
         .y_flipped(y_flipped)
         .with_width_from_item(widest_completion_ix)
@@ -705,8 +697,8 @@ impl CompletionsMenu {
         &self,
         style: &EditorStyle,
         max_size: Size<Pixels>,
-        workspace: Option<WeakView<Workspace>>,
-        cx: &mut ViewContext<Editor>,
+        workspace: Option<WeakEntity<Workspace>>,
+        cx: &mut Context<Editor>,
     ) -> Option<AnyElement> {
         if !self.show_completion_documentation {
             return None;
@@ -1008,14 +1000,14 @@ impl CodeActionsItem {
 
 pub struct CodeActionsMenu {
     pub actions: CodeActionContents,
-    pub buffer: Model<Buffer>,
+    pub buffer: Entity<Buffer>,
     pub selected_item: usize,
     pub scroll_handle: UniformListScrollHandle,
     pub deployed_from_indicator: Option<DisplayRow>,
 }
 
 impl CodeActionsMenu {
-    fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
+    fn select_first(&mut self, cx: &mut Context<Editor>) {
         self.selected_item = if self.scroll_handle.y_flipped() {
             self.actions.len() - 1
         } else {
@@ -1026,7 +1018,7 @@ impl CodeActionsMenu {
         cx.notify()
     }
 
-    fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
+    fn select_last(&mut self, cx: &mut Context<Editor>) {
         self.selected_item = if self.scroll_handle.y_flipped() {
             0
         } else {
@@ -1037,7 +1029,7 @@ impl CodeActionsMenu {
         cx.notify()
     }
 
-    fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
+    fn select_prev(&mut self, cx: &mut Context<Editor>) {
         self.selected_item = if self.scroll_handle.y_flipped() {
             self.next_match_index()
         } else {
@@ -1048,7 +1040,7 @@ impl CodeActionsMenu {
         cx.notify();
     }
 
-    fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
+    fn select_next(&mut self, cx: &mut Context<Editor>) {
         self.selected_item = if self.scroll_handle.y_flipped() {
             self.prev_match_index()
         } else {
@@ -1092,15 +1084,16 @@ impl CodeActionsMenu {
         _style: &EditorStyle,
         max_height_in_lines: u32,
         y_flipped: bool,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) -> AnyElement {
         let actions = self.actions.clone();
         let selected_item = self.selected_item;
         let list = uniform_list(
-            cx.view().clone(),
+            cx.model().clone(),
             "code_actions_menu",
             self.actions.len(),
-            move |_this, range, cx| {
+            move |_this, range, _, cx| {
                 actions
                     .iter()
                     .skip(range.start)
@@ -1115,12 +1108,13 @@ impl CodeActionsMenu {
                                 .inset(true)
                                 .toggle_state(selected)
                                 .when_some(action.as_code_action(), |this, action| {
-                                    this.on_click(cx.listener(move |editor, _, cx| {
+                                    this.on_click(cx.listener(move |editor, _, window, cx| {
                                         cx.stop_propagation();
                                         if let Some(task) = editor.confirm_code_action(
                                             &ConfirmCodeAction {
                                                 item_ix: Some(item_ix),
                                             },
+                                            window,
                                             cx,
                                         ) {
                                             task.detach_and_log_err(cx)
@@ -1139,12 +1133,13 @@ impl CodeActionsMenu {
                                     )
                                 })
                                 .when_some(action.as_task(), |this, task| {
-                                    this.on_click(cx.listener(move |editor, _, cx| {
+                                    this.on_click(cx.listener(move |editor, _, window, cx| {
                                         cx.stop_propagation();
                                         if let Some(task) = editor.confirm_code_action(
                                             &ConfirmCodeAction {
                                                 item_ix: Some(item_ix),
                                             },
+                                            window,
                                             cx,
                                         ) {
                                             task.detach_and_log_err(cx)
@@ -1165,7 +1160,7 @@ impl CodeActionsMenu {
             },
         )
         .occlude()
-        .max_h(max_height_in_lines as f32 * cx.line_height())
+        .max_h(max_height_in_lines as f32 * window.line_height())
         .track_scroll(self.scroll_handle.clone())
         .y_flipped(y_flipped)
         .with_width_from_item(

crates/editor/src/display_map.rs 🔗

@@ -39,10 +39,7 @@ use collections::{HashMap, HashSet};
 pub use crease_map::*;
 pub use fold_map::{Fold, FoldId, FoldPlaceholder, FoldPoint};
 use fold_map::{FoldMap, FoldSnapshot};
-use gpui::{
-    AnyElement, AppContext, Font, HighlightStyle, LineLayout, Model, ModelContext, Pixels,
-    UnderlineStyle,
-};
+use gpui::{App, Context, Entity, Font, HighlightStyle, LineLayout, Pixels, UnderlineStyle};
 pub use inlay_map::Inlay;
 use inlay_map::{InlayMap, InlaySnapshot};
 pub use inlay_map::{InlayOffset, InlayPoint};
@@ -69,7 +66,7 @@ use std::{
 use sum_tree::{Bias, TreeMap};
 use tab_map::{TabMap, TabSnapshot};
 use text::{BufferId, LineIndent};
-use ui::{px, SharedString, WindowContext};
+use ui::{px, SharedString};
 use unicode_segmentation::UnicodeSegmentation;
 use wrap_map::{WrapMap, WrapSnapshot};
 
@@ -79,8 +76,6 @@ pub enum FoldStatus {
     Foldable,
 }
 
-pub type RenderFoldToggle = Arc<dyn Fn(FoldStatus, &mut WindowContext) -> AnyElement>;
-
 pub trait ToDisplayPoint {
     fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint;
 }
@@ -94,7 +89,7 @@ type InlayHighlights = TreeMap<TypeId, TreeMap<InlayId, (HighlightStyle, InlayHi
 /// See the [module level documentation](self) for more information.
 pub struct DisplayMap {
     /// The buffer that we are displaying.
-    buffer: Model<MultiBuffer>,
+    buffer: Entity<MultiBuffer>,
     buffer_subscription: BufferSubscription,
     /// Decides where the [`Inlay`]s should be displayed.
     inlay_map: InlayMap,
@@ -103,7 +98,7 @@ pub struct DisplayMap {
     /// Keeps track of hard tabs in a buffer.
     tab_map: TabMap,
     /// Handles soft wrapping.
-    wrap_map: Model<WrapMap>,
+    wrap_map: Entity<WrapMap>,
     /// Tracks custom blocks such as diagnostics that should be displayed within buffer.
     block_map: BlockMap,
     /// Regions of text that should be highlighted.
@@ -120,7 +115,7 @@ pub struct DisplayMap {
 impl DisplayMap {
     #[allow(clippy::too_many_arguments)]
     pub fn new(
-        buffer: Model<MultiBuffer>,
+        buffer: Entity<MultiBuffer>,
         font: Font,
         font_size: Pixels,
         wrap_width: Option<Pixels>,
@@ -129,7 +124,7 @@ impl DisplayMap {
         excerpt_header_height: u32,
         excerpt_footer_height: u32,
         fold_placeholder: FoldPlaceholder,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Self {
         let buffer_subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
 
@@ -167,7 +162,7 @@ impl DisplayMap {
         }
     }
 
-    pub fn snapshot(&mut self, cx: &mut ModelContext<Self>) -> DisplaySnapshot {
+    pub fn snapshot(&mut self, cx: &mut Context<Self>) -> DisplaySnapshot {
         let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
         let edits = self.buffer_subscription.consume().into_inner();
         let (inlay_snapshot, edits) = self.inlay_map.sync(buffer_snapshot, edits);
@@ -195,7 +190,7 @@ impl DisplayMap {
         }
     }
 
-    pub fn set_state(&mut self, other: &DisplaySnapshot, cx: &mut ModelContext<Self>) {
+    pub fn set_state(&mut self, other: &DisplaySnapshot, cx: &mut Context<Self>) {
         self.fold(
             other
                 .folds_in_range(0..other.buffer_snapshot.len())
@@ -211,11 +206,7 @@ impl DisplayMap {
     }
 
     /// Creates folds for the given creases.
-    pub fn fold<T: Clone + ToOffset>(
-        &mut self,
-        creases: Vec<Crease<T>>,
-        cx: &mut ModelContext<Self>,
-    ) {
+    pub fn fold<T: Clone + ToOffset>(&mut self, creases: Vec<Crease<T>>, cx: &mut Context<Self>) {
         let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
         let edits = self.buffer_subscription.consume().into_inner();
         let tab_size = Self::tab_size(&self.buffer, cx);
@@ -287,7 +278,7 @@ impl DisplayMap {
         &mut self,
         ranges: impl IntoIterator<Item = Range<T>>,
         type_id: TypeId,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let snapshot = self.buffer.read(cx).snapshot(cx);
         let edits = self.buffer_subscription.consume().into_inner();
@@ -312,7 +303,7 @@ impl DisplayMap {
         &mut self,
         ranges: impl IntoIterator<Item = Range<T>>,
         inclusive: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let snapshot = self.buffer.read(cx).snapshot(cx);
         let offset_ranges = ranges
@@ -339,7 +330,7 @@ impl DisplayMap {
         block_map.remove_intersecting_replace_blocks(offset_ranges, inclusive);
     }
 
-    pub fn fold_buffer(&mut self, buffer_id: language::BufferId, cx: &mut ModelContext<Self>) {
+    pub fn fold_buffer(&mut self, buffer_id: language::BufferId, cx: &mut Context<Self>) {
         let snapshot = self.buffer.read(cx).snapshot(cx);
         let edits = self.buffer_subscription.consume().into_inner();
         let tab_size = Self::tab_size(&self.buffer, cx);
@@ -353,7 +344,7 @@ impl DisplayMap {
         block_map.fold_buffer(buffer_id, self.buffer.read(cx), cx)
     }
 
-    pub fn unfold_buffer(&mut self, buffer_id: language::BufferId, cx: &mut ModelContext<Self>) {
+    pub fn unfold_buffer(&mut self, buffer_id: language::BufferId, cx: &mut Context<Self>) {
         let snapshot = self.buffer.read(cx).snapshot(cx);
         let edits = self.buffer_subscription.consume().into_inner();
         let tab_size = Self::tab_size(&self.buffer, cx);
@@ -378,7 +369,7 @@ impl DisplayMap {
     pub fn insert_creases(
         &mut self,
         creases: impl IntoIterator<Item = Crease<Anchor>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Vec<CreaseId> {
         let snapshot = self.buffer.read(cx).snapshot(cx);
         self.crease_map.insert(creases, &snapshot)
@@ -387,7 +378,7 @@ impl DisplayMap {
     pub fn remove_creases(
         &mut self,
         crease_ids: impl IntoIterator<Item = CreaseId>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let snapshot = self.buffer.read(cx).snapshot(cx);
         self.crease_map.remove(crease_ids, &snapshot)
@@ -396,7 +387,7 @@ impl DisplayMap {
     pub fn insert_blocks(
         &mut self,
         blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Vec<CustomBlockId> {
         let snapshot = self.buffer.read(cx).snapshot(cx);
         let edits = self.buffer_subscription.consume().into_inner();
@@ -411,11 +402,7 @@ impl DisplayMap {
         block_map.insert(blocks)
     }
 
-    pub fn resize_blocks(
-        &mut self,
-        heights: HashMap<CustomBlockId, u32>,
-        cx: &mut ModelContext<Self>,
-    ) {
+    pub fn resize_blocks(&mut self, heights: HashMap<CustomBlockId, u32>, cx: &mut Context<Self>) {
         let snapshot = self.buffer.read(cx).snapshot(cx);
         let edits = self.buffer_subscription.consume().into_inner();
         let tab_size = Self::tab_size(&self.buffer, cx);
@@ -433,7 +420,7 @@ impl DisplayMap {
         self.block_map.replace_blocks(renderers);
     }
 
-    pub fn remove_blocks(&mut self, ids: HashSet<CustomBlockId>, cx: &mut ModelContext<Self>) {
+    pub fn remove_blocks(&mut self, ids: HashSet<CustomBlockId>, cx: &mut Context<Self>) {
         let snapshot = self.buffer.read(cx).snapshot(cx);
         let edits = self.buffer_subscription.consume().into_inner();
         let tab_size = Self::tab_size(&self.buffer, cx);
@@ -450,7 +437,7 @@ impl DisplayMap {
     pub fn row_for_block(
         &mut self,
         block_id: CustomBlockId,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<DisplayRow> {
         let snapshot = self.buffer.read(cx).snapshot(cx);
         let edits = self.buffer_subscription.consume().into_inner();
@@ -505,12 +492,12 @@ impl DisplayMap {
         cleared
     }
 
-    pub fn set_font(&self, font: Font, font_size: Pixels, cx: &mut ModelContext<Self>) -> bool {
+    pub fn set_font(&self, font: Font, font_size: Pixels, cx: &mut Context<Self>) -> bool {
         self.wrap_map
             .update(cx, |map, cx| map.set_font_with_size(font, font_size, cx))
     }
 
-    pub fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut ModelContext<Self>) -> bool {
+    pub fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut Context<Self>) -> bool {
         self.wrap_map
             .update(cx, |map, cx| map.set_wrap_width(width, cx))
     }
@@ -523,7 +510,7 @@ impl DisplayMap {
         &mut self,
         to_remove: Vec<InlayId>,
         to_insert: Vec<Inlay>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if to_remove.is_empty() && to_insert.is_empty() {
             return;
@@ -548,7 +535,7 @@ impl DisplayMap {
         self.block_map.read(snapshot, edits);
     }
 
-    fn tab_size(buffer: &Model<MultiBuffer>, cx: &AppContext) -> NonZeroU32 {
+    fn tab_size(buffer: &Entity<MultiBuffer>, cx: &App) -> NonZeroU32 {
         let buffer = buffer.read(cx).as_singleton().map(|buffer| buffer.read(cx));
         let language = buffer
             .and_then(|buffer| buffer.language())
@@ -558,7 +545,7 @@ impl DisplayMap {
     }
 
     #[cfg(test)]
-    pub fn is_rewrapping(&self, cx: &gpui::AppContext) -> bool {
+    pub fn is_rewrapping(&self, cx: &gpui::App) -> bool {
         self.wrap_map.read(cx).is_rewrapping()
     }
 
@@ -1452,7 +1439,7 @@ pub mod tests {
     use crate::{movement, test::marked_display_snapshot};
     use block_map::BlockPlacement;
     use gpui::{
-        div, font, observe, px, AppContext, BorrowAppContext, Context, Element, Hsla, Rgba,
+        div, font, observe, px, App, AppContext as _, BorrowAppContext, Element, Hsla, Rgba,
     };
     use language::{
         language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
@@ -1508,7 +1495,7 @@ pub mod tests {
             }
         });
 
-        let map = cx.new_model(|cx| {
+        let map = cx.new(|cx| {
             DisplayMap::new(
                 buffer.clone(),
                 font("Helvetica"),
@@ -1749,16 +1736,16 @@ pub mod tests {
         let editor = cx.editor.clone();
         let window = cx.window;
 
-        _ = cx.update_window(window, |_, cx| {
+        _ = cx.update_window(window, |_, window, cx| {
             let text_layout_details =
-                editor.update(cx, |editor, cx| editor.text_layout_details(cx));
+                editor.update(cx, |editor, _cx| editor.text_layout_details(window));
 
             let font_size = px(12.0);
             let wrap_width = Some(px(64.));
 
             let text = "one two three four five\nsix seven eight";
             let buffer = MultiBuffer::build_simple(text, cx);
-            let map = cx.new_model(|cx| {
+            let map = cx.new(|cx| {
                 DisplayMap::new(
                     buffer.clone(),
                     font("Helvetica"),
@@ -1862,14 +1849,14 @@ pub mod tests {
     }
 
     #[gpui::test]
-    fn test_text_chunks(cx: &mut gpui::AppContext) {
+    fn test_text_chunks(cx: &mut gpui::App) {
         init_test(cx, |_| {});
 
         let text = sample_text(6, 6, 'a');
         let buffer = MultiBuffer::build_simple(&text, cx);
 
         let font_size = px(14.0);
-        let map = cx.new_model(|cx| {
+        let map = cx.new(|cx| {
             DisplayMap::new(
                 buffer.clone(),
                 font("Helvetica"),
@@ -1959,13 +1946,13 @@ pub mod tests {
 
         cx.update(|cx| init_test(cx, |s| s.defaults.tab_size = Some(2.try_into().unwrap())));
 
-        let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
+        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
         cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
+        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 
         let font_size = px(14.0);
 
-        let map = cx.new_model(|cx| {
+        let map = cx.new(|cx| {
             DisplayMap::new(
                 buffer,
                 font("Helvetica"),
@@ -2062,12 +2049,12 @@ pub mod tests {
 
         cx.update(|cx| init_test(cx, |_| {}));
 
-        let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
+        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
         cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
+        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
         let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
 
-        let map = cx.new_model(|cx| {
+        let map = cx.new(|cx| {
             DisplayMap::new(
                 buffer,
                 font("Courier"),
@@ -2136,7 +2123,7 @@ pub mod tests {
 
         cx.update(|cx| init_test(cx, |_| {}));
 
-        let buffer = cx.new_model(|cx| Buffer::local(text, cx));
+        let buffer = cx.new(|cx| Buffer::local(text, cx));
 
         buffer.update(cx, |buffer, cx| {
             buffer.update_diagnostics(
@@ -2157,10 +2144,10 @@ pub mod tests {
             )
         });
 
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
+        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
         let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
 
-        let map = cx.new_model(|cx| {
+        let map = cx.new(|cx| {
             DisplayMap::new(
                 buffer,
                 font("Courier"),
@@ -2249,7 +2236,7 @@ pub mod tests {
 
         let buffer = cx.update(|cx| MultiBuffer::build_simple("abcde\nfghij\nklmno\npqrst", cx));
         let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
-        let map = cx.new_model(|cx| {
+        let map = cx.new(|cx| {
             DisplayMap::new(
                 buffer.clone(),
                 font("Courier"),
@@ -2387,13 +2374,13 @@ pub mod tests {
 
         cx.update(|cx| init_test(cx, |_| {}));
 
-        let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
+        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
         cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
+        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 
         let font_size = px(16.0);
 
-        let map = cx.new_model(|cx| {
+        let map = cx.new(|cx| {
             DisplayMap::new(
                 buffer,
                 font("Courier"),
@@ -2470,14 +2457,14 @@ pub mod tests {
 
         let (text, highlighted_ranges) = marked_text_ranges(r#"constˇ «a»: B = "c «d»""#, false);
 
-        let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
+        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
         cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
 
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
+        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
         let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
 
         let font_size = px(16.0);
-        let map = cx.new_model(|cx| {
+        let map = cx.new(|cx| {
             DisplayMap::new(
                 buffer,
                 font("Courier"),
@@ -2528,10 +2515,10 @@ pub mod tests {
     }
 
     #[gpui::test]
-    fn test_clip_point(cx: &mut gpui::AppContext) {
+    fn test_clip_point(cx: &mut gpui::App) {
         init_test(cx, |_| {});
 
-        fn assert(text: &str, shift_right: bool, bias: Bias, cx: &mut gpui::AppContext) {
+        fn assert(text: &str, shift_right: bool, bias: Bias, cx: &mut gpui::App) {
             let (unmarked_snapshot, mut markers) = marked_display_snapshot(text, cx);
 
             match bias {
@@ -2578,10 +2565,10 @@ pub mod tests {
     }
 
     #[gpui::test]
-    fn test_clip_at_line_ends(cx: &mut gpui::AppContext) {
+    fn test_clip_at_line_ends(cx: &mut gpui::App) {
         init_test(cx, |_| {});
 
-        fn assert(text: &str, cx: &mut gpui::AppContext) {
+        fn assert(text: &str, cx: &mut gpui::App) {
             let (mut unmarked_snapshot, markers) = marked_display_snapshot(text, cx);
             unmarked_snapshot.clip_at_line_ends = true;
             assert_eq!(
@@ -2597,13 +2584,13 @@ pub mod tests {
     }
 
     #[gpui::test]
-    fn test_creases(cx: &mut gpui::AppContext) {
+    fn test_creases(cx: &mut gpui::App) {
         init_test(cx, |_| {});
 
         let text = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll";
         let buffer = MultiBuffer::build_simple(text, cx);
         let font_size = px(14.0);
-        cx.new_model(|cx| {
+        cx.new(|cx| {
             let mut map = DisplayMap::new(
                 buffer.clone(),
                 font("Helvetica"),
@@ -2624,8 +2611,8 @@ pub mod tests {
                 [Crease::inline(
                     range,
                     FoldPlaceholder::test(),
-                    |_row, _status, _toggle, _cx| div(),
-                    |_row, _status, _cx| div(),
+                    |_row, _status, _toggle, _window, _cx| div(),
+                    |_row, _status, _window, _cx| div(),
                 )],
                 &map.buffer.read(cx).snapshot(cx),
             );
@@ -2635,14 +2622,14 @@ pub mod tests {
     }
 
     #[gpui::test]
-    fn test_tabs_with_multibyte_chars(cx: &mut gpui::AppContext) {
+    fn test_tabs_with_multibyte_chars(cx: &mut gpui::App) {
         init_test(cx, |_| {});
 
         let text = "✅\t\tα\nβ\t\n🏀β\t\tγ";
         let buffer = MultiBuffer::build_simple(text, cx);
         let font_size = px(14.0);
 
-        let map = cx.new_model(|cx| {
+        let map = cx.new(|cx| {
             DisplayMap::new(
                 buffer.clone(),
                 font("Helvetica"),
@@ -2714,12 +2701,12 @@ pub mod tests {
     }
 
     #[gpui::test]
-    fn test_max_point(cx: &mut gpui::AppContext) {
+    fn test_max_point(cx: &mut gpui::App) {
         init_test(cx, |_| {});
 
         let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", cx);
         let font_size = px(14.0);
-        let map = cx.new_model(|cx| {
+        let map = cx.new(|cx| {
             DisplayMap::new(
                 buffer.clone(),
                 font("Helvetica"),
@@ -2741,9 +2728,9 @@ pub mod tests {
 
     fn syntax_chunks(
         rows: Range<DisplayRow>,
-        map: &Model<DisplayMap>,
+        map: &Entity<DisplayMap>,
         theme: &SyntaxTheme,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Vec<(String, Option<Hsla>)> {
         chunks(rows, map, theme, cx)
             .into_iter()
@@ -2753,9 +2740,9 @@ pub mod tests {
 
     fn chunks(
         rows: Range<DisplayRow>,
-        map: &Model<DisplayMap>,
+        map: &Entity<DisplayMap>,
         theme: &SyntaxTheme,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Vec<(String, Option<Hsla>, Option<Hsla>)> {
         let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
         let mut chunks: Vec<(String, Option<Hsla>, Option<Hsla>)> = Vec::new();
@@ -2775,7 +2762,7 @@ pub mod tests {
         chunks
     }
 
-    fn init_test(cx: &mut AppContext, f: impl Fn(&mut AllLanguageSettingsContent)) {
+    fn init_test(cx: &mut App, f: impl Fn(&mut AllLanguageSettingsContent)) {
         let settings = SettingsStore::test(cx);
         cx.set_global(settings);
         language::init(cx);

crates/editor/src/display_map/block_map.rs 🔗

@@ -4,7 +4,7 @@ use super::{
 };
 use crate::{EditorStyle, GutterDimensions};
 use collections::{Bound, HashMap, HashSet};
-use gpui::{AnyElement, AppContext, EntityId, Pixels, WindowContext};
+use gpui::{AnyElement, App, EntityId, Pixels, Window};
 use language::{Chunk, Patch, Point};
 use multi_buffer::{
     Anchor, ExcerptId, ExcerptInfo, MultiBuffer, MultiBufferRow, MultiBufferSnapshot, RowInfo,
@@ -226,7 +226,8 @@ pub enum BlockStyle {
 }
 
 pub struct BlockContext<'a, 'b> {
-    pub context: &'b mut WindowContext<'a>,
+    pub window: &'a mut Window,
+    pub app: &'b mut App,
     pub anchor_x: Pixels,
     pub max_width: Pixels,
     pub gutter_dimensions: &'b GutterDimensions,
@@ -1232,22 +1233,12 @@ impl<'a> BlockMapWriter<'a> {
         self.remove(blocks_to_remove);
     }
 
-    pub fn fold_buffer(
-        &mut self,
-        buffer_id: BufferId,
-        multi_buffer: &MultiBuffer,
-        cx: &AppContext,
-    ) {
+    pub fn fold_buffer(&mut self, buffer_id: BufferId, multi_buffer: &MultiBuffer, cx: &App) {
         self.0.folded_buffers.insert(buffer_id);
         self.recompute_blocks_for_buffer(buffer_id, multi_buffer, cx);
     }
 
-    pub fn unfold_buffer(
-        &mut self,
-        buffer_id: BufferId,
-        multi_buffer: &MultiBuffer,
-        cx: &AppContext,
-    ) {
+    pub fn unfold_buffer(&mut self, buffer_id: BufferId, multi_buffer: &MultiBuffer, cx: &App) {
         self.0.folded_buffers.remove(&buffer_id);
         self.recompute_blocks_for_buffer(buffer_id, multi_buffer, cx);
     }
@@ -1256,7 +1247,7 @@ impl<'a> BlockMapWriter<'a> {
         &mut self,
         buffer_id: BufferId,
         multi_buffer: &MultiBuffer,
-        cx: &AppContext,
+        cx: &App,
     ) {
         let wrap_snapshot = self.0.wrap_snapshot.borrow().clone();
 
@@ -1934,16 +1925,16 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
 }
 
 impl<'a> Deref for BlockContext<'a, '_> {
-    type Target = WindowContext<'a>;
+    type Target = App;
 
     fn deref(&self) -> &Self::Target {
-        self.context
+        self.app
     }
 }
 
 impl DerefMut for BlockContext<'_, '_> {
     fn deref_mut(&mut self) -> &mut Self::Target {
-        self.context
+        self.app
     }
 }
 
@@ -2001,7 +1992,7 @@ mod tests {
     use crate::display_map::{
         fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap,
     };
-    use gpui::{div, font, px, AppContext, Context as _, Element};
+    use gpui::{div, font, px, App, AppContext as _, Element};
     use itertools::Itertools;
     use language::{Buffer, Capability};
     use multi_buffer::{ExcerptRange, MultiBuffer};
@@ -2195,15 +2186,15 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_multibuffer_headers_and_footers(cx: &mut AppContext) {
+    fn test_multibuffer_headers_and_footers(cx: &mut App) {
         init_test(cx);
 
-        let buffer1 = cx.new_model(|cx| Buffer::local("Buffer 1", cx));
-        let buffer2 = cx.new_model(|cx| Buffer::local("Buffer 2", cx));
-        let buffer3 = cx.new_model(|cx| Buffer::local("Buffer 3", cx));
+        let buffer1 = cx.new(|cx| Buffer::local("Buffer 1", cx));
+        let buffer2 = cx.new(|cx| Buffer::local("Buffer 2", cx));
+        let buffer3 = cx.new(|cx| Buffer::local("Buffer 3", cx));
 
         let mut excerpt_ids = Vec::new();
-        let multi_buffer = cx.new_model(|cx| {
+        let multi_buffer = cx.new(|cx| {
             let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
             excerpt_ids.extend(multi_buffer.push_excerpts(
                 buffer1.clone(),
@@ -3652,7 +3643,7 @@ mod tests {
         }
     }
 
-    fn init_test(cx: &mut gpui::AppContext) {
+    fn init_test(cx: &mut gpui::App) {
         let settings = SettingsStore::test(cx);
         cx.set_global(settings);
         theme::init(theme::LoadThemes::JustBase, cx);

crates/editor/src/display_map/crease_map.rs 🔗

@@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
 use std::{cmp::Ordering, fmt::Debug, ops::Range, sync::Arc};
 use sum_tree::{Bias, SeekTarget, SumTree};
 use text::Point;
-use ui::{IconName, SharedString, WindowContext};
+use ui::{App, IconName, SharedString, Window};
 
 use crate::{BlockStyle, FoldPlaceholder, RenderBlock};
 
@@ -117,12 +117,13 @@ type RenderToggleFn = Arc<
         + Fn(
             MultiBufferRow,
             bool,
-            Arc<dyn Send + Sync + Fn(bool, &mut WindowContext)>,
-            &mut WindowContext,
+            Arc<dyn Send + Sync + Fn(bool, &mut Window, &mut App)>,
+            &mut Window,
+            &mut App,
         ) -> AnyElement,
 >;
 type RenderTrailerFn =
-    Arc<dyn Send + Sync + Fn(MultiBufferRow, bool, &mut WindowContext) -> AnyElement>;
+    Arc<dyn Send + Sync + Fn(MultiBufferRow, bool, &mut Window, &mut App) -> AnyElement>;
 
 #[derive(Clone)]
 pub enum Crease<T> {
@@ -185,26 +186,27 @@ impl<T> Crease<T> {
             + Fn(
                 MultiBufferRow,
                 bool,
-                Arc<dyn Send + Sync + Fn(bool, &mut WindowContext)>,
-                &mut WindowContext,
+                Arc<dyn Send + Sync + Fn(bool, &mut Window, &mut App)>,
+                &mut Window,
+                &mut App,
             ) -> ToggleElement
             + 'static,
         ToggleElement: IntoElement,
         RenderTrailer: 'static
             + Send
             + Sync
-            + Fn(MultiBufferRow, bool, &mut WindowContext) -> TrailerElement
+            + Fn(MultiBufferRow, bool, &mut Window, &mut App) -> TrailerElement
             + 'static,
         TrailerElement: IntoElement,
     {
         Crease::Inline {
             range,
             placeholder,
-            render_toggle: Some(Arc::new(move |row, folded, toggle, cx| {
-                render_toggle(row, folded, toggle, cx).into_any_element()
+            render_toggle: Some(Arc::new(move |row, folded, toggle, window, cx| {
+                render_toggle(row, folded, toggle, window, cx).into_any_element()
             })),
-            render_trailer: Some(Arc::new(move |row, folded, cx| {
-                render_trailer(row, folded, cx).into_any_element()
+            render_trailer: Some(Arc::new(move |row, folded, window, cx| {
+                render_trailer(row, folded, window, cx).into_any_element()
             })),
             metadata: None,
         }
@@ -387,11 +389,11 @@ impl SeekTarget<'_, ItemSummary, ItemSummary> for Anchor {
 #[cfg(test)]
 mod test {
     use super::*;
-    use gpui::{div, AppContext};
+    use gpui::{div, App};
     use multi_buffer::MultiBuffer;
 
     #[gpui::test]
-    fn test_insert_and_remove_creases(cx: &mut AppContext) {
+    fn test_insert_and_remove_creases(cx: &mut App) {
         let text = "line1\nline2\nline3\nline4\nline5";
         let buffer = MultiBuffer::build_simple(text, cx);
         let snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
@@ -402,14 +404,14 @@ mod test {
             Crease::inline(
                 snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(1, 5)),
                 FoldPlaceholder::test(),
-                |_row, _folded, _toggle, _cx| div(),
-                |_row, _folded, _cx| div(),
+                |_row, _folded, _toggle, _window, _cx| div(),
+                |_row, _folded, _window, _cx| div(),
             ),
             Crease::inline(
                 snapshot.anchor_before(Point::new(3, 0))..snapshot.anchor_after(Point::new(3, 5)),
                 FoldPlaceholder::test(),
-                |_row, _folded, _toggle, _cx| div(),
-                |_row, _folded, _cx| div(),
+                |_row, _folded, _toggle, _window, _cx| div(),
+                |_row, _folded, _window, _cx| div(),
             ),
         ];
         let crease_ids = crease_map.insert(creases, &snapshot);
@@ -438,7 +440,7 @@ mod test {
     }
 
     #[gpui::test]
-    fn test_creases_in_range(cx: &mut AppContext) {
+    fn test_creases_in_range(cx: &mut App) {
         let text = "line1\nline2\nline3\nline4\nline5\nline6\nline7";
         let buffer = MultiBuffer::build_simple(text, cx);
         let snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
@@ -448,20 +450,20 @@ mod test {
             Crease::inline(
                 snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(1, 5)),
                 FoldPlaceholder::test(),
-                |_row, _folded, _toggle, _cx| div(),
-                |_row, _folded, _cx| div(),
+                |_row, _folded, _toggle, _window, _cx| div(),
+                |_row, _folded, _window, _cx| div(),
             ),
             Crease::inline(
                 snapshot.anchor_before(Point::new(3, 0))..snapshot.anchor_after(Point::new(3, 5)),
                 FoldPlaceholder::test(),
-                |_row, _folded, _toggle, _cx| div(),
-                |_row, _folded, _cx| div(),
+                |_row, _folded, _toggle, _window, _cx| div(),
+                |_row, _folded, _window, _cx| div(),
             ),
             Crease::inline(
                 snapshot.anchor_before(Point::new(5, 0))..snapshot.anchor_after(Point::new(5, 5)),
                 FoldPlaceholder::test(),
-                |_row, _folded, _toggle, _cx| div(),
-                |_row, _folded, _cx| div(),
+                |_row, _folded, _toggle, _window, _cx| div(),
+                |_row, _folded, _window, _cx| div(),
             ),
         ];
         crease_map.insert(creases, &snapshot);

crates/editor/src/display_map/fold_map.rs 🔗

@@ -2,7 +2,7 @@ use super::{
     inlay_map::{InlayBufferRows, InlayChunks, InlayEdit, InlayOffset, InlayPoint, InlaySnapshot},
     Highlights,
 };
-use gpui::{AnyElement, ElementId, WindowContext};
+use gpui::{AnyElement, App, ElementId, Window};
 use language::{Chunk, ChunkRenderer, Edit, Point, TextSummary};
 use multi_buffer::{
     Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, RowInfo, ToOffset,
@@ -21,7 +21,8 @@ use util::post_inc;
 #[derive(Clone)]
 pub struct FoldPlaceholder {
     /// Creates an element to represent this fold's placeholder.
-    pub render: Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut WindowContext) -> AnyElement>,
+    pub render:
+        Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut Window, &mut App) -> AnyElement>,
     /// If true, the element is constrained to the shaped width of an ellipsis.
     pub constrain_width: bool,
     /// If true, merges the fold with an adjacent one.
@@ -33,7 +34,7 @@ pub struct FoldPlaceholder {
 impl Default for FoldPlaceholder {
     fn default() -> Self {
         Self {
-            render: Arc::new(|_, _, _| gpui::Empty.into_any_element()),
+            render: Arc::new(|_, _, _, _| gpui::Empty.into_any_element()),
             constrain_width: true,
             merge_adjacent: true,
             type_tag: None,
@@ -45,7 +46,7 @@ impl FoldPlaceholder {
     #[cfg(any(test, feature = "test-support"))]
     pub fn test() -> Self {
         Self {
-            render: Arc::new(|_id, _range, _cx| gpui::Empty.into_any_element()),
+            render: Arc::new(|_id, _range, _window, _cx| gpui::Empty.into_any_element()),
             constrain_width: true,
             merge_adjacent: true,
             type_tag: None,
@@ -485,7 +486,8 @@ impl FoldMap {
                                             (fold.placeholder.render)(
                                                 fold_id,
                                                 fold.range.0.clone(),
-                                                cx,
+                                                cx.window,
+                                                cx.context,
                                             )
                                         }),
                                         constrain_width: fold.placeholder.constrain_width,
@@ -1395,7 +1397,7 @@ mod tests {
     use Bias::{Left, Right};
 
     #[gpui::test]
-    fn test_basic_folds(cx: &mut gpui::AppContext) {
+    fn test_basic_folds(cx: &mut gpui::App) {
         init_test(cx);
         let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
         let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
@@ -1474,7 +1476,7 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_adjacent_folds(cx: &mut gpui::AppContext) {
+    fn test_adjacent_folds(cx: &mut gpui::App) {
         init_test(cx);
         let buffer = MultiBuffer::build_simple("abcdefghijkl", cx);
         let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
@@ -1533,7 +1535,7 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_overlapping_folds(cx: &mut gpui::AppContext) {
+    fn test_overlapping_folds(cx: &mut gpui::App) {
         let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
         let buffer_snapshot = buffer.read(cx).snapshot(cx);
         let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
@@ -1550,7 +1552,7 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_merging_folds_via_edit(cx: &mut gpui::AppContext) {
+    fn test_merging_folds_via_edit(cx: &mut gpui::App) {
         init_test(cx);
         let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
         let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
@@ -1577,7 +1579,7 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_folds_in_range(cx: &mut gpui::AppContext) {
+    fn test_folds_in_range(cx: &mut gpui::App) {
         let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
         let buffer_snapshot = buffer.read(cx).snapshot(cx);
         let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
@@ -1608,7 +1610,7 @@ mod tests {
     }
 
     #[gpui::test(iterations = 100)]
-    fn test_random_folds(cx: &mut gpui::AppContext, mut rng: StdRng) {
+    fn test_random_folds(cx: &mut gpui::App, mut rng: StdRng) {
         init_test(cx);
         let operations = env::var("OPERATIONS")
             .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
@@ -1879,7 +1881,7 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_buffer_rows(cx: &mut gpui::AppContext) {
+    fn test_buffer_rows(cx: &mut gpui::App) {
         let text = sample_text(6, 6, 'a') + "\n";
         let buffer = MultiBuffer::build_simple(&text, cx);
 
@@ -1911,7 +1913,7 @@ mod tests {
         );
     }
 
-    fn init_test(cx: &mut gpui::AppContext) {
+    fn init_test(cx: &mut gpui::App) {
         let store = SettingsStore::test(cx);
         cx.set_global(store);
     }

crates/editor/src/display_map/inlay_map.rs 🔗

@@ -1070,7 +1070,7 @@ mod tests {
         hover_links::InlayHighlight,
         InlayId, MultiBuffer,
     };
-    use gpui::{AppContext, HighlightStyle};
+    use gpui::{App, HighlightStyle};
     use project::{InlayHint, InlayHintLabel, ResolveState};
     use rand::prelude::*;
     use settings::SettingsStore;
@@ -1163,7 +1163,7 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_basic_inlays(cx: &mut AppContext) {
+    fn test_basic_inlays(cx: &mut App) {
         let buffer = MultiBuffer::build_simple("abcdefghi", cx);
         let buffer_edits = buffer.update(cx, |buffer, _| buffer.subscribe());
         let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));
@@ -1451,7 +1451,7 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_inlay_buffer_rows(cx: &mut AppContext) {
+    fn test_inlay_buffer_rows(cx: &mut App) {
         let buffer = MultiBuffer::build_simple("abc\ndef\nghi", cx);
         let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));
         assert_eq!(inlay_snapshot.text(), "abc\ndef\nghi");
@@ -1488,7 +1488,7 @@ mod tests {
     }
 
     #[gpui::test(iterations = 100)]
-    fn test_random_inlays(cx: &mut AppContext, mut rng: StdRng) {
+    fn test_random_inlays(cx: &mut App, mut rng: StdRng) {
         init_test(cx);
 
         let operations = env::var("OPERATIONS")
@@ -1792,7 +1792,7 @@ mod tests {
         }
     }
 
-    fn init_test(cx: &mut AppContext) {
+    fn init_test(cx: &mut App) {
         let store = SettingsStore::test(cx);
         cx.set_global(store);
         theme::init(theme::LoadThemes::JustBase, cx);

crates/editor/src/display_map/tab_map.rs 🔗

@@ -608,7 +608,7 @@ mod tests {
     use rand::{prelude::StdRng, Rng};
 
     #[gpui::test]
-    fn test_expand_tabs(cx: &mut gpui::AppContext) {
+    fn test_expand_tabs(cx: &mut gpui::App) {
         let buffer = MultiBuffer::build_simple("", cx);
         let buffer_snapshot = buffer.read(cx).snapshot(cx);
         let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
@@ -621,7 +621,7 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_long_lines(cx: &mut gpui::AppContext) {
+    fn test_long_lines(cx: &mut gpui::App) {
         let max_expansion_column = 12;
         let input = "A\tBC\tDEF\tG\tHI\tJ\tK\tL\tM";
         let output = "A   BC  DEF G   HI J K L M";
@@ -669,7 +669,7 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_long_lines_with_character_spanning_max_expansion_column(cx: &mut gpui::AppContext) {
+    fn test_long_lines_with_character_spanning_max_expansion_column(cx: &mut gpui::App) {
         let max_expansion_column = 8;
         let input = "abcdefg⋯hij";
 
@@ -684,7 +684,7 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_marking_tabs(cx: &mut gpui::AppContext) {
+    fn test_marking_tabs(cx: &mut gpui::App) {
         let input = "\t \thello";
 
         let buffer = MultiBuffer::build_simple(input, cx);
@@ -735,7 +735,7 @@ mod tests {
     }
 
     #[gpui::test(iterations = 100)]
-    fn test_random_tabs(cx: &mut gpui::AppContext, mut rng: StdRng) {
+    fn test_random_tabs(cx: &mut gpui::App, mut rng: StdRng) {
         let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap();
         let len = rng.gen_range(0..30);
         let buffer = if rng.gen() {

crates/editor/src/display_map/wrap_map.rs 🔗

@@ -3,7 +3,7 @@ use super::{
     tab_map::{self, TabEdit, TabPoint, TabSnapshot},
     Highlights,
 };
-use gpui::{AppContext, Context, Font, LineWrapper, Model, ModelContext, Pixels, Task};
+use gpui::{App, AppContext as _, Context, Entity, Font, LineWrapper, Pixels, Task};
 use language::{Chunk, Point};
 use multi_buffer::{MultiBufferSnapshot, RowInfo};
 use smol::future::yield_now;
@@ -90,9 +90,9 @@ impl WrapMap {
         font: Font,
         font_size: Pixels,
         wrap_width: Option<Pixels>,
-        cx: &mut AppContext,
-    ) -> (Model<Self>, WrapSnapshot) {
-        let handle = cx.new_model(|cx| {
+        cx: &mut App,
+    ) -> (Entity<Self>, WrapSnapshot) {
+        let handle = cx.new(|cx| {
             let mut this = Self {
                 font_with_size: (font, font_size),
                 wrap_width: None,
@@ -119,7 +119,7 @@ impl WrapMap {
         &mut self,
         tab_snapshot: TabSnapshot,
         edits: Vec<TabEdit>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> (WrapSnapshot, Patch<u32>) {
         if self.wrap_width.is_some() {
             self.pending_edits.push_back((tab_snapshot, edits));
@@ -138,7 +138,7 @@ impl WrapMap {
         &mut self,
         font: Font,
         font_size: Pixels,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> bool {
         let font_with_size = (font, font_size);
 
@@ -151,11 +151,7 @@ impl WrapMap {
         }
     }
 
-    pub fn set_wrap_width(
-        &mut self,
-        wrap_width: Option<Pixels>,
-        cx: &mut ModelContext<Self>,
-    ) -> bool {
+    pub fn set_wrap_width(&mut self, wrap_width: Option<Pixels>, cx: &mut Context<Self>) -> bool {
         if wrap_width == self.wrap_width {
             return false;
         }
@@ -165,7 +161,7 @@ impl WrapMap {
         true
     }
 
-    fn rewrap(&mut self, cx: &mut ModelContext<Self>) {
+    fn rewrap(&mut self, cx: &mut Context<Self>) {
         self.background_task.take();
         self.interpolated_edits.clear();
         self.pending_edits.clear();
@@ -236,7 +232,7 @@ impl WrapMap {
         }
     }
 
-    fn flush_edits(&mut self, cx: &mut ModelContext<Self>) {
+    fn flush_edits(&mut self, cx: &mut Context<Self>) {
         if !self.snapshot.interpolated {
             let mut to_remove_len = 0;
             for (tab_snapshot, _) in &self.pending_edits {

crates/editor/src/editor.rs 🔗

@@ -77,14 +77,13 @@ use code_context_menus::{
 };
 use git::blame::GitBlame;
 use gpui::{
-    div, impl_actions, point, prelude::*, px, relative, size, Action, AnyElement, AppContext,
+    div, impl_actions, point, prelude::*, px, relative, size, Action, AnyElement, App,
     AsyncWindowContext, AvailableSpace, Bounds, ClipboardEntry, ClipboardItem, Context,
-    DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusOutEvent, FocusableView, FontId,
-    FontWeight, Global, HighlightStyle, Hsla, InteractiveText, KeyContext, Model, ModelContext,
-    MouseButton, PaintQuad, ParentElement, Pixels, Render, SharedString, Size, Styled, StyledText,
-    Subscription, Task, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
-    UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext, WeakFocusHandle,
-    WeakView, WindowContext,
+    DispatchPhase, ElementId, Entity, EventEmitter, FocusHandle, FocusOutEvent, Focusable, FontId,
+    FontWeight, Global, HighlightStyle, Hsla, InteractiveText, KeyContext, MouseButton, PaintQuad,
+    ParentElement, Pixels, Render, SharedString, Size, Styled, StyledText, Subscription, Task,
+    TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle,
+    ViewInputHandler, WeakEntity, WeakFocusHandle, Window,
 };
 use highlight_matching_bracket::refresh_matching_bracket_highlights;
 use hover_popover::{hide_hover, HoverState};
@@ -194,8 +193,8 @@ pub fn render_parsed_markdown(
     element_id: impl Into<ElementId>,
     parsed: &language::ParsedMarkdown,
     editor_style: &EditorStyle,
-    workspace: Option<WeakView<Workspace>>,
-    cx: &mut WindowContext,
+    workspace: Option<WeakEntity<Workspace>>,
+    cx: &mut App,
 ) -> InteractiveText {
     let code_span_background_color = cx
         .theme()
@@ -239,18 +238,21 @@ pub fn render_parsed_markdown(
         element_id,
         StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
     )
-    .on_click(link_ranges, move |clicked_range_ix, cx| {
-        match &links[clicked_range_ix] {
+    .on_click(
+        link_ranges,
+        move |clicked_range_ix, window, cx| match &links[clicked_range_ix] {
             markdown::Link::Web { url } => cx.open_url(url),
             markdown::Link::Path { path } => {
                 if let Some(workspace) = &workspace {
                     _ = workspace.update(cx, |workspace, cx| {
-                        workspace.open_abs_path(path.clone(), false, cx).detach();
+                        workspace
+                            .open_abs_path(path.clone(), false, window, cx)
+                            .detach();
                     });
                 }
             }
-        }
-    })
+        },
+    )
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -288,19 +290,19 @@ impl Navigated {
     }
 }
 
-pub fn init_settings(cx: &mut AppContext) {
+pub fn init_settings(cx: &mut App) {
     EditorSettings::register(cx);
 }
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     init_settings(cx);
 
     workspace::register_project_item::<Editor>(cx);
     workspace::FollowableViewRegistry::register::<Editor>(cx);
     workspace::register_serializable_item::<Editor>(cx);
 
-    cx.observe_new_views(
-        |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
+    cx.observe_new(
+        |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
             workspace.register_action(Editor::new_file);
             workspace.register_action(Editor::new_file_vertical);
             workspace.register_action(Editor::new_file_horizontal);
@@ -311,18 +313,28 @@ pub fn init(cx: &mut AppContext) {
     cx.on_action(move |_: &workspace::NewFile, cx| {
         let app_state = workspace::AppState::global(cx);
         if let Some(app_state) = app_state.upgrade() {
-            workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
-                Editor::new_file(workspace, &Default::default(), cx)
-            })
+            workspace::open_new(
+                Default::default(),
+                app_state,
+                cx,
+                |workspace, window, cx| {
+                    Editor::new_file(workspace, &Default::default(), window, cx)
+                },
+            )
             .detach();
         }
     });
     cx.on_action(move |_: &workspace::NewWindow, cx| {
         let app_state = workspace::AppState::global(cx);
         if let Some(app_state) = app_state.upgrade() {
-            workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
-                Editor::new_file(workspace, &Default::default(), cx)
-            })
+            workspace::open_new(
+                Default::default(),
+                app_state,
+                cx,
+                |workspace, window, cx| {
+                    Editor::new_file(workspace, &Default::default(), window, cx)
+                },
+            )
             .detach();
         }
     });
@@ -426,7 +438,7 @@ impl Default for EditorStyle {
     }
 }
 
-pub fn make_inlay_hints_style(cx: &WindowContext) -> HighlightStyle {
+pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
     let show_background = language_settings::language_settings(None, None, cx)
         .inlay_hints
         .show_background;
@@ -438,7 +450,7 @@ pub fn make_inlay_hints_style(cx: &WindowContext) -> HighlightStyle {
     }
 }
 
-pub fn make_suggestion_styles(cx: &WindowContext) -> InlineCompletionStyles {
+pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
     InlineCompletionStyles {
         insertion: HighlightStyle {
             color: Some(cx.theme().status().predictive),
@@ -525,7 +537,7 @@ impl EditorActionId {
 // type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
 
 type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
-type GutterHighlight = (fn(&AppContext) -> Hsla, Arc<[Range<Anchor>]>);
+type GutterHighlight = (fn(&App) -> Hsla, Arc<[Range<Anchor>]>);
 
 #[derive(Default)]
 struct ScrollbarMarkerState {
@@ -578,7 +590,7 @@ struct BufferOffset(usize);
 
 // Addons allow storing per-editor state in other crates (e.g. Vim)
 pub trait Addon: 'static {
-    fn extend_key_context(&self, _: &mut KeyContext, _: &AppContext) {}
+    fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
 
     fn to_any(&self) -> &dyn std::any::Any;
 }
@@ -589,17 +601,17 @@ pub enum IsVimMode {
     No,
 }
 
-/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
+/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
 ///
 /// See the [module level documentation](self) for more information.
 pub struct Editor {
     focus_handle: FocusHandle,
     last_focused_descendant: Option<WeakFocusHandle>,
     /// The text buffer being edited
-    buffer: Model<MultiBuffer>,
+    buffer: Entity<MultiBuffer>,
     /// Map of how text in the buffer should be displayed.
     /// Handles soft wraps, folds, fake inlay text insertions, etc.
-    pub display_map: Model<DisplayMap>,
+    pub display_map: Entity<DisplayMap>,
     pub selections: SelectionsCollection,
     pub scroll_manager: ScrollManager,
     /// When inline assist editors are linked, they all render cursors because
@@ -617,11 +629,11 @@ pub struct Editor {
     active_diagnostics: Option<ActiveDiagnosticGroup>,
     soft_wrap_mode_override: Option<language_settings::SoftWrap>,
 
-    project: Option<Model<Project>>,
+    project: Option<Entity<Project>>,
     semantics_provider: Option<Rc<dyn SemanticsProvider>>,
     completion_provider: Option<Box<dyn CompletionProvider>>,
     collaboration_hub: Option<Box<dyn CollaborationHub>>,
-    blink_manager: Model<BlinkManager>,
+    blink_manager: Entity<BlinkManager>,
     show_cursor_names: bool,
     hovered_cursors: HashMap<HoveredCursor, Task<()>>,
     pub show_local_selections: bool,
@@ -662,7 +674,7 @@ pub struct Editor {
     current_line_highlight: Option<CurrentLineHighlight>,
     collapse_matches: bool,
     autoindent_mode: Option<AutoindentMode>,
-    workspace: Option<(WeakView<Workspace>, Option<WorkspaceId>)>,
+    workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
     input_enabled: bool,
     use_modal_editing: bool,
     read_only: bool,
@@ -687,7 +699,8 @@ pub struct Editor {
     style: Option<EditorStyle>,
     text_style_refinement: Option<TextStyleRefinement>,
     next_editor_action_id: EditorActionId,
-    editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
+    editor_actions:
+        Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut Window, &mut Context<Self>)>>>>,
     use_autoclose: bool,
     use_auto_surround: bool,
     auto_replace_emoji_shortcode: bool,
@@ -697,12 +710,17 @@ pub struct Editor {
     git_blame_inline_enabled: bool,
     serialize_dirty_buffers: bool,
     show_selection_menu: Option<bool>,
-    blame: Option<Model<GitBlame>>,
+    blame: Option<Entity<GitBlame>>,
     blame_subscription: Option<Subscription>,
     custom_context_menu: Option<
         Box<
             dyn 'static
-                + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
+                + Fn(
+                    &mut Self,
+                    DisplayPoint,
+                    &mut Window,
+                    &mut Context<Self>,
+                ) -> Option<Entity<ui::ContextMenu>>,
         >,
     >,
     last_bounds: Option<Bounds<Pixels>>,
@@ -941,7 +959,7 @@ struct SnippetState {
 pub struct RenameState {
     pub range: Range<Anchor>,
     pub old_name: Arc<str>,
-    pub editor: View<Editor>,
+    pub editor: Entity<Editor>,
     block_id: CustomBlockId,
 }
 
@@ -1037,73 +1055,86 @@ pub enum MultibufferSelectionMode {
 }
 
 impl Editor {
-    pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
-        let buffer = cx.new_model(|cx| Buffer::local("", cx));
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
+    pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
+        let buffer = cx.new(|cx| Buffer::local("", cx));
+        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
         Self::new(
             EditorMode::SingleLine { auto_width: false },
             buffer,
             None,
             false,
+            window,
             cx,
         )
     }
 
-    pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
-        let buffer = cx.new_model(|cx| Buffer::local("", cx));
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-        Self::new(EditorMode::Full, buffer, None, false, cx)
+    pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
+        let buffer = cx.new(|cx| Buffer::local("", cx));
+        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
+        Self::new(EditorMode::Full, buffer, None, false, window, cx)
     }
 
-    pub fn auto_width(cx: &mut ViewContext<Self>) -> Self {
-        let buffer = cx.new_model(|cx| Buffer::local("", cx));
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
+    pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
+        let buffer = cx.new(|cx| Buffer::local("", cx));
+        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
         Self::new(
             EditorMode::SingleLine { auto_width: true },
             buffer,
             None,
             false,
+            window,
             cx,
         )
     }
 
-    pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
-        let buffer = cx.new_model(|cx| Buffer::local("", cx));
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
+    pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
+        let buffer = cx.new(|cx| Buffer::local("", cx));
+        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
         Self::new(
             EditorMode::AutoHeight { max_lines },
             buffer,
             None,
             false,
+            window,
             cx,
         )
     }
 
     pub fn for_buffer(
-        buffer: Model<Buffer>,
-        project: Option<Model<Project>>,
-        cx: &mut ViewContext<Self>,
+        buffer: Entity<Buffer>,
+        project: Option<Entity<Project>>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-        Self::new(EditorMode::Full, buffer, project, false, cx)
+        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
+        Self::new(EditorMode::Full, buffer, project, false, window, cx)
     }
 
     pub fn for_multibuffer(
-        buffer: Model<MultiBuffer>,
-        project: Option<Model<Project>>,
+        buffer: Entity<MultiBuffer>,
+        project: Option<Entity<Project>>,
         show_excerpt_controls: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
-        Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
+        Self::new(
+            EditorMode::Full,
+            buffer,
+            project,
+            show_excerpt_controls,
+            window,
+            cx,
+        )
     }
 
-    pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
+    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
         let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
         let mut clone = Self::new(
             self.mode,
             self.buffer.clone(),
             self.project.clone(),
             show_excerpt_controls,
+            window,
             cx,
         );
         self.display_map.update(cx, |display_map, cx| {
@@ -1120,17 +1151,18 @@ impl Editor {
 
     pub fn new(
         mode: EditorMode,
-        buffer: Model<MultiBuffer>,
-        project: Option<Model<Project>>,
+        buffer: Entity<MultiBuffer>,
+        project: Option<Entity<Project>>,
         show_excerpt_controls: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
-        let style = cx.text_style();
-        let font_size = style.font_size.to_pixels(cx.rem_size());
-        let editor = cx.view().downgrade();
+        let style = window.text_style();
+        let font_size = style.font_size.to_pixels(window.rem_size());
+        let editor = cx.model().downgrade();
         let fold_placeholder = FoldPlaceholder {
             constrain_width: true,
-            render: Arc::new(move |fold_id, fold_range, cx| {
+            render: Arc::new(move |fold_id, fold_range, _, cx| {
                 let editor = editor.clone();
                 div()
                     .id(fold_id)
@@ -1141,8 +1173,8 @@ impl Editor {
                     .size_full()
                     .cursor_pointer()
                     .child("⋯")
-                    .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
-                    .on_click(move |_, cx| {
+                    .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
+                    .on_click(move |_, _window, cx| {
                         editor
                             .update(cx, |editor, cx| {
                                 editor.unfold_ranges(
@@ -1160,7 +1192,7 @@ impl Editor {
             merge_adjacent: true,
             ..Default::default()
         };
-        let display_map = cx.new_model(|cx| {
+        let display_map = cx.new(|cx| {
             DisplayMap::new(
                 buffer.clone(),
                 style.font(),
@@ -1177,7 +1209,7 @@ impl Editor {
 
         let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
 
-        let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
+        let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
 
         let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
             .then(|| language_settings::SoftWrap::None);
@@ -1186,29 +1218,39 @@ impl Editor {
         if mode == EditorMode::Full {
             if let Some(project) = project.as_ref() {
                 if buffer.read(cx).is_singleton() {
-                    project_subscriptions.push(cx.observe(project, |_, _, cx| {
+                    project_subscriptions.push(cx.observe_in(project, window, |_, _, _, cx| {
                         cx.emit(EditorEvent::TitleChanged);
                     }));
                 }
-                project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
-                    if let project::Event::RefreshInlayHints = event {
-                        editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
-                    } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
-                        if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
-                            let focus_handle = editor.focus_handle(cx);
-                            if focus_handle.is_focused(cx) {
-                                let snapshot = buffer.read(cx).snapshot();
-                                for (range, snippet) in snippet_edits {
-                                    let editor_range =
-                                        language::range_from_lsp(*range).to_offset(&snapshot);
-                                    editor
-                                        .insert_snippet(&[editor_range], snippet.clone(), cx)
-                                        .ok();
+                project_subscriptions.push(cx.subscribe_in(
+                    project,
+                    window,
+                    |editor, _, event, window, cx| {
+                        if let project::Event::RefreshInlayHints = event {
+                            editor
+                                .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
+                        } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
+                            if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
+                                let focus_handle = editor.focus_handle(cx);
+                                if focus_handle.is_focused(window) {
+                                    let snapshot = buffer.read(cx).snapshot();
+                                    for (range, snippet) in snippet_edits {
+                                        let editor_range =
+                                            language::range_from_lsp(*range).to_offset(&snapshot);
+                                        editor
+                                            .insert_snippet(
+                                                &[editor_range],
+                                                snippet.clone(),
+                                                window,
+                                                cx,
+                                            )
+                                            .ok();
+                                    }
                                 }
                             }
                         }
-                    }
-                }));
+                    },
+                ));
                 if let Some(task_inventory) = project
                     .read(cx)
                     .task_store()
@@ -1216,9 +1258,13 @@ impl Editor {
                     .task_inventory()
                     .cloned()
                 {
-                    project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
-                        editor.tasks_update_task = Some(editor.refresh_runnables(cx));
-                    }));
+                    project_subscriptions.push(cx.observe_in(
+                        &task_inventory,
+                        window,
+                        |editor, _, window, cx| {
+                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
+                        },
+                    ));
                 }
             }
         }
@@ -1228,12 +1274,14 @@ impl Editor {
         let inlay_hint_settings =
             inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
         let focus_handle = cx.focus_handle();
-        cx.on_focus(&focus_handle, Self::handle_focus).detach();
-        cx.on_focus_in(&focus_handle, Self::handle_focus_in)
+        cx.on_focus(&focus_handle, window, Self::handle_focus)
+            .detach();
+        cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
             .detach();
-        cx.on_focus_out(&focus_handle, Self::handle_focus_out)
+        cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
+            .detach();
+        cx.on_blur(&focus_handle, window, Self::handle_blur)
             .detach();
-        cx.on_blur(&focus_handle, Self::handle_blur).detach();
 
         let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
             Some(false)
@@ -1359,12 +1407,12 @@ impl Editor {
             tasks: Default::default(),
             _subscriptions: vec![
                 cx.observe(&buffer, Self::on_buffer_changed),
-                cx.subscribe(&buffer, Self::on_buffer_event),
-                cx.observe(&display_map, Self::on_display_map_changed),
+                cx.subscribe_in(&buffer, window, Self::on_buffer_event),
+                cx.observe_in(&display_map, window, Self::on_display_map_changed),
                 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
-                cx.observe_global::<SettingsStore>(Self::settings_changed),
-                cx.observe_window_activation(|editor, cx| {
-                    let active = cx.is_window_active();
+                cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
+                cx.observe_window_activation(window, |editor, window, cx| {
+                    let active = window.is_window_active();
                     editor.blink_manager.update(cx, |blink_manager, cx| {
                         if active {
                             blink_manager.enable(cx);
@@ -1387,11 +1435,11 @@ impl Editor {
             toggle_fold_multiple_buffers: Task::ready(()),
             text_style_refinement: None,
         };
-        this.tasks_update_task = Some(this.refresh_runnables(cx));
+        this.tasks_update_task = Some(this.refresh_runnables(window, cx));
         this._subscriptions.extend(project_subscriptions);
 
-        this.end_selection(cx);
-        this.scroll_manager.show_scrollbar(cx);
+        this.end_selection(window, cx);
+        this.scroll_manager.show_scrollbar(window, cx);
 
         if mode == EditorMode::Full {
             let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
@@ -1399,7 +1447,7 @@ impl Editor {
 
             if this.git_blame_inline_enabled {
                 this.git_blame_inline_enabled = true;
-                this.start_git_blame_inline(false, cx);
+                this.start_git_blame_inline(false, window, cx);
             }
 
             if let Some(buffer) = buffer.read(cx).as_singleton() {
@@ -1418,13 +1466,13 @@ impl Editor {
         this
     }
 
-    pub fn mouse_menu_is_focused(&self, cx: &WindowContext) -> bool {
+    pub fn mouse_menu_is_focused(&self, window: &mut Window, cx: &mut App) -> bool {
         self.mouse_context_menu
             .as_ref()
-            .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(cx))
+            .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
     }
 
-    fn key_context(&self, cx: &ViewContext<Self>) -> KeyContext {
+    fn key_context(&self, window: &mut Window, cx: &mut Context<Self>) -> KeyContext {
         let mut key_context = KeyContext::new_with_defaults();
         key_context.add("Editor");
         let mode = match self.mode {
@@ -1454,8 +1502,8 @@ impl Editor {
         }
 
         // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
-        if !self.focus_handle(cx).contains_focused(cx)
-            || (self.is_focused(cx) || self.mouse_menu_is_focused(cx))
+        if !self.focus_handle(cx).contains_focused(window, cx)
+            || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
         {
             for addon in self.addons.values() {
                 addon.extend_key_context(&mut key_context, cx)
@@ -1486,12 +1534,14 @@ impl Editor {
     pub fn new_file(
         workspace: &mut Workspace,
         _: &workspace::NewFile,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
-        Self::new_in_workspace(workspace, cx).detach_and_prompt_err(
+        Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
             "Failed to create buffer",
+            window,
             cx,
-            |e, _| match e.error_code() {
+            |e, _, _| match e.error_code() {
                 ErrorCode::RemoteUpgradeRequired => Some(format!(
                 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
                 e.error_tag("required").unwrap_or("the latest version")
@@ -1503,17 +1553,18 @@ impl Editor {
 
     pub fn new_in_workspace(
         workspace: &mut Workspace,
-        cx: &mut ViewContext<Workspace>,
-    ) -> Task<Result<View<Editor>>> {
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Task<Result<Entity<Editor>>> {
         let project = workspace.project().clone();
         let create = project.update(cx, |project, cx| project.create_buffer(cx));
 
-        cx.spawn(|workspace, mut cx| async move {
+        cx.spawn_in(window, |workspace, mut cx| async move {
             let buffer = create.await?;
-            workspace.update(&mut cx, |workspace, cx| {
+            workspace.update_in(&mut cx, |workspace, window, cx| {
                 let editor =
-                    cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx));
-                workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
+                    cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
+                workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
                 editor
             })
         })
@@ -1522,46 +1573,52 @@ impl Editor {
     fn new_file_vertical(
         workspace: &mut Workspace,
         _: &workspace::NewFileSplitVertical,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
-        Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), cx)
+        Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
     }
 
     fn new_file_horizontal(
         workspace: &mut Workspace,
         _: &workspace::NewFileSplitHorizontal,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
-        Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), cx)
+        Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
     }
 
     fn new_file_in_direction(
         workspace: &mut Workspace,
         direction: SplitDirection,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
         let project = workspace.project().clone();
         let create = project.update(cx, |project, cx| project.create_buffer(cx));
 
-        cx.spawn(|workspace, mut cx| async move {
+        cx.spawn_in(window, |workspace, mut cx| async move {
             let buffer = create.await?;
-            workspace.update(&mut cx, move |workspace, cx| {
+            workspace.update_in(&mut cx, move |workspace, window, cx| {
                 workspace.split_item(
                     direction,
                     Box::new(
-                        cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
+                        cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
                     ),
+                    window,
                     cx,
                 )
             })?;
             anyhow::Ok(())
         })
-        .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
-            ErrorCode::RemoteUpgradeRequired => Some(format!(
+        .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
+            match e.error_code() {
+                ErrorCode::RemoteUpgradeRequired => Some(format!(
                 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
                 e.error_tag("required").unwrap_or("the latest version")
             )),
-            _ => None,
+                _ => None,
+            }
         });
     }
 
@@ -1569,19 +1626,19 @@ impl Editor {
         self.leader_peer_id
     }
 
-    pub fn buffer(&self) -> &Model<MultiBuffer> {
+    pub fn buffer(&self) -> &Entity<MultiBuffer> {
         &self.buffer
     }
 
-    pub fn workspace(&self) -> Option<View<Workspace>> {
+    pub fn workspace(&self) -> Option<Entity<Workspace>> {
         self.workspace.as_ref()?.0.upgrade()
     }
 
-    pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
+    pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
         self.buffer().read(cx).title(cx)
     }
 
-    pub fn snapshot(&self, cx: &mut WindowContext) -> EditorSnapshot {
+    pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
         let git_blame_gutter_max_author_length = self
             .render_git_blame_gutter(cx)
             .then(|| {
@@ -1607,7 +1664,7 @@ impl Editor {
             scroll_anchor: self.scroll_manager.anchor(),
             ongoing_scroll: self.scroll_manager.ongoing_scroll(),
             placeholder_text: self.placeholder_text.clone(),
-            is_focused: self.focus_handle.is_focused(cx),
+            is_focused: self.focus_handle.is_focused(window),
             current_line_highlight: self
                 .current_line_highlight
                 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
@@ -1615,22 +1672,18 @@ impl Editor {
         }
     }
 
-    pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
+    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
         self.buffer.read(cx).language_at(point, cx)
     }
 
-    pub fn file_at<T: ToOffset>(
-        &self,
-        point: T,
-        cx: &AppContext,
-    ) -> Option<Arc<dyn language::File>> {
+    pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
         self.buffer.read(cx).read(cx).file_at(point).cloned()
     }
 
     pub fn active_excerpt(
         &self,
-        cx: &AppContext,
-    ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
+        cx: &App,
+    ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
         self.buffer
             .read(cx)
             .excerpt_containing(self.selections.newest_anchor().head(), cx)
@@ -1651,7 +1704,12 @@ impl Editor {
     pub fn set_custom_context_menu(
         &mut self,
         f: impl 'static
-            + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
+            + Fn(
+                &mut Self,
+                DisplayPoint,
+                &mut Window,
+                &mut Context<Self>,
+            ) -> Option<Entity<ui::ContextMenu>>,
     ) {
         self.custom_context_menu = Some(Box::new(f))
     }
@@ -1670,31 +1728,32 @@ impl Editor {
 
     pub fn set_inline_completion_provider<T>(
         &mut self,
-        provider: Option<Model<T>>,
-        cx: &mut ViewContext<Self>,
+        provider: Option<Entity<T>>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) where
         T: InlineCompletionProvider,
     {
         self.inline_completion_provider =
             provider.map(|provider| RegisteredInlineCompletionProvider {
-                _subscription: cx.observe(&provider, |this, _, cx| {
-                    if this.focus_handle.is_focused(cx) {
-                        this.update_visible_inline_completion(cx);
+                _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
+                    if this.focus_handle.is_focused(window) {
+                        this.update_visible_inline_completion(window, cx);
                     }
                 }),
                 provider: Arc::new(provider),
             });
-        self.refresh_inline_completion(false, false, cx);
+        self.refresh_inline_completion(false, false, window, cx);
     }
 
-    pub fn placeholder_text(&self, _cx: &WindowContext) -> Option<&str> {
+    pub fn placeholder_text(&self) -> Option<&str> {
         self.placeholder_text.as_deref()
     }
 
     pub fn set_placeholder_text(
         &mut self,
         placeholder_text: impl Into<Arc<str>>,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let placeholder_text = Some(placeholder_text.into());
         if self.placeholder_text != placeholder_text {
@@ -1703,7 +1762,7 @@ impl Editor {
         }
     }
 
-    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
+    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
         self.cursor_shape = cursor_shape;
 
         // Disrupt blink for immediate user feedback that the cursor shape has changed
@@ -1723,7 +1782,7 @@ impl Editor {
         self.collapse_matches = collapse_matches;
     }
 
-    pub fn register_buffers_with_language_servers(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
         let buffers = self.buffer.read(cx).all_buffers();
         let Some(lsp_store) = self.lsp_store(cx) else {
             return;
@@ -1746,7 +1805,7 @@ impl Editor {
         range.clone()
     }
 
-    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
+    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
         if self.display_map.read(cx).clip_at_line_ends != clip {
             self.display_map
                 .update(cx, |map, _| map.clip_at_line_ends = clip);
@@ -1757,7 +1816,7 @@ impl Editor {
         self.input_enabled = input_enabled;
     }
 
-    pub fn set_inline_completions_enabled(&mut self, enabled: bool, cx: &mut ViewContext<Self>) {
+    pub fn set_inline_completions_enabled(&mut self, enabled: bool, cx: &mut Context<Self>) {
         self.enable_inline_completions = enabled;
         if !self.enable_inline_completions {
             self.take_active_inline_completion(cx);
@@ -1777,7 +1836,7 @@ impl Editor {
         }
     }
 
-    pub fn read_only(&self, cx: &AppContext) -> bool {
+    pub fn read_only(&self, cx: &App) -> bool {
         self.read_only || self.buffer.read(cx).read_only()
     }
 
@@ -1800,10 +1859,11 @@ impl Editor {
     pub fn toggle_inline_completions(
         &mut self,
         _: &ToggleInlineCompletions,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         if self.show_inline_completions_override.is_some() {
-            self.set_show_inline_completions(None, cx);
+            self.set_show_inline_completions(None, window, cx);
         } else {
             let cursor = self.selections.newest_anchor().head();
             if let Some((buffer, cursor_buffer_position)) =
@@ -1811,7 +1871,7 @@ impl Editor {
             {
                 let show_inline_completions =
                     !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx);
-                self.set_show_inline_completions(Some(show_inline_completions), cx);
+                self.set_show_inline_completions(Some(show_inline_completions), window, cx);
             }
         }
     }
@@ -1819,13 +1879,14 @@ impl Editor {
     pub fn set_show_inline_completions(
         &mut self,
         show_inline_completions: Option<bool>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.show_inline_completions_override = show_inline_completions;
-        self.refresh_inline_completion(false, true, cx);
+        self.refresh_inline_completion(false, true, window, cx);
     }
 
-    pub fn inline_completions_enabled(&self, cx: &AppContext) -> bool {
+    pub fn inline_completions_enabled(&self, cx: &App) -> bool {
         let cursor = self.selections.newest_anchor().head();
         if let Some((buffer, buffer_position)) =
             self.buffer.read(cx).text_anchor_for_position(cursor, cx)
@@ -1838,9 +1899,9 @@ impl Editor {
 
     fn should_show_inline_completions(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         buffer_position: language::Anchor,
-        cx: &AppContext,
+        cx: &App,
     ) -> bool {
         if !self.snippet_stack.is_empty() {
             return false;
@@ -1863,9 +1924,9 @@ impl Editor {
 
     fn inline_completions_disabled_in_scope(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         buffer_position: language::Anchor,
-        cx: &AppContext,
+        cx: &App,
     ) -> bool {
         let snapshot = buffer.read(cx).snapshot();
         let settings = snapshot.settings_at(buffer_position, cx);
@@ -1895,9 +1956,10 @@ impl Editor {
         local: bool,
         old_cursor_position: &Anchor,
         show_completions: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
-        cx.invalidate_character_coordinates();
+        window.invalidate_character_coordinates();
 
         // Copy selections to primary selection buffer
         #[cfg(any(target_os = "linux", target_os = "freebsd"))]
@@ -1922,7 +1984,7 @@ impl Editor {
             }
         }
 
-        if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
+        if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
             self.buffer.update(cx, |buffer, cx| {
                 buffer.set_active_selections(
                     &self.selections.disjoint_anchors(),
@@ -1943,7 +2005,7 @@ impl Editor {
         self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
         self.snippet_stack
             .invalidate(&self.selections.disjoint_anchors(), buffer);
-        self.take_rename(false, cx);
+        self.take_rename(false, window, cx);
 
         let new_cursor_position = self.selections.newest_anchor().head();
 
@@ -1999,11 +2061,11 @@ impl Editor {
                     .detach();
 
                     if show_completions {
-                        self.show_completions(&ShowCompletions { trigger: None }, cx);
+                        self.show_completions(&ShowCompletions { trigger: None }, window, cx);
                     }
                 } else {
                     drop(context_menu);
-                    self.hide_context_menu(cx);
+                    self.hide_context_menu(window, cx);
                 }
             } else {
                 drop(context_menu);
@@ -2016,13 +2078,13 @@ impl Editor {
             {
                 self.available_code_actions.take();
             }
-            self.refresh_code_actions(cx);
+            self.refresh_code_actions(window, cx);
             self.refresh_document_highlights(cx);
-            refresh_matching_bracket_highlights(self, cx);
-            self.update_visible_inline_completion(cx);
-            linked_editing_ranges::refresh_linked_ranges(self, cx);
+            refresh_matching_bracket_highlights(self, window, cx);
+            self.update_visible_inline_completion(window, cx);
+            linked_editing_ranges::refresh_linked_ranges(self, window, cx);
             if self.git_blame_inline_enabled {
-                self.start_inline_blame_timer(cx);
+                self.start_inline_blame_timer(window, cx);
             }
         }
 
@@ -2038,17 +2100,19 @@ impl Editor {
     pub fn change_selections<R>(
         &mut self,
         autoscroll: Option<Autoscroll>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
         change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
     ) -> R {
-        self.change_selections_inner(autoscroll, true, cx, change)
+        self.change_selections_inner(autoscroll, true, window, cx, change)
     }
 
     pub fn change_selections_inner<R>(
         &mut self,
         autoscroll: Option<Autoscroll>,
         request_completions: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
         change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
     ) -> R {
         let old_cursor_position = self.selections.newest_anchor().head();
@@ -2060,14 +2124,14 @@ impl Editor {
             if let Some(autoscroll) = autoscroll {
                 self.request_autoscroll(autoscroll, cx);
             }
-            self.selections_did_change(true, &old_cursor_position, request_completions, cx);
+            self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
 
             if self.should_open_signature_help_automatically(
                 &old_cursor_position,
                 self.signature_help_state.backspace_pressed(),
                 cx,
             ) {
-                self.show_signature_help(&ShowSignatureHelp, cx);
+                self.show_signature_help(&ShowSignatureHelp, window, cx);
             }
             self.signature_help_state.set_backspace_pressed(false);
         }

crates/editor/src/editor_settings.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::AppContext;
+use gpui::App;
 use language::CursorShape;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
@@ -463,7 +463,7 @@ pub struct GutterContent {
 }
 
 impl EditorSettings {
-    pub fn jupyter_enabled(cx: &AppContext) -> bool {
+    pub fn jupyter_enabled(cx: &App) -> bool {
         EditorSettings::get_global(cx).jupyter.enabled
     }
 }
@@ -473,10 +473,7 @@ impl Settings for EditorSettings {
 
     type FileContent = EditorSettingsContent;
 
-    fn load(
-        sources: SettingsSources<Self::FileContent>,
-        _: &mut AppContext,
-    ) -> anyhow::Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> anyhow::Result<Self> {
         sources.json_merge()
     }
 }

crates/editor/src/editor_settings_controls.rs 🔗

@@ -1,6 +1,6 @@
 use std::sync::Arc;
 
-use gpui::{AppContext, FontFeatures, FontWeight};
+use gpui::{App, FontFeatures, FontWeight};
 use project::project_settings::{InlineBlameSettings, ProjectSettings};
 use settings::{EditableSettingControl, Settings};
 use theme::{FontFamilyCache, ThemeSettings};
@@ -27,7 +27,7 @@ impl EditorSettingsControls {
 }
 
 impl RenderOnce for EditorSettingsControls {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         SettingsContainer::new()
             .child(
                 SettingsGroup::new("Font")
@@ -65,7 +65,7 @@ impl EditableSettingControl for BufferFontFamilyControl {
         "Buffer Font Family".into()
     }
 
-    fn read(cx: &AppContext) -> Self::Value {
+    fn read(cx: &App) -> Self::Value {
         let settings = ThemeSettings::get_global(cx);
         settings.buffer_font.family.clone()
     }
@@ -73,14 +73,14 @@ impl EditableSettingControl for BufferFontFamilyControl {
     fn apply(
         settings: &mut <Self::Settings as Settings>::FileContent,
         value: Self::Value,
-        _cx: &AppContext,
+        _cx: &App,
     ) {
         settings.buffer_font_family = Some(value.to_string());
     }
 }
 
 impl RenderOnce for BufferFontFamilyControl {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
         let value = Self::read(cx);
 
         h_flex()
@@ -89,18 +89,18 @@ impl RenderOnce for BufferFontFamilyControl {
             .child(DropdownMenu::new(
                 "buffer-font-family",
                 value.clone(),
-                ContextMenu::build(cx, |mut menu, cx| {
+                ContextMenu::build(window, cx, |mut menu, _, cx| {
                     let font_family_cache = FontFamilyCache::global(cx);
 
                     for font_name in font_family_cache.list_font_families(cx) {
                         menu = menu.custom_entry(
                             {
                                 let font_name = font_name.clone();
-                                move |_cx| Label::new(font_name.clone()).into_any_element()
+                                move |_window, _cx| Label::new(font_name.clone()).into_any_element()
                             },
                             {
                                 let font_name = font_name.clone();
-                                move |cx| {
+                                move |_window, cx| {
                                     Self::write(font_name.clone(), cx);
                                 }
                             },
@@ -124,7 +124,7 @@ impl EditableSettingControl for BufferFontSizeControl {
         "Buffer Font Size".into()
     }
 
-    fn read(cx: &AppContext) -> Self::Value {
+    fn read(cx: &App) -> Self::Value {
         let settings = ThemeSettings::get_global(cx);
         settings.buffer_font_size
     }
@@ -132,14 +132,14 @@ impl EditableSettingControl for BufferFontSizeControl {
     fn apply(
         settings: &mut <Self::Settings as Settings>::FileContent,
         value: Self::Value,
-        _cx: &AppContext,
+        _cx: &App,
     ) {
         settings.buffer_font_size = Some(value.into());
     }
 }
 
 impl RenderOnce for BufferFontSizeControl {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let value = Self::read(cx);
 
         h_flex()
@@ -148,10 +148,10 @@ impl RenderOnce for BufferFontSizeControl {
             .child(NumericStepper::new(
                 "buffer-font-size",
                 value.to_string(),
-                move |_, cx| {
+                move |_, _, cx| {
                     Self::write(value - px(1.), cx);
                 },
-                move |_, cx| {
+                move |_, _, cx| {
                     Self::write(value + px(1.), cx);
                 },
             ))
@@ -169,7 +169,7 @@ impl EditableSettingControl for BufferFontWeightControl {
         "Buffer Font Weight".into()
     }
 
-    fn read(cx: &AppContext) -> Self::Value {
+    fn read(cx: &App) -> Self::Value {
         let settings = ThemeSettings::get_global(cx);
         settings.buffer_font.weight
     }
@@ -177,14 +177,14 @@ impl EditableSettingControl for BufferFontWeightControl {
     fn apply(
         settings: &mut <Self::Settings as Settings>::FileContent,
         value: Self::Value,
-        _cx: &AppContext,
+        _cx: &App,
     ) {
         settings.buffer_font_weight = Some(value.0);
     }
 }
 
 impl RenderOnce for BufferFontWeightControl {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
         let value = Self::read(cx);
 
         h_flex()
@@ -193,12 +193,12 @@ impl RenderOnce for BufferFontWeightControl {
             .child(DropdownMenu::new(
                 "buffer-font-weight",
                 value.0.to_string(),
-                ContextMenu::build(cx, |mut menu, _cx| {
+                ContextMenu::build(window, cx, |mut menu, _window, _cx| {
                     for weight in FontWeight::ALL {
                         menu = menu.custom_entry(
-                            move |_cx| Label::new(weight.0.to_string()).into_any_element(),
+                            move |_window, _cx| Label::new(weight.0.to_string()).into_any_element(),
                             {
-                                move |cx| {
+                                move |_, cx| {
                                     Self::write(weight, cx);
                                 }
                             },
@@ -222,7 +222,7 @@ impl EditableSettingControl for BufferFontLigaturesControl {
         "Buffer Font Ligatures".into()
     }
 
-    fn read(cx: &AppContext) -> Self::Value {
+    fn read(cx: &App) -> Self::Value {
         let settings = ThemeSettings::get_global(cx);
         settings
             .buffer_font
@@ -234,7 +234,7 @@ impl EditableSettingControl for BufferFontLigaturesControl {
     fn apply(
         settings: &mut <Self::Settings as Settings>::FileContent,
         value: Self::Value,
-        _cx: &AppContext,
+        _cx: &App,
     ) {
         let value = if value { 1 } else { 0 };
 
@@ -255,14 +255,14 @@ impl EditableSettingControl for BufferFontLigaturesControl {
 }
 
 impl RenderOnce for BufferFontLigaturesControl {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let value = Self::read(cx);
 
         CheckboxWithLabel::new(
             "buffer-font-ligatures",
             Label::new(self.name()),
             value.into(),
-            |selection, cx| {
+            |selection, _, cx| {
                 Self::write(
                     match selection {
                         ToggleState::Selected => true,
@@ -286,7 +286,7 @@ impl EditableSettingControl for InlineGitBlameControl {
         "Inline Git Blame".into()
     }
 
-    fn read(cx: &AppContext) -> Self::Value {
+    fn read(cx: &App) -> Self::Value {
         let settings = ProjectSettings::get_global(cx);
         settings.git.inline_blame_enabled()
     }
@@ -294,7 +294,7 @@ impl EditableSettingControl for InlineGitBlameControl {
     fn apply(
         settings: &mut <Self::Settings as Settings>::FileContent,
         value: Self::Value,
-        _cx: &AppContext,
+        _cx: &App,
     ) {
         if let Some(inline_blame) = settings.git.inline_blame.as_mut() {
             inline_blame.enabled = value;
@@ -308,14 +308,14 @@ impl EditableSettingControl for InlineGitBlameControl {
 }
 
 impl RenderOnce for InlineGitBlameControl {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let value = Self::read(cx);
 
         CheckboxWithLabel::new(
             "inline-git-blame",
             Label::new(self.name()),
             value.into(),
-            |selection, cx| {
+            |selection, _, cx| {
                 Self::write(
                     match selection {
                         ToggleState::Selected => true,
@@ -339,7 +339,7 @@ impl EditableSettingControl for LineNumbersControl {
         "Line Numbers".into()
     }
 
-    fn read(cx: &AppContext) -> Self::Value {
+    fn read(cx: &App) -> Self::Value {
         let settings = EditorSettings::get_global(cx);
         settings.gutter.line_numbers
     }
@@ -347,7 +347,7 @@ impl EditableSettingControl for LineNumbersControl {
     fn apply(
         settings: &mut <Self::Settings as Settings>::FileContent,
         value: Self::Value,
-        _cx: &AppContext,
+        _cx: &App,
     ) {
         if let Some(gutter) = settings.gutter.as_mut() {
             gutter.line_numbers = Some(value);
@@ -361,14 +361,14 @@ impl EditableSettingControl for LineNumbersControl {
 }
 
 impl RenderOnce for LineNumbersControl {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let value = Self::read(cx);
 
         CheckboxWithLabel::new(
             "line-numbers",
             Label::new(self.name()),
             value.into(),
-            |selection, cx| {
+            |selection, _, cx| {
                 Self::write(
                     match selection {
                         ToggleState::Selected => true,
@@ -392,7 +392,7 @@ impl EditableSettingControl for RelativeLineNumbersControl {
         "Relative Line Numbers".into()
     }
 
-    fn read(cx: &AppContext) -> Self::Value {
+    fn read(cx: &App) -> Self::Value {
         let settings = EditorSettings::get_global(cx);
         settings.relative_line_numbers
     }
@@ -400,27 +400,27 @@ impl EditableSettingControl for RelativeLineNumbersControl {
     fn apply(
         settings: &mut <Self::Settings as Settings>::FileContent,
         value: Self::Value,
-        _cx: &AppContext,
+        _cx: &App,
     ) {
         settings.relative_line_numbers = Some(value);
     }
 }
 
 impl RenderOnce for RelativeLineNumbersControl {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
         let value = Self::read(cx);
 
         DropdownMenu::new(
             "relative-line-numbers",
             if value { "Relative" } else { "Ascending" },
-            ContextMenu::build(cx, |menu, _cx| {
+            ContextMenu::build(window, cx, |menu, _window, _cx| {
                 menu.custom_entry(
-                    |_cx| Label::new("Ascending").into_any_element(),
-                    move |cx| Self::write(false, cx),
+                    |_window, _cx| Label::new("Ascending").into_any_element(),
+                    move |_, cx| Self::write(false, cx),
                 )
                 .custom_entry(
-                    |_cx| Label::new("Relative").into_any_element(),
-                    move |cx| Self::write(true, cx),
+                    |_window, _cx| Label::new("Relative").into_any_element(),
+                    move |_, cx| Self::write(true, cx),
                 )
             }),
         )

crates/editor/src/editor_tests.rs 🔗

@@ -52,7 +52,7 @@ use workspace::{
 fn test_edit_events(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let buffer = cx.new_model(|cx| {
+    let buffer = cx.new(|cx| {
         let mut buffer = language::Buffer::local("123456", cx);
         buffer.set_group_interval(Duration::from_secs(1));
         buffer
@@ -61,24 +61,31 @@ fn test_edit_events(cx: &mut TestAppContext) {
     let events = Rc::new(RefCell::new(Vec::new()));
     let editor1 = cx.add_window({
         let events = events.clone();
-        |cx| {
-            let view = cx.view().clone();
-            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
-                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
-                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
-                _ => {}
-            })
+        |window, cx| {
+            let model = cx.model().clone();
+            cx.subscribe_in(
+                &model,
+                window,
+                move |_, _, event: &EditorEvent, _, _| match event {
+                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
+                    EditorEvent::BufferEdited => {
+                        events.borrow_mut().push(("editor1", "buffer edited"))
+                    }
+                    _ => {}
+                },
+            )
             .detach();
-            Editor::for_buffer(buffer.clone(), None, cx)
+            Editor::for_buffer(buffer.clone(), None, window, cx)
         }
     });
 
     let editor2 = cx.add_window({
         let events = events.clone();
-        |cx| {
-            cx.subscribe(
-                &cx.view().clone(),
-                move |_, _, event: &EditorEvent, _| match event {
+        |window, cx| {
+            cx.subscribe_in(
+                &cx.model().clone(),
+                window,
+                move |_, _, event: &EditorEvent, _, _| match event {
                     EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
                     EditorEvent::BufferEdited => {
                         events.borrow_mut().push(("editor2", "buffer edited"))
@@ -87,14 +94,14 @@ fn test_edit_events(cx: &mut TestAppContext) {
                 },
             )
             .detach();
-            Editor::for_buffer(buffer.clone(), None, cx)
+            Editor::for_buffer(buffer.clone(), None, window, cx)
         }
     });
 
     assert_eq!(mem::take(&mut *events.borrow_mut()), []);
 
     // Mutating editor 1 will emit an `Edited` event only for that editor.
-    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
+    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
     assert_eq!(
         mem::take(&mut *events.borrow_mut()),
         [
@@ -105,7 +112,7 @@ fn test_edit_events(cx: &mut TestAppContext) {
     );
 
     // Mutating editor 2 will emit an `Edited` event only for that editor.
-    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
+    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
     assert_eq!(
         mem::take(&mut *events.borrow_mut()),
         [
@@ -116,7 +123,7 @@ fn test_edit_events(cx: &mut TestAppContext) {
     );
 
     // Undoing on editor 1 will emit an `Edited` event only for that editor.
-    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
+    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
     assert_eq!(
         mem::take(&mut *events.borrow_mut()),
         [
@@ -127,7 +134,7 @@ fn test_edit_events(cx: &mut TestAppContext) {
     );
 
     // Redoing on editor 1 will emit an `Edited` event only for that editor.
-    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
+    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
     assert_eq!(
         mem::take(&mut *events.borrow_mut()),
         [
@@ -138,7 +145,7 @@ fn test_edit_events(cx: &mut TestAppContext) {
     );
 
     // Undoing on editor 2 will emit an `Edited` event only for that editor.
-    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
+    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
     assert_eq!(
         mem::take(&mut *events.borrow_mut()),
         [
@@ -149,7 +156,7 @@ fn test_edit_events(cx: &mut TestAppContext) {
     );
 
     // Redoing on editor 2 will emit an `Edited` event only for that editor.
-    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
+    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
     assert_eq!(
         mem::take(&mut *events.borrow_mut()),
         [
@@ -160,10 +167,10 @@ fn test_edit_events(cx: &mut TestAppContext) {
     );
 
     // No event is emitted when the mutation is a no-op.
-    _ = editor2.update(cx, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
+    _ = editor2.update(cx, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 
-        editor.backspace(&Backspace, cx);
+        editor.backspace(&Backspace, window, cx);
     });
     assert_eq!(mem::take(&mut *events.borrow_mut()), []);
 }
@@ -174,32 +181,32 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
 
     let mut now = Instant::now();
     let group_interval = Duration::from_millis(1);
-    let buffer = cx.new_model(|cx| {
+    let buffer = cx.new(|cx| {
         let mut buf = language::Buffer::local("123456", cx);
         buf.set_group_interval(group_interval);
         buf
     });
-    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
+    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
+    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 
-    _ = editor.update(cx, |editor, cx| {
-        editor.start_transaction_at(now, cx);
-        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.start_transaction_at(now, window, cx);
+        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
 
-        editor.insert("cd", cx);
+        editor.insert("cd", window, cx);
         editor.end_transaction_at(now, cx);
         assert_eq!(editor.text(cx), "12cd56");
         assert_eq!(editor.selections.ranges(cx), vec![4..4]);
 
-        editor.start_transaction_at(now, cx);
-        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
-        editor.insert("e", cx);
+        editor.start_transaction_at(now, window, cx);
+        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
+        editor.insert("e", window, cx);
         editor.end_transaction_at(now, cx);
         assert_eq!(editor.text(cx), "12cde6");
         assert_eq!(editor.selections.ranges(cx), vec![5..5]);
 
         now += group_interval + Duration::from_millis(1);
-        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
+        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
 
         // Simulate an edit in another editor
         buffer.update(cx, |buffer, cx| {
@@ -214,31 +221,31 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
 
         // Last transaction happened past the group interval in a different editor.
         // Undo it individually and don't restore selections.
-        editor.undo(&Undo, cx);
+        editor.undo(&Undo, window, cx);
         assert_eq!(editor.text(cx), "12cde6");
         assert_eq!(editor.selections.ranges(cx), vec![2..2]);
 
         // First two transactions happened within the group interval in this editor.
         // Undo them together and restore selections.
-        editor.undo(&Undo, cx);
-        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
+        editor.undo(&Undo, window, cx);
+        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
         assert_eq!(editor.text(cx), "123456");
         assert_eq!(editor.selections.ranges(cx), vec![0..0]);
 
         // Redo the first two transactions together.
-        editor.redo(&Redo, cx);
+        editor.redo(&Redo, window, cx);
         assert_eq!(editor.text(cx), "12cde6");
         assert_eq!(editor.selections.ranges(cx), vec![5..5]);
 
         // Redo the last transaction on its own.
-        editor.redo(&Redo, cx);
+        editor.redo(&Redo, window, cx);
         assert_eq!(editor.text(cx), "ab2cde6");
         assert_eq!(editor.selections.ranges(cx), vec![6..6]);
 
         // Test empty transactions.
-        editor.start_transaction_at(now, cx);
+        editor.start_transaction_at(now, window, cx);
         editor.end_transaction_at(now, cx);
-        editor.undo(&Undo, cx);
+        editor.undo(&Undo, window, cx);
         assert_eq!(editor.text(cx), "12cde6");
     });
 }
@@ -247,21 +254,21 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
 fn test_ime_composition(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let buffer = cx.new_model(|cx| {
+    let buffer = cx.new(|cx| {
         let mut buffer = language::Buffer::local("abcde", cx);
         // Ensure automatic grouping doesn't occur.
         buffer.set_group_interval(Duration::ZERO);
         buffer
     });
 
-    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-    cx.add_window(|cx| {
-        let mut editor = build_editor(buffer.clone(), cx);
+    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
+    cx.add_window(|window, cx| {
+        let mut editor = build_editor(buffer.clone(), window, cx);
 
         // Start a new IME composition.
-        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
-        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
-        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
+        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
+        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
+        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
         assert_eq!(editor.text(cx), "äbcde");
         assert_eq!(
             editor.marked_text_ranges(cx),
@@ -269,32 +276,32 @@ fn test_ime_composition(cx: &mut TestAppContext) {
         );
 
         // Finalize IME composition.
-        editor.replace_text_in_range(None, "ā", cx);
+        editor.replace_text_in_range(None, "ā", window, cx);
         assert_eq!(editor.text(cx), "ābcde");
         assert_eq!(editor.marked_text_ranges(cx), None);
 
         // IME composition edits are grouped and are undone/redone at once.
-        editor.undo(&Default::default(), cx);
+        editor.undo(&Default::default(), window, cx);
         assert_eq!(editor.text(cx), "abcde");
         assert_eq!(editor.marked_text_ranges(cx), None);
-        editor.redo(&Default::default(), cx);
+        editor.redo(&Default::default(), window, cx);
         assert_eq!(editor.text(cx), "ābcde");
         assert_eq!(editor.marked_text_ranges(cx), None);
 
         // Start a new IME composition.
-        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
+        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
         assert_eq!(
             editor.marked_text_ranges(cx),
             Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
         );
 
         // Undoing during an IME composition cancels it.
-        editor.undo(&Default::default(), cx);
+        editor.undo(&Default::default(), window, cx);
         assert_eq!(editor.text(cx), "ābcde");
         assert_eq!(editor.marked_text_ranges(cx), None);
 
         // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
-        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
+        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
         assert_eq!(editor.text(cx), "ābcdè");
         assert_eq!(
             editor.marked_text_ranges(cx),
@@ -302,19 +309,19 @@ fn test_ime_composition(cx: &mut TestAppContext) {
         );
 
         // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
-        editor.replace_text_in_range(Some(4..999), "ę", cx);
+        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
         assert_eq!(editor.text(cx), "ābcdę");
         assert_eq!(editor.marked_text_ranges(cx), None);
 
         // Start a new IME composition with multiple cursors.
-        editor.change_selections(None, cx, |s| {
+        editor.change_selections(None, window, cx, |s| {
             s.select_ranges([
                 OffsetUtf16(1)..OffsetUtf16(1),
                 OffsetUtf16(3)..OffsetUtf16(3),
                 OffsetUtf16(5)..OffsetUtf16(5),
             ])
         });
-        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
+        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
         assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
         assert_eq!(
             editor.marked_text_ranges(cx),
@@ -326,7 +333,7 @@ fn test_ime_composition(cx: &mut TestAppContext) {
         );
 
         // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
-        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
+        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
         assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
         assert_eq!(
             editor.marked_text_ranges(cx),
@@ -338,7 +345,7 @@ fn test_ime_composition(cx: &mut TestAppContext) {
         );
 
         // Finalize IME composition with multiple cursors.
-        editor.replace_text_in_range(Some(9..10), "2", cx);
+        editor.replace_text_in_range(Some(9..10), "2", window, cx);
         assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
         assert_eq!(editor.marked_text_ranges(cx), None);
 
@@ -350,83 +357,87 @@ fn test_ime_composition(cx: &mut TestAppContext) {
 fn test_selection_with_mouse(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let editor = cx.add_window(|cx| {
+    let editor = cx.add_window(|window, cx| {
         let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
-        build_editor(buffer, cx)
+        build_editor(buffer, window, cx)
     });
 
-    _ = editor.update(cx, |view, cx| {
-        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
     });
     assert_eq!(
         editor
-            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
             .unwrap(),
         [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
     );
 
-    _ = editor.update(cx, |view, cx| {
-        view.update_selection(
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.update_selection(
             DisplayPoint::new(DisplayRow(3), 3),
             0,
             gpui::Point::<f32>::default(),
+            window,
             cx,
         );
     });
 
     assert_eq!(
         editor
-            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
             .unwrap(),
         [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
     );
 
-    _ = editor.update(cx, |view, cx| {
-        view.update_selection(
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.update_selection(
             DisplayPoint::new(DisplayRow(1), 1),
             0,
             gpui::Point::<f32>::default(),
+            window,
             cx,
         );
     });
 
     assert_eq!(
         editor
-            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
             .unwrap(),
         [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
     );
 
-    _ = editor.update(cx, |view, cx| {
-        view.end_selection(cx);
-        view.update_selection(
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.end_selection(window, cx);
+        editor.update_selection(
             DisplayPoint::new(DisplayRow(3), 3),
             0,
             gpui::Point::<f32>::default(),
+            window,
             cx,
         );
     });
 
     assert_eq!(
         editor
-            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
             .unwrap(),
         [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
     );
 
-    _ = editor.update(cx, |view, cx| {
-        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
-        view.update_selection(
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
+        editor.update_selection(
             DisplayPoint::new(DisplayRow(0), 0),
             0,
             gpui::Point::<f32>::default(),
+            window,
             cx,
         );
     });
 
     assert_eq!(
         editor
-            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
             .unwrap(),
         [
             DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
@@ -434,13 +445,13 @@ fn test_selection_with_mouse(cx: &mut TestAppContext) {
         ]
     );
 
-    _ = editor.update(cx, |view, cx| {
-        view.end_selection(cx);
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.end_selection(window, cx);
     });
 
     assert_eq!(
         editor
-            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
             .unwrap(),
         [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
     );
@@ -450,30 +461,30 @@ fn test_selection_with_mouse(cx: &mut TestAppContext) {
 fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let editor = cx.add_window(|cx| {
+    let editor = cx.add_window(|window, cx| {
         let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
-        build_editor(buffer, cx)
+        build_editor(buffer, window, cx)
     });
 
-    _ = editor.update(cx, |view, cx| {
-        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
     });
 
-    _ = editor.update(cx, |view, cx| {
-        view.end_selection(cx);
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.end_selection(window, cx);
     });
 
-    _ = editor.update(cx, |view, cx| {
-        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
     });
 
-    _ = editor.update(cx, |view, cx| {
-        view.end_selection(cx);
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.end_selection(window, cx);
     });
 
     assert_eq!(
         editor
-            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
             .unwrap(),
         [
             DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
@@ -481,17 +492,17 @@ fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
         ]
     );
 
-    _ = editor.update(cx, |view, cx| {
-        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
     });
 
-    _ = editor.update(cx, |view, cx| {
-        view.end_selection(cx);
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.end_selection(window, cx);
     });
 
     assert_eq!(
         editor
-            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
             .unwrap(),
         [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
     );
@@ -501,42 +512,44 @@ fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
 fn test_canceling_pending_selection(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx.add_window(|cx| {
+    let editor = cx.add_window(|window, cx| {
         let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
-        build_editor(buffer, cx)
+        build_editor(buffer, window, cx)
     });
 
-    _ = view.update(cx, |view, cx| {
-        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
         assert_eq!(
-            view.selections.display_ranges(cx),
+            editor.selections.display_ranges(cx),
             [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
         );
     });
 
-    _ = view.update(cx, |view, cx| {
-        view.update_selection(
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.update_selection(
             DisplayPoint::new(DisplayRow(3), 3),
             0,
             gpui::Point::<f32>::default(),
+            window,
             cx,
         );
         assert_eq!(
-            view.selections.display_ranges(cx),
+            editor.selections.display_ranges(cx),
             [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
         );
     });
 
-    _ = view.update(cx, |view, cx| {
-        view.cancel(&Cancel, cx);
-        view.update_selection(
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.cancel(&Cancel, window, cx);
+        editor.update_selection(
             DisplayPoint::new(DisplayRow(1), 1),
             0,
             gpui::Point::<f32>::default(),
+            window,
             cx,
         );
         assert_eq!(
-            view.selections.display_ranges(cx),
+            editor.selections.display_ranges(cx),
             [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
         );
     });
@@ -546,33 +559,33 @@ fn test_canceling_pending_selection(cx: &mut TestAppContext) {
 fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx.add_window(|cx| {
+    let editor = cx.add_window(|window, cx| {
         let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
-        build_editor(buffer, cx)
+        build_editor(buffer, window, cx)
     });
 
-    _ = view.update(cx, |view, cx| {
-        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
         assert_eq!(
-            view.selections.display_ranges(cx),
+            editor.selections.display_ranges(cx),
             [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
         );
 
-        view.move_down(&Default::default(), cx);
+        editor.move_down(&Default::default(), window, cx);
         assert_eq!(
-            view.selections.display_ranges(cx),
+            editor.selections.display_ranges(cx),
             [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
         );
 
-        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
+        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
         assert_eq!(
-            view.selections.display_ranges(cx),
+            editor.selections.display_ranges(cx),
             [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
         );
 
-        view.move_up(&Default::default(), cx);
+        editor.move_up(&Default::default(), window, cx);
         assert_eq!(
-            view.selections.display_ranges(cx),
+            editor.selections.display_ranges(cx),
             [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
         );
     });
@@ -593,38 +606,47 @@ fn test_clone(cx: &mut TestAppContext) {
         true,
     );
 
-    let editor = cx.add_window(|cx| {
+    let editor = cx.add_window(|window, cx| {
         let buffer = MultiBuffer::build_simple(&text, cx);
-        build_editor(buffer, cx)
+        build_editor(buffer, window, cx)
     });
 
-    _ = editor.update(cx, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |s| {
+            s.select_ranges(selection_ranges.clone())
+        });
         editor.fold_creases(
             vec![
                 Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
                 Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
             ],
             true,
+            window,
             cx,
         );
     });
 
     let cloned_editor = editor
-        .update(cx, |editor, cx| {
-            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
+        .update(cx, |editor, _, cx| {
+            cx.open_window(Default::default(), |window, cx| {
+                cx.new(|cx| editor.clone(window, cx))
+            })
         })
         .unwrap()
         .unwrap();
 
-    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
-    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
+    let snapshot = editor
+        .update(cx, |e, window, cx| e.snapshot(window, cx))
+        .unwrap();
+    let cloned_snapshot = cloned_editor
+        .update(cx, |e, window, cx| e.snapshot(window, cx))
+        .unwrap();
 
     assert_eq!(
         cloned_editor
-            .update(cx, |e, cx| e.display_text(cx))
+            .update(cx, |e, _, cx| e.display_text(cx))
             .unwrap(),
-        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
+        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
     );
     assert_eq!(
         cloned_snapshot
@@ -634,18 +656,18 @@ fn test_clone(cx: &mut TestAppContext) {
     );
     assert_set_eq!(
         cloned_editor
-            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
+            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
             .unwrap(),
         editor
-            .update(cx, |editor, cx| editor.selections.ranges(cx))
+            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
             .unwrap()
     );
     assert_set_eq!(
         cloned_editor
-            .update(cx, |e, cx| e.selections.display_ranges(cx))
+            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
             .unwrap(),
         editor
-            .update(cx, |e, cx| e.selections.display_ranges(cx))
+            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
             .unwrap()
     );
 }
@@ -658,30 +680,30 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
 
     let fs = FakeFs::new(cx.executor());
     let project = Project::test(fs, [], cx).await;
-    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
+    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
     let pane = workspace
-        .update(cx, |workspace, _| workspace.active_pane().clone())
+        .update(cx, |workspace, _, _| workspace.active_pane().clone())
         .unwrap();
 
-    _ = workspace.update(cx, |_v, cx| {
-        cx.new_view(|cx| {
+    _ = workspace.update(cx, |_v, window, cx| {
+        cx.new(|cx| {
             let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
-            let mut editor = build_editor(buffer.clone(), cx);
-            let handle = cx.view();
-            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(handle)));
+            let mut editor = build_editor(buffer.clone(), window, cx);
+            let handle = cx.model();
+            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
 
-            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
+            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
                 editor.nav_history.as_mut().unwrap().pop_backward(cx)
             }
 
             // Move the cursor a small distance.
             // Nothing is added to the navigation history.
-            editor.change_selections(None, cx, |s| {
+            editor.change_selections(None, window, cx, |s| {
                 s.select_display_ranges([
                     DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
                 ])
             });
-            editor.change_selections(None, cx, |s| {
+            editor.change_selections(None, window, cx, |s| {
                 s.select_display_ranges([
                     DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
                 ])
@@ -690,13 +712,13 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
 
             // Move the cursor a large distance.
             // The history can jump back to the previous position.
-            editor.change_selections(None, cx, |s| {
+            editor.change_selections(None, window, cx, |s| {
                 s.select_display_ranges([
                     DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
                 ])
             });
             let nav_entry = pop_history(&mut editor, cx).unwrap();
-            editor.navigate(nav_entry.data.unwrap(), cx);
+            editor.navigate(nav_entry.data.unwrap(), window, cx);
             assert_eq!(nav_entry.item.id(), cx.entity_id());
             assert_eq!(
                 editor.selections.display_ranges(cx),
@@ -706,8 +728,8 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
 
             // Move the cursor a small distance via the mouse.
             // Nothing is added to the navigation history.
-            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
-            editor.end_selection(cx);
+            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
+            editor.end_selection(window, cx);
             assert_eq!(
                 editor.selections.display_ranges(cx),
                 &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
@@ -716,14 +738,14 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
 
             // Move the cursor a large distance via the mouse.
             // The history can jump back to the previous position.
-            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
-            editor.end_selection(cx);
+            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
+            editor.end_selection(window, cx);
             assert_eq!(
                 editor.selections.display_ranges(cx),
                 &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
             );
             let nav_entry = pop_history(&mut editor, cx).unwrap();
-            editor.navigate(nav_entry.data.unwrap(), cx);
+            editor.navigate(nav_entry.data.unwrap(), window, cx);
             assert_eq!(nav_entry.item.id(), cx.entity_id());
             assert_eq!(
                 editor.selections.display_ranges(cx),
@@ -732,16 +754,16 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
             assert!(pop_history(&mut editor, cx).is_none());
 
             // Set scroll position to check later
-            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
+            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
             let original_scroll_position = editor.scroll_manager.anchor();
 
             // Jump to the end of the document and adjust scroll
-            editor.move_to_end(&MoveToEnd, cx);
-            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
+            editor.move_to_end(&MoveToEnd, window, cx);
+            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
             assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
 
             let nav_entry = pop_history(&mut editor, cx).unwrap();
-            editor.navigate(nav_entry.data.unwrap(), cx);
+            editor.navigate(nav_entry.data.unwrap(), window, cx);
             assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
 
             // Ensure we don't panic when navigation data contains invalid anchors *and* points.
@@ -758,6 +780,7 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
                     },
                     scroll_top_row: invalid_point.row,
                 }),
+                window,
                 cx,
             );
             assert_eq!(
@@ -778,31 +801,33 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
 fn test_cancel(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx.add_window(|cx| {
+    let editor = cx.add_window(|window, cx| {
         let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
-        build_editor(buffer, cx)
+        build_editor(buffer, window, cx)
     });
 
-    _ = view.update(cx, |view, cx| {
-        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
-        view.update_selection(
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
+        editor.update_selection(
             DisplayPoint::new(DisplayRow(1), 1),
             0,
             gpui::Point::<f32>::default(),
+            window,
             cx,
         );
-        view.end_selection(cx);
+        editor.end_selection(window, cx);
 
-        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
-        view.update_selection(
+        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
+        editor.update_selection(
             DisplayPoint::new(DisplayRow(0), 3),
             0,
             gpui::Point::<f32>::default(),
+            window,
             cx,
         );
-        view.end_selection(cx);
+        editor.end_selection(window, cx);
         assert_eq!(
-            view.selections.display_ranges(cx),
+            editor.selections.display_ranges(cx),
             [
                 DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
                 DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
@@ -810,18 +835,18 @@ fn test_cancel(cx: &mut TestAppContext) {
         );
     });
 
-    _ = view.update(cx, |view, cx| {
-        view.cancel(&Cancel, cx);
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.cancel(&Cancel, window, cx);
         assert_eq!(
-            view.selections.display_ranges(cx),
+            editor.selections.display_ranges(cx),
             [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
         );
     });
 
-    _ = view.update(cx, |view, cx| {
-        view.cancel(&Cancel, cx);
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.cancel(&Cancel, window, cx);
         assert_eq!(
-            view.selections.display_ranges(cx),
+            editor.selections.display_ranges(cx),
             [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
         );
     });
@@ -831,7 +856,7 @@ fn test_cancel(cx: &mut TestAppContext) {
 fn test_fold_action(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx.add_window(|cx| {
+    let editor = cx.add_window(|window, cx| {
         let buffer = MultiBuffer::build_simple(
             &"
                 impl Foo {
@@ -853,18 +878,18 @@ fn test_fold_action(cx: &mut TestAppContext) {
             .unindent(),
             cx,
         );
-        build_editor(buffer.clone(), cx)
+        build_editor(buffer.clone(), window, cx)
     });
 
-    _ = view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
             ]);
         });
-        view.fold(&Fold, cx);
+        editor.fold(&Fold, window, cx);
         assert_eq!(
-            view.display_text(cx),
+            editor.display_text(cx),
             "
                 impl Foo {
                     // Hello!
@@ -883,9 +908,9 @@ fn test_fold_action(cx: &mut TestAppContext) {
             .unindent(),
         );
 
-        view.fold(&Fold, cx);
+        editor.fold(&Fold, window, cx);
         assert_eq!(
-            view.display_text(cx),
+            editor.display_text(cx),
             "
                 impl Foo {⋯
                 }
@@ -893,9 +918,9 @@ fn test_fold_action(cx: &mut TestAppContext) {
             .unindent(),
         );
 
-        view.unfold_lines(&UnfoldLines, cx);
+        editor.unfold_lines(&UnfoldLines, window, cx);
         assert_eq!(
-            view.display_text(cx),
+            editor.display_text(cx),
             "
                 impl Foo {
                     // Hello!
@@ -914,8 +939,11 @@ fn test_fold_action(cx: &mut TestAppContext) {
             .unindent(),
         );
 
-        view.unfold_lines(&UnfoldLines, cx);
-        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
+        editor.unfold_lines(&UnfoldLines, window, cx);
+        assert_eq!(
+            editor.display_text(cx),
+            editor.buffer.read(cx).read(cx).text()
+        );
     });
 }
 
@@ -923,7 +951,7 @@ fn test_fold_action(cx: &mut TestAppContext) {
 fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx.add_window(|cx| {
+    let editor = cx.add_window(|window, cx| {
         let buffer = MultiBuffer::build_simple(
             &"
                 class Foo:
@@ -941,18 +969,18 @@ fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
             .unindent(),
             cx,
         );
-        build_editor(buffer.clone(), cx)
+        build_editor(buffer.clone(), window, cx)
     });
 
-    _ = view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
+    _ = editor.update(cx, |editor, window, cx| {
+        editor.change_selections(None, window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
             ]);
         });
-        view.fold(&Fold, cx);
+        editor.fold(&Fold, window, cx);
         assert_eq!(
-            view.display_text(cx),
+            editor.display_text(cx),
             "
                 class Foo:
                     # Hello!
@@ -967,18 +995,18 @@ fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
             .unindent(),
         );
 
-        view.fold(&Fold, cx);
+        editor.fold(&Fold, window, cx);
         assert_eq!(
-            view.display_text(cx),
+            editor.display_text(cx),
             "
                 class Foo:⋯
             "
             .unindent(),
         );
 
-        view.unfold_lines(&UnfoldLines, cx);
+        editor.unfold_lines(&UnfoldLines, window, cx);
         assert_eq!(
-            view.display_text(cx),
+            editor.display_text(cx),
             "
                 class Foo:
                     # Hello!

crates/editor/src/element.rs 🔗

@@ -30,13 +30,13 @@ use file_icons::FileIcons;
 use git::{blame::BlameEntry, diff::DiffHunkStatus, Oid};
 use gpui::{
     anchored, deferred, div, fill, linear_color_stop, linear_gradient, outline, point, px, quad,
-    relative, size, svg, transparent_black, Action, AnyElement, AvailableSpace, Axis, Bounds,
-    ClickEvent, ClipboardItem, ContentMask, Corner, Corners, CursorStyle, DispatchPhase, Edges,
-    Element, ElementInputHandler, Entity, FontId, GlobalElementId, Hitbox, Hsla,
-    InteractiveElement, IntoElement, Length, ModifiersChangedEvent, MouseButton, MouseDownEvent,
-    MouseMoveEvent, MouseUpEvent, PaintQuad, ParentElement, Pixels, ScrollDelta, ScrollWheelEvent,
-    ShapedLine, SharedString, Size, StatefulInteractiveElement, Style, Styled, Subscription,
-    TextRun, TextStyleRefinement, View, ViewContext, WeakView, WindowContext,
+    relative, size, svg, transparent_black, Action, AnyElement, App, AvailableSpace, Axis, Bounds,
+    ClickEvent, ClipboardItem, ContentMask, Context, Corner, Corners, CursorStyle, DispatchPhase,
+    Edges, Element, ElementInputHandler, Entity, Focusable as _, FontId, GlobalElementId, Hitbox,
+    Hsla, InteractiveElement, IntoElement, Length, ModifiersChangedEvent, MouseButton,
+    MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, ParentElement, Pixels, ScrollDelta,
+    ScrollWheelEvent, ShapedLine, SharedString, Size, StatefulInteractiveElement, Style, Styled,
+    Subscription, TextRun, TextStyleRefinement, WeakEntity, Window,
 };
 use itertools::Itertools;
 use language::{
@@ -159,7 +159,7 @@ impl SelectionLayout {
 }
 
 pub struct EditorElement {
-    editor: View<Editor>,
+    editor: Entity<Editor>,
     style: EditorStyle,
 }
 
@@ -168,332 +168,318 @@ type DisplayRowDelta = u32;
 impl EditorElement {
     pub(crate) const SCROLLBAR_WIDTH: Pixels = px(15.);
 
-    pub fn new(editor: &View<Editor>, style: EditorStyle) -> Self {
+    pub fn new(editor: &Entity<Editor>, style: EditorStyle) -> Self {
         Self {
             editor: editor.clone(),
             style,
         }
     }
 
-    fn register_actions(&self, cx: &mut WindowContext) {
-        let view = &self.editor;
-        view.update(cx, |editor, cx| {
+    fn register_actions(&self, window: &mut Window, cx: &mut App) {
+        let editor = &self.editor;
+        editor.update(cx, |editor, cx| {
             for action in editor.editor_actions.borrow().values() {
-                (action)(cx)
+                (action)(window, cx)
             }
         });
 
-        crate::rust_analyzer_ext::apply_related_actions(view, cx);
-        crate::clangd_ext::apply_related_actions(view, cx);
-        register_action(view, cx, Editor::open_context_menu);
-        register_action(view, cx, Editor::move_left);
-        register_action(view, cx, Editor::move_right);
-        register_action(view, cx, Editor::move_down);
-        register_action(view, cx, Editor::move_down_by_lines);
-        register_action(view, cx, Editor::select_down_by_lines);
-        register_action(view, cx, Editor::move_up);
-        register_action(view, cx, Editor::move_up_by_lines);
-        register_action(view, cx, Editor::select_up_by_lines);
-        register_action(view, cx, Editor::select_page_down);
-        register_action(view, cx, Editor::select_page_up);
-        register_action(view, cx, Editor::cancel);
-        register_action(view, cx, Editor::newline);
-        register_action(view, cx, Editor::newline_above);
-        register_action(view, cx, Editor::newline_below);
-        register_action(view, cx, Editor::backspace);
-        register_action(view, cx, Editor::delete);
-        register_action(view, cx, Editor::tab);
-        register_action(view, cx, Editor::tab_prev);
-        register_action(view, cx, Editor::indent);
-        register_action(view, cx, Editor::outdent);
-        register_action(view, cx, Editor::autoindent);
-        register_action(view, cx, Editor::delete_line);
-        register_action(view, cx, Editor::join_lines);
-        register_action(view, cx, Editor::sort_lines_case_sensitive);
-        register_action(view, cx, Editor::sort_lines_case_insensitive);
-        register_action(view, cx, Editor::reverse_lines);
-        register_action(view, cx, Editor::shuffle_lines);
-        register_action(view, cx, Editor::convert_to_upper_case);
-        register_action(view, cx, Editor::convert_to_lower_case);
-        register_action(view, cx, Editor::convert_to_title_case);
-        register_action(view, cx, Editor::convert_to_snake_case);
-        register_action(view, cx, Editor::convert_to_kebab_case);
-        register_action(view, cx, Editor::convert_to_upper_camel_case);
-        register_action(view, cx, Editor::convert_to_lower_camel_case);
-        register_action(view, cx, Editor::convert_to_opposite_case);
-        register_action(view, cx, Editor::delete_to_previous_word_start);
-        register_action(view, cx, Editor::delete_to_previous_subword_start);
-        register_action(view, cx, Editor::delete_to_next_word_end);
-        register_action(view, cx, Editor::delete_to_next_subword_end);
-        register_action(view, cx, Editor::delete_to_beginning_of_line);
-        register_action(view, cx, Editor::delete_to_end_of_line);
-        register_action(view, cx, Editor::cut_to_end_of_line);
-        register_action(view, cx, Editor::duplicate_line_up);
-        register_action(view, cx, Editor::duplicate_line_down);
-        register_action(view, cx, Editor::duplicate_selection);
-        register_action(view, cx, Editor::move_line_up);
-        register_action(view, cx, Editor::move_line_down);
-        register_action(view, cx, Editor::transpose);
-        register_action(view, cx, Editor::rewrap);
-        register_action(view, cx, Editor::cut);
-        register_action(view, cx, Editor::kill_ring_cut);
-        register_action(view, cx, Editor::kill_ring_yank);
-        register_action(view, cx, Editor::copy);
-        register_action(view, cx, Editor::paste);
-        register_action(view, cx, Editor::undo);
-        register_action(view, cx, Editor::redo);
-        register_action(view, cx, Editor::move_page_up);
-        register_action(view, cx, Editor::move_page_down);
-        register_action(view, cx, Editor::next_screen);
-        register_action(view, cx, Editor::scroll_cursor_top);
-        register_action(view, cx, Editor::scroll_cursor_center);
-        register_action(view, cx, Editor::scroll_cursor_bottom);
-        register_action(view, cx, Editor::scroll_cursor_center_top_bottom);
-        register_action(view, cx, |editor, _: &LineDown, cx| {
-            editor.scroll_screen(&ScrollAmount::Line(1.), cx)
+        crate::rust_analyzer_ext::apply_related_actions(editor, window, cx);
+        crate::clangd_ext::apply_related_actions(editor, window, cx);
+        register_action(editor, window, Editor::open_context_menu);
+        register_action(editor, window, Editor::move_left);
+        register_action(editor, window, Editor::move_right);
+        register_action(editor, window, Editor::move_down);
+        register_action(editor, window, Editor::move_down_by_lines);
+        register_action(editor, window, Editor::select_down_by_lines);
+        register_action(editor, window, Editor::move_up);
+        register_action(editor, window, Editor::move_up_by_lines);
+        register_action(editor, window, Editor::select_up_by_lines);
+        register_action(editor, window, Editor::select_page_down);
+        register_action(editor, window, Editor::select_page_up);
+        register_action(editor, window, Editor::cancel);
+        register_action(editor, window, Editor::newline);
+        register_action(editor, window, Editor::newline_above);
+        register_action(editor, window, Editor::newline_below);
+        register_action(editor, window, Editor::backspace);
+        register_action(editor, window, Editor::delete);
+        register_action(editor, window, Editor::tab);
+        register_action(editor, window, Editor::tab_prev);
+        register_action(editor, window, Editor::indent);
+        register_action(editor, window, Editor::outdent);
+        register_action(editor, window, Editor::autoindent);
+        register_action(editor, window, Editor::delete_line);
+        register_action(editor, window, Editor::join_lines);
+        register_action(editor, window, Editor::sort_lines_case_sensitive);
+        register_action(editor, window, Editor::sort_lines_case_insensitive);
+        register_action(editor, window, Editor::reverse_lines);
+        register_action(editor, window, Editor::shuffle_lines);
+        register_action(editor, window, Editor::convert_to_upper_case);
+        register_action(editor, window, Editor::convert_to_lower_case);
+        register_action(editor, window, Editor::convert_to_title_case);
+        register_action(editor, window, Editor::convert_to_snake_case);
+        register_action(editor, window, Editor::convert_to_kebab_case);
+        register_action(editor, window, Editor::convert_to_upper_camel_case);
+        register_action(editor, window, Editor::convert_to_lower_camel_case);
+        register_action(editor, window, Editor::convert_to_opposite_case);
+        register_action(editor, window, Editor::delete_to_previous_word_start);
+        register_action(editor, window, Editor::delete_to_previous_subword_start);
+        register_action(editor, window, Editor::delete_to_next_word_end);
+        register_action(editor, window, Editor::delete_to_next_subword_end);
+        register_action(editor, window, Editor::delete_to_beginning_of_line);
+        register_action(editor, window, Editor::delete_to_end_of_line);
+        register_action(editor, window, Editor::cut_to_end_of_line);
+        register_action(editor, window, Editor::duplicate_line_up);
+        register_action(editor, window, Editor::duplicate_line_down);
+        register_action(editor, window, Editor::duplicate_selection);
+        register_action(editor, window, Editor::move_line_up);
+        register_action(editor, window, Editor::move_line_down);
+        register_action(editor, window, Editor::transpose);
+        register_action(editor, window, Editor::rewrap);
+        register_action(editor, window, Editor::cut);
+        register_action(editor, window, Editor::kill_ring_cut);
+        register_action(editor, window, Editor::kill_ring_yank);
+        register_action(editor, window, Editor::copy);
+        register_action(editor, window, Editor::paste);
+        register_action(editor, window, Editor::undo);
+        register_action(editor, window, Editor::redo);
+        register_action(editor, window, Editor::move_page_up);
+        register_action(editor, window, Editor::move_page_down);
+        register_action(editor, window, Editor::next_screen);
+        register_action(editor, window, Editor::scroll_cursor_top);
+        register_action(editor, window, Editor::scroll_cursor_center);
+        register_action(editor, window, Editor::scroll_cursor_bottom);
+        register_action(editor, window, Editor::scroll_cursor_center_top_bottom);
+        register_action(editor, window, |editor, _: &LineDown, window, cx| {
+            editor.scroll_screen(&ScrollAmount::Line(1.), window, cx)
         });
-        register_action(view, cx, |editor, _: &LineUp, cx| {
-            editor.scroll_screen(&ScrollAmount::Line(-1.), cx)
+        register_action(editor, window, |editor, _: &LineUp, window, cx| {
+            editor.scroll_screen(&ScrollAmount::Line(-1.), window, cx)
         });
-        register_action(view, cx, |editor, _: &HalfPageDown, cx| {
-            editor.scroll_screen(&ScrollAmount::Page(0.5), cx)
+        register_action(editor, window, |editor, _: &HalfPageDown, window, cx| {
+            editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx)
         });
-        register_action(view, cx, |editor, HandleInput(text): &HandleInput, cx| {
-            if text.is_empty() {
-                return;
-            }
-            editor.handle_input(text, cx);
-        });
-        register_action(view, cx, |editor, _: &HalfPageUp, cx| {
-            editor.scroll_screen(&ScrollAmount::Page(-0.5), cx)
+        register_action(
+            editor,
+            window,
+            |editor, HandleInput(text): &HandleInput, window, cx| {
+                if text.is_empty() {
+                    return;
+                }
+                editor.handle_input(text, window, cx);
+            },
+        );
+        register_action(editor, window, |editor, _: &HalfPageUp, window, cx| {
+            editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx)
         });
-        register_action(view, cx, |editor, _: &PageDown, cx| {
-            editor.scroll_screen(&ScrollAmount::Page(1.), cx)
+        register_action(editor, window, |editor, _: &PageDown, window, cx| {
+            editor.scroll_screen(&ScrollAmount::Page(1.), window, cx)
         });
-        register_action(view, cx, |editor, _: &PageUp, cx| {
-            editor.scroll_screen(&ScrollAmount::Page(-1.), cx)
+        register_action(editor, window, |editor, _: &PageUp, window, cx| {
+            editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx)
         });
-        register_action(view, cx, Editor::move_to_previous_word_start);
-        register_action(view, cx, Editor::move_to_previous_subword_start);
-        register_action(view, cx, Editor::move_to_next_word_end);
-        register_action(view, cx, Editor::move_to_next_subword_end);
-        register_action(view, cx, Editor::move_to_beginning_of_line);
-        register_action(view, cx, Editor::move_to_end_of_line);
-        register_action(view, cx, Editor::move_to_start_of_paragraph);
-        register_action(view, cx, Editor::move_to_end_of_paragraph);
-        register_action(view, cx, Editor::move_to_beginning);
-        register_action(view, cx, Editor::move_to_end);
-        register_action(view, cx, Editor::select_up);
-        register_action(view, cx, Editor::select_down);
-        register_action(view, cx, Editor::select_left);
-        register_action(view, cx, Editor::select_right);
-        register_action(view, cx, Editor::select_to_previous_word_start);
-        register_action(view, cx, Editor::select_to_previous_subword_start);
-        register_action(view, cx, Editor::select_to_next_word_end);
-        register_action(view, cx, Editor::select_to_next_subword_end);
-        register_action(view, cx, Editor::select_to_beginning_of_line);
-        register_action(view, cx, Editor::select_to_end_of_line);
-        register_action(view, cx, Editor::select_to_start_of_paragraph);
-        register_action(view, cx, Editor::select_to_end_of_paragraph);
-        register_action(view, cx, Editor::select_to_beginning);
-        register_action(view, cx, Editor::select_to_end);
-        register_action(view, cx, Editor::select_all);
-        register_action(view, cx, |editor, action, cx| {
-            editor.select_all_matches(action, cx).log_err();
+        register_action(editor, window, Editor::move_to_previous_word_start);
+        register_action(editor, window, Editor::move_to_previous_subword_start);
+        register_action(editor, window, Editor::move_to_next_word_end);
+        register_action(editor, window, Editor::move_to_next_subword_end);
+        register_action(editor, window, Editor::move_to_beginning_of_line);
+        register_action(editor, window, Editor::move_to_end_of_line);
+        register_action(editor, window, Editor::move_to_start_of_paragraph);
+        register_action(editor, window, Editor::move_to_end_of_paragraph);
+        register_action(editor, window, Editor::move_to_beginning);
+        register_action(editor, window, Editor::move_to_end);
+        register_action(editor, window, Editor::select_up);
+        register_action(editor, window, Editor::select_down);
+        register_action(editor, window, Editor::select_left);
+        register_action(editor, window, Editor::select_right);
+        register_action(editor, window, Editor::select_to_previous_word_start);
+        register_action(editor, window, Editor::select_to_previous_subword_start);
+        register_action(editor, window, Editor::select_to_next_word_end);
+        register_action(editor, window, Editor::select_to_next_subword_end);
+        register_action(editor, window, Editor::select_to_beginning_of_line);
+        register_action(editor, window, Editor::select_to_end_of_line);
+        register_action(editor, window, Editor::select_to_start_of_paragraph);
+        register_action(editor, window, Editor::select_to_end_of_paragraph);
+        register_action(editor, window, Editor::select_to_beginning);
+        register_action(editor, window, Editor::select_to_end);
+        register_action(editor, window, Editor::select_all);
+        register_action(editor, window, |editor, action, window, cx| {
+            editor.select_all_matches(action, window, cx).log_err();
         });
-        register_action(view, cx, Editor::select_line);
-        register_action(view, cx, Editor::split_selection_into_lines);
-        register_action(view, cx, Editor::add_selection_above);
-        register_action(view, cx, Editor::add_selection_below);
-        register_action(view, cx, |editor, action, cx| {
-            editor.select_next(action, cx).log_err();
+        register_action(editor, window, Editor::select_line);
+        register_action(editor, window, Editor::split_selection_into_lines);
+        register_action(editor, window, Editor::add_selection_above);
+        register_action(editor, window, Editor::add_selection_below);
+        register_action(editor, window, |editor, action, window, cx| {
+            editor.select_next(action, window, cx).log_err();
         });
-        register_action(view, cx, |editor, action, cx| {
-            editor.select_previous(action, cx).log_err();
+        register_action(editor, window, |editor, action, window, cx| {
+            editor.select_previous(action, window, cx).log_err();
         });
-        register_action(view, cx, Editor::toggle_comments);
-        register_action(view, cx, Editor::select_larger_syntax_node);
-        register_action(view, cx, Editor::select_smaller_syntax_node);
-        register_action(view, cx, Editor::select_enclosing_symbol);
-        register_action(view, cx, Editor::move_to_enclosing_bracket);
-        register_action(view, cx, Editor::undo_selection);
-        register_action(view, cx, Editor::redo_selection);
-        if !view.read(cx).is_singleton(cx) {
-            register_action(view, cx, Editor::expand_excerpts);
-            register_action(view, cx, Editor::expand_excerpts_up);
-            register_action(view, cx, Editor::expand_excerpts_down);
+        register_action(editor, window, Editor::toggle_comments);
+        register_action(editor, window, Editor::select_larger_syntax_node);
+        register_action(editor, window, Editor::select_smaller_syntax_node);
+        register_action(editor, window, Editor::select_enclosing_symbol);
+        register_action(editor, window, Editor::move_to_enclosing_bracket);
+        register_action(editor, window, Editor::undo_selection);
+        register_action(editor, window, Editor::redo_selection);
+        if !editor.read(cx).is_singleton(cx) {
+            register_action(editor, window, Editor::expand_excerpts);
+            register_action(editor, window, Editor::expand_excerpts_up);
+            register_action(editor, window, Editor::expand_excerpts_down);
         }
-        register_action(view, cx, Editor::go_to_diagnostic);
-        register_action(view, cx, Editor::go_to_prev_diagnostic);
-        register_action(view, cx, Editor::go_to_next_hunk);
-        register_action(view, cx, Editor::go_to_prev_hunk);
-        register_action(view, cx, |editor, a, cx| {
-            editor.go_to_definition(a, cx).detach_and_log_err(cx);
-        });
-        register_action(view, cx, |editor, a, cx| {
-            editor.go_to_definition_split(a, cx).detach_and_log_err(cx);
-        });
-        register_action(view, cx, |editor, a, cx| {
-            editor.go_to_declaration(a, cx).detach_and_log_err(cx);
-        });
-        register_action(view, cx, |editor, a, cx| {
-            editor.go_to_declaration_split(a, cx).detach_and_log_err(cx);
-        });
-        register_action(view, cx, |editor, a, cx| {
-            editor.go_to_implementation(a, cx).detach_and_log_err(cx);
-        });
-        register_action(view, cx, |editor, a, cx| {
+        register_action(editor, window, Editor::go_to_diagnostic);
+        register_action(editor, window, Editor::go_to_prev_diagnostic);
+        register_action(editor, window, Editor::go_to_next_hunk);
+        register_action(editor, window, Editor::go_to_prev_hunk);
+        register_action(editor, window, |editor, a, window, cx| {
             editor
-                .go_to_implementation_split(a, cx)
+                .go_to_definition(a, window, cx)
                 .detach_and_log_err(cx);
         });
-        register_action(view, cx, |editor, a, cx| {
-            editor.go_to_type_definition(a, cx).detach_and_log_err(cx);
-        });
-        register_action(view, cx, |editor, a, cx| {
+        register_action(editor, window, |editor, a, window, cx| {
             editor
-                .go_to_type_definition_split(a, cx)
+                .go_to_definition_split(a, window, cx)
                 .detach_and_log_err(cx);
         });
-        register_action(view, cx, Editor::open_url);
-        register_action(view, cx, Editor::open_selected_filename);
-        register_action(view, cx, Editor::fold);
-        register_action(view, cx, Editor::fold_at_level);
-        register_action(view, cx, Editor::fold_all);
-        register_action(view, cx, Editor::fold_function_bodies);
-        register_action(view, cx, Editor::fold_at);
-        register_action(view, cx, Editor::fold_recursive);
-        register_action(view, cx, Editor::toggle_fold);
-        register_action(view, cx, Editor::toggle_fold_recursive);
-        register_action(view, cx, Editor::unfold_lines);
-        register_action(view, cx, Editor::unfold_recursive);
-        register_action(view, cx, Editor::unfold_all);
-        register_action(view, cx, Editor::unfold_at);
-        register_action(view, cx, Editor::fold_selected_ranges);
-        register_action(view, cx, Editor::set_mark);
-        register_action(view, cx, Editor::swap_selection_ends);
-        register_action(view, cx, Editor::show_completions);
-        register_action(view, cx, Editor::toggle_code_actions);
-        register_action(view, cx, Editor::open_excerpts);
-        register_action(view, cx, Editor::open_excerpts_in_split);
-        register_action(view, cx, Editor::open_proposed_changes_editor);
-        register_action(view, cx, Editor::toggle_soft_wrap);
-        register_action(view, cx, Editor::toggle_tab_bar);
-        register_action(view, cx, Editor::toggle_line_numbers);
-        register_action(view, cx, Editor::toggle_relative_line_numbers);
-        register_action(view, cx, Editor::toggle_indent_guides);
-        register_action(view, cx, Editor::toggle_inlay_hints);
-        register_action(view, cx, Editor::toggle_inline_completions);
-        register_action(view, cx, hover_popover::hover);
-        register_action(view, cx, Editor::reveal_in_finder);
-        register_action(view, cx, Editor::copy_path);
-        register_action(view, cx, Editor::copy_relative_path);
-        register_action(view, cx, Editor::copy_highlight_json);
-        register_action(view, cx, Editor::copy_permalink_to_line);
-        register_action(view, cx, Editor::open_permalink_to_line);
-        register_action(view, cx, Editor::copy_file_location);
-        register_action(view, cx, Editor::toggle_git_blame);
-        register_action(view, cx, Editor::toggle_git_blame_inline);
-        register_action(view, cx, Editor::toggle_selected_diff_hunks);
-        register_action(view, cx, Editor::expand_all_diff_hunks);
-        register_action(view, cx, |editor, action, cx| {
-            if let Some(task) = editor.format(action, cx) {
-                task.detach_and_notify_err(cx);
+        register_action(editor, window, Editor::open_url);
+        register_action(editor, window, Editor::open_selected_filename);
+        register_action(editor, window, Editor::fold);
+        register_action(editor, window, Editor::fold_at_level);
+        register_action(editor, window, Editor::fold_all);
+        register_action(editor, window, Editor::fold_function_bodies);
+        register_action(editor, window, Editor::fold_at);
+        register_action(editor, window, Editor::fold_recursive);
+        register_action(editor, window, Editor::toggle_fold);
+        register_action(editor, window, Editor::toggle_fold_recursive);
+        register_action(editor, window, Editor::unfold_lines);
+        register_action(editor, window, Editor::unfold_recursive);
+        register_action(editor, window, Editor::unfold_all);
+        register_action(editor, window, Editor::unfold_at);
+        register_action(editor, window, Editor::fold_selected_ranges);
+        register_action(editor, window, Editor::set_mark);
+        register_action(editor, window, Editor::swap_selection_ends);
+        register_action(editor, window, Editor::show_completions);
+        register_action(editor, window, Editor::toggle_code_actions);
+        register_action(editor, window, Editor::open_excerpts);
+        register_action(editor, window, Editor::open_excerpts_in_split);
+        register_action(editor, window, Editor::open_proposed_changes_editor);
+        register_action(editor, window, Editor::toggle_soft_wrap);
+        register_action(editor, window, Editor::toggle_tab_bar);
+        register_action(editor, window, Editor::toggle_line_numbers);
+        register_action(editor, window, Editor::toggle_relative_line_numbers);
+        register_action(editor, window, Editor::toggle_indent_guides);
+        register_action(editor, window, Editor::toggle_inlay_hints);
+        register_action(editor, window, Editor::toggle_inline_completions);
+        register_action(editor, window, hover_popover::hover);
+        register_action(editor, window, Editor::reveal_in_finder);
+        register_action(editor, window, Editor::copy_path);
+        register_action(editor, window, Editor::copy_relative_path);
+        register_action(editor, window, Editor::copy_highlight_json);
+        register_action(editor, window, Editor::copy_permalink_to_line);
+        register_action(editor, window, Editor::open_permalink_to_line);
+        register_action(editor, window, Editor::copy_file_location);
+        register_action(editor, window, Editor::toggle_git_blame);
+        register_action(editor, window, Editor::toggle_git_blame_inline);
+        register_action(editor, window, Editor::toggle_selected_diff_hunks);
+        register_action(editor, window, Editor::expand_all_diff_hunks);
+        register_action(editor, window, |editor, action, window, cx| {
+            if let Some(task) = editor.format(action, window, cx) {
+                task.detach_and_notify_err(window, cx);
             } else {
                 cx.propagate();
             }
         });
-        register_action(view, cx, |editor, action, cx| {
-            if let Some(task) = editor.format_selections(action, cx) {
-                task.detach_and_notify_err(cx);
+        register_action(editor, window, |editor, action, window, cx| {
+            if let Some(task) = editor.format_selections(action, window, cx) {
+                task.detach_and_notify_err(window, cx);
             } else {
                 cx.propagate();
             }
         });
-        register_action(view, cx, Editor::restart_language_server);
-        register_action(view, cx, Editor::cancel_language_server_work);
-        register_action(view, cx, Editor::show_character_palette);
-        register_action(view, cx, |editor, action, cx| {
-            if let Some(task) = editor.confirm_completion(action, cx) {
-                task.detach_and_notify_err(cx);
+        register_action(editor, window, Editor::restart_language_server);
+        register_action(editor, window, Editor::cancel_language_server_work);
+        register_action(editor, window, Editor::show_character_palette);
+        register_action(editor, window, |editor, action, window, cx| {
+            if let Some(task) = editor.confirm_completion(action, window, cx) {
+                task.detach_and_notify_err(window, cx);
             } else {
                 cx.propagate();
             }
         });
-        register_action(view, cx, |editor, action, cx| {
-            if let Some(task) = editor.compose_completion(action, cx) {
-                task.detach_and_notify_err(cx);
+        register_action(editor, window, |editor, action, window, cx| {
+            if let Some(task) = editor.compose_completion(action, window, cx) {
+                task.detach_and_notify_err(window, cx);
             } else {
                 cx.propagate();
             }
         });
-        register_action(view, cx, |editor, action, cx| {
-            if let Some(task) = editor.confirm_code_action(action, cx) {
-                task.detach_and_notify_err(cx);
+        register_action(editor, window, |editor, action, window, cx| {
+            if let Some(task) = editor.confirm_code_action(action, window, cx) {
+                task.detach_and_notify_err(window, cx);
             } else {
                 cx.propagate();
             }
         });
-        register_action(view, cx, |editor, action, cx| {
-            if let Some(task) = editor.rename(action, cx) {
-                task.detach_and_notify_err(cx);
+        register_action(editor, window, |editor, action, window, cx| {
+            if let Some(task) = editor.rename(action, window, cx) {
+                task.detach_and_notify_err(window, cx);
             } else {
                 cx.propagate();
             }
         });
-        register_action(view, cx, |editor, action, cx| {
-            if let Some(task) = editor.confirm_rename(action, cx) {
-                task.detach_and_notify_err(cx);
+        register_action(editor, window, |editor, action, window, cx| {
+            if let Some(task) = editor.confirm_rename(action, window, cx) {
+                task.detach_and_notify_err(window, cx);
             } else {
                 cx.propagate();
             }
         });
-        register_action(view, cx, |editor, action, cx| {
-            if let Some(task) = editor.find_all_references(action, cx) {
+        register_action(editor, window, |editor, action, window, cx| {
+            if let Some(task) = editor.find_all_references(action, window, cx) {
                 task.detach_and_log_err(cx);
             } else {
                 cx.propagate();
             }
         });
-        register_action(view, cx, Editor::show_signature_help);
-        register_action(view, cx, Editor::next_inline_completion);
-        register_action(view, cx, Editor::previous_inline_completion);
-        register_action(view, cx, Editor::show_inline_completion);
-        register_action(view, cx, Editor::context_menu_first);
-        register_action(view, cx, Editor::context_menu_prev);
-        register_action(view, cx, Editor::context_menu_next);
-        register_action(view, cx, Editor::context_menu_last);
-        register_action(view, cx, Editor::display_cursor_names);
-        register_action(view, cx, Editor::unique_lines_case_insensitive);
-        register_action(view, cx, Editor::unique_lines_case_sensitive);
-        register_action(view, cx, Editor::accept_partial_inline_completion);
-        register_action(view, cx, Editor::accept_inline_completion);
-        register_action(view, cx, Editor::revert_file);
-        register_action(view, cx, Editor::revert_selected_hunks);
-        register_action(view, cx, Editor::apply_all_diff_hunks);
-        register_action(view, cx, Editor::apply_selected_diff_hunks);
-        register_action(view, cx, Editor::open_active_item_in_terminal);
-        register_action(view, cx, Editor::reload_file);
-        register_action(view, cx, Editor::spawn_nearest_task);
-        register_action(view, cx, Editor::insert_uuid_v4);
-        register_action(view, cx, Editor::insert_uuid_v7);
-        register_action(view, cx, Editor::open_selections_in_multibuffer);
+        register_action(editor, window, Editor::show_signature_help);
+        register_action(editor, window, Editor::next_inline_completion);
+        register_action(editor, window, Editor::previous_inline_completion);
+        register_action(editor, window, Editor::show_inline_completion);
+        register_action(editor, window, Editor::context_menu_first);
+        register_action(editor, window, Editor::context_menu_prev);
+        register_action(editor, window, Editor::context_menu_next);
+        register_action(editor, window, Editor::context_menu_last);
+        register_action(editor, window, Editor::display_cursor_names);
+        register_action(editor, window, Editor::unique_lines_case_insensitive);
+        register_action(editor, window, Editor::unique_lines_case_sensitive);
+        register_action(editor, window, Editor::accept_partial_inline_completion);
+        register_action(editor, window, Editor::accept_inline_completion);
+        register_action(editor, window, Editor::revert_file);
+        register_action(editor, window, Editor::revert_selected_hunks);
+        register_action(editor, window, Editor::apply_all_diff_hunks);
+        register_action(editor, window, Editor::apply_selected_diff_hunks);
+        register_action(editor, window, Editor::open_active_item_in_terminal);
+        register_action(editor, window, Editor::reload_file);
+        register_action(editor, window, Editor::spawn_nearest_task);
+        register_action(editor, window, Editor::insert_uuid_v4);
+        register_action(editor, window, Editor::insert_uuid_v7);
+        register_action(editor, window, Editor::open_selections_in_multibuffer);
     }
 
-    fn register_key_listeners(&self, cx: &mut WindowContext, layout: &EditorLayout) {
+    fn register_key_listeners(&self, window: &mut Window, _: &mut App, layout: &EditorLayout) {
         let position_map = layout.position_map.clone();
-        cx.on_key_event({
+        window.on_key_event({
             let editor = self.editor.clone();
             let text_hitbox = layout.text_hitbox.clone();
-            move |event: &ModifiersChangedEvent, phase, cx| {
+            move |event: &ModifiersChangedEvent, phase, window, cx| {
                 if phase != DispatchPhase::Bubble {
                     return;
                 }
                 editor.update(cx, |editor, cx| {
-                    if editor.hover_state.focused(cx) {
+                    if editor.hover_state.focused(window, cx) {
                         return;
                     }
-                    Self::modifiers_changed(editor, event, &position_map, &text_hitbox, cx)
+                    Self::modifiers_changed(editor, event, &position_map, &text_hitbox, window, cx)
                 })
             }
         });
@@ -504,10 +490,11 @@ impl EditorElement {
         event: &ModifiersChangedEvent,
         position_map: &PositionMap,
         text_hitbox: &Hitbox,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) {
-        let mouse_position = cx.mouse_position();
-        if !text_hitbox.is_hovered(cx) {
+        let mouse_position = window.mouse_position();
+        if !text_hitbox.is_hovered(window) {
             return;
         }
 
@@ -515,6 +502,7 @@ impl EditorElement {
             position_map.point_for_position(text_hitbox.bounds, mouse_position),
             &position_map.snapshot,
             event.modifiers,
+            window,
             cx,
         )
     }
@@ -528,9 +516,10 @@ impl EditorElement {
         text_hitbox: &Hitbox,
         gutter_hitbox: &Hitbox,
         line_numbers: &HashMap<MultiBufferRow, LineNumberLayout>,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) {
-        if cx.default_prevented() {
+        if window.default_prevented() {
             return;
         }
 
@@ -541,9 +530,9 @@ impl EditorElement {
             editor.toggle_diff_hunks_in_ranges(vec![hovered_hunk], cx);
             cx.notify();
             return;
-        } else if gutter_hitbox.is_hovered(cx) {
+        } else if gutter_hitbox.is_hovered(window) {
             click_count = 3; // Simulate triple-click when clicking the gutter to select lines
-        } else if !text_hitbox.is_hovered(cx) {
+        } else if !text_hitbox.is_hovered(window) {
             return;
         }
 
@@ -581,6 +570,7 @@ impl EditorElement {
                                 line_offset_from_top,
                             }),
                             false,
+                            window,
                             cx,
                         );
                         return;
@@ -599,6 +589,7 @@ impl EditorElement {
                     reset: false,
                     goal_column: point_for_position.exact_unclipped.column(),
                 },
+                window,
                 cx,
             );
         } else if modifiers.shift && !modifiers.control && !modifiers.alt && !modifiers.secondary()
@@ -608,6 +599,7 @@ impl EditorElement {
                     position,
                     click_count,
                 },
+                window,
                 cx,
             );
         } else {
@@ -622,6 +614,7 @@ impl EditorElement {
                     add: multi_cursor_modifier,
                     click_count,
                 },
+                window,
                 cx,
             );
         }
@@ -650,6 +643,7 @@ impl EditorElement {
                         line_offset_from_top,
                     }),
                     modifiers.alt,
+                    window,
                     cx,
                 );
                 cx.stop_propagation();
@@ -662,9 +656,10 @@ impl EditorElement {
         event: &MouseDownEvent,
         position_map: &PositionMap,
         text_hitbox: &Hitbox,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) {
-        if !text_hitbox.is_hovered(cx) {
+        if !text_hitbox.is_hovered(window) {
             return;
         }
         let point_for_position =
@@ -673,6 +668,7 @@ impl EditorElement {
             editor,
             Some(event.position),
             point_for_position.previous_valid,
+            window,
             cx,
         );
         cx.stop_propagation();
@@ -683,9 +679,10 @@ impl EditorElement {
         event: &MouseDownEvent,
         position_map: &PositionMap,
         text_hitbox: &Hitbox,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) {
-        if !text_hitbox.is_hovered(cx) || cx.default_prevented() {
+        if !text_hitbox.is_hovered(window) || window.default_prevented() {
             return;
         }
 
@@ -699,6 +696,7 @@ impl EditorElement {
                 reset: true,
                 goal_column: point_for_position.exact_unclipped.column(),
             },
+            window,
             cx,
         );
     }
@@ -708,13 +706,14 @@ impl EditorElement {
         event: &MouseUpEvent,
         position_map: &PositionMap,
         text_hitbox: &Hitbox,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) {
         let end_selection = editor.has_pending_selection();
         let pending_nonempty_selections = editor.has_pending_nonempty_selection();
 
         if end_selection {
-            editor.select(SelectPhase::End, cx);
+            editor.select(SelectPhase::End, window, cx);
         }
 
         let multi_cursor_setting = EditorSettings::get_global(cx).multi_cursor_modifier;
@@ -723,9 +722,9 @@ impl EditorElement {
             MultiCursorModifier::CmdOrCtrl => event.modifiers.alt,
         };
 
-        if !pending_nonempty_selections && multi_cursor_modifier && text_hitbox.is_hovered(cx) {
+        if !pending_nonempty_selections && multi_cursor_modifier && text_hitbox.is_hovered(window) {
             let point = position_map.point_for_position(text_hitbox.bounds, event.position);
-            editor.handle_click_hovered_link(point, event.modifiers, cx);
+            editor.handle_click_hovered_link(point, event.modifiers, window, cx);
 
             cx.stop_propagation();
         } else if end_selection && pending_nonempty_selections {
@@ -733,7 +732,7 @@ impl EditorElement {
         } else if cfg!(any(target_os = "linux", target_os = "freebsd"))
             && event.button == MouseButton::Middle
         {
-            if !text_hitbox.is_hovered(cx) || editor.read_only(cx) {
+            if !text_hitbox.is_hovered(window) || editor.read_only(cx) {
                 return;
             }
 
@@ -750,9 +749,10 @@ impl EditorElement {
                             add: false,
                             click_count: 1,
                         },
+                        window,
                         cx,
                     );
-                    editor.insert(&text, cx);
+                    editor.insert(&text, window, cx);
                 }
                 cx.stop_propagation()
             }
@@ -764,7 +764,8 @@ impl EditorElement {
         event: &MouseMoveEvent,
         position_map: &PositionMap,
         text_bounds: Bounds<Pixels>,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) {
         if !editor.has_pending_selection() {
             return;
@@ -784,9 +785,9 @@ impl EditorElement {
 
         // We need horizontal width of text
         let style = editor.style.clone().unwrap_or_default();
-        let font_id = cx.text_system().resolve_font(&style.text.font());
-        let font_size = style.text.font_size.to_pixels(cx.rem_size());
-        let em_width = cx
+        let font_id = window.text_system().resolve_font(&style.text.font());
+        let font_size = style.text.font_size.to_pixels(window.rem_size());
+        let em_width = window
             .text_system()
             .typographic_bounds(font_id, font_size, 'm')
             .unwrap()
@@ -813,6 +814,7 @@ impl EditorElement {
                 goal_column: point_for_position.exact_unclipped.column(),
                 scroll_delta,
             },
+            window,
             cx,
         );
     }
@@ -823,32 +825,39 @@ impl EditorElement {
         position_map: &PositionMap,
         text_hitbox: &Hitbox,
         gutter_hitbox: &Hitbox,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) {
         let modifiers = event.modifiers;
-        let gutter_hovered = gutter_hitbox.is_hovered(cx);
+        let gutter_hovered = gutter_hitbox.is_hovered(window);
         editor.set_gutter_hovered(gutter_hovered, cx);
 
         // Don't trigger hover popover if mouse is hovering over context menu
-        if text_hitbox.is_hovered(cx) {
+        if text_hitbox.is_hovered(window) {
             let point_for_position =
                 position_map.point_for_position(text_hitbox.bounds, event.position);
 
-            editor.update_hovered_link(point_for_position, &position_map.snapshot, modifiers, cx);
+            editor.update_hovered_link(
+                point_for_position,
+                &position_map.snapshot,
+                modifiers,
+                window,
+                cx,
+            );
 
             if let Some(point) = point_for_position.as_valid() {
                 let anchor = position_map
                     .snapshot
                     .buffer_snapshot
                     .anchor_before(point.to_offset(&position_map.snapshot, Bias::Left));
-                hover_at(editor, Some(anchor), cx);
-                Self::update_visible_cursor(editor, point, position_map, cx);
+                hover_at(editor, Some(anchor), window, cx);
+                Self::update_visible_cursor(editor, point, position_map, window, cx);
             } else {
-                hover_at(editor, None, cx);
+                hover_at(editor, None, window, cx);
             }
         } else {
             editor.hide_hovered_link(cx);
-            hover_at(editor, None, cx);
+            hover_at(editor, None, window, cx);
             if gutter_hovered {
                 cx.stop_propagation();
             }
@@ -859,7 +868,8 @@ impl EditorElement {
         editor: &mut Editor,
         point: DisplayPoint,
         position_map: &PositionMap,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) {
         let snapshot = &position_map.snapshot;
         let Some(hub) = editor.collaboration_hub() else {
@@ -893,7 +903,7 @@ impl EditorElement {
         };
         editor.hovered_cursors.insert(
             key.clone(),
-            cx.spawn(|editor, mut cx| async move {
+            cx.spawn_in(window, |editor, mut cx| async move {
                 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
                 editor
                     .update(&mut cx, |editor, cx| {
@@ -915,7 +925,8 @@ impl EditorElement {
         snapshot: &EditorSnapshot,
         start_row: DisplayRow,
         end_row: DisplayRow,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (
         Vec<(PlayerColor, Vec<SelectionLayout>)>,
         BTreeMap<DisplayRow, bool>,
@@ -1021,7 +1032,7 @@ impl EditorElement {
                 }
 
                 selections.extend(remote_selections.into_values());
-            } else if !editor.is_focused(cx) && editor.show_cursor_when_unfocused {
+            } else if !editor.is_focused(window) && editor.show_cursor_when_unfocused {
                 let player = if editor.read_only(cx) {
                     cx.theme().players().read_only()
                 } else {
@@ -1051,7 +1062,7 @@ impl EditorElement {
     fn collect_cursors(
         &self,
         snapshot: &EditorSnapshot,
-        cx: &mut WindowContext,
+        cx: &mut App,
     ) -> Vec<(DisplayPoint, Hsla)> {
         let editor = self.editor.read(cx);
         let mut cursors = Vec::new();
@@ -1102,7 +1113,8 @@ impl EditorElement {
         em_width: Pixels,
         em_advance: Pixels,
         autoscroll_containing_element: bool,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Vec<CursorLayout> {
         let mut autoscroll_bounds = None;
         let cursor_layouts = self.editor.update(cx, |editor, cx| {
@@ -1112,7 +1124,7 @@ impl EditorElement {
                     let cursor_position = selection.head;
 
                     let in_range = visible_display_row_range.contains(&cursor_position.row());
-                    if (selection.is_local && !editor.show_local_cursors(cx))
+                    if (selection.is_local && !editor.show_local_cursors(window, cx))
                         || !in_range
                         || block_start_rows.contains(&cursor_position.row())
                     {
@@ -1147,7 +1159,7 @@ impl EditorElement {
                                 let font = cursor_row_layout
                                     .font_id_for_index(cursor_column)
                                     .and_then(|cursor_font_id| {
-                                        cx.text_system().get_font_for_id(cursor_font_id)
+                                        window.text_system().get_font_for_id(cursor_font_id)
                                     })
                                     .unwrap_or(self.style.text.font());
 
@@ -1167,7 +1179,8 @@ impl EditorElement {
                                     cx.theme().colors().editor_background
                                 };
 
-                                cx.text_system()
+                                window
+                                    .text_system()
                                     .shape_line(
                                         text,
                                         cursor_row_layout.font_size,
@@ -1231,7 +1244,7 @@ impl EditorElement {
                         color: self.style.background,
                         is_top_row: cursor_position.row().0 == 0,
                     });
-                    cursor.layout(content_origin, cursor_name, cx);
+                    cursor.layout(content_origin, cursor_name, window, cx);
                     cursors.push(cursor);
                 }
             }
@@ -1239,7 +1252,7 @@ impl EditorElement {
         });
 
         if let Some(bounds) = autoscroll_bounds {
-            cx.request_autoscroll(bounds);
+            window.request_autoscroll(bounds);
         }
 
         cursor_layouts
@@ -1251,7 +1264,8 @@ impl EditorElement {
         scrollbar_range_data: ScrollbarRangeData,
         scroll_position: gpui::Point<f32>,
         non_visible_cursors: bool,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> AxisPair<Option<ScrollbarLayout>> {
         let letter_size = scrollbar_range_data.letter_size;
         let text_units_per_page = axis_pair(
@@ -1398,7 +1412,7 @@ impl EditorElement {
             .zip(thumb_size.horizontal)
             .map(
                 |(((track_bounds, visible_range), text_unit_size), thumb_size)| ScrollbarLayout {
-                    hitbox: cx.insert_hitbox(track_bounds, false),
+                    hitbox: window.insert_hitbox(track_bounds, false),
                     visible_range,
                     text_unit_size,
                     visible: show_scrollbars,
@@ -1414,7 +1428,7 @@ impl EditorElement {
             .zip(thumb_size.vertical)
             .map(
                 |(((track_bounds, visible_range), text_unit_size), thumb_size)| ScrollbarLayout {
-                    hitbox: cx.insert_hitbox(track_bounds, false),
+                    hitbox: window.insert_hitbox(track_bounds, false),
                     visible_range,
                     text_unit_size,
                     visible: show_scrollbars,
@@ -1435,7 +1449,8 @@ impl EditorElement {
         gutter_settings: crate::editor_settings::Gutter,
         scroll_pixel_position: gpui::Point<Pixels>,
         gutter_hitbox: &Hitbox,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         for (ix, crease_toggle) in crease_toggles.iter_mut().enumerate() {
             if let Some(crease_toggle) = crease_toggle {

crates/editor/src/git/blame.rs 🔗

@@ -4,7 +4,7 @@ use git::{
     blame::{Blame, BlameEntry},
     parse_git_remote_url, GitHostingProvider, GitHostingProviderRegistry, Oid, PullRequest,
 };
-use gpui::{AppContext, Model, ModelContext, Subscription, Task};
+use gpui::{App, Context, Entity, Subscription, Task};
 use http_client::HttpClient;
 use language::{markdown, Bias, Buffer, BufferSnapshot, Edit, LanguageRegistry, ParsedMarkdown};
 use multi_buffer::RowInfo;
@@ -96,8 +96,8 @@ pub struct CommitDetails {
 }
 
 pub struct GitBlame {
-    project: Model<Project>,
-    buffer: Model<Buffer>,
+    project: Entity<Project>,
+    buffer: Entity<Buffer>,
     entries: SumTree<GitBlameEntry>,
     commit_details: HashMap<Oid, CommitDetails>,
     buffer_snapshot: BufferSnapshot,
@@ -113,11 +113,11 @@ pub struct GitBlame {
 
 impl GitBlame {
     pub fn new(
-        buffer: Model<Buffer>,
-        project: Model<Project>,
+        buffer: Entity<Buffer>,
+        project: Entity<Project>,
         user_triggered: bool,
         focused: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Self {
         let entries = SumTree::from_item(
             GitBlameEntry {
@@ -194,7 +194,7 @@ impl GitBlame {
     pub fn blame_for_rows<'a>(
         &'a mut self,
         rows: &'a [RowInfo],
-        cx: &AppContext,
+        cx: &App,
     ) -> impl 'a + Iterator<Item = Option<BlameEntry>> {
         self.sync(cx);
 
@@ -206,7 +206,7 @@ impl GitBlame {
         })
     }
 
-    pub fn max_author_length(&mut self, cx: &AppContext) -> usize {
+    pub fn max_author_length(&mut self, cx: &App) -> usize {
         self.sync(cx);
 
         let mut max_author_length = 0;
@@ -227,11 +227,11 @@ impl GitBlame {
         max_author_length
     }
 
-    pub fn blur(&mut self, _: &mut ModelContext<Self>) {
+    pub fn blur(&mut self, _: &mut Context<Self>) {
         self.focused = false;
     }
 
-    pub fn focus(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn focus(&mut self, cx: &mut Context<Self>) {
         self.focused = true;
         if self.changed_while_blurred {
             self.changed_while_blurred = false;
@@ -239,7 +239,7 @@ impl GitBlame {
         }
     }
 
-    fn sync(&mut self, cx: &AppContext) {
+    fn sync(&mut self, cx: &App) {
         let edits = self.buffer_edits.consume();
         let new_snapshot = self.buffer.read(cx).snapshot();
 
@@ -342,7 +342,7 @@ impl GitBlame {
     }
 
     #[cfg(test)]
-    fn check_invariants(&mut self, cx: &mut ModelContext<Self>) {
+    fn check_invariants(&mut self, cx: &mut Context<Self>) {
         self.sync(cx);
         assert_eq!(
             self.entries.summary().rows,
@@ -350,7 +350,7 @@ impl GitBlame {
         );
     }
 
-    fn generate(&mut self, cx: &mut ModelContext<Self>) {
+    fn generate(&mut self, cx: &mut Context<Self>) {
         if !self.focused {
             self.changed_while_blurred = true;
             return;
@@ -422,7 +422,7 @@ impl GitBlame {
         });
     }
 
-    fn regenerate_on_edit(&mut self, cx: &mut ModelContext<Self>) {
+    fn regenerate_on_edit(&mut self, cx: &mut Context<Self>) {
         self.regenerate_on_edit_task = cx.spawn(|this, mut cx| async move {
             cx.background_executor()
                 .timer(REGENERATE_ON_EDIT_DEBOUNCE_INTERVAL)
@@ -552,7 +552,7 @@ async fn parse_markdown(text: &str, language_registry: &Arc<LanguageRegistry>) -
 #[cfg(test)]
 mod tests {
     use super::*;
-    use gpui::Context;
+    use gpui::{AppContext as _, Context};
     use language::{Point, Rope};
     use project::FakeFs;
     use rand::prelude::*;
@@ -578,7 +578,7 @@ mod tests {
         blame: &mut GitBlame,
         rows: Range<u32>,
         expected: Vec<Option<BlameEntry>>,
-        cx: &mut ModelContext<GitBlame>,
+        cx: &mut Context<GitBlame>,
     ) {
         assert_eq!(
             blame
@@ -640,8 +640,7 @@ mod tests {
             .await
             .unwrap();
 
-        let blame =
-            cx.new_model(|cx| GitBlame::new(buffer.clone(), project.clone(), true, true, cx));
+        let blame = cx.new(|cx| GitBlame::new(buffer.clone(), project.clone(), true, true, cx));
 
         let event = project.next_event(cx).await;
         assert_eq!(
@@ -720,7 +719,7 @@ mod tests {
             .await
             .unwrap();
 
-        let git_blame = cx.new_model(|cx| GitBlame::new(buffer.clone(), project, false, true, cx));
+        let git_blame = cx.new(|cx| GitBlame::new(buffer.clone(), project, false, true, cx));
 
         cx.executor().run_until_parked();
 
@@ -826,7 +825,7 @@ mod tests {
             .await
             .unwrap();
 
-        let git_blame = cx.new_model(|cx| GitBlame::new(buffer.clone(), project, false, true, cx));
+        let git_blame = cx.new(|cx| GitBlame::new(buffer.clone(), project, false, true, cx));
 
         cx.executor().run_until_parked();
 
@@ -975,7 +974,7 @@ mod tests {
             .await
             .unwrap();
 
-        let git_blame = cx.new_model(|cx| GitBlame::new(buffer.clone(), project, false, true, cx));
+        let git_blame = cx.new(|cx| GitBlame::new(buffer.clone(), project, false, true, cx));
         cx.executor().run_until_parked();
         git_blame.update(cx, |blame, cx| blame.check_invariants(cx));
 

crates/editor/src/git/project_diff.rs 🔗

@@ -11,8 +11,8 @@ use collections::{BTreeMap, HashMap};
 use feature_flags::FeatureFlagAppExt;
 use git::diff::{BufferDiff, DiffHunk};
 use gpui::{
-    actions, AnyElement, AnyView, AppContext, EventEmitter, FocusHandle, FocusableView,
-    InteractiveElement, Model, Render, Subscription, Task, View, WeakView,
+    actions, AnyElement, AnyView, App, Entity, EventEmitter, FocusHandle, Focusable,
+    InteractiveElement, Render, Subscription, Task, WeakEntity,
 };
 use language::{Buffer, BufferRow};
 use multi_buffer::{ExcerptId, ExcerptRange, ExpandExcerptDirection, MultiBuffer};
@@ -30,8 +30,8 @@ use crate::{Editor, EditorEvent, DEFAULT_MULTIBUFFER_CONTEXT};
 
 actions!(project_diff, [Deploy]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(ProjectDiffEditor::register).detach();
+pub fn init(cx: &mut App) {
+    cx.observe_new(ProjectDiffEditor::register).detach();
 }
 
 const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
@@ -39,11 +39,11 @@ const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
 struct ProjectDiffEditor {
     buffer_changes: BTreeMap<WorktreeId, HashMap<ProjectEntryId, Changes>>,
     entry_order: HashMap<WorktreeId, Vec<(ProjectPath, ProjectEntryId)>>,
-    excerpts: Model<MultiBuffer>,
-    editor: View<Editor>,
+    excerpts: Entity<MultiBuffer>,
+    editor: Entity<Editor>,
 
-    project: Model<Project>,
-    workspace: WeakView<Workspace>,
+    project: Entity<Project>,
+    workspace: WeakEntity<Workspace>,
     focus_handle: FocusHandle,
     worktree_rescans: HashMap<WorktreeId, Task<()>>,
     _subscriptions: Vec<Subscription>,
@@ -51,40 +51,50 @@ struct ProjectDiffEditor {
 
 #[derive(Debug)]
 struct Changes {
-    buffer: Model<Buffer>,
+    buffer: Entity<Buffer>,
     hunks: Vec<DiffHunk>,
 }
 
 impl ProjectDiffEditor {
-    fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+    fn register(
+        workspace: &mut Workspace,
+        _window: Option<&mut Window>,
+        _: &mut Context<Workspace>,
+    ) {
         workspace.register_action(Self::deploy);
     }
 
-    fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext<Workspace>) {
+    fn deploy(
+        workspace: &mut Workspace,
+        _: &Deploy,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) {
         if !cx.is_staff() {
             return;
         }
 
         if let Some(existing) = workspace.item_of_type::<Self>(cx) {
-            workspace.activate_item(&existing, true, true, cx);
+            workspace.activate_item(&existing, true, true, window, cx);
         } else {
-            let workspace_handle = cx.view().downgrade();
+            let workspace_handle = cx.model().downgrade();
             let project_diff =
-                cx.new_view(|cx| Self::new(workspace.project().clone(), workspace_handle, cx));
-            workspace.add_item_to_active_pane(Box::new(project_diff), None, true, cx);
+                cx.new(|cx| Self::new(workspace.project().clone(), workspace_handle, window, cx));
+            workspace.add_item_to_active_pane(Box::new(project_diff), None, true, window, cx);
         }
     }
 
     fn new(
-        project: Model<Project>,
-        workspace: WeakView<Workspace>,
-        cx: &mut ViewContext<Self>,
+        project: Entity<Project>,
+        workspace: WeakEntity<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         // TODO diff change subscriptions. For that, needed:
         // * `-20/+50` stats retrieval: some background process that reacts on file changes
         let focus_handle = cx.focus_handle();
         let changed_entries_subscription =
-            cx.subscribe(&project, |project_diff_editor, _, e, cx| {
+            cx.subscribe_in(&project, window, |project_diff_editor, _, e, window, cx| {
                 let mut worktree_to_rescan = None;
                 match e {
                     project::Event::WorktreeAdded(id) => {
@@ -137,15 +147,15 @@ impl ProjectDiffEditor {
                 }
 
                 if let Some(worktree_to_rescan) = worktree_to_rescan {
-                    project_diff_editor.schedule_worktree_rescan(worktree_to_rescan, cx);
+                    project_diff_editor.schedule_worktree_rescan(worktree_to_rescan, window, cx);
                 }
             });
 
-        let excerpts = cx.new_model(|cx| MultiBuffer::new(project.read(cx).capability()));
+        let excerpts = cx.new(|cx| MultiBuffer::new(project.read(cx).capability()));
 
-        let editor = cx.new_view(|cx| {
+        let editor = cx.new(|cx| {
             let mut diff_display_editor =
-                Editor::for_multibuffer(excerpts.clone(), Some(project.clone()), true, cx);
+                Editor::for_multibuffer(excerpts.clone(), Some(project.clone()), true, window, cx);
             diff_display_editor.set_expand_all_diff_hunks(cx);
             diff_display_editor
         });
@@ -161,16 +171,16 @@ impl ProjectDiffEditor {
             excerpts,
             _subscriptions: vec![changed_entries_subscription],
         };
-        new_self.schedule_rescan_all(cx);
+        new_self.schedule_rescan_all(window, cx);
         new_self
     }
 
-    fn schedule_rescan_all(&mut self, cx: &mut ViewContext<Self>) {
+    fn schedule_rescan_all(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let mut current_worktrees = HashSet::<WorktreeId>::default();
         for worktree in self.project.read(cx).worktrees(cx).collect::<Vec<_>>() {
             let worktree_id = worktree.read(cx).id();
             current_worktrees.insert(worktree_id);
-            self.schedule_worktree_rescan(worktree_id, cx);
+            self.schedule_worktree_rescan(worktree_id, window, cx);
         }
 
         self.worktree_rescans
@@ -181,11 +191,16 @@ impl ProjectDiffEditor {
             .retain(|worktree_id, _| current_worktrees.contains(worktree_id));
     }
 
-    fn schedule_worktree_rescan(&mut self, id: WorktreeId, cx: &mut ViewContext<Self>) {
+    fn schedule_worktree_rescan(
+        &mut self,
+        id: WorktreeId,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let project = self.project.clone();
         self.worktree_rescans.insert(
             id,
-            cx.spawn(|project_diff_editor, mut cx| async move {
+            cx.spawn_in(window, |project_diff_editor, mut cx| async move {
                 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
                 let open_tasks = project
                     .update(&mut cx, |project, cx| {
@@ -229,7 +244,7 @@ impl ProjectDiffEditor {
                         let mut new_entries = Vec::new();
                         let mut buffers = HashMap::<
                             ProjectEntryId,
-                            (text::BufferSnapshot, Model<Buffer>, BufferDiff),
+                            (text::BufferSnapshot, Entity<Buffer>, BufferDiff),
                         >::default();
                         let mut change_sets = Vec::new();
                         for (entry_id, entry_path, open_task) in open_tasks {
@@ -258,7 +273,7 @@ impl ProjectDiffEditor {
                                 continue;
                             };
 
-                            cx.update(|cx| {
+                            cx.update(|_, cx| {
                                 buffers.insert(
                                     entry_id,
                                     (
@@ -307,7 +322,7 @@ impl ProjectDiffEditor {
                     .await;
 
                 project_diff_editor
-                    .update(&mut cx, |project_diff_editor, cx| {
+                    .update_in(&mut cx, |project_diff_editor, _window, cx| {
                         project_diff_editor.update_excerpts(id, new_changes, new_entry_order, cx);
                         project_diff_editor.editor.update(cx, |editor, cx| {
                             editor.buffer.update(cx, |buffer, cx| {
@@ -327,7 +342,8 @@ impl ProjectDiffEditor {
         worktree_id: WorktreeId,
         new_changes: HashMap<ProjectEntryId, Changes>,
         new_entry_order: Vec<(ProjectPath, ProjectEntryId)>,
-        cx: &mut ViewContext<ProjectDiffEditor>,
+
+        cx: &mut Context<ProjectDiffEditor>,
     ) {
         if let Some(current_order) = self.entry_order.get(&worktree_id) {
             let current_entries = self.buffer_changes.entry(worktree_id).or_default();
@@ -335,7 +351,7 @@ impl ProjectDiffEditor {
             let mut excerpts_to_remove = Vec::new();
             let mut new_excerpt_hunks = BTreeMap::<
                 ExcerptId,
-                Vec<(ProjectPath, Model<Buffer>, Vec<Range<text::Anchor>>)>,
+                Vec<(ProjectPath, Entity<Buffer>, Vec<Range<text::Anchor>>)>,
             >::new();
             let mut excerpt_to_expand =
                 HashMap::<(u32, ExpandExcerptDirection), Vec<ExcerptId>>::default();
@@ -902,8 +918,8 @@ impl ProjectDiffEditor {
 
 impl EventEmitter<EditorEvent> for ProjectDiffEditor {}
 
-impl FocusableView for ProjectDiffEditor {
-    fn focus_handle(&self, _: &AppContext) -> FocusHandle {
+impl Focusable for ProjectDiffEditor {
+    fn focus_handle(&self, _: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -915,20 +931,26 @@ impl Item for ProjectDiffEditor {
         Editor::to_item_events(event, f)
     }
 
-    fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
-        self.editor.update(cx, |editor, cx| editor.deactivated(cx));
+    fn deactivated(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        self.editor
+            .update(cx, |editor, cx| editor.deactivated(window, cx));
     }
 
-    fn navigate(&mut self, data: Box<dyn Any>, cx: &mut ViewContext<Self>) -> bool {
+    fn navigate(
+        &mut self,
+        data: Box<dyn Any>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> bool {
         self.editor
-            .update(cx, |editor, cx| editor.navigate(data, cx))
+            .update(cx, |editor, cx| editor.navigate(data, window, cx))
     }
 
-    fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
+    fn tab_tooltip_text(&self, _: &App) -> Option<SharedString> {
         Some("Project Diff".into())
     }
 
-    fn tab_content(&self, params: TabContentParams, _: &WindowContext) -> AnyElement {
+    fn tab_content(&self, params: TabContentParams, _window: &Window, _: &App) -> AnyElement {
         if self.buffer_changes.is_empty() {
             Label::new("No changes")
                 .color(if params.selected {
@@ -978,17 +1000,22 @@ impl Item for ProjectDiffEditor {
 
     fn for_each_project_item(
         &self,
-        cx: &AppContext,
+        cx: &App,
         f: &mut dyn FnMut(gpui::EntityId, &dyn project::ProjectItem),
     ) {
         self.editor.for_each_project_item(cx, f)
     }
 
-    fn is_singleton(&self, _: &AppContext) -> bool {
+    fn is_singleton(&self, _: &App) -> bool {
         false
     }
 
-    fn set_nav_history(&mut self, nav_history: ItemNavHistory, cx: &mut ViewContext<Self>) {
+    fn set_nav_history(
+        &mut self,
+        nav_history: ItemNavHistory,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.editor.update(cx, |editor, _| {
             editor.set_nav_history(Some(nav_history));
         });
@@ -997,59 +1024,63 @@ impl Item for ProjectDiffEditor {
     fn clone_on_split(
         &self,
         _workspace_id: Option<workspace::WorkspaceId>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<View<Self>>
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Entity<Self>>
     where
         Self: Sized,
     {
-        Some(cx.new_view(|cx| {
-            ProjectDiffEditor::new(self.project.clone(), self.workspace.clone(), cx)
+        Some(cx.new(|cx| {
+            ProjectDiffEditor::new(self.project.clone(), self.workspace.clone(), window, cx)
         }))
     }
 
-    fn is_dirty(&self, cx: &AppContext) -> bool {
+    fn is_dirty(&self, cx: &App) -> bool {
         self.excerpts.read(cx).is_dirty(cx)
     }
 
-    fn has_conflict(&self, cx: &AppContext) -> bool {
+    fn has_conflict(&self, cx: &App) -> bool {
         self.excerpts.read(cx).has_conflict(cx)
     }
 
-    fn can_save(&self, _: &AppContext) -> bool {
+    fn can_save(&self, _: &App) -> bool {
         true
     }
 
     fn save(
         &mut self,
         format: bool,
-        project: Model<Project>,
-        cx: &mut ViewContext<Self>,
+        project: Entity<Project>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<anyhow::Result<()>> {
-        self.editor.save(format, project, cx)
+        self.editor.save(format, project, window, cx)
     }
 
     fn save_as(
         &mut self,
-        _: Model<Project>,
+        _: Entity<Project>,
         _: ProjectPath,
-        _: &mut ViewContext<Self>,
+        _window: &mut Window,
+        _: &mut Context<Self>,
     ) -> Task<anyhow::Result<()>> {
         unreachable!()
     }
 
     fn reload(
         &mut self,
-        project: Model<Project>,
-        cx: &mut ViewContext<Self>,
+        project: Entity<Project>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<anyhow::Result<()>> {
-        self.editor.reload(project, cx)
+        self.editor.reload(project, window, cx)
     }
 
     fn act_as_type<'a>(
         &'a self,
         type_id: TypeId,
-        self_handle: &'a View<Self>,
-        _: &'a AppContext,
+        self_handle: &'a Entity<Self>,
+        _: &'a App,
     ) -> Option<AnyView> {
         if type_id == TypeId::of::<Self>() {
             Some(self_handle.to_any())
@@ -1060,22 +1091,28 @@ impl Item for ProjectDiffEditor {
         }
     }
 
-    fn breadcrumb_location(&self, _: &AppContext) -> ToolbarItemLocation {
+    fn breadcrumb_location(&self, _: &App) -> ToolbarItemLocation {
         ToolbarItemLocation::PrimaryLeft
     }
 
-    fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
+    fn breadcrumbs(&self, theme: &theme::Theme, cx: &App) -> Option<Vec<BreadcrumbText>> {
         self.editor.breadcrumbs(theme, cx)
     }
 
-    fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
-        self.editor
-            .update(cx, |editor, cx| editor.added_to_workspace(workspace, cx));
+    fn added_to_workspace(
+        &mut self,
+        workspace: &mut Workspace,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.editor.update(cx, |editor, cx| {
+            editor.added_to_workspace(workspace, window, cx)
+        });
     }
 }
 
 impl Render for ProjectDiffEditor {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let child = if self.buffer_changes.is_empty() {
             div()
                 .bg(cx.theme().colors().editor_background)
@@ -1142,14 +1179,15 @@ mod tests {
         .await;
 
         let project = Project::test(fs.clone(), [Path::new("/root")], cx).await;
-        let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
+        let workspace =
+            cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
         let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 
         let file_a_editor = workspace
-            .update(cx, |workspace, cx| {
+            .update(cx, |workspace, window, cx| {
                 let file_a_editor =
-                    workspace.open_abs_path(PathBuf::from("/root/file_a"), true, cx);
-                ProjectDiffEditor::deploy(workspace, &Deploy, cx);
+                    workspace.open_abs_path(PathBuf::from("/root/file_a"), true, window, cx);
+                ProjectDiffEditor::deploy(workspace, &Deploy, window, cx);
                 file_a_editor
             })
             .unwrap()
@@ -1158,7 +1196,7 @@ mod tests {
             .downcast::<Editor>()
             .expect("did not open an editor for file_a");
         let project_diff_editor = workspace
-            .update(cx, |workspace, cx| {
+            .update(cx, |workspace, _, cx| {
                 workspace
                     .active_pane()
                     .read(cx)
@@ -1177,14 +1215,14 @@ mod tests {
         let old_text = file_a_editor.update(cx, |editor, cx| editor.text(cx));
         let change = "an edit after git add";
         file_a_editor
-            .update(cx, |file_a_editor, cx| {
-                file_a_editor.insert(change, cx);
-                file_a_editor.save(false, project.clone(), cx)
+            .update_in(cx, |file_a_editor, window, cx| {
+                file_a_editor.insert(change, window, cx);
+                file_a_editor.save(false, project.clone(), window, cx)
             })
             .await
             .expect("failed to save a file");
-        file_a_editor.update(cx, |file_a_editor, cx| {
-            let change_set = cx.new_model(|cx| {
+        file_a_editor.update_in(cx, |file_a_editor, _window, cx| {
+            let change_set = cx.new(|cx| {
                 BufferChangeSet::new_with_base_text(
                     old_text.clone(),
                     &file_a_editor.buffer().read(cx).as_singleton().unwrap(),
@@ -1223,7 +1261,7 @@ mod tests {
         cx.executor()
             .advance_clock(UPDATE_DEBOUNCE + Duration::from_millis(100));
         cx.run_until_parked();
-        let editor = project_diff_editor.update(cx, |view, _| view.editor.clone());
+        let editor = project_diff_editor.update(cx, |diff_editor, _| diff_editor.editor.clone());
 
         assert_state_with_diff(
             &editor,

crates/editor/src/highlight_matching_bracket.rs 🔗

@@ -1,11 +1,14 @@
-use gpui::ViewContext;
-use language::CursorShape;
-
 use crate::{Editor, RangeToAnchorExt};
+use gpui::{Context, Window};
+use language::CursorShape;
 
 enum MatchingBracketHighlight {}
 
-pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
+pub fn refresh_matching_bracket_highlights(
+    editor: &mut Editor,
+    window: &mut Window,
+    cx: &mut Context<Editor>,
+) {
     editor.clear_background_highlights::<MatchingBracketHighlight>(cx);
 
     let newest_selection = editor.selections.newest::<usize>(cx);
@@ -14,7 +17,7 @@ pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewCon
         return;
     }
 
-    let snapshot = editor.snapshot(cx);
+    let snapshot = editor.snapshot(window, cx);
     let head = newest_selection.head();
     let mut tail = head;
     if (editor.cursor_shape == CursorShape::Block || editor.cursor_shape == CursorShape::Hollow)

crates/editor/src/hover_links.rs 🔗

@@ -5,7 +5,7 @@ use crate::{
     Anchor, Editor, EditorSettings, EditorSnapshot, FindAllReferences, GoToDefinition,
     GoToTypeDefinition, GotoDefinitionKind, InlayId, Navigated, PointForPosition, SelectPhase,
 };
-use gpui::{px, AppContext, AsyncWindowContext, Model, Modifiers, Task, ViewContext};
+use gpui::{px, App, AsyncWindowContext, Context, Entity, Modifiers, Task, Window};
 use language::{Bias, ToOffset};
 use linkify::{LinkFinder, LinkKind};
 use lsp::LanguageServerId;
@@ -93,10 +93,10 @@ impl TriggerPoint {
 }
 
 pub fn exclude_link_to_position(
-    buffer: &Model<language::Buffer>,
+    buffer: &Entity<language::Buffer>,
     current_position: &text::Anchor,
     location: &LocationLink,
-    cx: &AppContext,
+    cx: &App,
 ) -> bool {
     // Exclude definition links that points back to cursor position.
     // (i.e., currently cursor upon definition).
@@ -117,7 +117,8 @@ impl Editor {
         point_for_position: PointForPosition,
         snapshot: &EditorSnapshot,
         modifiers: Modifiers,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let multi_cursor_setting = EditorSettings::get_global(cx).multi_cursor_modifier;
         let hovered_link_modifier = match multi_cursor_setting {
@@ -137,7 +138,7 @@ impl Editor {
                         .anchor_before(point.to_offset(&snapshot.display_snapshot, Bias::Left)),
                 );
 
-                show_link_definition(modifiers.shift, self, trigger_point, snapshot, cx);
+                show_link_definition(modifiers.shift, self, trigger_point, snapshot, window, cx);
             }
             None => {
                 update_inlay_link_and_hover_points(
@@ -146,13 +147,14 @@ impl Editor {
                     self,
                     hovered_link_modifier,
                     modifiers.shift,
+                    window,
                     cx,
                 );
             }
         }
     }
 
-    pub(crate) fn hide_hovered_link(&mut self, cx: &mut ViewContext<Self>) {
+    pub(crate) fn hide_hovered_link(&mut self, cx: &mut Context<Self>) {
         self.hovered_link_state.take();
         self.clear_highlights::<HoveredLinkState>(cx);
     }
@@ -161,17 +163,18 @@ impl Editor {
         &mut self,
         point: PointForPosition,
         modifiers: Modifiers,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) {
-        let reveal_task = self.cmd_click_reveal_task(point, modifiers, cx);
-        cx.spawn(|editor, mut cx| async move {
+        let reveal_task = self.cmd_click_reveal_task(point, modifiers, window, cx);
+        cx.spawn_in(window, |editor, mut cx| async move {
             let definition_revealed = reveal_task.await.log_err().unwrap_or(Navigated::No);
             let find_references = editor
-                .update(&mut cx, |editor, cx| {
+                .update_in(&mut cx, |editor, window, cx| {
                     if definition_revealed == Navigated::Yes {
                         return None;
                     }
-                    editor.find_all_references(&FindAllReferences, cx)
+                    editor.find_all_references(&FindAllReferences, window, cx)
                 })
                 .ok()
                 .flatten();
@@ -182,9 +185,14 @@ impl Editor {
         .detach();
     }
 
-    pub fn scroll_hover(&mut self, amount: &ScrollAmount, cx: &mut ViewContext<Self>) -> bool {
+    pub fn scroll_hover(
+        &mut self,
+        amount: &ScrollAmount,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> bool {
         let selection = self.selections.newest_anchor().head();
-        let snapshot = self.snapshot(cx);
+        let snapshot = self.snapshot(window, cx);
 
         let Some(popover) = self.hover_state.info_popovers.iter().find(|popover| {
             popover
@@ -193,7 +201,7 @@ impl Editor {
         }) else {
             return false;
         };
-        popover.scroll(amount, cx);
+        popover.scroll(amount, window, cx);
         true
     }
 
@@ -201,19 +209,20 @@ impl Editor {
         &mut self,
         point: PointForPosition,
         modifiers: Modifiers,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) -> Task<anyhow::Result<Navigated>> {
         if let Some(hovered_link_state) = self.hovered_link_state.take() {
             self.hide_hovered_link(cx);
             if !hovered_link_state.links.is_empty() {
-                if !self.focus_handle.is_focused(cx) {
-                    cx.focus(&self.focus_handle);
+                if !self.focus_handle.is_focused(window) {
+                    window.focus(&self.focus_handle);
                 }
 
                 // exclude links pointing back to the current anchor
                 let current_position = point
                     .next_valid
-                    .to_point(&self.snapshot(cx).display_snapshot);
+                    .to_point(&self.snapshot(window, cx).display_snapshot);
                 let Some((buffer, anchor)) = self
                     .buffer()
                     .read(cx)
@@ -233,7 +242,7 @@ impl Editor {
                     })
                     .collect();
 
-                return self.navigate_to_hover_links(None, links, modifiers.alt, cx);
+                return self.navigate_to_hover_links(None, links, modifiers.alt, window, cx);
             }
         }
 
@@ -245,14 +254,15 @@ impl Editor {
                 add: false,
                 click_count: 1,
             },
+            window,
             cx,
         );
 
         if point.as_valid().is_some() {
             if modifiers.shift {
-                self.go_to_type_definition(&GoToTypeDefinition, cx)
+                self.go_to_type_definition(&GoToTypeDefinition, window, cx)
             } else {
-                self.go_to_definition(&GoToDefinition, cx)
+                self.go_to_definition(&GoToDefinition, window, cx)
             }
         } else {
             Task::ready(Ok(Navigated::No))
@@ -266,7 +276,8 @@ pub fn update_inlay_link_and_hover_points(
     editor: &mut Editor,
     secondary_held: bool,
     shift_held: bool,
-    cx: &mut ViewContext<Editor>,
+    window: &mut Window,
+    cx: &mut Context<Editor>,
 ) {
     let hovered_offset = if point_for_position.column_overshoot_after_line_end == 0 {
         Some(snapshot.display_point_to_inlay_offset(point_for_position.exact_unclipped, Bias::Left))
@@ -310,6 +321,7 @@ pub fn update_inlay_link_and_hover_points(
                                 buffer_id,
                                 excerpt_id,
                                 hovered_hint.id,
+                                window,
                                 cx,
                             );
                         }
@@ -349,6 +361,7 @@ pub fn update_inlay_link_and_hover_points(
                                                     ..hovered_hint.text.len() + extra_shift_right,
                                             },
                                         },
+                                        window,
                                         cx,
                                     );
                                     hover_updated = true;
@@ -393,6 +406,7 @@ pub fn update_inlay_link_and_hover_points(
                                                 },
                                                 range: highlight.clone(),
                                             },
+                                            window,
                                             cx,
                                         );
                                         hover_updated = true;
@@ -413,6 +427,7 @@ pub fn update_inlay_link_and_hover_points(
                                                     language_server_id,
                                                 ),
                                                 snapshot,
+                                                window,
                                                 cx,
                                             );
                                         }
@@ -431,7 +446,7 @@ pub fn update_inlay_link_and_hover_points(
         editor.hide_hovered_link(cx)
     }
     if !hover_updated {
-        hover_popover::hover_at(editor, None, cx);
+        hover_popover::hover_at(editor, None, window, cx);
     }
 }
 
@@ -440,7 +455,8 @@ pub fn show_link_definition(
     editor: &mut Editor,
     trigger_point: TriggerPoint,
     snapshot: &EditorSnapshot,
-    cx: &mut ViewContext<Editor>,
+    window: &mut Window,
+    cx: &mut Context<Editor>,
 ) {
     let preferred_kind = match trigger_point {
         TriggerPoint::Text(_) if !shift_held => GotoDefinitionKind::Symbol,
@@ -509,7 +525,7 @@ pub fn show_link_definition(
     let provider = editor.semantics_provider.clone();
 
     let snapshot = snapshot.buffer_snapshot.clone();
-    hovered_link_state.task = Some(cx.spawn(|this, mut cx| {
+    hovered_link_state.task = Some(cx.spawn_in(window, |this, mut cx| {
         async move {
             let result = match &trigger_point {
                 TriggerPoint::Text(_) => {
@@ -536,7 +552,7 @@ pub fn show_link_definition(
 
                         Some((range, vec![HoverLink::File(filename)]))
                     } else if let Some(provider) = provider {
-                        let task = cx.update(|cx| {
+                        let task = cx.update(|_, cx| {
                             provider.definitions(&buffer, buffer_position, preferred_kind, cx)
                         })?;
                         if let Some(task) = task {
@@ -633,7 +649,7 @@ pub fn show_link_definition(
 }
 
 pub(crate) fn find_url(
-    buffer: &Model<language::Buffer>,
+    buffer: &Entity<language::Buffer>,
     position: text::Anchor,
     mut cx: AsyncWindowContext,
 ) -> Option<(Range<text::Anchor>, String)> {
@@ -695,7 +711,7 @@ pub(crate) fn find_url(
 }
 
 pub(crate) fn find_url_from_range(
-    buffer: &Model<language::Buffer>,
+    buffer: &Entity<language::Buffer>,
     range: Range<text::Anchor>,
     mut cx: AsyncWindowContext,
 ) -> Option<String> {
@@ -754,8 +770,8 @@ pub(crate) fn find_url_from_range(
 }
 
 pub(crate) async fn find_file(
-    buffer: &Model<language::Buffer>,
-    project: Option<Model<Project>>,
+    buffer: &Entity<language::Buffer>,
+    project: Option<Entity<Project>>,
     position: text::Anchor,
     cx: &mut AsyncWindowContext,
 ) -> Option<(Range<text::Anchor>, ResolvedPath)> {
@@ -766,8 +782,8 @@ pub(crate) async fn find_file(
 
     async fn check_path(
         candidate_file_path: &str,
-        project: &Model<Project>,
-        buffer: &Model<language::Buffer>,
+        project: &Entity<Project>,
+        buffer: &Entity<language::Buffer>,
         cx: &mut AsyncWindowContext,
     ) -> Option<ResolvedPath> {
         project
@@ -926,7 +942,7 @@ mod tests {
             struct A;
             let vˇariable = A;
         "});
-        let screen_coord = cx.editor(|editor, cx| editor.pixel_position_of_cursor(cx));
+        let screen_coord = cx.editor(|editor, _, cx| editor.pixel_position_of_cursor(cx));
 
         // Basic hold cmd+shift, expect highlight in region if response contains type definition
         let symbol_range = cx.lsp_range(indoc! {"
@@ -1226,11 +1242,11 @@ mod tests {
                 fn do_work() { test(); }
             "})[0]
             .clone();
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, window, cx| {
             let snapshot = editor.buffer().read(cx).snapshot(cx);
             let anchor_range = snapshot.anchor_before(selection_range.start)
                 ..snapshot.anchor_after(selection_range.end);
-            editor.change_selections(Some(crate::Autoscroll::fit()), cx, |s| {
+            editor.change_selections(Some(crate::Autoscroll::fit()), window, cx, |s| {
                 s.set_pending_anchor_range(anchor_range, crate::SelectMode::Character)
             });
         });
@@ -1319,7 +1335,7 @@ mod tests {
             .next()
             .await;
         cx.background_executor.run_until_parked();
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, _window, cx| {
             let expected_layers = vec![hint_label.to_string()];
             assert_eq!(expected_layers, cached_hint_labels(editor));
             assert_eq!(expected_layers, visible_hint_labels(editor, cx));
@@ -1336,8 +1352,8 @@ mod tests {
             .first()
             .cloned()
             .unwrap();
-        let midpoint = cx.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
+        let midpoint = cx.update_editor(|editor, window, cx| {
+            let snapshot = editor.snapshot(window, cx);
             let previous_valid = inlay_range.start.to_display_point(&snapshot);
             let next_valid = inlay_range.end.to_display_point(&snapshot);
             assert_eq!(previous_valid.row(), next_valid.row());
@@ -1351,8 +1367,8 @@ mod tests {
         let hover_point = cx.pixel_position_for(midpoint);
         cx.simulate_mouse_move(hover_point, None, Modifiers::secondary_key());
         cx.background_executor.run_until_parked();
-        cx.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
+        cx.update_editor(|editor, window, cx| {
+            let snapshot = editor.snapshot(window, cx);
             let actual_highlights = snapshot
                 .inlay_highlights::<HoveredLinkState>()
                 .into_iter()
@@ -1370,8 +1386,8 @@ mod tests {
 
         cx.simulate_mouse_move(hover_point, None, Modifiers::none());
         // Assert no link highlights
-        cx.update_editor(|editor, cx| {
-                let snapshot = editor.snapshot(cx);
+        cx.update_editor(|editor, window, cx| {
+                let snapshot = editor.snapshot(window, cx);
                 let actual_ranges = snapshot
                     .text_highlight_ranges::<HoveredLinkState>()
                     .map(|ranges| ranges.as_ref().clone().1)
@@ -1515,7 +1531,7 @@ mod tests {
         for (input, expected) in test_cases {
             cx.set_state(input);
 
-            let (position, snapshot) = cx.editor(|editor, cx| {
+            let (position, snapshot) = cx.editor(|editor, _, cx| {
                 let positions = editor.selections.newest_anchor().head().text_anchor;
                 let snapshot = editor
                     .buffer()
@@ -1556,7 +1572,7 @@ mod tests {
         .await;
 
         // Insert a new file
-        let fs = cx.update_workspace(|workspace, cx| workspace.project().read(cx).fs().clone());
+        let fs = cx.update_workspace(|workspace, _, cx| workspace.project().read(cx).fs().clone());
         fs.as_fake()
             .insert_file("/root/dir/file2.rs", "This is file2.rs".as_bytes().to_vec())
             .await;
@@ -1579,9 +1595,9 @@ mod tests {
         "});
         cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key());
         // No highlight
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, window, cx| {
             assert!(editor
-                .snapshot(cx)
+                .snapshot(window, cx)
                 .text_highlight_ranges::<HoveredLinkState>()
                 .unwrap_or_default()
                 .1
@@ -1662,8 +1678,8 @@ mod tests {
 
         cx.simulate_click(screen_coord, Modifiers::secondary_key());
 
-        cx.update_workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 2));
-        cx.update_workspace(|workspace, cx| {
+        cx.update_workspace(|workspace, _, cx| assert_eq!(workspace.items(cx).count(), 2));
+        cx.update_workspace(|workspace, _, cx| {
             let active_editor = workspace.active_item_as::<Editor>(cx).unwrap();
 
             let buffer = active_editor
@@ -1692,7 +1708,7 @@ mod tests {
         .await;
 
         // Insert a new file
-        let fs = cx.update_workspace(|workspace, cx| workspace.project().read(cx).fs().clone());
+        let fs = cx.update_workspace(|workspace, _, cx| workspace.project().read(cx).fs().clone());
         fs.as_fake()
             .insert_file("/root/dir/file2.rs", "This is file2.rs".as_bytes().to_vec())
             .await;
@@ -1708,9 +1724,9 @@ mod tests {
         cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key());
 
         // No highlight
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, window, cx| {
             assert!(editor
-                .snapshot(cx)
+                .snapshot(window, cx)
                 .text_highlight_ranges::<HoveredLinkState>()
                 .unwrap_or_default()
                 .1
@@ -1719,6 +1735,6 @@ mod tests {
 
         // Does not open the directory
         cx.simulate_click(screen_coord, Modifiers::secondary_key());
-        cx.update_workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 1));
+        cx.update_workspace(|workspace, _, cx| assert_eq!(workspace.items(cx).count(), 1));
     }
 }

crates/editor/src/hover_popover.rs 🔗

@@ -6,9 +6,10 @@ use crate::{
     Hover,
 };
 use gpui::{
-    div, px, AnyElement, AsyncWindowContext, FontWeight, Hsla, InteractiveElement, IntoElement,
-    MouseButton, ParentElement, Pixels, ScrollHandle, Size, Stateful, StatefulInteractiveElement,
-    StyleRefinement, Styled, Task, TextStyleRefinement, View, ViewContext,
+    div, px, AnyElement, AsyncWindowContext, Context, Entity, Focusable as _, FontWeight, Hsla,
+    InteractiveElement, IntoElement, MouseButton, ParentElement, Pixels, ScrollHandle, Size,
+    Stateful, StatefulInteractiveElement, StyleRefinement, Styled, Task, TextStyleRefinement,
+    Window,
 };
 use itertools::Itertools;
 use language::{DiagnosticEntry, Language, LanguageRegistry};
@@ -21,7 +22,7 @@ use std::rc::Rc;
 use std::{borrow::Cow, cell::RefCell};
 use std::{ops::Range, sync::Arc, time::Duration};
 use theme::ThemeSettings;
-use ui::{prelude::*, window_is_transparent, Scrollbar, ScrollbarState};
+use ui::{prelude::*, theme_is_transparent, Scrollbar, ScrollbarState};
 use util::TryFutureExt;
 pub const HOVER_REQUEST_DELAY_MILLIS: u64 = 200;
 
@@ -30,33 +31,42 @@ pub const MIN_POPOVER_LINE_HEIGHT: Pixels = px(4.);
 pub const HOVER_POPOVER_GAP: Pixels = px(10.);
 
 /// Bindable action which uses the most recent selection head to trigger a hover
-pub fn hover(editor: &mut Editor, _: &Hover, cx: &mut ViewContext<Editor>) {
+pub fn hover(editor: &mut Editor, _: &Hover, window: &mut Window, cx: &mut Context<Editor>) {
     let head = editor.selections.newest_anchor().head();
-    show_hover(editor, head, true, cx);
+    show_hover(editor, head, true, window, cx);
 }
 
 /// The internal hover action dispatches between `show_hover` or `hide_hover`
 /// depending on whether a point to hover over is provided.
-pub fn hover_at(editor: &mut Editor, anchor: Option<Anchor>, cx: &mut ViewContext<Editor>) {
+pub fn hover_at(
+    editor: &mut Editor,
+    anchor: Option<Anchor>,
+    window: &mut Window,
+    cx: &mut Context<Editor>,
+) {
     if EditorSettings::get_global(cx).hover_popover_enabled {
-        if show_keyboard_hover(editor, cx) {
+        if show_keyboard_hover(editor, window, cx) {
             return;
         }
         if let Some(anchor) = anchor {
-            show_hover(editor, anchor, false, cx);
+            show_hover(editor, anchor, false, window, cx);
         } else {
             hide_hover(editor, cx);
         }
     }
 }
 
-pub fn show_keyboard_hover(editor: &mut Editor, cx: &mut ViewContext<Editor>) -> bool {
+pub fn show_keyboard_hover(
+    editor: &mut Editor,
+    window: &mut Window,
+    cx: &mut Context<Editor>,
+) -> bool {
     let info_popovers = editor.hover_state.info_popovers.clone();
     for p in info_popovers {
         let keyboard_grace = p.keyboard_grace.borrow();
         if *keyboard_grace {
             if let Some(anchor) = p.anchor {
-                show_hover(editor, anchor, false, cx);
+                show_hover(editor, anchor, false, window, cx);
                 return true;
             }
         }
@@ -67,7 +77,7 @@ pub fn show_keyboard_hover(editor: &mut Editor, cx: &mut ViewContext<Editor>) ->
         let keyboard_grace = d.keyboard_grace.borrow();
         if *keyboard_grace {
             if let Some(anchor) = d.anchor {
-                show_hover(editor, anchor, false, cx);
+                show_hover(editor, anchor, false, window, cx);
                 return true;
             }
         }
@@ -103,7 +113,12 @@ pub fn find_hovered_hint_part(
     None
 }
 
-pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut ViewContext<Editor>) {
+pub fn hover_at_inlay(
+    editor: &mut Editor,
+    inlay_hover: InlayHover,
+    window: &mut Window,
+    cx: &mut Context<Editor>,
+) {
     if EditorSettings::get_global(cx).hover_popover_enabled {
         if editor.pending_rename.is_some() {
             return;
@@ -132,7 +147,7 @@ pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut Vie
 
         let hover_popover_delay = EditorSettings::get_global(cx).hover_popover_delay;
 
-        let task = cx.spawn(|this, mut cx| {
+        let task = cx.spawn_in(window, |this, mut cx| {
             async move {
                 cx.background_executor()
                     .timer(Duration::from_millis(hover_popover_delay))
@@ -173,7 +188,7 @@ pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut Vie
 /// Hides the type information popup.
 /// Triggered by the `Hover` action when the cursor is not over a symbol or when the
 /// selections changed.
-pub fn hide_hover(editor: &mut Editor, cx: &mut ViewContext<Editor>) -> bool {
+pub fn hide_hover(editor: &mut Editor, cx: &mut Context<Editor>) -> bool {
     let info_popovers = editor.hover_state.info_popovers.drain(..);
     let diagnostics_popover = editor.hover_state.diagnostic_popover.take();
     let did_hide = info_popovers.count() > 0 || diagnostics_popover.is_some();
@@ -197,13 +212,14 @@ fn show_hover(
     editor: &mut Editor,
     anchor: Anchor,
     ignore_timeout: bool,
-    cx: &mut ViewContext<Editor>,
+    window: &mut Window,
+    cx: &mut Context<Editor>,
 ) -> Option<()> {
     if editor.pending_rename.is_some() {
         return None;
     }
 
-    let snapshot = editor.snapshot(cx);
+    let snapshot = editor.snapshot(window, cx);
 
     let (buffer, buffer_position) = editor
         .buffer
@@ -239,7 +255,7 @@ fn show_hover(
 
     let hover_popover_delay = EditorSettings::get_global(cx).hover_popover_delay;
 
-    let task = cx.spawn(|this, mut cx| {
+    let task = cx.spawn_in(window, |this, mut cx| {
         async move {
             // If we need to delay, delay a set amount initially before making the lsp request
             let delay = if ignore_timeout {
@@ -257,7 +273,7 @@ fn show_hover(
                 total_delay
             };
 
-            let hover_request = cx.update(|cx| provider.hover(&buffer, buffer_position, cx))?;
+            let hover_request = cx.update(|_, cx| provider.hover(&buffer, buffer_position, cx))?;
 
             if let Some(delay) = delay {
                 delay.await;
@@ -290,7 +306,7 @@ fn show_hover(
                 let mut background_color: Option<Hsla> = None;
 
                 let parsed_content = cx
-                    .new_view(|cx| {
+                    .new_window_model(|window, cx| {
                         let status_colors = cx.theme().status();
 
                         match local_diagnostic.diagnostic.severity {
@@ -316,7 +332,7 @@ fn show_hover(
                             }
                         };
                         let settings = ThemeSettings::get_global(cx);
-                        let mut base_text_style = cx.text_style();
+                        let mut base_text_style = window.text_style();
                         base_text_style.refine(&TextStyleRefinement {
                             font_family: Some(settings.ui_font.family.clone()),
                             font_fallbacks: settings.ui_font.fallbacks.clone(),
@@ -339,7 +355,7 @@ fn show_hover(
                             },
                             ..Default::default()
                         };
-                        Markdown::new_text(text, markdown_style.clone(), None, None, cx)
+                        Markdown::new_text(text, markdown_style.clone(), None, None, window, cx)
                     })
                     .ok();
 
@@ -389,7 +405,7 @@ fn show_hover(
             } else {
                 Vec::new()
             };
-            let snapshot = this.update(&mut cx, |this, cx| this.snapshot(cx))?;
+            let snapshot = this.update_in(&mut cx, |this, window, cx| this.snapshot(window, cx))?;
             let mut hover_highlights = Vec::with_capacity(hovers_response.len());
             let mut info_popovers = Vec::with_capacity(
                 hovers_response.len() + if invisible_char.is_some() { 1 } else { 0 },
@@ -451,7 +467,7 @@ fn show_hover(
                 });
             }
 
-            this.update(&mut cx, |editor, cx| {
+            this.update_in(&mut cx, |editor, window, cx| {
                 if hover_highlights.is_empty() {
                     editor.clear_background_highlights::<HoverState>(cx);
                 } else {
@@ -465,7 +481,7 @@ fn show_hover(
 
                 editor.hover_state.info_popovers = info_popovers;
                 cx.notify();
-                cx.refresh();
+                window.refresh();
             })?;
 
             anyhow::Ok(())
@@ -519,7 +535,7 @@ async fn parse_blocks(
     language_registry: &Arc<LanguageRegistry>,
     language: Option<Arc<Language>>,
     cx: &mut AsyncWindowContext,
-) -> Option<View<Markdown>> {
+) -> Option<Entity<Markdown>> {
     let fallback_language_name = if let Some(ref l) = language {
         let l = Arc::clone(l);
         Some(l.lsp_id().clone())
@@ -540,14 +556,14 @@ async fn parse_blocks(
         .join("\n\n");
 
     let rendered_block = cx
-        .new_view(|cx| {
+        .new_window_model(|window, cx| {
             let settings = ThemeSettings::get_global(cx);
             let ui_font_family = settings.ui_font.family.clone();
             let ui_font_fallbacks = settings.ui_font.fallbacks.clone();
             let buffer_font_family = settings.buffer_font.family.clone();
             let buffer_font_fallbacks = settings.buffer_font.fallbacks.clone();
 
-            let mut base_text_style = cx.text_style();
+            let mut base_text_style = window.text_style();
             base_text_style.refine(&TextStyleRefinement {
                 font_family: Some(ui_font_family.clone()),
                 font_fallbacks: ui_font_fallbacks,
@@ -594,6 +610,7 @@ async fn parse_blocks(
                 markdown_style.clone(),
                 Some(language_registry.clone()),
                 fallback_language_name,
+                window,
                 cx,
             )
             .copy_code_block_buttons(false)
@@ -621,7 +638,7 @@ impl HoverState {
         snapshot: &EditorSnapshot,
         visible_rows: Range<DisplayRow>,
         max_size: Size<Pixels>,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) -> Option<(DisplayPoint, Vec<AnyElement>)> {
         // If there is a diagnostic, position the popovers based on that.
         // Otherwise use the start of the hover range
@@ -664,18 +681,18 @@ impl HoverState {
         Some((point, elements))
     }
 
-    pub fn focused(&self, cx: &mut ViewContext<Editor>) -> bool {
+    pub fn focused(&self, window: &mut Window, cx: &mut Context<Editor>) -> bool {
         let mut hover_popover_is_focused = false;
         for info_popover in &self.info_popovers {
             if let Some(markdown_view) = &info_popover.parsed_content {
-                if markdown_view.focus_handle(cx).is_focused(cx) {
+                if markdown_view.focus_handle(cx).is_focused(window) {
                     hover_popover_is_focused = true;
                 }
             }
         }
         if let Some(diagnostic_popover) = &self.diagnostic_popover {
             if let Some(markdown_view) = &diagnostic_popover.parsed_content {
-                if markdown_view.focus_handle(cx).is_focused(cx) {
+                if markdown_view.focus_handle(cx).is_focused(window) {
                     hover_popover_is_focused = true;
                 }
             }
@@ -687,7 +704,7 @@ impl HoverState {
 #[derive(Debug, Clone)]
 pub(crate) struct InfoPopover {
     pub(crate) symbol_range: RangeInEditor,
-    pub(crate) parsed_content: Option<View<Markdown>>,
+    pub(crate) parsed_content: Option<Entity<Markdown>>,
     pub(crate) scroll_handle: ScrollHandle,
     pub(crate) scrollbar_state: ScrollbarState,
     pub(crate) keyboard_grace: Rc<RefCell<bool>>,
@@ -698,7 +715,7 @@ impl InfoPopover {
     pub(crate) fn render(
         &mut self,
         max_size: Size<Pixels>,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) -> AnyElement {
         let keyboard_grace = Rc::clone(&self.keyboard_grace);
         let mut d = div()
@@ -706,8 +723,8 @@ impl InfoPopover {
             .elevation_2(cx)
             // Prevent a mouse down/move on the popover from being propagated to the editor,
             // because that would dismiss the popover.
-            .on_mouse_move(|_, cx| cx.stop_propagation())
-            .on_mouse_down(MouseButton::Left, move |_, cx| {
+            .on_mouse_move(|_, _, cx| cx.stop_propagation())
+            .on_mouse_down(MouseButton::Left, move |_, _, cx| {
                 let mut keyboard_grace = keyboard_grace.borrow_mut();
                 *keyboard_grace = false;
                 cx.stop_propagation();
@@ -730,37 +747,37 @@ impl InfoPopover {
         d.into_any_element()
     }
 
-    pub fn scroll(&self, amount: &ScrollAmount, cx: &mut ViewContext<Editor>) {
+    pub fn scroll(&self, amount: &ScrollAmount, window: &mut Window, cx: &mut Context<Editor>) {
         let mut current = self.scroll_handle.offset();
         current.y -= amount.pixels(
-            cx.line_height(),
+            window.line_height(),
             self.scroll_handle.bounds().size.height - px(16.),
         ) / 2.0;
         cx.notify();
         self.scroll_handle.set_offset(current);
     }
 
-    fn render_vertical_scrollbar(&self, cx: &mut ViewContext<Editor>) -> Stateful<Div> {
+    fn render_vertical_scrollbar(&self, cx: &mut Context<Editor>) -> Stateful<Div> {
         div()
             .occlude()
             .id("info-popover-vertical-scroll")
-            .on_mouse_move(cx.listener(|_, _, cx| {
+            .on_mouse_move(cx.listener(|_, _, _, cx| {
                 cx.notify();
                 cx.stop_propagation()
             }))
-            .on_hover(|_, cx| {
+            .on_hover(|_, _, cx| {
                 cx.stop_propagation();
             })
-            .on_any_mouse_down(|_, cx| {
+            .on_any_mouse_down(|_, _, cx| {
                 cx.stop_propagation();
             })
             .on_mouse_up(
                 MouseButton::Left,
-                cx.listener(|_, _, cx| {
+                cx.listener(|_, _, _, cx| {
                     cx.stop_propagation();
                 }),
             )
-            .on_scroll_wheel(cx.listener(|_, _, cx| {
+            .on_scroll_wheel(cx.listener(|_, _, _, cx| {
                 cx.notify();
             }))
             .h_full()
@@ -777,7 +794,7 @@ impl InfoPopover {
 #[derive(Debug, Clone)]
 pub struct DiagnosticPopover {
     pub(crate) local_diagnostic: DiagnosticEntry<Anchor>,
-    parsed_content: Option<View<Markdown>>,
+    parsed_content: Option<Entity<Markdown>>,
     border_color: Option<Hsla>,
     background_color: Option<Hsla>,
     pub keyboard_grace: Rc<RefCell<bool>>,
@@ -785,7 +802,7 @@ pub struct DiagnosticPopover {
 }
 
 impl DiagnosticPopover {
-    pub fn render(&self, max_size: Size<Pixels>, cx: &mut ViewContext<Editor>) -> AnyElement {
+    pub fn render(&self, max_size: Size<Pixels>, cx: &mut Context<Editor>) -> AnyElement {
         let keyboard_grace = Rc::clone(&self.keyboard_grace);
         let mut markdown_div = div().py_1().px_2();
         if let Some(markdown) = &self.parsed_content {
@@ -812,15 +829,15 @@ impl DiagnosticPopover {
             .elevation_2_borderless(cx)
             // Don't draw the background color if the theme
             // allows transparent surfaces.
-            .when(window_is_transparent(cx), |this| {
+            .when(theme_is_transparent(cx), |this| {
                 this.bg(gpui::transparent_black())
             })
             // Prevent a mouse move on the popover from being propagated to the editor,
             // because that would dismiss the popover.
-            .on_mouse_move(|_, cx| cx.stop_propagation())
+            .on_mouse_move(|_, _, cx| cx.stop_propagation())
             // Prevent a mouse down on the popover from being propagated to the editor,
             // because that would move the cursor.
-            .on_mouse_down(MouseButton::Left, move |_, cx| {
+            .on_mouse_down(MouseButton::Left, move |_, _, cx| {
                 let mut keyboard_grace = keyboard_grace.borrow_mut();
                 *keyboard_grace = false;
                 cx.stop_propagation();
@@ -843,7 +860,7 @@ mod tests {
         InlayId, PointForPosition,
     };
     use collections::BTreeSet;
-    use gpui::AppContext;
+    use gpui::App;
     use indoc::indoc;
     use language::{language_settings::InlayHintSettings, Diagnostic, DiagnosticSet};
     use lsp::LanguageServerId;
@@ -854,11 +871,11 @@ mod tests {
     use text::Bias;
 
     fn get_hover_popover_delay(cx: &gpui::TestAppContext) -> u64 {
-        cx.read(|cx: &AppContext| -> u64 { EditorSettings::get_global(cx).hover_popover_delay })
+        cx.read(|cx: &App| -> u64 { EditorSettings::get_global(cx).hover_popover_delay })
     }
 
     impl InfoPopover {
-        fn get_rendered_text(&self, cx: &gpui::AppContext) -> String {
+        fn get_rendered_text(&self, cx: &gpui::App) -> String {
             let mut rendered_text = String::new();
             if let Some(parsed_content) = self.parsed_content.clone() {
                 let markdown = parsed_content.read(cx);
@@ -927,14 +944,14 @@ mod tests {
                 three
                 fn test() { printˇln!(); }
             "});
-        cx.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
+        cx.update_editor(|editor, window, cx| {
+            let snapshot = editor.snapshot(window, cx);
             let anchor = snapshot
                 .buffer_snapshot
                 .anchor_before(hover_point.to_offset(&snapshot, Bias::Left));
-            hover_at(editor, Some(anchor), cx)
+            hover_at(editor, Some(anchor), window, cx)
         });
-        assert!(!cx.editor(|editor, _| editor.hover_state.visible()));
+        assert!(!cx.editor(|editor, _window, _cx| editor.hover_state.visible()));
 
         // After delay, hover should be visible.
         let symbol_range = cx.lsp_range(indoc! {"
@@ -957,7 +974,7 @@ mod tests {
             .advance_clock(Duration::from_millis(get_hover_popover_delay(&cx) + 100));
         requests.next().await;
 
-        cx.editor(|editor, cx| {
+        cx.editor(|editor, _window, cx| {
             assert!(editor.hover_state.visible());
             assert_eq!(
                 editor.hover_state.info_popovers.len(),
@@ -975,14 +992,14 @@ mod tests {
         });
 
         // check that the completion menu is still visible and that there still has only been 1 completion request
-        cx.editor(|editor, _| assert!(editor.context_menu_visible()));
+        cx.editor(|editor, _, _| assert!(editor.context_menu_visible()));
         assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 
         //apply a completion and check it was successfully applied
-        let _apply_additional_edits = cx.update_editor(|editor, cx| {
-            editor.context_menu_next(&Default::default(), cx);
+        let _apply_additional_edits = cx.update_editor(|editor, window, cx| {
+            editor.context_menu_next(&Default::default(), window, cx);
             editor
-                .confirm_completion(&ConfirmCompletion::default(), cx)
+                .confirm_completion(&ConfirmCompletion::default(), window, cx)
                 .unwrap()
         });
         cx.assert_editor_state(indoc! {"
@@ -993,11 +1010,11 @@ mod tests {
         "});
 
         // check that the completion menu is no longer visible and that there still has only been 1 completion request
-        cx.editor(|editor, _| assert!(!editor.context_menu_visible()));
+        cx.editor(|editor, _, _| assert!(!editor.context_menu_visible()));
         assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 
         //verify the information popover is still visible and unchanged
-        cx.editor(|editor, cx| {
+        cx.editor(|editor, _, cx| {
             assert!(editor.hover_state.visible());
             assert_eq!(
                 editor.hover_state.info_popovers.len(),
@@ -1025,19 +1042,19 @@ mod tests {
         let mut request = cx
             .lsp
             .handle_request::<lsp::request::HoverRequest, _, _>(|_, _| async move { Ok(None) });
-        cx.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
+        cx.update_editor(|editor, window, cx| {
+            let snapshot = editor.snapshot(window, cx);
             let anchor = snapshot
                 .buffer_snapshot
                 .anchor_before(hover_point.to_offset(&snapshot, Bias::Left));
-            hover_at(editor, Some(anchor), cx)
+            hover_at(editor, Some(anchor), window, cx)
         });
         cx.background_executor
             .advance_clock(Duration::from_millis(get_hover_popover_delay(&cx) + 100));
         request.next().await;
 
         // verify that the information popover is no longer visible
-        cx.editor(|editor, _| {
+        cx.editor(|editor, _, _| {
             assert!(!editor.hover_state.visible());
         });
     }
@@ -1063,14 +1080,14 @@ mod tests {
             fn test() { printˇln!(); }
         "});
 
-        cx.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
+        cx.update_editor(|editor, window, cx| {
+            let snapshot = editor.snapshot(window, cx);
             let anchor = snapshot
                 .buffer_snapshot
                 .anchor_before(hover_point.to_offset(&snapshot, Bias::Left));
-            hover_at(editor, Some(anchor), cx)
+            hover_at(editor, Some(anchor), window, cx)
         });
-        assert!(!cx.editor(|editor, _| editor.hover_state.visible()));
+        assert!(!cx.editor(|editor, _window, _cx| editor.hover_state.visible()));
 
         // After delay, hover should be visible.
         let symbol_range = cx.lsp_range(indoc! {"
@@ -1090,7 +1107,7 @@ mod tests {
             .advance_clock(Duration::from_millis(get_hover_popover_delay(&cx) + 100));
         requests.next().await;
 
-        cx.editor(|editor, cx| {
+        cx.editor(|editor, _, cx| {
             assert!(editor.hover_state.visible());
             assert_eq!(
                 editor.hover_state.info_popovers.len(),
@@ -1115,17 +1132,17 @@ mod tests {
         let mut request = cx
             .lsp
             .handle_request::<lsp::request::HoverRequest, _, _>(|_, _| async move { Ok(None) });
-        cx.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
+        cx.update_editor(|editor, window, cx| {
+            let snapshot = editor.snapshot(window, cx);
             let anchor = snapshot
                 .buffer_snapshot
                 .anchor_before(hover_point.to_offset(&snapshot, Bias::Left));
-            hover_at(editor, Some(anchor), cx)
+            hover_at(editor, Some(anchor), window, cx)
         });
         cx.background_executor
             .advance_clock(Duration::from_millis(get_hover_popover_delay(&cx) + 100));
         request.next().await;
-        cx.editor(|editor, _| {
+        cx.editor(|editor, _, _| {
             assert!(!editor.hover_state.visible());
         });
     }
@@ -1147,12 +1164,12 @@ mod tests {
         cx.set_state(indoc! {"
             fˇn test() { println!(); }
         "});
-        cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
+        cx.update_editor(|editor, window, cx| hover(editor, &Hover, window, cx));
         let symbol_range = cx.lsp_range(indoc! {"
             «fn» test() { println!(); }
         "});
 
-        cx.editor(|editor, _cx| {
+        cx.editor(|editor, _window, _cx| {
             assert!(!editor.hover_state.visible());
 
             assert_eq!(
@@ -1178,7 +1195,7 @@ mod tests {
         cx.dispatch_action(Hover);
 
         cx.condition(|editor, _| editor.hover_state.visible()).await;
-        cx.editor(|editor, cx| {
+        cx.editor(|editor, _, cx| {
             assert_eq!(
                 editor.hover_state.info_popovers.len(),
                 1,
@@ -1214,7 +1231,7 @@ mod tests {
         cx.set_state(indoc! {"
             fˇn test() { println!(); }
         "});
-        cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
+        cx.update_editor(|editor, window, cx| hover(editor, &Hover, window, cx));
         let symbol_range = cx.lsp_range(indoc! {"
             «fn» test() { println!(); }
         "});
@@ -1236,7 +1253,7 @@ mod tests {
         cx.dispatch_action(Hover);
 
         cx.condition(|editor, _| editor.hover_state.visible()).await;
-        cx.editor(|editor, cx| {
+        cx.editor(|editor, _, cx| {
             assert_eq!(
                 editor.hover_state.info_popovers.len(),
                 1,
@@ -1275,7 +1292,7 @@ mod tests {
         cx.set_state(indoc! {"
             fˇn test() { println!(); }
         "});
-        cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
+        cx.update_editor(|editor, window, cx| hover(editor, &Hover, window, cx));
         let symbol_range = cx.lsp_range(indoc! {"
             «fn» test() { println!(); }
         "});
@@ -1302,7 +1319,7 @@ mod tests {
         cx.dispatch_action(Hover);
 
         cx.condition(|editor, _| editor.hover_state.visible()).await;
-        cx.editor(|editor, cx| {
+        cx.editor(|editor, _, cx| {
             assert_eq!(
                 editor.hover_state.info_popovers.len(),
                 1,
@@ -1362,10 +1379,10 @@ mod tests {
         });
 
         // Hover pops diagnostic immediately
-        cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
+        cx.update_editor(|editor, window, cx| hover(editor, &Hover, window, cx));
         cx.background_executor.run_until_parked();
 
-        cx.editor(|Editor { hover_state, .. }, _| {
+        cx.editor(|Editor { hover_state, .. }, _, _| {
             assert!(
                 hover_state.diagnostic_popover.is_some() && hover_state.info_popovers.is_empty()
             )
@@ -1388,7 +1405,7 @@ mod tests {
             .advance_clock(Duration::from_millis(get_hover_popover_delay(&cx) + 100));
 
         cx.background_executor.run_until_parked();
-        cx.editor(|Editor { hover_state, .. }, _| {
+        cx.editor(|Editor { hover_state, .. }, _, _| {
             hover_state.diagnostic_popover.is_some() && hover_state.info_task.is_some()
         });
     }
@@ -1437,10 +1454,10 @@ mod tests {
                 }))
             }
         });
-        cx.update_editor(|editor, cx| hover(editor, &Default::default(), cx));
+        cx.update_editor(|editor, window, cx| hover(editor, &Default::default(), window, cx));
         cx.run_until_parked();
 
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, _, cx| {
             let popover = editor.hover_state.info_popovers.first().unwrap();
             let content = popover.get_rendered_text(cx);
 
@@ -1552,7 +1569,7 @@ mod tests {
             .next()
             .await;
         cx.background_executor.run_until_parked();
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, _, cx| {
             let expected_layers = vec![entire_hint_label.to_string()];
             assert_eq!(expected_layers, cached_hint_labels(editor));
             assert_eq!(expected_layers, visible_hint_labels(editor, cx));
@@ -1573,8 +1590,8 @@ mod tests {
             .first()
             .cloned()
             .unwrap();
-        let new_type_hint_part_hover_position = cx.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
+        let new_type_hint_part_hover_position = cx.update_editor(|editor, window, cx| {
+            let snapshot = editor.snapshot(window, cx);
             let previous_valid = inlay_range.start.to_display_point(&snapshot);
             let next_valid = inlay_range.end.to_display_point(&snapshot);
             assert_eq!(previous_valid.row(), next_valid.row());
@@ -1592,13 +1609,14 @@ mod tests {
                 column_overshoot_after_line_end: 0,
             }
         });
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, window, cx| {
             update_inlay_link_and_hover_points(
-                &editor.snapshot(cx),
+                &editor.snapshot(window, cx),
                 new_type_hint_part_hover_position,
                 editor,
                 true,
                 false,
+                window,
                 cx,
             );
         });
@@ -1662,20 +1680,21 @@ mod tests {
             .await;
         cx.background_executor.run_until_parked();
 
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, window, cx| {
             update_inlay_link_and_hover_points(
-                &editor.snapshot(cx),
+                &editor.snapshot(window, cx),
                 new_type_hint_part_hover_position,
                 editor,
                 true,
                 false,
+                window,
                 cx,
             );
         });
         cx.background_executor
             .advance_clock(Duration::from_millis(get_hover_popover_delay(&cx) + 100));
         cx.background_executor.run_until_parked();
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, _, cx| {
             let hover_state = &editor.hover_state;
             assert!(
                 hover_state.diagnostic_popover.is_none() && hover_state.info_popovers.len() == 1
@@ -1697,8 +1716,8 @@ mod tests {
             );
         });
 
-        let struct_hint_part_hover_position = cx.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
+        let struct_hint_part_hover_position = cx.update_editor(|editor, window, cx| {
+            let snapshot = editor.snapshot(window, cx);
             let previous_valid = inlay_range.start.to_display_point(&snapshot);
             let next_valid = inlay_range.end.to_display_point(&snapshot);
             assert_eq!(previous_valid.row(), next_valid.row());
@@ -1716,20 +1735,21 @@ mod tests {
                 column_overshoot_after_line_end: 0,
             }
         });
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, window, cx| {
             update_inlay_link_and_hover_points(
-                &editor.snapshot(cx),
+                &editor.snapshot(window, cx),
                 struct_hint_part_hover_position,
                 editor,
                 true,
                 false,
+                window,
                 cx,
             );
         });
         cx.background_executor
             .advance_clock(Duration::from_millis(get_hover_popover_delay(&cx) + 100));
         cx.background_executor.run_until_parked();
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, _, cx| {
             let hover_state = &editor.hover_state;
             assert!(
                 hover_state.diagnostic_popover.is_none() && hover_state.info_popovers.len() == 1

crates/editor/src/hunk_diff.rs 🔗

@@ -0,0 +1,1586 @@
+use collections::{HashMap, HashSet};
+use git::diff::DiffHunkStatus;
+use gpui::{
+    Action, AppContext, Corner, CursorStyle, Focusable as _, Hsla, Model, MouseButton,
+    Subscription, Task,
+};
+use language::{Buffer, BufferId, Point};
+use multi_buffer::{
+    Anchor, AnchorRangeExt, ExcerptRange, MultiBuffer, MultiBufferDiffHunk, MultiBufferRow,
+    MultiBufferSnapshot, ToOffset, ToPoint,
+};
+use project::buffer_store::BufferChangeSet;
+use std::{ops::Range, sync::Arc};
+use sum_tree::TreeMap;
+use text::OffsetRangeExt;
+use ui::{
+    prelude::*, ActiveTheme, ContextMenu, IconButtonShape, InteractiveElement, IntoElement,
+    ModelContext, ParentElement, PopoverMenu, Styled, Tooltip, Window,
+};
+use util::RangeExt;
+use workspace::Item;
+
+use crate::{
+    editor_settings::CurrentLineHighlight, hunk_status, hunks_for_selections, ApplyAllDiffHunks,
+    ApplyDiffHunk, BlockPlacement, BlockProperties, BlockStyle, CustomBlockId, DiffRowHighlight,
+    DisplayRow, DisplaySnapshot, Editor, EditorElement, ExpandAllHunkDiffs, GoToHunk, GoToPrevHunk,
+    RevertFile, RevertSelectedHunks, ToDisplayPoint, ToggleHunkDiff,
+};
+
+#[derive(Debug, Clone)]
+pub(super) struct HoveredHunk {
+    pub multi_buffer_range: Range<Anchor>,
+    pub status: DiffHunkStatus,
+    pub diff_base_byte_range: Range<usize>,
+}
+
+#[derive(Default)]
+pub(super) struct DiffMap {
+    pub(crate) hunks: Vec<ExpandedHunk>,
+    pub(crate) diff_bases: HashMap<BufferId, DiffBaseState>,
+    pub(crate) snapshot: DiffMapSnapshot,
+    hunk_update_tasks: HashMap<Option<BufferId>, Task<()>>,
+    expand_all: bool,
+}
+
+#[derive(Debug, Clone)]
+pub(super) struct ExpandedHunk {
+    pub blocks: Vec<CustomBlockId>,
+    pub hunk_range: Range<Anchor>,
+    pub diff_base_byte_range: Range<usize>,
+    pub status: DiffHunkStatus,
+    pub folded: bool,
+}
+
+#[derive(Clone, Debug, Default)]
+pub(crate) struct DiffMapSnapshot(TreeMap<BufferId, git::diff::BufferDiff>);
+
+pub(crate) struct DiffBaseState {
+    pub(crate) change_set: Model<BufferChangeSet>,
+    pub(crate) last_version: Option<usize>,
+    _subscription: Subscription,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum DisplayDiffHunk {
+    Folded {
+        display_row: DisplayRow,
+    },
+
+    Unfolded {
+        diff_base_byte_range: Range<usize>,
+        display_row_range: Range<DisplayRow>,
+        multi_buffer_range: Range<Anchor>,
+        status: DiffHunkStatus,
+    },
+}
+
+impl DiffMap {
+    pub fn snapshot(&self) -> DiffMapSnapshot {
+        self.snapshot.clone()
+    }
+
+    pub fn add_change_set(
+        &mut self,
+        change_set: Model<BufferChangeSet>,
+        window: &mut Window,
+        cx: &mut ModelContext<Editor>,
+    ) {
+        let buffer_id = change_set.read(cx).buffer_id;
+        self.snapshot
+            .0
+            .insert(buffer_id, change_set.read(cx).diff_to_buffer.clone());
+        self.diff_bases.insert(
+            buffer_id,
+            DiffBaseState {
+                last_version: None,
+                _subscription: cx.observe_in(
+                    &change_set,
+                    window,
+                    move |editor, change_set, window, cx| {
+                        editor
+                            .diff_map
+                            .snapshot
+                            .0
+                            .insert(buffer_id, change_set.read(cx).diff_to_buffer.clone());
+                        Editor::sync_expanded_diff_hunks(
+                            &mut editor.diff_map,
+                            buffer_id,
+                            window,
+                            cx,
+                        );
+                    },
+                ),
+                change_set,
+            },
+        );
+        Editor::sync_expanded_diff_hunks(self, buffer_id, window, cx);
+    }
+
+    pub fn hunks(&self, include_folded: bool) -> impl Iterator<Item = &ExpandedHunk> {
+        self.hunks
+            .iter()
+            .filter(move |hunk| include_folded || !hunk.folded)
+    }
+}
+
+impl DiffMapSnapshot {
+    pub fn is_empty(&self) -> bool {
+        self.0.values().all(|diff| diff.is_empty())
+    }
+
+    pub fn diff_hunks<'a>(
+        &'a self,
+        buffer_snapshot: &'a MultiBufferSnapshot,
+    ) -> impl Iterator<Item = MultiBufferDiffHunk> + 'a {
+        self.diff_hunks_in_range(0..buffer_snapshot.len(), buffer_snapshot)
+    }
+
+    pub fn diff_hunks_in_range<'a, T: ToOffset>(
+        &'a self,
+        range: Range<T>,
+        buffer_snapshot: &'a MultiBufferSnapshot,
+    ) -> impl Iterator<Item = MultiBufferDiffHunk> + 'a {
+        let range = range.start.to_offset(buffer_snapshot)..range.end.to_offset(buffer_snapshot);
+        buffer_snapshot
+            .excerpts_for_range(range.clone())
+            .filter_map(move |excerpt| {
+                let buffer = excerpt.buffer();
+                let buffer_id = buffer.remote_id();
+                let diff = self.0.get(&buffer_id)?;
+                let buffer_range = excerpt.map_range_to_buffer(range.clone());
+                let buffer_range =
+                    buffer.anchor_before(buffer_range.start)..buffer.anchor_after(buffer_range.end);
+                Some(
+                    diff.hunks_intersecting_range(buffer_range, excerpt.buffer())
+                        .map(move |hunk| {
+                            let start =
+                                excerpt.map_point_from_buffer(Point::new(hunk.row_range.start, 0));
+                            let end =
+                                excerpt.map_point_from_buffer(Point::new(hunk.row_range.end, 0));
+                            MultiBufferDiffHunk {
+                                row_range: MultiBufferRow(start.row)..MultiBufferRow(end.row),
+                                buffer_id,
+                                buffer_range: hunk.buffer_range.clone(),
+                                diff_base_byte_range: hunk.diff_base_byte_range.clone(),
+                            }
+                        }),
+                )
+            })
+            .flatten()
+    }
+
+    pub fn diff_hunks_in_range_rev<'a, T: ToOffset>(
+        &'a self,
+        range: Range<T>,
+        buffer_snapshot: &'a MultiBufferSnapshot,
+    ) -> impl Iterator<Item = MultiBufferDiffHunk> + 'a {
+        let range = range.start.to_offset(buffer_snapshot)..range.end.to_offset(buffer_snapshot);
+        buffer_snapshot
+            .excerpts_for_range_rev(range.clone())
+            .filter_map(move |excerpt| {
+                let buffer = excerpt.buffer();
+                let buffer_id = buffer.remote_id();
+                let diff = self.0.get(&buffer_id)?;
+                let buffer_range = excerpt.map_range_to_buffer(range.clone());
+                let buffer_range =
+                    buffer.anchor_before(buffer_range.start)..buffer.anchor_after(buffer_range.end);
+                Some(
+                    diff.hunks_intersecting_range_rev(buffer_range, excerpt.buffer())
+                        .map(move |hunk| {
+                            let start_row = excerpt
+                                .map_point_from_buffer(Point::new(hunk.row_range.start, 0))
+                                .row;
+                            let end_row = excerpt
+                                .map_point_from_buffer(Point::new(hunk.row_range.end, 0))
+                                .row;
+                            MultiBufferDiffHunk {
+                                row_range: MultiBufferRow(start_row)..MultiBufferRow(end_row),
+                                buffer_id,
+                                buffer_range: hunk.buffer_range.clone(),
+                                diff_base_byte_range: hunk.diff_base_byte_range.clone(),
+                            }
+                        }),
+                )
+            })
+            .flatten()
+    }
+}
+
+impl Editor {
+    pub fn set_expand_all_diff_hunks(&mut self) {
+        self.diff_map.expand_all = true;
+    }
+
+    pub(super) fn toggle_hovered_hunk(
+        &mut self,
+        hovered_hunk: &HoveredHunk,
+        window: &mut Window,
+        cx: &mut ModelContext<Editor>,
+    ) {
+        let editor_snapshot = self.snapshot(window, cx);
+        if let Some(diff_hunk) = to_diff_hunk(hovered_hunk, &editor_snapshot.buffer_snapshot) {
+            self.toggle_hunks_expanded(vec![diff_hunk], window, cx);
+            self.change_selections(None, window, cx, |selections| selections.refresh());
+        }
+    }
+
+    pub fn toggle_hunk_diff(
+        &mut self,
+        _: &ToggleHunkDiff,
+        window: &mut Window,
+        cx: &mut ModelContext<Self>,
+    ) {
+        let snapshot = self.snapshot(window, cx);
+        let selections = self.selections.all(cx);
+        self.toggle_hunks_expanded(hunks_for_selections(&snapshot, &selections), window, cx);
+    }
+
+    pub fn expand_all_hunk_diffs(
+        &mut self,
+        _: &ExpandAllHunkDiffs,
+        window: &mut Window,
+        cx: &mut ModelContext<Self>,
+    ) {
+        let snapshot = self.snapshot(window, cx);
+        let display_rows_with_expanded_hunks = self
+            .diff_map
+            .hunks(false)
+            .map(|hunk| &hunk.hunk_range)
+            .map(|anchor_range| {
+                (
+                    anchor_range
+                        .start
+                        .to_display_point(&snapshot.display_snapshot)
+                        .row(),
+                    anchor_range
+                        .end
+                        .to_display_point(&snapshot.display_snapshot)
+                        .row(),
+                )
+            })
+            .collect::<HashMap<_, _>>();
+        let hunks = self
+            .diff_map
+            .snapshot
+            .diff_hunks(&snapshot.display_snapshot.buffer_snapshot)
+            .filter(|hunk| {
+                let hunk_display_row_range = Point::new(hunk.row_range.start.0, 0)
+                    .to_display_point(&snapshot.display_snapshot)
+                    ..Point::new(hunk.row_range.end.0, 0)
+                        .to_display_point(&snapshot.display_snapshot);
+                let row_range_end =
+                    display_rows_with_expanded_hunks.get(&hunk_display_row_range.start.row());
+                row_range_end.is_none() || row_range_end != Some(&hunk_display_row_range.end.row())
+            });
+        self.toggle_hunks_expanded(hunks.collect(), window, cx);
+    }
+
+    fn toggle_hunks_expanded(
+        &mut self,
+        hunks_to_toggle: Vec<MultiBufferDiffHunk>,
+        window: &mut Window,
+        cx: &mut ModelContext<Self>,
+    ) {
+        if self.diff_map.expand_all {
+            return;
+        }
+
+        let previous_toggle_task = self.diff_map.hunk_update_tasks.remove(&None);
+        let new_toggle_task = cx.spawn_in(window, move |editor, mut cx| async move {
+            if let Some(task) = previous_toggle_task {
+                task.await;
+            }
+
+            editor
+                .update_in(&mut cx, |editor, window, cx| {
+                    let snapshot = editor.snapshot(window, cx);
+                    let mut hunks_to_toggle = hunks_to_toggle.into_iter().fuse().peekable();
+                    let mut highlights_to_remove = Vec::with_capacity(editor.diff_map.hunks.len());
+                    let mut blocks_to_remove = HashSet::default();
+                    let mut hunks_to_expand = Vec::new();
+                    editor.diff_map.hunks.retain(|expanded_hunk| {
+                        if expanded_hunk.folded {
+                            return true;
+                        }
+                        let expanded_hunk_row_range = expanded_hunk
+                            .hunk_range
+                            .start
+                            .to_display_point(&snapshot)
+                            .row()
+                            ..expanded_hunk
+                                .hunk_range
+                                .end
+                                .to_display_point(&snapshot)
+                                .row();
+                        let mut retain = true;
+                        while let Some(hunk_to_toggle) = hunks_to_toggle.peek() {
+                            match diff_hunk_to_display(hunk_to_toggle, &snapshot) {
+                                DisplayDiffHunk::Folded { .. } => {
+                                    hunks_to_toggle.next();
+                                    continue;
+                                }
+                                DisplayDiffHunk::Unfolded {
+                                    diff_base_byte_range,
+                                    display_row_range,
+                                    multi_buffer_range,
+                                    status,
+                                } => {
+                                    let hunk_to_toggle_row_range = display_row_range;
+                                    if hunk_to_toggle_row_range.start > expanded_hunk_row_range.end
+                                    {
+                                        break;
+                                    } else if expanded_hunk_row_range == hunk_to_toggle_row_range {
+                                        highlights_to_remove.push(expanded_hunk.hunk_range.clone());
+                                        blocks_to_remove
+                                            .extend(expanded_hunk.blocks.iter().copied());
+                                        hunks_to_toggle.next();
+                                        retain = false;
+                                        break;
+                                    } else {
+                                        hunks_to_expand.push(HoveredHunk {
+                                            status,
+                                            multi_buffer_range,
+                                            diff_base_byte_range,
+                                        });
+                                        hunks_to_toggle.next();
+                                        continue;
+                                    }
+                                }
+                            }
+                        }
+
+                        retain
+                    });
+                    for hunk in hunks_to_toggle {
+                        let remaining_hunk_point_range = Point::new(hunk.row_range.start.0, 0)
+                            ..Point::new(hunk.row_range.end.0, 0);
+                        let hunk_start = snapshot
+                            .buffer_snapshot
+                            .anchor_before(remaining_hunk_point_range.start);
+                        let hunk_end = snapshot
+                            .buffer_snapshot
+                            .anchor_in_excerpt(hunk_start.excerpt_id, hunk.buffer_range.end)
+                            .unwrap();
+                        hunks_to_expand.push(HoveredHunk {
+                            status: hunk_status(&hunk),
+                            multi_buffer_range: hunk_start..hunk_end,
+                            diff_base_byte_range: hunk.diff_base_byte_range.clone(),
+                        });
+                    }
+
+                    editor.remove_highlighted_rows::<DiffRowHighlight>(highlights_to_remove, cx);
+                    editor.remove_blocks(blocks_to_remove, None, cx);
+                    for hunk in hunks_to_expand {
+                        editor.expand_diff_hunk(None, &hunk, window, cx);
+                    }
+                    cx.notify();
+                })
+                .ok();
+        });
+
+        self.diff_map
+            .hunk_update_tasks
+            .insert(None, cx.background_executor().spawn(new_toggle_task));
+    }
+
+    pub(super) fn expand_diff_hunk(
+        &mut self,
+        diff_base_buffer: Option<Model<Buffer>>,
+        hunk: &HoveredHunk,
+        window: &mut Window,
+        cx: &mut ModelContext<Editor>,
+    ) -> Option<()> {
+        let buffer = self.buffer.clone();
+        let multi_buffer_snapshot = buffer.read(cx).snapshot(cx);
+        let hunk_range = hunk.multi_buffer_range.clone();
+        let buffer_id = hunk_range.start.buffer_id?;
+        let diff_base_buffer = diff_base_buffer.or_else(|| {
+            self.diff_map
+                .diff_bases
+                .get(&buffer_id)?
+                .change_set
+                .read(cx)
+                .base_text
+                .clone()
+        })?;
+
+        let diff_base = diff_base_buffer.read(cx);
+        let diff_start_row = diff_base
+            .offset_to_point(hunk.diff_base_byte_range.start)
+            .row;
+        let diff_end_row = diff_base.offset_to_point(hunk.diff_base_byte_range.end).row;
+        let deleted_text_lines = diff_end_row - diff_start_row;
+
+        let block_insert_index = self
+            .diff_map
+            .hunks
+            .binary_search_by(|probe| {
+                probe
+                    .hunk_range
+                    .start
+                    .cmp(&hunk_range.start, &multi_buffer_snapshot)
+            })
+            .err()?;
+
+        let blocks;
+        match hunk.status {
+            DiffHunkStatus::Removed => {
+                blocks = self.insert_blocks(
+                    [
+                        self.hunk_header_block(&hunk, cx),
+                        Self::deleted_text_block(
+                            hunk,
+                            diff_base_buffer,
+                            deleted_text_lines,
+                            window,
+                            cx,
+                        ),
+                    ],
+                    None,
+                    cx,
+                );
+            }
+            DiffHunkStatus::Added => {
+                self.highlight_rows::<DiffRowHighlight>(
+                    hunk_range.clone(),
+                    added_hunk_color(cx),
+                    false,
+                    cx,
+                );
+                blocks = self.insert_blocks([self.hunk_header_block(&hunk, cx)], None, cx);
+            }
+            DiffHunkStatus::Modified => {
+                self.highlight_rows::<DiffRowHighlight>(
+                    hunk_range.clone(),
+                    added_hunk_color(cx),
+                    false,
+                    cx,
+                );
+                blocks = self.insert_blocks(
+                    [
+                        self.hunk_header_block(&hunk, cx),
+                        Self::deleted_text_block(
+                            hunk,
+                            diff_base_buffer,
+                            deleted_text_lines,
+                            window,
+                            cx,
+                        ),
+                    ],
+                    None,
+                    cx,
+                );
+            }
+        };
+        self.diff_map.hunks.insert(
+            block_insert_index,
+            ExpandedHunk {
+                blocks,
+                hunk_range,
+                status: hunk.status,
+                folded: false,
+                diff_base_byte_range: hunk.diff_base_byte_range.clone(),
+            },
+        );
+
+        Some(())
+    }
+
+    fn apply_diff_hunks_in_range(
+        &mut self,
+        range: Range<Anchor>,
+        window: &mut Window,
+        cx: &mut ModelContext<Editor>,
+    ) -> Option<()> {
+        let multi_buffer = self.buffer.read(cx);
+        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
+        let (excerpt, range) = multi_buffer_snapshot
+            .range_to_buffer_ranges(range)
+            .into_iter()
+            .next()?;
+
+        multi_buffer
+            .buffer(excerpt.buffer_id())
+            .unwrap()
+            .update(cx, |branch_buffer, cx| {
+                branch_buffer.merge_into_base(vec![range], cx);
+            });
+
+        if let Some(project) = self.project.clone() {
+            self.save(true, project, window, cx).detach_and_log_err(cx);
+        }
+
+        None
+    }
+
+    pub(crate) fn apply_all_diff_hunks(
+        &mut self,
+        _: &ApplyAllDiffHunks,
+        window: &mut Window,
+        cx: &mut ModelContext<Self>,
+    ) {
+        let buffers = self.buffer.read(cx).all_buffers();
+        for branch_buffer in buffers {
+            branch_buffer.update(cx, |branch_buffer, cx| {
+                branch_buffer.merge_into_base(Vec::new(), cx);
+            });
+        }
+
+        if let Some(project) = self.project.clone() {
+            self.save(true, project, window, cx).detach_and_log_err(cx);
+        }
+    }
+
+    pub(crate) fn apply_selected_diff_hunks(
+        &mut self,
+        _: &ApplyDiffHunk,
+        window: &mut Window,
+        cx: &mut ModelContext<Self>,
+    ) {
+        let snapshot = self.snapshot(window, cx);
+        let hunks = hunks_for_selections(&snapshot, &self.selections.all(cx));
+        let mut ranges_by_buffer = HashMap::default();
+        self.transact(window, cx, |editor, _, cx| {
+            for hunk in hunks {
+                if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
+                    ranges_by_buffer
+                        .entry(buffer.clone())
+                        .or_insert_with(Vec::new)
+                        .push(hunk.buffer_range.to_offset(buffer.read(cx)));
+                }
+            }
+
+            for (buffer, ranges) in ranges_by_buffer {
+                buffer.update(cx, |buffer, cx| {
+                    buffer.merge_into_base(ranges, cx);
+                });
+            }
+        });
+
+        if let Some(project) = self.project.clone() {
+            self.save(true, project, window, cx).detach_and_log_err(cx);
+        }
+    }
+
+    fn has_multiple_hunks(&self, cx: &AppContext) -> bool {
+        let snapshot = self.buffer.read(cx).snapshot(cx);
+        let mut hunks = self.diff_map.snapshot.diff_hunks(&snapshot);
+        hunks.nth(1).is_some()
+    }
+
+    fn hunk_header_block(
+        &self,
+        hunk: &HoveredHunk,
+        cx: &mut ModelContext<Editor>,
+    ) -> BlockProperties<Anchor> {
+        let is_branch_buffer = self
+            .buffer
+            .read(cx)
+            .point_to_buffer_offset(hunk.multi_buffer_range.start, cx)
+            .map_or(false, |(buffer, _, _)| {
+                buffer.read(cx).base_buffer().is_some()
+            });
+
+        let border_color = cx.theme().colors().border_variant;
+        let bg_color = cx.theme().colors().editor_background;
+        let gutter_color = match hunk.status {
+            DiffHunkStatus::Added => cx.theme().status().created,
+            DiffHunkStatus::Modified => cx.theme().status().modified,
+            DiffHunkStatus::Removed => cx.theme().status().deleted,
+        };
+
+        BlockProperties {
+            placement: BlockPlacement::Above(hunk.multi_buffer_range.start),
+            height: 1,
+            style: BlockStyle::Sticky,
+            priority: 0,
+            render: Arc::new({
+                let editor = cx.model().clone();
+                let hunk = hunk.clone();
+                let has_multiple_hunks = self.has_multiple_hunks(cx);
+
+                move |cx| {
+                    let hunk_controls_menu_handle =
+                        editor.read(cx).hunk_controls_menu_handle.clone();
+
+                    h_flex()
+                        .id(cx.block_id)
+                        .block_mouse_down()
+                        .h(cx.window.line_height())
+                        .w_full()
+                        .border_t_1()
+                        .border_color(border_color)
+                        .bg(bg_color)
+                        .child(
+                            div()
+                                .id("gutter-strip")
+                                .w(EditorElement::diff_hunk_strip_width(
+                                    cx.window.line_height(),
+                                ))
+                                .h_full()
+                                .bg(gutter_color)
+                                .cursor(CursorStyle::PointingHand)
+                                .on_click({
+                                    let editor = editor.clone();
+                                    let hunk = hunk.clone();
+                                    move |_event, window, cx| {
+                                        editor.update(cx, |editor, cx| {
+                                            editor.toggle_hovered_hunk(&hunk, window, cx);
+                                        });
+                                    }
+                                }),
+                        )
+                        .child(
+                            h_flex()
+                                .px_6()
+                                .size_full()
+                                .justify_end()
+                                .child(
+                                    h_flex()
+                                        .gap_1()
+                                        .when(!is_branch_buffer, |row| {
+                                            row.child(
+                                                IconButton::new("next-hunk", IconName::ArrowDown)
+                                                    .shape(IconButtonShape::Square)
+                                                    .icon_size(IconSize::Small)
+                                                    .disabled(!has_multiple_hunks)
+                                                    .tooltip({
+                                                        let focus_handle = editor.focus_handle(cx);
+                                                        move |window, cx| {
+                                                            Tooltip::for_action_in(
+                                                                "Next Hunk",
+                                                                &GoToHunk,
+                                                                &focus_handle,
+                                                                window,
+                                                                cx,
+                                                            )
+                                                        }
+                                                    })
+                                                    .on_click({
+                                                        let editor = editor.clone();
+                                                        let hunk = hunk.clone();
+                                                        move |_event, window, cx| {
+                                                            editor.update(cx, |editor, cx| {
+                                                                editor.go_to_subsequent_hunk(
+                                                                    hunk.multi_buffer_range.end,
+                                                                    window,
+                                                                    cx,
+                                                                );
+                                                            });
+                                                        }
+                                                    }),
+                                            )
+                                            .child(
+                                                IconButton::new("prev-hunk", IconName::ArrowUp)
+                                                    .shape(IconButtonShape::Square)
+                                                    .icon_size(IconSize::Small)
+                                                    .disabled(!has_multiple_hunks)
+                                                    .tooltip({
+                                                        let focus_handle = editor.focus_handle(cx);
+                                                        move |window, cx| {
+                                                            Tooltip::for_action_in(
+                                                                "Previous Hunk",
+                                                                &GoToPrevHunk,
+                                                                &focus_handle,
+                                                                window,
+                                                                cx,
+                                                            )
+                                                        }
+                                                    })
+                                                    .on_click({
+                                                        let editor = editor.clone();
+                                                        let hunk = hunk.clone();
+                                                        move |_event, window, cx| {
+                                                            editor.update(cx, |editor, cx| {
+                                                                editor.go_to_preceding_hunk(
+                                                                    hunk.multi_buffer_range.start,
+                                                                    window,
+                                                                    cx,
+                                                                );
+                                                            });
+                                                        }
+                                                    }),
+                                            )
+                                        })
+                                        .child(
+                                            IconButton::new("discard", IconName::Undo)
+                                                .shape(IconButtonShape::Square)
+                                                .icon_size(IconSize::Small)
+                                                .tooltip({
+                                                    let focus_handle = editor.focus_handle(cx);
+                                                    move |window, cx| {
+                                                        Tooltip::for_action_in(
+                                                            "Discard Hunk",
+                                                            &RevertSelectedHunks,
+                                                            &focus_handle,
+                                                            window,
+                                                            cx,
+                                                        )
+                                                    }
+                                                })
+                                                .on_click({
+                                                    let editor = editor.clone();
+                                                    let hunk = hunk.clone();
+                                                    move |_event, window, cx| {
+                                                        editor.update(cx, |editor, cx| {
+                                                            editor.revert_hunk(
+                                                                hunk.clone(),
+                                                                window,
+                                                                cx,
+                                                            );
+                                                        });
+                                                    }
+                                                }),
+                                        )
+                                        .map(|this| {
+                                            if is_branch_buffer {
+                                                this.child(
+                                                    IconButton::new("apply", IconName::Check)
+                                                        .shape(IconButtonShape::Square)
+                                                        .icon_size(IconSize::Small)
+                                                        .tooltip({
+                                                            let focus_handle =
+                                                                editor.focus_handle(cx);
+                                                            move |window, cx| {
+                                                                Tooltip::for_action_in(
+                                                                    "Apply Hunk",
+                                                                    &ApplyDiffHunk,
+                                                                    &focus_handle,
+                                                                    window,
+                                                                    cx,
+                                                                )
+                                                            }
+                                                        })
+                                                        .on_click({
+                                                            let editor = editor.clone();
+                                                            let hunk = hunk.clone();
+                                                            move |_event, window, cx| {
+                                                                editor.update(cx, |editor, cx| {
+                                                                    editor
+                                                                        .apply_diff_hunks_in_range(
+                                                                            hunk.multi_buffer_range
+                                                                                .clone(),
+                                                                            window,
+                                                                            cx,
+                                                                        );
+                                                                });
+                                                            }
+                                                        }),
+                                                )
+                                            } else {
+                                                this.child({
+                                                    let focus = editor.focus_handle(cx);
+                                                    PopoverMenu::new("hunk-controls-dropdown")
+                                                        .trigger(
+                                                            IconButton::new(
+                                                                "toggle_editor_selections_icon",
+                                                                IconName::EllipsisVertical,
+                                                            )
+                                                            .shape(IconButtonShape::Square)
+                                                            .icon_size(IconSize::Small)
+                                                            .style(ButtonStyle::Subtle)
+                                                            .toggle_state(
+                                                                hunk_controls_menu_handle
+                                                                    .is_deployed(),
+                                                            )
+                                                            .when(
+                                                                !hunk_controls_menu_handle
+                                                                    .is_deployed(),
+                                                                |this| {
+                                                                    this.tooltip(|_, cx| {
+                                                                        Tooltip::simple(
+                                                                            "Hunk Controls",
+                                                                            cx,
+                                                                        )
+                                                                    })
+                                                                },
+                                                            ),
+                                                        )
+                                                        .anchor(Corner::TopRight)
+                                                        .with_handle(hunk_controls_menu_handle)
+                                                        .menu(move |window, cx| {
+                                                            let focus = focus.clone();
+                                                            let menu = ContextMenu::build(
+                                                                window,
+                                                                cx,
+                                                                move |menu, _, _| {
+                                                                    menu.context(focus.clone())
+                                                                        .action(
+                                                                            "Discard All Hunks",
+                                                                            RevertFile
+                                                                                .boxed_clone(),
+                                                                        )
+                                                                },
+                                                            );
+                                                            Some(menu)
+                                                        })
+                                                })
+                                            }
+                                        }),
+                                )
+                                .when(!is_branch_buffer, |div| {
+                                    div.child(
+                                        IconButton::new("collapse", IconName::Close)
+                                            .shape(IconButtonShape::Square)
+                                            .icon_size(IconSize::Small)
+                                            .tooltip({
+                                                let focus_handle = editor.focus_handle(cx);
+                                                move |window, cx| {
+                                                    Tooltip::for_action_in(
+                                                        "Collapse Hunk",
+                                                        &ToggleHunkDiff,
+                                                        &focus_handle,
+                                                        window,
+                                                        cx,
+                                                    )
+                                                }
+                                            })
+                                            .on_click({
+                                                let editor = editor.clone();
+                                                let hunk = hunk.clone();
+                                                move |_event, window, cx| {
+                                                    editor.update(cx, |editor, cx| {
+                                                        editor
+                                                            .toggle_hovered_hunk(&hunk, window, cx);
+                                                    });
+                                                }
+                                            }),
+                                    )
+                                }),
+                        )
+                        .into_any_element()
+                }
+            }),
+        }
+    }
+
+    fn deleted_text_block(
+        hunk: &HoveredHunk,
+        diff_base_buffer: Model<Buffer>,
+        deleted_text_height: u32,
+        window: &mut Window,
+        cx: &mut ModelContext<Editor>,
+    ) -> BlockProperties<Anchor> {
+        let gutter_color = match hunk.status {
+            DiffHunkStatus::Added => unreachable!(),
+            DiffHunkStatus::Modified => cx.theme().status().modified,
+            DiffHunkStatus::Removed => cx.theme().status().deleted,
+        };
+        let deleted_hunk_color = deleted_hunk_color(cx);
+        let (editor_height, editor_with_deleted_text) =
+            editor_with_deleted_text(diff_base_buffer, deleted_hunk_color, hunk, window, cx);
+        let editor = cx.model().clone();
+        let hunk = hunk.clone();
+        let height = editor_height.max(deleted_text_height);
+        BlockProperties {
+            placement: BlockPlacement::Above(hunk.multi_buffer_range.start),
+            height,
+            style: BlockStyle::Flex,
+            priority: 0,
+            render: Arc::new(move |cx| {
+                let width = EditorElement::diff_hunk_strip_width(cx.window.line_height());
+                let gutter_dimensions = editor.read(cx.app).gutter_dimensions;
+
+                h_flex()
+                    .id(cx.block_id)
+                    .block_mouse_down()
+                    .bg(deleted_hunk_color)
+                    .h(height as f32 * cx.window.line_height())
+                    .w_full()
+                    .child(
+                        h_flex()
+                            .id("gutter")
+                            .max_w(gutter_dimensions.full_width())
+                            .min_w(gutter_dimensions.full_width())
+                            .size_full()
+                            .child(
+                                h_flex()
+                                    .id("gutter hunk")
+                                    .bg(gutter_color)
+                                    .pl(gutter_dimensions.margin
+                                        + gutter_dimensions
+                                            .git_blame_entries_width
+                                            .unwrap_or_default())
+                                    .max_w(width)
+                                    .min_w(width)
+                                    .size_full()
+                                    .cursor(CursorStyle::PointingHand)
+                                    .on_mouse_down(MouseButton::Left, {
+                                        let editor = editor.clone();
+                                        let hunk = hunk.clone();
+                                        move |_event, window, cx| {
+                                            editor.update(cx, |editor, cx| {
+                                                editor.toggle_hovered_hunk(&hunk, window, cx);
+                                            });
+                                        }
+                                    }),
+                            ),
+                    )
+                    .child(editor_with_deleted_text.clone())
+                    .into_any_element()
+            }),
+        }
+    }
+
+    pub(super) fn clear_expanded_diff_hunks(&mut self, cx: &mut ModelContext<Editor>) -> bool {
+        if self.diff_map.expand_all {
+            return false;
+        }
+        self.diff_map.hunk_update_tasks.clear();
+        self.clear_row_highlights::<DiffRowHighlight>();
+        let to_remove = self
+            .diff_map
+            .hunks
+            .drain(..)
+            .flat_map(|expanded_hunk| expanded_hunk.blocks.into_iter())
+            .collect::<HashSet<_>>();
+        if to_remove.is_empty() {
+            false
+        } else {
+            self.remove_blocks(to_remove, None, cx);
+            true
+        }
+    }
+
+    pub(super) fn sync_expanded_diff_hunks(
+        diff_map: &mut DiffMap,
+        buffer_id: BufferId,
+        window: &mut Window,
+        cx: &mut ModelContext<Self>,
+    ) {
+        let diff_base_state = diff_map.diff_bases.get_mut(&buffer_id);
+        let mut diff_base_buffer = None;
+        let mut diff_base_buffer_unchanged = true;
+        if let Some(diff_base_state) = diff_base_state {
+            diff_base_state.change_set.update(cx, |change_set, _| {
+                if diff_base_state.last_version != Some(change_set.base_text_version) {
+                    diff_base_state.last_version = Some(change_set.base_text_version);
+                    diff_base_buffer_unchanged = false;
+                }
+                diff_base_buffer = change_set.base_text.clone();
+            })
+        }
+
+        diff_map.hunk_update_tasks.remove(&Some(buffer_id));
+
+        let new_sync_task = cx.spawn_in(window, move |editor, mut cx| async move {
+            editor
+                .update_in(&mut cx, |editor, window, cx| {
+                    let snapshot = editor.snapshot(window, cx);
+                    let mut recalculated_hunks = snapshot
+                        .diff_map
+                        .diff_hunks(&snapshot.buffer_snapshot)
+                        .filter(|hunk| hunk.buffer_id == buffer_id)
+                        .fuse()
+                        .peekable();
+                    let mut highlights_to_remove = Vec::with_capacity(editor.diff_map.hunks.len());
+                    let mut blocks_to_remove = HashSet::default();
+                    let mut hunks_to_reexpand = Vec::with_capacity(editor.diff_map.hunks.len());
+                    editor.diff_map.hunks.retain_mut(|expanded_hunk| {
+                        if expanded_hunk.hunk_range.start.buffer_id != Some(buffer_id) {
+                            return true;
+                        };
+
+                        let mut retain = false;
+                        if diff_base_buffer_unchanged {
+                            let expanded_hunk_display_range = expanded_hunk
+                                .hunk_range
+                                .start
+                                .to_display_point(&snapshot)
+                                .row()
+                                ..expanded_hunk
+                                    .hunk_range
+                                    .end
+                                    .to_display_point(&snapshot)
+                                    .row();
+                            while let Some(buffer_hunk) = recalculated_hunks.peek() {
+                                match diff_hunk_to_display(buffer_hunk, &snapshot) {
+                                    DisplayDiffHunk::Folded { display_row } => {
+                                        recalculated_hunks.next();
+                                        if !expanded_hunk.folded
+                                            && expanded_hunk_display_range
+                                                .to_inclusive()
+                                                .contains(&display_row)
+                                        {
+                                            retain = true;
+                                            expanded_hunk.folded = true;
+                                            highlights_to_remove
+                                                .push(expanded_hunk.hunk_range.clone());
+                                            for block in expanded_hunk.blocks.drain(..) {
+                                                blocks_to_remove.insert(block);
+                                            }
+                                            break;
+                                        } else {
+                                            continue;
+                                        }
+                                    }
+                                    DisplayDiffHunk::Unfolded {
+                                        diff_base_byte_range,
+                                        display_row_range,
+                                        multi_buffer_range,
+                                        status,
+                                    } => {
+                                        let hunk_display_range = display_row_range;
+
+                                        if expanded_hunk_display_range.start
+                                            > hunk_display_range.end
+                                        {
+                                            recalculated_hunks.next();
+                                            if editor.diff_map.expand_all {
+                                                hunks_to_reexpand.push(HoveredHunk {
+                                                    status,
+                                                    multi_buffer_range,
+                                                    diff_base_byte_range,
+                                                });
+                                            }
+                                            continue;
+                                        }
+
+                                        if expanded_hunk_display_range.end
+                                            < hunk_display_range.start
+                                        {
+                                            break;
+                                        }
+
+                                        if !expanded_hunk.folded
+                                            && expanded_hunk_display_range == hunk_display_range
+                                            && expanded_hunk.status == hunk_status(buffer_hunk)
+                                            && expanded_hunk.diff_base_byte_range
+                                                == buffer_hunk.diff_base_byte_range
+                                        {
+                                            recalculated_hunks.next();
+                                            retain = true;
+                                        } else {
+                                            hunks_to_reexpand.push(HoveredHunk {
+                                                status,
+                                                multi_buffer_range,
+                                                diff_base_byte_range,
+                                            });
+                                        }
+                                        break;
+                                    }
+                                }
+                            }
+                        }
+                        if !retain {
+                            blocks_to_remove.extend(expanded_hunk.blocks.drain(..));
+                            highlights_to_remove.push(expanded_hunk.hunk_range.clone());
+                        }
+                        retain
+                    });
+
+                    if editor.diff_map.expand_all {
+                        for hunk in recalculated_hunks {
+                            match diff_hunk_to_display(&hunk, &snapshot) {
+                                DisplayDiffHunk::Folded { .. } => {}
+                                DisplayDiffHunk::Unfolded {
+                                    diff_base_byte_range,
+                                    multi_buffer_range,
+                                    status,
+                                    ..
+                                } => {
+                                    hunks_to_reexpand.push(HoveredHunk {
+                                        status,
+                                        multi_buffer_range,
+                                        diff_base_byte_range,
+                                    });
+                                }
+                            }
+                        }
+                    } else {
+                        drop(recalculated_hunks);
+                    }
+
+                    editor.remove_highlighted_rows::<DiffRowHighlight>(highlights_to_remove, cx);
+                    editor.remove_blocks(blocks_to_remove, None, cx);
+
+                    if let Some(diff_base_buffer) = &diff_base_buffer {
+                        for hunk in hunks_to_reexpand {
+                            editor.expand_diff_hunk(
+                                Some(diff_base_buffer.clone()),
+                                &hunk,
+                                window,
+                                cx,
+                            );
+                        }
+                    }
+                })
+                .ok();
+        });
+
+        diff_map.hunk_update_tasks.insert(
+            Some(buffer_id),
+            cx.background_executor().spawn(new_sync_task),
+        );
+    }
+
+    fn go_to_subsequent_hunk(
+        &mut self,
+        position: Anchor,
+        window: &mut Window,
+        cx: &mut ModelContext<Self>,
+    ) {
+        let snapshot = self.snapshot(window, cx);
+        let position = position.to_point(&snapshot.buffer_snapshot);
+        if let Some(hunk) = self.go_to_hunk_after_position(&snapshot, position, window, cx) {
+            let multi_buffer_start = snapshot
+                .buffer_snapshot
+                .anchor_before(Point::new(hunk.row_range.start.0, 0));
+            let multi_buffer_end = snapshot
+                .buffer_snapshot
+                .anchor_after(Point::new(hunk.row_range.end.0, 0));
+            self.expand_diff_hunk(
+                None,
+                &HoveredHunk {
+                    multi_buffer_range: multi_buffer_start..multi_buffer_end,
+                    status: hunk_status(&hunk),
+                    diff_base_byte_range: hunk.diff_base_byte_range,
+                },
+                window,
+                cx,
+            );
+        }
+    }
+
+    fn go_to_preceding_hunk(
+        &mut self,
+        position: Anchor,
+        window: &mut Window,
+        cx: &mut ModelContext<Self>,
+    ) {
+        let snapshot = self.snapshot(window, cx);
+        let position = position.to_point(&snapshot.buffer_snapshot);
+        let hunk = self.go_to_hunk_before_position(&snapshot, position, window, cx);
+        if let Some(hunk) = hunk {
+            let multi_buffer_start = snapshot
+                .buffer_snapshot
+                .anchor_before(Point::new(hunk.row_range.start.0, 0));
+            let multi_buffer_end = snapshot
+                .buffer_snapshot
+                .anchor_after(Point::new(hunk.row_range.end.0, 0));
+            self.expand_diff_hunk(
+                None,
+                &HoveredHunk {
+                    multi_buffer_range: multi_buffer_start..multi_buffer_end,
+                    status: hunk_status(&hunk),
+                    diff_base_byte_range: hunk.diff_base_byte_range,
+                },
+                window,
+                cx,
+            );
+        }
+    }
+}
+
+pub(crate) fn to_diff_hunk(
+    hovered_hunk: &HoveredHunk,
+    multi_buffer_snapshot: &MultiBufferSnapshot,
+) -> Option<MultiBufferDiffHunk> {
+    let buffer_id = hovered_hunk
+        .multi_buffer_range
+        .start
+        .buffer_id
+        .or(hovered_hunk.multi_buffer_range.end.buffer_id)?;
+    let buffer_range = hovered_hunk.multi_buffer_range.start.text_anchor
+        ..hovered_hunk.multi_buffer_range.end.text_anchor;
+    let point_range = hovered_hunk
+        .multi_buffer_range
+        .to_point(multi_buffer_snapshot);
+    Some(MultiBufferDiffHunk {
+        row_range: MultiBufferRow(point_range.start.row)..MultiBufferRow(point_range.end.row),
+        buffer_id,
+        buffer_range,
+        diff_base_byte_range: hovered_hunk.diff_base_byte_range.clone(),
+    })
+}
+
+fn added_hunk_color(cx: &AppContext) -> Hsla {
+    let mut created_color = cx.theme().status().git().created;
+    created_color.fade_out(0.7);
+    created_color
+}
+
+fn deleted_hunk_color(cx: &AppContext) -> Hsla {
+    let mut deleted_color = cx.theme().status().deleted;
+    deleted_color.fade_out(0.7);
+    deleted_color
+}
+
+fn editor_with_deleted_text(
+    diff_base_buffer: Model<Buffer>,
+    deleted_color: Hsla,
+    hunk: &HoveredHunk,
+    window: &mut Window,
+    cx: &mut ModelContext<Editor>,
+) -> (u32, Model<Editor>) {
+    let parent_editor = cx.model().downgrade();
+    let editor = cx.new(|cx| {
+        let multi_buffer = cx.new(|_| MultiBuffer::without_headers(language::Capability::ReadOnly));
+        multi_buffer.update(cx, |multi_buffer, cx| {
+            multi_buffer.push_excerpts(
+                diff_base_buffer,
+                Some(ExcerptRange {
+                    context: hunk.diff_base_byte_range.clone(),
+                    primary: None,
+                }),
+                cx,
+            );
+        });
+
+        let mut editor = Editor::for_multibuffer(multi_buffer, None, true, window, cx);
+        editor.set_soft_wrap_mode(language::language_settings::SoftWrap::None, cx);
+        editor.set_show_wrap_guides(false, cx);
+        editor.set_show_gutter(false, cx);
+        editor.set_show_line_numbers(false, cx);
+        editor.set_show_scrollbars(false, cx);
+        editor.set_show_runnables(false, cx);
+        editor.set_show_git_diff_gutter(false, cx);
+        editor.set_show_code_actions(false, cx);
+        editor.scroll_manager.set_forbid_vertical_scroll(true);
+        editor.set_read_only(true);
+        editor.set_show_inline_completions(Some(false), window, cx);
+
+        enum DeletedBlockRowHighlight {}
+        editor.highlight_rows::<DeletedBlockRowHighlight>(
+            Anchor::min()..Anchor::max(),
+            deleted_color,
+            false,
+            cx,
+        );
+        editor.set_current_line_highlight(Some(CurrentLineHighlight::None));
+        editor._subscriptions.extend([cx.on_blur(
+            &editor.focus_handle,
+            window,
+            |editor, window, cx| {
+                editor.change_selections(None, window, cx, |s| {
+                    s.try_cancel();
+                });
+            },
+        )]);
+
+        editor
+            .register_action::<RevertSelectedHunks>({
+                let hunk = hunk.clone();
+                let parent_editor = parent_editor.clone();
+                move |_, window, cx| {
+                    parent_editor
+                        .update(cx, |editor, cx| {
+                            editor.revert_hunk(hunk.clone(), window, cx)
+                        })
+                        .ok();
+                }
+            })
+            .detach();
+        editor
+            .register_action::<ToggleHunkDiff>({
+                let hunk = hunk.clone();
+                move |_, window, cx| {
+                    parent_editor
+                        .update(cx, |editor, cx| {
+                            editor.toggle_hovered_hunk(&hunk, window, cx);
+                        })
+                        .ok();
+                }
+            })
+            .detach();
+        editor
+    });
+
+    let editor_height = editor.update(cx, |editor, cx| editor.max_point(cx).row().0);
+    (editor_height, editor)
+}
+
+impl DisplayDiffHunk {
+    pub fn start_display_row(&self) -> DisplayRow {
+        match self {
+            &DisplayDiffHunk::Folded { display_row } => display_row,
+            DisplayDiffHunk::Unfolded {
+                display_row_range, ..
+            } => display_row_range.start,
+        }
+    }
+
+    pub fn contains_display_row(&self, display_row: DisplayRow) -> bool {
+        let range = match self {
+            &DisplayDiffHunk::Folded { display_row } => display_row..=display_row,
+
+            DisplayDiffHunk::Unfolded {
+                display_row_range, ..
+            } => display_row_range.start..=display_row_range.end,
+        };
+
+        range.contains(&display_row)
+    }
+}
+
+pub fn diff_hunk_to_display(
+    hunk: &MultiBufferDiffHunk,
+    snapshot: &DisplaySnapshot,
+) -> DisplayDiffHunk {
+    let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
+    let hunk_start_point_sub = Point::new(hunk.row_range.start.0.saturating_sub(1), 0);
+    let hunk_end_point_sub = Point::new(
+        hunk.row_range
+            .end
+            .0
+            .saturating_sub(1)
+            .max(hunk.row_range.start.0),
+        0,
+    );
+
+    let status = hunk_status(hunk);
+    let is_removal = status == DiffHunkStatus::Removed;
+
+    let folds_start = Point::new(hunk.row_range.start.0.saturating_sub(2), 0);
+    let folds_end = Point::new(hunk.row_range.end.0 + 2, 0);
+    let folds_range = folds_start..folds_end;
+
+    let containing_fold = snapshot.folds_in_range(folds_range).find(|fold| {
+        let fold_point_range = fold.range.to_point(&snapshot.buffer_snapshot);
+        let fold_point_range = fold_point_range.start..=fold_point_range.end;
+
+        let folded_start = fold_point_range.contains(&hunk_start_point);
+        let folded_end = fold_point_range.contains(&hunk_end_point_sub);
+        let folded_start_sub = fold_point_range.contains(&hunk_start_point_sub);
+
+        (folded_start && folded_end) || (is_removal && folded_start_sub)
+    });
+
+    if let Some(fold) = containing_fold {
+        let row = fold.range.start.to_display_point(snapshot).row();
+        DisplayDiffHunk::Folded { display_row: row }
+    } else {
+        let start = hunk_start_point.to_display_point(snapshot).row();
+
+        let hunk_end_row = hunk.row_range.end.max(hunk.row_range.start);
+        let hunk_end_point = Point::new(hunk_end_row.0, 0);
+
+        let multi_buffer_start = snapshot.buffer_snapshot.anchor_before(hunk_start_point);
+        let multi_buffer_end = snapshot
+            .buffer_snapshot
+            .anchor_in_excerpt(multi_buffer_start.excerpt_id, hunk.buffer_range.end)
+            .unwrap();
+        let end = hunk_end_point.to_display_point(snapshot).row();
+
+        DisplayDiffHunk::Unfolded {
+            display_row_range: start..end,
+            multi_buffer_range: multi_buffer_start..multi_buffer_end,
+            status,
+            diff_base_byte_range: hunk.diff_base_byte_range.clone(),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::{editor_tests::init_test, hunk_status};
+    use gpui::{Context, TestAppContext};
+    use language::Capability::ReadWrite;
+    use multi_buffer::{ExcerptRange, MultiBuffer, MultiBufferRow};
+    use project::{FakeFs, Project};
+    use unindent::Unindent as _;
+
+    #[gpui::test]
+    async fn test_diff_hunks_in_range(cx: &mut TestAppContext) {
+        use git::diff::DiffHunkStatus;
+        init_test(cx, |_| {});
+
+        let fs = FakeFs::new(cx.background_executor.clone());
+        let project = Project::test(fs, [], cx).await;
+
+        // buffer has two modified hunks with two rows each
+        let diff_base_1 = "
+            1.zero
+            1.one
+            1.two
+            1.three
+            1.four
+            1.five
+            1.six
+        "
+        .unindent();
+
+        let text_1 = "
+            1.zero
+            1.ONE
+            1.TWO
+            1.three
+            1.FOUR
+            1.FIVE
+            1.six
+        "
+        .unindent();
+
+        // buffer has a deletion hunk and an insertion hunk
+        let diff_base_2 = "
+            2.zero
+            2.one
+            2.one-and-a-half
+            2.two
+            2.three
+            2.four
+            2.six
+        "
+        .unindent();
+
+        let text_2 = "
+            2.zero
+            2.one
+            2.two
+            2.three
+            2.four
+            2.five
+            2.six
+        "
+        .unindent();
+
+        let buffer_1 = project.update(cx, |project, cx| {
+            project.create_local_buffer(text_1.as_str(), None, cx)
+        });
+        let buffer_2 = project.update(cx, |project, cx| {
+            project.create_local_buffer(text_2.as_str(), None, cx)
+        });
+
+        let multibuffer = cx.new(|cx| {
+            let mut multibuffer = MultiBuffer::new(ReadWrite);
+            multibuffer.push_excerpts(
+                buffer_1.clone(),
+                [
+                    // excerpt ends in the middle of a modified hunk
+                    ExcerptRange {
+                        context: Point::new(0, 0)..Point::new(1, 5),
+                        primary: Default::default(),
+                    },
+                    // excerpt begins in the middle of a modified hunk
+                    ExcerptRange {
+                        context: Point::new(5, 0)..Point::new(6, 5),
+                        primary: Default::default(),
+                    },
+                ],
+                cx,
+            );
+            multibuffer.push_excerpts(
+                buffer_2.clone(),
+                [
+                    // excerpt ends at a deletion
+                    ExcerptRange {
+                        context: Point::new(0, 0)..Point::new(1, 5),
+                        primary: Default::default(),
+                    },
+                    // excerpt starts at a deletion
+                    ExcerptRange {
+                        context: Point::new(2, 0)..Point::new(2, 5),
+                        primary: Default::default(),
+                    },
+                    // excerpt fully contains a deletion hunk
+                    ExcerptRange {
+                        context: Point::new(1, 0)..Point::new(2, 5),
+                        primary: Default::default(),
+                    },
+                    // excerpt fully contains an insertion hunk
+                    ExcerptRange {
+                        context: Point::new(4, 0)..Point::new(6, 5),
+                        primary: Default::default(),
+                    },
+                ],
+                cx,
+            );
+            multibuffer
+        });
+
+        let editor = cx
+            .add_window(|window, cx| Editor::for_multibuffer(multibuffer, None, false, window, cx));
+        editor
+            .update(cx, |editor, window, cx| {
+                for (buffer, diff_base) in [
+                    (buffer_1.clone(), diff_base_1),
+                    (buffer_2.clone(), diff_base_2),
+                ] {
+                    let change_set = cx.new(|cx| {
+                        BufferChangeSet::new_with_base_text(
+                            diff_base.to_string(),
+                            buffer.read(cx).text_snapshot(),
+                            cx,
+                        )
+                    });
+                    editor.diff_map.add_change_set(change_set, window, cx)
+                }
+            })
+            .unwrap();
+        cx.background_executor.run_until_parked();
+
+        let snapshot = editor
+            .update(cx, |editor, window, cx| editor.snapshot(window, cx))
+            .unwrap();
+
+        assert_eq!(
+            snapshot.buffer_snapshot.text(),
+            "
+                1.zero
+                1.ONE
+                1.FIVE
+                1.six
+                2.zero
+                2.one
+                2.two
+                2.one
+                2.two
+                2.four
+                2.five
+                2.six"
+                .unindent()
+        );
+
+        let expected = [
+            (
+                DiffHunkStatus::Modified,
+                MultiBufferRow(1)..MultiBufferRow(2),
+            ),
+            (
+                DiffHunkStatus::Modified,
+                MultiBufferRow(2)..MultiBufferRow(3),
+            ),
+            //TODO: Define better when and where removed hunks show up at range extremities
+            (
+                DiffHunkStatus::Removed,
+                MultiBufferRow(6)..MultiBufferRow(6),
+            ),
+            (
+                DiffHunkStatus::Removed,
+                MultiBufferRow(8)..MultiBufferRow(8),
+            ),
+            (
+                DiffHunkStatus::Added,
+                MultiBufferRow(10)..MultiBufferRow(11),
+            ),
+        ];
+
+        assert_eq!(
+            snapshot
+                .diff_map
+                .diff_hunks_in_range(Point::zero()..Point::new(12, 0), &snapshot.buffer_snapshot)
+                .map(|hunk| (hunk_status(&hunk), hunk.row_range))
+                .collect::<Vec<_>>(),
+            &expected,
+        );
+
+        assert_eq!(
+            snapshot
+                .diff_map
+                .diff_hunks_in_range_rev(
+                    Point::zero()..Point::new(12, 0),
+                    &snapshot.buffer_snapshot
+                )
+                .map(|hunk| (hunk_status(&hunk), hunk.row_range))
+                .collect::<Vec<_>>(),
+            expected
+                .iter()
+                .rev()
+                .cloned()
+                .collect::<Vec<_>>()
+                .as_slice(),
+        );
+    }
+}

crates/editor/src/indent_guides.rs 🔗

@@ -1,11 +1,10 @@
 use std::{ops::Range, time::Duration};
 
 use collections::HashSet;
-use gpui::{AppContext, Task};
+use gpui::{App, Context, Task, Window};
 use language::language_settings::language_settings;
 use multi_buffer::{IndentGuide, MultiBufferRow};
 use text::{LineIndent, Point};
-use ui::ViewContext;
 use util::ResultExt;
 
 use crate::{DisplaySnapshot, Editor};
@@ -34,7 +33,7 @@ impl Editor {
         &self,
         visible_buffer_range: Range<MultiBufferRow>,
         snapshot: &DisplaySnapshot,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) -> Option<Vec<IndentGuide>> {
         let show_indent_guides = self.should_show_indent_guides().unwrap_or_else(|| {
             if let Some(buffer) = self.buffer().read(cx).as_singleton() {
@@ -67,7 +66,8 @@ impl Editor {
         &mut self,
         indent_guides: &[IndentGuide],
         snapshot: &DisplaySnapshot,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) -> Option<HashSet<usize>> {
         let selection = self.selections.newest::<Point>(cx);
         let cursor_row = MultiBufferRow(selection.head().row);
@@ -113,15 +113,16 @@ impl Editor {
             {
                 Ok(result) => state.active_indent_range = result,
                 Err(future) => {
-                    state.pending_refresh = Some(cx.spawn(|editor, mut cx| async move {
-                        let result = cx.background_executor().spawn(future).await;
-                        editor
-                            .update(&mut cx, |editor, _| {
-                                editor.active_indent_guides_state.active_indent_range = result;
-                                editor.active_indent_guides_state.pending_refresh = None;
-                            })
-                            .log_err();
-                    }));
+                    state.pending_refresh =
+                        Some(cx.spawn_in(window, |editor, mut cx| async move {
+                            let result = cx.background_executor().spawn(future).await;
+                            editor
+                                .update(&mut cx, |editor, _| {
+                                    editor.active_indent_guides_state.active_indent_range = result;
+                                    editor.active_indent_guides_state.pending_refresh = None;
+                                })
+                                .log_err();
+                        }));
                     return None;
                 }
             }
@@ -154,7 +155,7 @@ pub fn indent_guides_in_range(
     visible_buffer_range: Range<MultiBufferRow>,
     ignore_disabled_for_language: bool,
     snapshot: &DisplaySnapshot,
-    cx: &AppContext,
+    cx: &App,
 ) -> Vec<IndentGuide> {
     let start_anchor = snapshot
         .buffer_snapshot

crates/editor/src/inlay_hint_cache.rs 🔗

@@ -16,10 +16,10 @@ use std::{
 use crate::{
     display_map::Inlay, Anchor, Editor, ExcerptId, InlayId, MultiBuffer, MultiBufferSnapshot,
 };
-use anyhow::Context;
+use anyhow::Context as _;
 use clock::Global;
 use futures::future;
-use gpui::{AsyncWindowContext, Model, ModelContext, Task, ViewContext};
+use gpui::{AsyncAppContext, Context, Entity, Task, Window};
 use language::{language_settings::InlayHintKind, Buffer, BufferSnapshot};
 use parking_lot::RwLock;
 use project::{InlayHint, ResolveState};
@@ -281,10 +281,10 @@ impl InlayHintCache {
     /// Does not update inlay hint cache state on disabling or inlay hint kinds change: only reenabling forces new LSP queries.
     pub(super) fn update_settings(
         &mut self,
-        multi_buffer: &Model<MultiBuffer>,
+        multi_buffer: &Entity<MultiBuffer>,
         new_hint_settings: InlayHintSettings,
         visible_hints: Vec<Inlay>,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) -> ControlFlow<Option<InlaySplice>> {
         let old_enabled = self.enabled;
         // If the setting for inlay hints has changed, update `enabled`. This condition avoids inlay
@@ -348,10 +348,10 @@ impl InlayHintCache {
     pub(super) fn spawn_hint_refresh(
         &mut self,
         reason_description: &'static str,
-        excerpts_to_query: HashMap<ExcerptId, (Model<Buffer>, Global, Range<usize>)>,
+        excerpts_to_query: HashMap<ExcerptId, (Entity<Buffer>, Global, Range<usize>)>,
         invalidate: InvalidationStrategy,
         ignore_debounce: bool,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) -> Option<InlaySplice> {
         if !self.enabled {
             return None;
@@ -411,10 +411,10 @@ impl InlayHintCache {
 
     fn new_allowed_hint_kinds_splice(
         &self,
-        multi_buffer: &Model<MultiBuffer>,
+        multi_buffer: &Entity<MultiBuffer>,
         visible_hints: &[Inlay],
         new_kinds: &HashSet<Option<InlayHintKind>>,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) -> Option<InlaySplice> {
         let old_kinds = &self.allowed_hint_kinds;
         if new_kinds == old_kinds {
@@ -582,7 +582,8 @@ impl InlayHintCache {
         buffer_id: BufferId,
         excerpt_id: ExcerptId,
         id: InlayId,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) {
         if let Some(excerpt_hints) = self.hints.get(&excerpt_id) {
             let mut guard = excerpt_hints.write();
@@ -592,7 +593,7 @@ impl InlayHintCache {
                     let server_id = *server_id;
                     cached_hint.resolve_state = ResolveState::Resolving;
                     drop(guard);
-                    cx.spawn(|editor, mut cx| async move {
+                    cx.spawn_in(window, |editor, mut cx| async move {
                         let resolved_hint_task = editor.update(&mut cx, |editor, cx| {
                             let buffer = editor.buffer().read(cx).buffer(buffer_id)?;
                             editor.semantics_provider.as_ref()?.resolve_inlay_hint(
@@ -640,10 +641,10 @@ fn debounce_value(debounce_ms: u64) -> Option<Duration> {
 fn spawn_new_update_tasks(
     editor: &mut Editor,
     reason: &'static str,
-    excerpts_to_query: HashMap<ExcerptId, (Model<Buffer>, Global, Range<usize>)>,
+    excerpts_to_query: HashMap<ExcerptId, (Entity<Buffer>, Global, Range<usize>)>,
     invalidate: InvalidationStrategy,
     update_cache_version: usize,
-    cx: &mut ViewContext<Editor>,
+    cx: &mut Context<Editor>,
 ) {
     for (excerpt_id, (excerpt_buffer, new_task_buffer_version, excerpt_visible_range)) in
         excerpts_to_query
@@ -738,9 +739,9 @@ impl QueryRanges {
 fn determine_query_ranges(
     multi_buffer: &mut MultiBuffer,
     excerpt_id: ExcerptId,
-    excerpt_buffer: &Model<Buffer>,
+    excerpt_buffer: &Entity<Buffer>,
     excerpt_visible_range: Range<usize>,
-    cx: &mut ModelContext<'_, MultiBuffer>,
+    cx: &mut Context<'_, MultiBuffer>,
 ) -> Option<QueryRanges> {
     let full_excerpt_range = multi_buffer
         .excerpts_for_buffer(excerpt_buffer, cx)
@@ -809,8 +810,8 @@ const INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS: u64 = 400;
 fn new_update_task(
     query: ExcerptQuery,
     query_ranges: QueryRanges,
-    excerpt_buffer: Model<Buffer>,
-    cx: &mut ViewContext<Editor>,
+    excerpt_buffer: Entity<Buffer>,
+    cx: &mut Context<Editor>,
 ) -> Task<()> {
     cx.spawn(move |editor, mut cx| async move {
         let visible_range_update_results = future::join_all(
@@ -839,7 +840,7 @@ fn new_update_task(
         ));
 
         let query_range_failed =
-            |range: &Range<language::Anchor>, e: anyhow::Error, cx: &mut AsyncWindowContext| {
+            |range: &Range<language::Anchor>, e: anyhow::Error, cx: &mut AsyncAppContext| {
                 log::error!("inlay hint update task for range failed: {e:#?}");
                 editor
                     .update(cx, |editor, cx| {
@@ -892,11 +893,11 @@ fn new_update_task(
 }
 
 fn fetch_and_update_hints(
-    excerpt_buffer: Model<Buffer>,
+    excerpt_buffer: Entity<Buffer>,
     query: ExcerptQuery,
     fetch_range: Range<language::Anchor>,
     invalidate: bool,
-    cx: &mut ViewContext<Editor>,
+    cx: &mut Context<Editor>,
 ) -> Task<anyhow::Result<()>> {
     cx.spawn(|editor, mut cx| async move {
         let buffer_snapshot = excerpt_buffer.update(&mut cx, |buffer, _| buffer.snapshot())?;
@@ -1018,7 +1019,7 @@ fn fetch_and_update_hints(
             );
             log::trace!("New update: {new_update:?}");
             editor
-                .update(&mut cx, |editor, cx| {
+                .update(&mut cx, |editor,  cx| {
                     apply_hint_update(
                         editor,
                         new_update,
@@ -1142,7 +1143,7 @@ fn apply_hint_update(
     invalidate: bool,
     buffer_snapshot: BufferSnapshot,
     multi_buffer_snapshot: MultiBufferSnapshot,
-    cx: &mut ViewContext<Editor>,
+    cx: &mut Context<Editor>,
 ) {
     let cached_excerpt_hints = editor
         .inlay_hint_cache
@@ -1262,7 +1263,7 @@ pub mod tests {
     use crate::scroll::ScrollAmount;
     use crate::{scroll::Autoscroll, test::editor_lsp_test_context::rust_lang, ExcerptRange};
     use futures::StreamExt;
-    use gpui::{Context, SemanticVersion, TestAppContext, WindowHandle};
+    use gpui::{AppContext as _, Context, SemanticVersion, TestAppContext, WindowHandle};
     use itertools::Itertools as _;
     use language::{language_settings::AllLanguageSettingsContent, Capability, FakeLspAdapter};
     use language::{Language, LanguageConfig, LanguageMatcher};
@@ -1317,7 +1318,7 @@ pub mod tests {
         cx.executor().run_until_parked();
 
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 let expected_hints = vec!["1".to_string()];
                 assert_eq!(
                     expected_hints,
@@ -1334,14 +1335,14 @@ pub mod tests {
             .unwrap();
 
         editor
-            .update(cx, |editor, cx| {
-                editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-                editor.handle_input("some change", cx);
+            .update(cx, |editor, window, cx| {
+                editor.change_selections(None, window, cx, |s| s.select_ranges([13..13]));
+                editor.handle_input("some change", window, cx);
             })
             .unwrap();
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 let expected_hints = vec!["2".to_string()];
                 assert_eq!(
                     expected_hints,
@@ -1363,7 +1364,7 @@ pub mod tests {
             .expect("inlay refresh request failed");
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 let expected_hints = vec!["3".to_string()];
                 assert_eq!(
                     expected_hints,
@@ -1422,7 +1423,7 @@ pub mod tests {
         cx.executor().run_until_parked();
 
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 let expected_hints = vec!["0".to_string()];
                 assert_eq!(
                     expected_hints,
@@ -1450,7 +1451,7 @@ pub mod tests {
         cx.executor().run_until_parked();
 
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 let expected_hints = vec!["0".to_string()];
                 assert_eq!(
                     expected_hints,
@@ -1470,7 +1471,7 @@ pub mod tests {
         cx.executor().run_until_parked();
 
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 let expected_hints = vec!["1".to_string()];
                 assert_eq!(
                     expected_hints,
@@ -1588,14 +1589,15 @@ pub mod tests {
             })
             .await
             .unwrap();
-        let rs_editor =
-            cx.add_window(|cx| Editor::for_buffer(rs_buffer, Some(project.clone()), cx));
+        let rs_editor = cx.add_window(|window, cx| {
+            Editor::for_buffer(rs_buffer, Some(project.clone()), window, cx)
+        });
         cx.executor().run_until_parked();
 
         let _rs_fake_server = rs_fake_servers.unwrap().next().await.unwrap();
         cx.executor().run_until_parked();
         rs_editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 let expected_hints = vec!["1".to_string()];
                 assert_eq!(
                     expected_hints,
@@ -1613,13 +1615,14 @@ pub mod tests {
             })
             .await
             .unwrap();
-        let md_editor = cx.add_window(|cx| Editor::for_buffer(md_buffer, Some(project), cx));
+        let md_editor =
+            cx.add_window(|window, cx| Editor::for_buffer(md_buffer, Some(project), window, cx));
         cx.executor().run_until_parked();
 
         let _md_fake_server = md_fake_servers.unwrap().next().await.unwrap();
         cx.executor().run_until_parked();
         md_editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 let expected_hints = vec!["1".to_string()];
                 assert_eq!(
                     expected_hints,
@@ -1631,14 +1634,14 @@ pub mod tests {
             .unwrap();
 
         rs_editor
-            .update(cx, |editor, cx| {
-                editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-                editor.handle_input("some rs change", cx);
+            .update(cx, |editor, window, cx| {
+                editor.change_selections(None, window, cx, |s| s.select_ranges([13..13]));
+                editor.handle_input("some rs change", window, cx);
             })
             .unwrap();
         cx.executor().run_until_parked();
         rs_editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 // TODO: Here, we do not get "2", because inserting another language server will trigger `RefreshInlayHints` event from the `LspStore`
                 // A project is listened in every editor, so each of them will react to this event.
                 //
@@ -1654,7 +1657,7 @@ pub mod tests {
             })
             .unwrap();
         md_editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 let expected_hints = vec!["1".to_string()];
                 assert_eq!(
                     expected_hints,
@@ -1666,14 +1669,14 @@ pub mod tests {
             .unwrap();
 
         md_editor
-            .update(cx, |editor, cx| {
-                editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-                editor.handle_input("some md change", cx);
+            .update(cx, |editor, window, cx| {
+                editor.change_selections(None, window, cx, |s| s.select_ranges([13..13]));
+                editor.handle_input("some md change", window, cx);
             })
             .unwrap();
         cx.executor().run_until_parked();
         md_editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 let expected_hints = vec!["2".to_string()];
                 assert_eq!(
                     expected_hints,
@@ -1684,7 +1687,7 @@ pub mod tests {
             })
             .unwrap();
         rs_editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 let expected_hints = vec!["3".to_string()];
                 assert_eq!(
                     expected_hints,
@@ -1767,7 +1770,7 @@ pub mod tests {
         cx.executor().run_until_parked();
 
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 assert_eq!(
                     lsp_request_count.load(Ordering::Relaxed),
                     1,
@@ -1800,7 +1803,7 @@ pub mod tests {
             .expect("inlay refresh request failed");
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 assert_eq!(
                     lsp_request_count.load(Ordering::Relaxed),
                     2,
@@ -1870,7 +1873,7 @@ pub mod tests {
                 })
             });
             cx.executor().run_until_parked();
-            editor.update(cx, |editor, cx| {
+            editor.update(cx, |editor, _, cx| {
                 assert_eq!(
                     lsp_request_count.load(Ordering::Relaxed),
                     2,
@@ -1913,7 +1916,7 @@ pub mod tests {
         });
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 assert_eq!(
                     lsp_request_count.load(Ordering::Relaxed),
                     2,
@@ -1941,7 +1944,7 @@ pub mod tests {
             .expect("inlay refresh request failed");
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 assert_eq!(
                     lsp_request_count.load(Ordering::Relaxed),
                     2,
@@ -1967,7 +1970,7 @@ pub mod tests {
         });
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 assert_eq!(
                     lsp_request_count.load(Ordering::Relaxed),
                     3,
@@ -2001,7 +2004,7 @@ pub mod tests {
             .expect("inlay refresh request failed");
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 assert_eq!(
                     lsp_request_count.load(Ordering::Relaxed),
                     4,
@@ -2075,9 +2078,9 @@ pub mod tests {
             "initial change #3",
         ] {
             editor
-                .update(cx, |editor, cx| {
-                    editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-                    editor.handle_input(change_after_opening, cx);
+                .update(cx, |editor, window, cx| {
+                    editor.change_selections(None, window, cx, |s| s.select_ranges([13..13]));
+                    editor.handle_input(change_after_opening, window, cx);
                 })
                 .unwrap();
             expected_changes.push(change_after_opening);
@@ -2086,7 +2089,7 @@ pub mod tests {
         cx.executor().run_until_parked();
 
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 let current_text = editor.text(cx);
                 for change in &expected_changes {
                     assert!(
@@ -2119,9 +2122,9 @@ pub mod tests {
             let task_editor = editor;
             edits.push(cx.spawn(|mut cx| async move {
                 task_editor
-                    .update(&mut cx, |editor, cx| {
-                        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-                        editor.handle_input(async_later_change, cx);
+                    .update(&mut cx, |editor, window, cx| {
+                        editor.change_selections(None, window, cx, |s| s.select_ranges([13..13]));
+                        editor.handle_input(async_later_change, window, cx);
                     })
                     .unwrap();
             }));
@@ -2130,7 +2133,7 @@ pub mod tests {
         cx.executor().run_until_parked();
 
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 let current_text = editor.text(cx);
                 for change in &expected_changes {
                     assert!(
@@ -2238,7 +2241,8 @@ pub mod tests {
             })
             .await
             .unwrap();
-        let editor = cx.add_window(|cx| Editor::for_buffer(buffer, Some(project), cx));
+        let editor =
+            cx.add_window(|window, cx| Editor::for_buffer(buffer, Some(project), window, cx));
 
         cx.executor().run_until_parked();
 
@@ -2266,7 +2270,7 @@ pub mod tests {
             lsp::Position::new(initial_visible_range.end.row * 2, 2);
         let mut expected_invisible_query_start = lsp_initial_visible_range.end;
         expected_invisible_query_start.character += 1;
-        editor.update(cx, |editor, cx| {
+        editor.update(cx, |editor, _window, cx| {
             let ranges = lsp_request_ranges.lock().drain(..).collect::<Vec<_>>();
             assert_eq!(ranges.len(), 2,
                 "When scroll is at the edge of a big document, its visible part and the same range further should be queried in order, but got: {ranges:?}");
@@ -2290,14 +2294,14 @@ pub mod tests {
         }).unwrap();
 
         editor
-            .update(cx, |editor, cx| {
-                editor.scroll_screen(&ScrollAmount::Page(1.0), cx);
+            .update(cx, |editor, window, cx| {
+                editor.scroll_screen(&ScrollAmount::Page(1.0), window, cx);
             })
             .unwrap();
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
-                editor.scroll_screen(&ScrollAmount::Page(1.0), cx);
+            .update(cx, |editor, window, cx| {
+                editor.scroll_screen(&ScrollAmount::Page(1.0), window, cx);
             })
             .unwrap();
         cx.executor().advance_clock(Duration::from_millis(
@@ -2306,10 +2310,12 @@ pub mod tests {
         cx.executor().run_until_parked();
         let visible_range_after_scrolls = editor_visible_range(&editor, cx);
         let visible_line_count = editor
-            .update(cx, |editor, _| editor.visible_line_count().unwrap())
+            .update(cx, |editor, _window, _| {
+                editor.visible_line_count().unwrap()
+            })
             .unwrap();
         let selection_in_cached_range = editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 let ranges = lsp_request_ranges
                     .lock()
                     .drain(..)
@@ -2362,8 +2368,8 @@ pub mod tests {
             .unwrap();
 
         editor
-            .update(cx, |editor, cx| {
-                editor.change_selections(Some(Autoscroll::center()), cx, |s| {
+            .update(cx, |editor, window, cx| {
+                editor.change_selections(Some(Autoscroll::center()), window, cx, |s| {
                     s.select_ranges([selection_in_cached_range..selection_in_cached_range])
                 });
             })
@@ -2372,7 +2378,7 @@ pub mod tests {
             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
         ));
         cx.executor().run_until_parked();
-        editor.update(cx, |_, _| {
+        editor.update(cx, |_, _, _| {
             let ranges = lsp_request_ranges
                 .lock()
                 .drain(..)
@@ -2383,15 +2389,15 @@ pub mod tests {
         }).unwrap();
 
         editor
-            .update(cx, |editor, cx| {
-                editor.handle_input("++++more text++++", cx);
+            .update(cx, |editor, window, cx| {
+                editor.handle_input("++++more text++++", window, cx);
             })
             .unwrap();
         cx.executor().advance_clock(Duration::from_millis(
             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
         ));
         cx.executor().run_until_parked();
-        editor.update(cx, |editor, cx| {
+        editor.update(cx, |editor, _window, cx| {
             let mut ranges = lsp_request_ranges.lock().drain(..).collect::<Vec<_>>();
             ranges.sort_by_key(|r| r.start);
 
@@ -2427,7 +2433,7 @@ pub mod tests {
         cx: &mut gpui::TestAppContext,
     ) -> Range<Point> {
         let ranges = editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 editor.excerpts_for_inlay_hints_query(None, cx)
             })
             .unwrap();
@@ -2501,7 +2507,7 @@ pub mod tests {
             })
             .await
             .unwrap();
-        let multibuffer = cx.new_model(|cx| {
+        let multibuffer = cx.new(|cx| {
             let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
             multibuffer.push_excerpts(
                 buffer_1.clone(),
@@ -2567,8 +2573,9 @@ pub mod tests {
         });
 
         cx.executor().run_until_parked();
-        let editor = cx
-            .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), true, cx));
+        let editor = cx.add_window(|window, cx| {
+            Editor::for_multibuffer(multibuffer, Some(project.clone()), true, window, cx)
+        });
 
         let editor_edited = Arc::new(AtomicBool::new(false));
         let fake_server = fake_servers.next().await.unwrap();
@@ -2641,7 +2648,7 @@ pub mod tests {
         cx.executor().run_until_parked();
 
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 let expected_hints = vec![
                     "main hint #0".to_string(),
                     "main hint #1".to_string(),
@@ -2660,21 +2667,21 @@ pub mod tests {
             .unwrap();
 
         editor
-            .update(cx, |editor, cx| {
-                editor.change_selections(Some(Autoscroll::Next), cx, |s| {
+            .update(cx, |editor, window, cx| {
+                editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
                     s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
                 });
-                editor.change_selections(Some(Autoscroll::Next), cx, |s| {
+                editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
                     s.select_ranges([Point::new(22, 0)..Point::new(22, 0)])
                 });
-                editor.change_selections(Some(Autoscroll::Next), cx, |s| {
+                editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
                     s.select_ranges([Point::new(50, 0)..Point::new(50, 0)])
                 });
             })
             .unwrap();
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 let expected_hints = vec![
                     "main hint #0".to_string(),
                     "main hint #1".to_string(),
@@ -2693,8 +2700,8 @@ pub mod tests {
             .unwrap();
 
         editor
-            .update(cx, |editor, cx| {
-                editor.change_selections(Some(Autoscroll::Next), cx, |s| {
+            .update(cx, |editor, window, cx| {
+                editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
                     s.select_ranges([Point::new(100, 0)..Point::new(100, 0)])
                 });
             })
@@ -2704,7 +2711,7 @@ pub mod tests {
         ));
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 let expected_hints = vec![
                     "main hint #0".to_string(),
                     "main hint #1".to_string(),
@@ -2726,8 +2733,8 @@ pub mod tests {
             .unwrap();
 
         editor
-            .update(cx, |editor, cx| {
-                editor.change_selections(Some(Autoscroll::Next), cx, |s| {
+            .update(cx, |editor, window, cx| {
+                editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
                     s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
                 });
             })
@@ -2737,7 +2744,7 @@ pub mod tests {
         ));
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 let expected_hints = vec![
                     "main hint #0".to_string(),
                     "main hint #1".to_string(),
@@ -2760,16 +2767,16 @@ pub mod tests {
 
         editor_edited.store(true, Ordering::Release);
         editor
-            .update(cx, |editor, cx| {
-                editor.change_selections(None, cx, |s| {
+            .update(cx, |editor, window, cx| {
+                editor.change_selections(None, window, cx, |s| {
                     s.select_ranges([Point::new(57, 0)..Point::new(57, 0)])
                 });
-                editor.handle_input("++++more text++++", cx);
+                editor.handle_input("++++more text++++", window, cx);
             })
             .unwrap();
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 let expected_hints = vec![
                     "main hint #0".to_string(),
                     "main hint #1".to_string(),
@@ -2843,7 +2850,7 @@ pub mod tests {
             })
             .await
             .unwrap();
-        let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
+        let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
         let (buffer_1_excerpts, buffer_2_excerpts) = multibuffer.update(cx, |multibuffer, cx| {
             let buffer_1_excerpts = multibuffer.push_excerpts(
                 buffer_1.clone(),
@@ -2868,8 +2875,9 @@ pub mod tests {
         assert!(!buffer_2_excerpts.is_empty());
 
         cx.executor().run_until_parked();
-        let editor = cx
-            .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), true, cx));
+        let editor = cx.add_window(|window, cx| {
+            Editor::for_multibuffer(multibuffer, Some(project.clone()), true, window, cx)
+        });
         let editor_edited = Arc::new(AtomicBool::new(false));
         let fake_server = fake_servers.next().await.unwrap();
         let closure_editor_edited = Arc::clone(&editor_edited);
@@ -2939,7 +2947,7 @@ pub mod tests {
             .await;
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 assert_eq!(
                     vec!["main hint #0".to_string(), "other hint #0".to_string()],
                     sorted_cached_hint_labels(editor),
@@ -2953,7 +2961,7 @@ pub mod tests {
             .unwrap();
 
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 editor.buffer().update(cx, |multibuffer, cx| {
                     multibuffer.remove_excerpts(buffer_2_excerpts, cx)
                 })
@@ -2961,7 +2969,7 @@ pub mod tests {
             .unwrap();
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 assert_eq!(
                     vec!["main hint #0".to_string()],
                     cached_hint_labels(editor),
@@ -2987,7 +2995,7 @@ pub mod tests {
         });
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 let expected_hints = vec!["main hint #0".to_string()];
                 assert_eq!(
                     expected_hints,
@@ -3073,19 +3081,20 @@ pub mod tests {
             })
             .await
             .unwrap();
-        let editor = cx.add_window(|cx| Editor::for_buffer(buffer, Some(project), cx));
+        let editor =
+            cx.add_window(|window, cx| Editor::for_buffer(buffer, Some(project), window, cx));
 
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
-                editor.change_selections(None, cx, |s| {
+            .update(cx, |editor, window, cx| {
+                editor.change_selections(None, window, cx, |s| {
                     s.select_ranges([Point::new(10, 0)..Point::new(10, 0)])
                 })
             })
             .unwrap();
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 let expected_hints = vec!["1".to_string()];
                 assert_eq!(expected_hints, cached_hint_labels(editor));
                 assert_eq!(expected_hints, visible_hint_labels(editor, cx));
@@ -3134,14 +3143,14 @@ pub mod tests {
         .await;
 
         editor
-            .update(cx, |editor, cx| {
-                editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
+            .update(cx, |editor, window, cx| {
+                editor.toggle_inlay_hints(&crate::ToggleInlayHints, window, cx)
             })
             .unwrap();
 
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 let expected_hints = vec!["1".to_string()];
                 assert_eq!(
                     expected_hints,
@@ -3153,13 +3162,13 @@ pub mod tests {
             .unwrap();
 
         editor
-            .update(cx, |editor, cx| {
-                editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
+            .update(cx, |editor, window, cx| {
+                editor.toggle_inlay_hints(&crate::ToggleInlayHints, window, cx)
             })
             .unwrap();
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 assert!(
                     cached_hint_labels(editor).is_empty(),
                     "Should clear hints after 2nd toggle"
@@ -3181,7 +3190,7 @@ pub mod tests {
         });
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 let expected_hints = vec!["2".to_string()];
                 assert_eq!(
                     expected_hints,
@@ -3193,13 +3202,13 @@ pub mod tests {
             .unwrap();
 
         editor
-            .update(cx, |editor, cx| {
-                editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
+            .update(cx, |editor, window, cx| {
+                editor.toggle_inlay_hints(&crate::ToggleInlayHints, window, cx)
             })
             .unwrap();
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 assert!(
                     cached_hint_labels(editor).is_empty(),
                     "Should clear hints after enabling in settings and a 3rd toggle"
@@ -3209,12 +3218,12 @@ pub mod tests {
             .unwrap();
 
         editor
-            .update(cx, |editor, cx| {
-                editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
+            .update(cx, |editor, window, cx| {
+                editor.toggle_inlay_hints(&crate::ToggleInlayHints, window, cx)
             })
             .unwrap();
         cx.executor().run_until_parked();
-        editor.update(cx, |editor, cx| {
+        editor.update(cx, |editor, _, cx| {
             let expected_hints = vec!["3".to_string()];
             assert_eq!(
                 expected_hints,
@@ -3346,19 +3355,20 @@ pub mod tests {
             })
             .await
             .unwrap();
-        let editor = cx.add_window(|cx| Editor::for_buffer(buffer, Some(project), cx));
+        let editor =
+            cx.add_window(|window, cx| Editor::for_buffer(buffer, Some(project), window, cx));
 
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
-                editor.change_selections(None, cx, |s| {
+            .update(cx, |editor, window, cx| {
+                editor.change_selections(None, window, cx, |s| {
                     s.select_ranges([Point::new(10, 0)..Point::new(10, 0)])
                 })
             })
             .unwrap();
         cx.executor().run_until_parked();
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _window, cx| {
                 let expected_hints = vec![
                     "move".to_string(),
                     "(".to_string(),
@@ -3429,10 +3439,11 @@ pub mod tests {
             })
             .await
             .unwrap();
-        let editor = cx.add_window(|cx| Editor::for_buffer(buffer, Some(project), cx));
+        let editor =
+            cx.add_window(|window, cx| Editor::for_buffer(buffer, Some(project), window, cx));
 
         editor
-            .update(cx, |editor, cx| {
+            .update(cx, |editor, _, cx| {
                 assert!(cached_hint_labels(editor).is_empty());
                 assert!(visible_hint_labels(editor, cx).is_empty());
             })
@@ -3471,7 +3482,7 @@ pub mod tests {
         labels
     }
 
-    pub fn visible_hint_labels(editor: &Editor, cx: &ViewContext<Editor>) -> Vec<String> {
+    pub fn visible_hint_labels(editor: &Editor, cx: &Context<Editor>) -> Vec<String> {
         editor
             .visible_inlay_hints(cx)
             .into_iter()

crates/editor/src/inline_completion_tests.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{prelude::*, Model};
+use gpui::{prelude::*, Entity};
 use indoc::indoc;
 use inline_completion::InlineCompletionProvider;
 use language::{Language, LanguageConfig};
@@ -15,12 +15,12 @@ async fn test_inline_completion_insert(cx: &mut gpui::TestAppContext) {
     init_test(cx, |_| {});
 
     let mut cx = EditorTestContext::new(cx).await;
-    let provider = cx.new_model(|_| FakeInlineCompletionProvider::default());
+    let provider = cx.new(|_| FakeInlineCompletionProvider::default());
     assign_editor_completion_provider(provider.clone(), &mut cx);
     cx.set_state("let absolute_zero_celsius = ˇ;");
 
     propose_edits(&provider, vec![(28..28, "-273.15")], &mut cx);
-    cx.update_editor(|editor, cx| editor.update_visible_inline_completion(cx));
+    cx.update_editor(|editor, window, cx| editor.update_visible_inline_completion(window, cx));
 
     assert_editor_active_edit_completion(&mut cx, |_, edits| {
         assert_eq!(edits.len(), 1);
@@ -37,12 +37,12 @@ async fn test_inline_completion_modification(cx: &mut gpui::TestAppContext) {
     init_test(cx, |_| {});
 
     let mut cx = EditorTestContext::new(cx).await;
-    let provider = cx.new_model(|_| FakeInlineCompletionProvider::default());
+    let provider = cx.new(|_| FakeInlineCompletionProvider::default());
     assign_editor_completion_provider(provider.clone(), &mut cx);
     cx.set_state("let pi = ˇ\"foo\";");
 
     propose_edits(&provider, vec![(9..14, "3.14159")], &mut cx);
-    cx.update_editor(|editor, cx| editor.update_visible_inline_completion(cx));
+    cx.update_editor(|editor, window, cx| editor.update_visible_inline_completion(window, cx));
 
     assert_editor_active_edit_completion(&mut cx, |_, edits| {
         assert_eq!(edits.len(), 1);
@@ -59,7 +59,7 @@ async fn test_inline_completion_jump_button(cx: &mut gpui::TestAppContext) {
     init_test(cx, |_| {});
 
     let mut cx = EditorTestContext::new(cx).await;
-    let provider = cx.new_model(|_| FakeInlineCompletionProvider::default());
+    let provider = cx.new(|_| FakeInlineCompletionProvider::default());
     assign_editor_completion_provider(provider.clone(), &mut cx);
 
     // Cursor is 2+ lines above the proposed edit
@@ -77,7 +77,7 @@ async fn test_inline_completion_jump_button(cx: &mut gpui::TestAppContext) {
         &mut cx,
     );
 
-    cx.update_editor(|editor, cx| editor.update_visible_inline_completion(cx));
+    cx.update_editor(|editor, window, cx| editor.update_visible_inline_completion(window, cx));
     assert_editor_active_move_completion(&mut cx, |snapshot, move_target| {
         assert_eq!(move_target.to_point(&snapshot), Point::new(4, 3));
     });
@@ -107,7 +107,7 @@ async fn test_inline_completion_jump_button(cx: &mut gpui::TestAppContext) {
         &mut cx,
     );
 
-    cx.update_editor(|editor, cx| editor.update_visible_inline_completion(cx));
+    cx.update_editor(|editor, window, cx| editor.update_visible_inline_completion(window, cx));
     assert_editor_active_move_completion(&mut cx, |snapshot, move_target| {
         assert_eq!(move_target.to_point(&snapshot), Point::new(1, 3));
     });
@@ -140,7 +140,7 @@ async fn test_indentation(cx: &mut gpui::TestAppContext) {
 
     let mut cx = EditorTestContext::new(cx).await;
     cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
-    let provider = cx.new_model(|_| FakeInlineCompletionProvider::default());
+    let provider = cx.new(|_| FakeInlineCompletionProvider::default());
     assign_editor_completion_provider(provider.clone(), &mut cx);
 
     cx.set_state(indoc! {"
@@ -154,7 +154,7 @@ async fn test_indentation(cx: &mut gpui::TestAppContext) {
         vec![(Point::new(1, 0)..Point::new(1, 0), "    const function()")],
         &mut cx,
     );
-    cx.update_editor(|editor, cx| editor.update_visible_inline_completion(cx));
+    cx.update_editor(|editor, window, cx| editor.update_visible_inline_completion(window, cx));
 
     assert_editor_active_edit_completion(&mut cx, |_, edits| {
         assert_eq!(edits.len(), 1);
@@ -176,7 +176,7 @@ async fn test_inline_completion_invalidation_range(cx: &mut gpui::TestAppContext
     init_test(cx, |_| {});
 
     let mut cx = EditorTestContext::new(cx).await;
-    let provider = cx.new_model(|_| FakeInlineCompletionProvider::default());
+    let provider = cx.new(|_| FakeInlineCompletionProvider::default());
     assign_editor_completion_provider(provider.clone(), &mut cx);
 
     // Cursor is 3+ lines above the proposed edit
@@ -196,7 +196,7 @@ async fn test_inline_completion_invalidation_range(cx: &mut gpui::TestAppContext
         &mut cx,
     );
 
-    cx.update_editor(|editor, cx| editor.update_visible_inline_completion(cx));
+    cx.update_editor(|editor, window, cx| editor.update_visible_inline_completion(window, cx));
     assert_editor_active_move_completion(&mut cx, |snapshot, move_target| {
         assert_eq!(move_target.to_point(&snapshot), edit_location);
     });
@@ -223,7 +223,7 @@ async fn test_inline_completion_invalidation_range(cx: &mut gpui::TestAppContext
         line 4
         line
     "});
-    cx.editor(|editor, _| {
+    cx.editor(|editor, _, _| {
         assert!(editor.active_inline_completion.is_none());
     });
 
@@ -244,7 +244,7 @@ async fn test_inline_completion_invalidation_range(cx: &mut gpui::TestAppContext
         &mut cx,
     );
 
-    cx.update_editor(|editor, cx| editor.update_visible_inline_completion(cx));
+    cx.update_editor(|editor, window, cx| editor.update_visible_inline_completion(window, cx));
     assert_editor_active_move_completion(&mut cx, |snapshot, move_target| {
         assert_eq!(move_target.to_point(&snapshot), edit_location);
     });
@@ -271,7 +271,7 @@ async fn test_inline_completion_invalidation_range(cx: &mut gpui::TestAppContext
         line 4
         line ˇ5
     "});
-    cx.editor(|editor, _| {
+    cx.editor(|editor, _, _| {
         assert!(editor.active_inline_completion.is_none());
     });
 }
@@ -280,7 +280,7 @@ fn assert_editor_active_edit_completion(
     cx: &mut EditorTestContext,
     assert: impl FnOnce(MultiBufferSnapshot, &Vec<(Range<Anchor>, String)>),
 ) {
-    cx.editor(|editor, cx| {
+    cx.editor(|editor, _, cx| {
         let completion_state = editor
             .active_inline_completion
             .as_ref()
@@ -298,7 +298,7 @@ fn assert_editor_active_move_completion(
     cx: &mut EditorTestContext,
     assert: impl FnOnce(MultiBufferSnapshot, Anchor),
 ) {
-    cx.editor(|editor, cx| {
+    cx.editor(|editor, _, cx| {
         let completion_state = editor
             .active_inline_completion
             .as_ref()
@@ -313,13 +313,13 @@ fn assert_editor_active_move_completion(
 }
 
 fn accept_completion(cx: &mut EditorTestContext) {
-    cx.update_editor(|editor, cx| {
-        editor.accept_inline_completion(&crate::AcceptInlineCompletion, cx)
+    cx.update_editor(|editor, window, cx| {
+        editor.accept_inline_completion(&crate::AcceptInlineCompletion, window, cx)
     })
 }
 
 fn propose_edits<T: ToOffset>(
-    provider: &Model<FakeInlineCompletionProvider>,
+    provider: &Entity<FakeInlineCompletionProvider>,
     edits: Vec<(Range<T>, &str)>,
     cx: &mut EditorTestContext,
 ) {
@@ -329,7 +329,7 @@ fn propose_edits<T: ToOffset>(
         (range, text.into())
     });
 
-    cx.update(|cx| {
+    cx.update(|_, cx| {
         provider.update(cx, |provider, _| {
             provider.set_inline_completion(Some(inline_completion::InlineCompletion {
                 edits: edits.collect(),
@@ -340,11 +340,11 @@ fn propose_edits<T: ToOffset>(
 }
 
 fn assign_editor_completion_provider(
-    provider: Model<FakeInlineCompletionProvider>,
+    provider: Entity<FakeInlineCompletionProvider>,
     cx: &mut EditorTestContext,
 ) {
-    cx.update_editor(|editor, cx| {
-        editor.set_inline_completion_provider(Some(provider), cx);
+    cx.update_editor(|editor, window, cx| {
+        editor.set_inline_completion_provider(Some(provider), window, cx);
     })
 }
 
@@ -381,9 +381,9 @@ impl InlineCompletionProvider for FakeInlineCompletionProvider {
 
     fn is_enabled(
         &self,
-        _buffer: &gpui::Model<language::Buffer>,
+        _buffer: &gpui::Entity<language::Buffer>,
         _cursor_position: language::Anchor,
-        _cx: &gpui::AppContext,
+        _cx: &gpui::App,
     ) -> bool {
         true
     }
@@ -394,31 +394,31 @@ impl InlineCompletionProvider for FakeInlineCompletionProvider {
 
     fn refresh(
         &mut self,
-        _buffer: gpui::Model<language::Buffer>,
+        _buffer: gpui::Entity<language::Buffer>,
         _cursor_position: language::Anchor,
         _debounce: bool,
-        _cx: &mut gpui::ModelContext<Self>,
+        _cx: &mut gpui::Context<Self>,
     ) {
     }
 
     fn cycle(
         &mut self,
-        _buffer: gpui::Model<language::Buffer>,
+        _buffer: gpui::Entity<language::Buffer>,
         _cursor_position: language::Anchor,
         _direction: inline_completion::Direction,
-        _cx: &mut gpui::ModelContext<Self>,
+        _cx: &mut gpui::Context<Self>,
     ) {
     }
 
-    fn accept(&mut self, _cx: &mut gpui::ModelContext<Self>) {}
+    fn accept(&mut self, _cx: &mut gpui::Context<Self>) {}
 
-    fn discard(&mut self, _cx: &mut gpui::ModelContext<Self>) {}
+    fn discard(&mut self, _cx: &mut gpui::Context<Self>) {}
 
     fn suggest<'a>(
         &mut self,
-        _buffer: &gpui::Model<language::Buffer>,
+        _buffer: &gpui::Entity<language::Buffer>,
         _cursor_position: language::Anchor,
-        _cx: &mut gpui::ModelContext<Self>,
+        _cx: &mut gpui::Context<Self>,
     ) -> Option<inline_completion::InlineCompletion> {
         self.completion.clone()
     }

crates/editor/src/items.rs 🔗

@@ -11,9 +11,8 @@ use file_icons::FileIcons;
 use futures::future::try_join_all;
 use git::status::GitSummary;
 use gpui::{
-    point, AnyElement, AppContext, AsyncWindowContext, Context, Entity, EntityId, EventEmitter,
-    IntoElement, Model, ParentElement, Pixels, SharedString, Styled, Task, View, ViewContext,
-    VisualContext, WeakView, WindowContext,
+    point, AnyElement, App, AsyncWindowContext, Context, Entity, EntityId, EventEmitter,
+    IntoElement, ParentElement, Pixels, SharedString, Styled, Task, WeakEntity, Window,
 };
 use language::{
     proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, CharKind, DiskState, Point,
@@ -55,11 +54,12 @@ impl FollowableItem for Editor {
     }
 
     fn from_state_proto(
-        workspace: View<Workspace>,
+        workspace: Entity<Workspace>,
         remote_id: ViewId,
         state: &mut Option<proto::view::Variant>,
-        cx: &mut WindowContext,
-    ) -> Option<Task<Result<View<Self>>>> {
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Option<Task<Result<Entity<Self>>>> {
         let project = workspace.read(cx).project().to_owned();
         let Some(proto::view::Variant::Editor(_)) = state else {
             return None;
@@ -80,13 +80,13 @@ impl FollowableItem for Editor {
                 .collect::<Result<Vec<_>>>()
         });
 
-        Some(cx.spawn(|mut cx| async move {
+        Some(window.spawn(cx, |mut cx| async move {
             let mut buffers = futures::future::try_join_all(buffers?)
                 .await
                 .debug_assert_ok("leaders don't share views for unshared buffers")?;
 
-            let editor = cx.update(|cx| {
-                let multibuffer = cx.new_model(|cx| {
+            let editor = cx.update(|window, cx| {
+                let multibuffer = cx.new(|cx| {
                     let mut multibuffer;
                     if state.singleton && buffers.len() == 1 {
                         multibuffer = MultiBuffer::singleton(buffers.pop().unwrap(), cx)
@@ -121,9 +121,14 @@ impl FollowableItem for Editor {
                     multibuffer
                 });
 
-                cx.new_view(|cx| {
-                    let mut editor =
-                        Editor::for_multibuffer(multibuffer, Some(project.clone()), true, cx);
+                cx.new(|cx| {
+                    let mut editor = Editor::for_multibuffer(
+                        multibuffer,
+                        Some(project.clone()),
+                        true,
+                        window,
+                        cx,
+                    );
                     editor.remote_id = Some(remote_id);
                     editor
                 })
@@ -148,13 +153,18 @@ impl FollowableItem for Editor {
         }))
     }
 
-    fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>) {
+    fn set_leader_peer_id(
+        &mut self,
+        leader_peer_id: Option<PeerId>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.leader_peer_id = leader_peer_id;
         if self.leader_peer_id.is_some() {
             self.buffer.update(cx, |buffer, cx| {
                 buffer.remove_active_selections(cx);
             });
-        } else if self.focus_handle.is_focused(cx) {
+        } else if self.focus_handle.is_focused(window) {
             self.buffer.update(cx, |buffer, cx| {
                 buffer.set_active_selections(
                     &self.selections.disjoint_anchors(),
@@ -167,7 +177,7 @@ impl FollowableItem for Editor {
         cx.notify();
     }
 
-    fn to_state_proto(&self, cx: &WindowContext) -> Option<proto::view::Variant> {
+    fn to_state_proto(&self, _: &Window, cx: &App) -> Option<proto::view::Variant> {
         let buffer = self.buffer.read(cx);
         if buffer
             .as_singleton()
@@ -237,7 +247,8 @@ impl FollowableItem for Editor {
         &self,
         event: &EditorEvent,
         update: &mut Option<proto::update_view::Variant>,
-        cx: &WindowContext,
+        _: &Window,
+        cx: &App,
     ) -> bool {
         let update =
             update.get_or_insert_with(|| proto::update_view::Variant::Editor(Default::default()));
@@ -299,22 +310,23 @@ impl FollowableItem for Editor {
 
     fn apply_update_proto(
         &mut self,
-        project: &Model<Project>,
+        project: &Entity<Project>,
         message: update_view::Variant,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let update_view::Variant::Editor(message) = message;
         let project = project.clone();
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             update_editor_from_message(this, project, message, &mut cx).await
         })
     }
 
-    fn is_project_item(&self, _cx: &WindowContext) -> bool {
+    fn is_project_item(&self, _window: &Window, _cx: &App) -> bool {
         true
     }
 
-    fn dedup(&self, existing: &Self, cx: &WindowContext) -> Option<Dedup> {
+    fn dedup(&self, existing: &Self, _: &Window, cx: &App) -> Option<Dedup> {
         let self_singleton = self.buffer.read(cx).as_singleton()?;
         let other_singleton = existing.buffer.read(cx).as_singleton()?;
         if self_singleton == other_singleton {
@@ -326,8 +338,8 @@ impl FollowableItem for Editor {
 }
 
 async fn update_editor_from_message(
-    this: WeakView<Editor>,
-    project: Model<Project>,
+    this: WeakEntity<Editor>,
+    project: Entity<Project>,
     message: proto::update_view::Editor,
     cx: &mut AsyncWindowContext,
 ) -> Result<()> {
@@ -437,9 +449,9 @@ async fn update_editor_from_message(
     .await?;
 
     // Update the editor's state.
-    this.update(cx, |editor, cx| {
+    this.update_in(cx, |editor, window, cx| {
         if !selections.is_empty() || pending_selection.is_some() {
-            editor.set_selections_from_remote(selections, pending_selection, cx);
+            editor.set_selections_from_remote(selections, pending_selection, window, cx);
             editor.request_autoscroll_remotely(Autoscroll::newest(), cx);
         } else if let Some(scroll_top_anchor) = scroll_top_anchor {
             editor.set_scroll_anchor_remote(
@@ -447,6 +459,7 @@ async fn update_editor_from_message(
                     anchor: scroll_top_anchor,
                     offset: point(message.scroll_x, message.scroll_y),
                 },
+                window,
                 cx,
             );
         }
@@ -534,7 +547,12 @@ fn deserialize_anchor(buffer: &MultiBufferSnapshot, anchor: proto::EditorAnchor)
 impl Item for Editor {
     type Event = EditorEvent;
 
-    fn navigate(&mut self, data: Box<dyn std::any::Any>, cx: &mut ViewContext<Self>) -> bool {
+    fn navigate(
+        &mut self,
+        data: Box<dyn std::any::Any>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> bool {
         if let Ok(data) = data.downcast::<NavigationData>() {
             let newest_selection = self.selections.newest::<Point>(cx);
             let buffer = self.buffer.read(cx).read(cx);
@@ -557,8 +575,8 @@ impl Item for Editor {
                 false
             } else {
                 let nav_history = self.nav_history.take();
-                self.set_scroll_anchor(scroll_anchor, cx);
-                self.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                self.set_scroll_anchor(scroll_anchor, window, cx);
+                self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.select_ranges([offset..offset])
                 });
                 self.nav_history = nav_history;
@@ -569,7 +587,7 @@ impl Item for Editor {
         }
     }
 
-    fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString> {
+    fn tab_tooltip_text(&self, cx: &App) -> Option<SharedString> {
         let file_path = self
             .buffer()
             .read(cx)
@@ -588,12 +606,12 @@ impl Item for Editor {
         None
     }
 
-    fn tab_description(&self, detail: usize, cx: &AppContext) -> Option<SharedString> {
+    fn tab_description(&self, detail: usize, cx: &App) -> Option<SharedString> {
         let path = path_for_buffer(&self.buffer, detail, true, cx)?;
         Some(path.to_string_lossy().to_string().into())
     }
 
-    fn tab_icon(&self, cx: &WindowContext) -> Option<Icon> {
+    fn tab_icon(&self, _: &Window, cx: &App) -> Option<Icon> {
         ItemSettings::get_global(cx)
             .file_icons
             .then(|| {
@@ -607,7 +625,7 @@ impl Item for Editor {
             .map(Icon::from_path)
     }
 
-    fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement {
+    fn tab_content(&self, params: TabContentParams, _: &Window, cx: &App) -> AnyElement {
         let label_color = if ItemSettings::get_global(cx).git_status {
             self.buffer()
                 .read(cx)
@@ -673,7 +691,7 @@ impl Item for Editor {
 
     fn for_each_project_item(
         &self,
-        cx: &AppContext,
+        cx: &App,
         f: &mut dyn FnMut(EntityId, &dyn project::ProjectItem),
     ) {
         self.buffer
@@ -681,53 +699,59 @@ impl Item for Editor {
             .for_each_buffer(|buffer| f(buffer.entity_id(), buffer.read(cx)));
     }
 
-    fn is_singleton(&self, cx: &AppContext) -> bool {
+    fn is_singleton(&self, cx: &App) -> bool {
         self.buffer.read(cx).is_singleton()
     }
 
     fn clone_on_split(
         &self,
         _workspace_id: Option<WorkspaceId>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<View<Editor>>
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Entity<Editor>>
     where
         Self: Sized,
     {
-        Some(cx.new_view(|cx| self.clone(cx)))
+        Some(cx.new(|cx| self.clone(window, cx)))
     }
 
-    fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
+    fn set_nav_history(
+        &mut self,
+        history: ItemNavHistory,
+        _window: &mut Window,
+        _: &mut Context<Self>,
+    ) {
         self.nav_history = Some(history);
     }
 
-    fn discarded(&self, _project: Model<Project>, cx: &mut ViewContext<Self>) {
+    fn discarded(&self, _project: Entity<Project>, _: &mut Window, cx: &mut Context<Self>) {
         for buffer in self.buffer().clone().read(cx).all_buffers() {
             buffer.update(cx, |buffer, cx| buffer.discarded(cx))
         }
     }
 
-    fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
+    fn deactivated(&mut self, _: &mut Window, cx: &mut Context<Self>) {
         let selection = self.selections.newest_anchor();
         self.push_to_nav_history(selection.head(), None, cx);
     }
 
-    fn workspace_deactivated(&mut self, cx: &mut ViewContext<Self>) {
+    fn workspace_deactivated(&mut self, _: &mut Window, cx: &mut Context<Self>) {
         self.hide_hovered_link(cx);
     }
 
-    fn is_dirty(&self, cx: &AppContext) -> bool {
+    fn is_dirty(&self, cx: &App) -> bool {
         self.buffer().read(cx).read(cx).is_dirty()
     }
 
-    fn has_deleted_file(&self, cx: &AppContext) -> bool {
+    fn has_deleted_file(&self, cx: &App) -> bool {
         self.buffer().read(cx).read(cx).has_deleted_file()
     }
 
-    fn has_conflict(&self, cx: &AppContext) -> bool {
+    fn has_conflict(&self, cx: &App) -> bool {
         self.buffer().read(cx).read(cx).has_conflict()
     }
 
-    fn can_save(&self, cx: &AppContext) -> bool {
+    fn can_save(&self, cx: &App) -> bool {
         let buffer = &self.buffer().read(cx);
         if let Some(buffer) = buffer.as_singleton() {
             buffer.read(cx).project_path(cx).is_some()
@@ -739,8 +763,9 @@ impl Item for Editor {
     fn save(
         &mut self,
         format: bool,
-        project: Model<Project>,
-        cx: &mut ViewContext<Self>,
+        project: Entity<Project>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         self.report_editor_event("Editor Saved", None, cx);
         let buffers = self.buffer().clone().read(cx).all_buffers();
@@ -748,13 +773,14 @@ impl Item for Editor {
             .into_iter()
             .map(|handle| handle.read(cx).base_buffer().unwrap_or(handle.clone()))
             .collect::<HashSet<_>>();
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             if format {
-                this.update(&mut cx, |editor, cx| {
+                this.update_in(&mut cx, |editor, window, cx| {
                     editor.perform_format(
                         project.clone(),
                         FormatTrigger::Save,
                         FormatTarget::Buffers,
+                        window,
                         cx,
                     )
                 })?
@@ -800,9 +826,10 @@ impl Item for Editor {
 
     fn save_as(
         &mut self,
-        project: Model<Project>,
+        project: Entity<Project>,
         path: ProjectPath,
-        cx: &mut ViewContext<Self>,
+        _: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let buffer = self
             .buffer()
@@ -819,12 +846,17 @@ impl Item for Editor {
         project.update(cx, |project, cx| project.save_buffer_as(buffer, path, cx))
     }
 
-    fn reload(&mut self, project: Model<Project>, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
+    fn reload(
+        &mut self,
+        project: Entity<Project>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Task<Result<()>> {
         let buffer = self.buffer().clone();
         let buffers = self.buffer.read(cx).all_buffers();
         let reload_buffers =
             project.update(cx, |project, cx| project.reload_buffers(buffers, true, cx));
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let transaction = reload_buffers.log_err().await;
             this.update(&mut cx, |editor, cx| {
                 editor.request_autoscroll(Autoscroll::fit(), cx)
@@ -842,15 +874,15 @@ impl Item for Editor {
         })
     }
 
-    fn as_searchable(&self, handle: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
+    fn as_searchable(&self, handle: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {
         Some(Box::new(handle.clone()))
     }
 
-    fn pixel_position_of_cursor(&self, _: &AppContext) -> Option<gpui::Point<Pixels>> {
+    fn pixel_position_of_cursor(&self, _: &App) -> Option<gpui::Point<Pixels>> {
         self.pixel_position_of_newest_cursor
     }
 
-    fn breadcrumb_location(&self, _: &AppContext) -> ToolbarItemLocation {
+    fn breadcrumb_location(&self, _: &App) -> ToolbarItemLocation {
         if self.show_breadcrumbs {
             ToolbarItemLocation::PrimaryLeft
         } else {
@@ -858,7 +890,7 @@ impl Item for Editor {
         }
     }
 
-    fn breadcrumbs(&self, variant: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
+    fn breadcrumbs(&self, variant: &Theme, cx: &App) -> Option<Vec<BreadcrumbText>> {
         let cursor = self.selections.newest_anchor().head();
         let multibuffer = &self.buffer().read(cx);
         let (buffer_id, symbols) =
@@ -902,7 +934,12 @@ impl Item for Editor {
         Some(breadcrumbs)
     }
 
-    fn added_to_workspace(&mut self, workspace: &mut Workspace, _: &mut ViewContext<Self>) {
+    fn added_to_workspace(
+        &mut self,
+        workspace: &mut Workspace,
+        _window: &mut Window,
+        _: &mut Context<Self>,
+    ) {
         self.workspace = Some((workspace.weak_handle(), workspace.database_id()));
     }
 
@@ -940,7 +977,7 @@ impl Item for Editor {
         }
     }
 
-    fn preserve_preview(&self, cx: &AppContext) -> bool {
+    fn preserve_preview(&self, cx: &App) -> bool {
         self.buffer.read(cx).preserve_preview(cx)
     }
 }
@@ -953,18 +990,20 @@ impl SerializableItem for Editor {
     fn cleanup(
         workspace_id: WorkspaceId,
         alive_items: Vec<ItemId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<()>> {
-        cx.spawn(|_| DB.delete_unloaded_items(workspace_id, alive_items))
+        window.spawn(cx, |_| DB.delete_unloaded_items(workspace_id, alive_items))
     }
 
     fn deserialize(
-        project: Model<Project>,
-        workspace: WeakView<Workspace>,
+        project: Entity<Project>,
+        workspace: WeakEntity<Workspace>,
         workspace_id: workspace::WorkspaceId,
         item_id: ItemId,
-        cx: &mut WindowContext,
-    ) -> Task<Result<View<Self>>> {
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Task<Result<Entity<Self>>> {
         let serialized_editor = match DB
             .get_serialized_editor(item_id, workspace_id)
             .context("Failed to query editor state")
@@ -998,7 +1037,7 @@ impl SerializableItem for Editor {
                 contents: Some(contents),
                 language,
                 ..
-            } => cx.spawn(|mut cx| {
+            } => window.spawn(cx, |mut cx| {
                 let project = project.clone();
                 async move {
                     let language = if let Some(language_name) = language {
@@ -1028,11 +1067,11 @@ impl SerializableItem for Editor {
                         buffer.set_text(contents, cx);
                     })?;
 
-                    cx.update(|cx| {
-                        cx.new_view(|cx| {
-                            let mut editor = Editor::for_buffer(buffer, Some(project), cx);
+                    cx.update(|window, cx| {
+                        cx.new(|cx| {
+                            let mut editor = Editor::for_buffer(buffer, Some(project), window, cx);
 
-                            editor.read_scroll_position_from_db(item_id, workspace_id, cx);
+                            editor.read_scroll_position_from_db(item_id, workspace_id, window, cx);
                             editor
                         })
                     })
@@ -1055,7 +1094,7 @@ impl SerializableItem for Editor {
 
                 match project_item {
                     Some(project_item) => {
-                        cx.spawn(|mut cx| async move {
+                        window.spawn(cx, |mut cx| async move {
                             let (_, project_item) = project_item.await?;
                             let buffer = project_item.downcast::<Buffer>().map_err(|_| {
                                 anyhow!("Project item at stored path was not a buffer")
@@ -1082,11 +1121,17 @@ impl SerializableItem for Editor {
                                 })?;
                             }
 
-                            cx.update(|cx| {
-                                cx.new_view(|cx| {
-                                    let mut editor = Editor::for_buffer(buffer, Some(project), cx);
-
-                                    editor.read_scroll_position_from_db(item_id, workspace_id, cx);
+                            cx.update(|window, cx| {
+                                cx.new(|cx| {
+                                    let mut editor =
+                                        Editor::for_buffer(buffer, Some(project), window, cx);
+
+                                    editor.read_scroll_position_from_db(
+                                        item_id,
+                                        workspace_id,
+                                        window,
+                                        cx,
+                                    );
                                     editor
                                 })
                             })
@@ -1094,12 +1139,12 @@ impl SerializableItem for Editor {
                     }
                     None => {
                         let open_by_abs_path = workspace.update(cx, |workspace, cx| {
-                            workspace.open_abs_path(abs_path.clone(), false, cx)
+                            workspace.open_abs_path(abs_path.clone(), false, window, cx)
                         });
-                        cx.spawn(|mut cx| async move {
+                        window.spawn(cx, |mut cx| async move {
                             let editor = open_by_abs_path?.await?.downcast::<Editor>().with_context(|| format!("Failed to downcast to Editor after opening abs path {abs_path:?}"))?;
-                            editor.update(&mut cx, |editor, cx| {
-                                editor.read_scroll_position_from_db(item_id, workspace_id, cx);
+                            editor.update_in(&mut cx, |editor, window, cx| {
+                                editor.read_scroll_position_from_db(item_id, workspace_id, window, cx);
                             })?;
                             Ok(editor)
                         })
@@ -1119,7 +1164,8 @@ impl SerializableItem for Editor {
         workspace: &mut Workspace,
         item_id: ItemId,
         closing: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<Task<Result<()>>> {
         let mut serialize_dirty_buffers = self.serialize_dirty_buffers;
 
@@ -1156,7 +1202,7 @@ impl SerializableItem for Editor {
 
         let snapshot = buffer.read(cx).snapshot();
 
-        Some(cx.spawn(|_this, cx| async move {
+        Some(cx.spawn_in(window, |_this, cx| async move {
             cx.background_executor()
                 .spawn(async move {
                     let (contents, language) = if serialize_dirty_buffers && is_dirty {
@@ -1173,7 +1219,6 @@ impl SerializableItem for Editor {
                         language,
                         mtime,
                     };
-
                     DB.save_serialized_editor(item_id, workspace_id, editor)
                         .await
                         .context("failed to save serialized editor")
@@ -1197,11 +1242,12 @@ impl ProjectItem for Editor {
     type Item = Buffer;
 
     fn for_project_item(
-        project: Model<Project>,
-        buffer: Model<Buffer>,
-        cx: &mut ViewContext<Self>,
+        project: Entity<Project>,
+        buffer: Entity<Buffer>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
-        Self::for_buffer(buffer, Some(project), cx)
+        Self::for_buffer(buffer, Some(project), window, cx)
     }
 }
 
@@ -1211,7 +1257,7 @@ pub(crate) enum BufferSearchHighlights {}
 impl SearchableItem for Editor {
     type Match = Range<Anchor>;
 
-    fn get_matches(&self, _: &mut WindowContext) -> Vec<Range<Anchor>> {
+    fn get_matches(&self, _window: &mut Window, _: &mut App) -> Vec<Range<Anchor>> {
         self.background_highlights
             .get(&TypeId::of::<BufferSearchHighlights>())
             .map_or(Vec::new(), |(_color, ranges)| {
@@ -1219,7 +1265,7 @@ impl SearchableItem for Editor {
             })
     }
 
-    fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
+    fn clear_matches(&mut self, _: &mut Window, cx: &mut Context<Self>) {
         if self
             .clear_background_highlights::<BufferSearchHighlights>(cx)
             .is_some()
@@ -1228,7 +1274,12 @@ impl SearchableItem for Editor {
         }
     }
 
-    fn update_matches(&mut self, matches: &[Range<Anchor>], cx: &mut ViewContext<Self>) {
+    fn update_matches(
+        &mut self,
+        matches: &[Range<Anchor>],
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let existing_range = self
             .background_highlights
             .get(&TypeId::of::<BufferSearchHighlights>())
@@ -1248,7 +1299,12 @@ impl SearchableItem for Editor {
         self.has_background_highlights::<SearchWithinRange>()
     }
 
-    fn toggle_filtered_search_ranges(&mut self, enabled: bool, cx: &mut ViewContext<Self>) {
+    fn toggle_filtered_search_ranges(
+        &mut self,
+        enabled: bool,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if self.has_filtered_search_ranges() {
             self.previous_search_ranges = self
                 .clear_background_highlights::<SearchWithinRange>(cx)
@@ -1267,9 +1323,9 @@ impl SearchableItem for Editor {
         }
     }
 
-    fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
+    fn query_suggestion(&mut self, window: &mut Window, cx: &mut Context<Self>) -> String {
         let setting = EditorSettings::get_global(cx).seed_search_query_from_cursor;
-        let snapshot = &self.snapshot(cx).buffer_snapshot;
+        let snapshot = &self.snapshot(window, cx).buffer_snapshot;
         let selection = self.selections.newest::<usize>(cx);
 
         match setting {
@@ -1302,28 +1358,35 @@ impl SearchableItem for Editor {
         &mut self,
         index: usize,
         matches: &[Range<Anchor>],
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.unfold_ranges(&[matches[index].clone()], false, true, cx);
         let range = self.range_for_match(&matches[index]);
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
+        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
             s.select_ranges([range]);
         })
     }
 
-    fn select_matches(&mut self, matches: &[Self::Match], cx: &mut ViewContext<Self>) {
+    fn select_matches(
+        &mut self,
+        matches: &[Self::Match],
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.unfold_ranges(matches, false, false, cx);
         let mut ranges = Vec::new();
         for m in matches {
             ranges.push(self.range_for_match(m))
         }
-        self.change_selections(None, cx, |s| s.select_ranges(ranges));
+        self.change_selections(None, window, cx, |s| s.select_ranges(ranges));
     }
     fn replace(
         &mut self,
         identifier: &Self::Match,
         query: &SearchQuery,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let text = self.buffer.read(cx);
         let text = text.snapshot(cx);
@@ -1336,7 +1399,7 @@ impl SearchableItem for Editor {
         };
 
         if let Some(replacement) = query.replacement_for(&text) {
-            self.transact(cx, |this, cx| {
+            self.transact(window, cx, |this, _, cx| {
                 this.edit([(identifier.clone(), Arc::from(&*replacement))], cx);
             });
         }
@@ -1345,7 +1408,8 @@ impl SearchableItem for Editor {
         &mut self,
         matches: &mut dyn Iterator<Item = &Self::Match>,
         query: &SearchQuery,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let text = self.buffer.read(cx);
         let text = text.snapshot(cx);
@@ -1365,7 +1429,7 @@ impl SearchableItem for Editor {
         }
 
         if !edits.is_empty() {
-            self.transact(cx, |this, cx| {
+            self.transact(window, cx, |this, _, cx| {
                 this.edit(edits, cx);
             });
         }
@@ -1376,7 +1440,8 @@ impl SearchableItem for Editor {
         current_index: usize,
         direction: Direction,
         count: usize,
-        cx: &mut ViewContext<Self>,
+        _: &mut Window,
+        cx: &mut Context<Self>,
     ) -> usize {
         let buffer = self.buffer().read(cx).snapshot(cx);
         let current_index_position = if self.selections.disjoint_anchors().len() == 1 {
@@ -1422,7 +1487,8 @@ impl SearchableItem for Editor {
     fn find_matches(
         &mut self,
         query: Arc<project::search::SearchQuery>,
-        cx: &mut ViewContext<Self>,
+        _: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<Vec<Range<Anchor>>> {
         let buffer = self.buffer().read(cx).snapshot(cx);
         let search_within_ranges = self
@@ -1470,7 +1536,8 @@ impl SearchableItem for Editor {
     fn active_match_index(
         &mut self,
         matches: &[Range<Anchor>],
-        cx: &mut ViewContext<Self>,
+        _: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<usize> {
         active_match_index(
             matches,
@@ -1479,7 +1546,7 @@ impl SearchableItem for Editor {
         )
     }
 
-    fn search_bar_visibility_changed(&mut self, _visible: bool, _cx: &mut ViewContext<Self>) {
+    fn search_bar_visibility_changed(&mut self, _: bool, _: &mut Window, _: &mut Context<Self>) {
         self.expect_bounds_change = self.last_bounds;
     }
 }
@@ -1550,10 +1617,10 @@ pub fn entry_git_aware_label_color(git_status: GitSummary, ignored: bool, select
 }
 
 fn path_for_buffer<'a>(
-    buffer: &Model<MultiBuffer>,
+    buffer: &Entity<MultiBuffer>,
     height: usize,
     include_filename: bool,
-    cx: &'a AppContext,
+    cx: &'a App,
 ) -> Option<Cow<'a, Path>> {
     let file = buffer.read(cx).as_singleton()?.read(cx).file()?;
     path_for_file(file.as_ref(), height, include_filename, cx)
@@ -1563,7 +1630,7 @@ fn path_for_file<'a>(
     file: &'a dyn language::File,
     mut height: usize,
     include_filename: bool,
-    cx: &'a AppContext,
+    cx: &'a App,
 ) -> Option<Cow<'a, Path>> {
     // Ensure we always render at least the filename.
     height += 1;
@@ -1604,13 +1671,13 @@ mod tests {
 
     use super::*;
     use fs::MTime;
-    use gpui::{AppContext, VisualTestContext};
+    use gpui::{App, VisualTestContext};
     use language::{LanguageMatcher, TestFile};
     use project::FakeFs;
     use std::path::{Path, PathBuf};
 
     #[gpui::test]
-    fn test_path_for_file(cx: &mut AppContext) {
+    fn test_path_for_file(cx: &mut App) {
         let file = TestFile {
             path: Path::new("").into(),
             root_name: String::new(),
@@ -1621,12 +1688,12 @@ mod tests {
     async fn deserialize_editor(
         item_id: ItemId,
         workspace_id: WorkspaceId,
-        workspace: View<Workspace>,
-        project: Model<Project>,
+        workspace: Entity<Workspace>,
+        project: Entity<Project>,
         cx: &mut VisualTestContext,
-    ) -> View<Editor> {
+    ) -> Entity<Editor> {
         workspace
-            .update(cx, |workspace, cx| {
+            .update_in(cx, |workspace, window, cx| {
                 let pane = workspace.active_pane();
                 pane.update(cx, |_, cx| {
                     Editor::deserialize(
@@ -1634,6 +1701,7 @@ mod tests {
                         workspace.weak_handle(),
                         workspace_id,
                         item_id,
+                        window,
                         cx,
                     )
                 })
@@ -1666,7 +1734,8 @@ mod tests {
         // Test case 1: Deserialize with path and contents
         {
             let project = Project::test(fs.clone(), ["/file.rs".as_ref()], cx).await;
-            let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+            let (workspace, cx) =
+                cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
             let workspace_id = workspace::WORKSPACE_DB.next_id().await.unwrap();
             let item_id = 1234 as ItemId;
             let mtime = fs
@@ -1702,7 +1771,8 @@ mod tests {
         // Test case 2: Deserialize with only path
         {
             let project = Project::test(fs.clone(), ["/file.rs".as_ref()], cx).await;
-            let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+            let (workspace, cx) =
+                cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
             let workspace_id = workspace::WORKSPACE_DB.next_id().await.unwrap();
 
@@ -1737,7 +1807,8 @@ mod tests {
             // Add Rust to the language, so that we can restore the language of the buffer
             project.update(cx, |project, _| project.languages().add(rust_language()));
 
-            let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+            let (workspace, cx) =
+                cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
             let workspace_id = workspace::WORKSPACE_DB.next_id().await.unwrap();
 
@@ -1772,7 +1843,8 @@ mod tests {
         // Test case 4: Deserialize with path, content, and old mtime
         {
             let project = Project::test(fs.clone(), ["/file.rs".as_ref()], cx).await;
-            let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+            let (workspace, cx) =
+                cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
             let workspace_id = workspace::WORKSPACE_DB.next_id().await.unwrap();
 

crates/editor/src/linked_editing_ranges.rs 🔗

@@ -1,9 +1,8 @@
-use std::{ops::Range, time::Duration};
-
 use collections::HashMap;
+use gpui::{Context, Window};
 use itertools::Itertools;
+use std::{ops::Range, time::Duration};
 use text::{AnchorRangeExt, BufferId, ToPoint};
-use ui::ViewContext;
 use util::ResultExt;
 
 use crate::Editor;
@@ -42,14 +41,15 @@ const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
 // TODO do not refresh anything at all, if the settings/capabilities do not have it enabled.
 pub(super) fn refresh_linked_ranges(
     editor: &mut Editor,
-    cx: &mut ViewContext<Editor>,
+    window: &mut Window,
+    cx: &mut Context<Editor>,
 ) -> Option<()> {
     if editor.pending_rename.is_some() {
         return None;
     }
     let project = editor.project.as_ref()?.downgrade();
 
-    editor.linked_editing_range_task = Some(cx.spawn(|editor, mut cx| async move {
+    editor.linked_editing_range_task = Some(cx.spawn_in(window, |editor, mut cx| async move {
         cx.background_executor().timer(UPDATE_DEBOUNCE).await;
 
         let mut applicable_selections = Vec::new();

crates/editor/src/lsp_ext.rs 🔗

@@ -3,7 +3,7 @@ use std::sync::Arc;
 
 use crate::Editor;
 use collections::HashMap;
-use gpui::{Model, WindowContext};
+use gpui::{App, Entity};
 use language::Buffer;
 use language::Language;
 use lsp::LanguageServerId;
@@ -11,10 +11,10 @@ use multi_buffer::Anchor;
 
 pub(crate) fn find_specific_language_server_in_selection<F>(
     editor: &Editor,
-    cx: &WindowContext,
+    cx: &mut App,
     filter_language: F,
     language_server_name: &str,
-) -> Option<(Anchor, Arc<Language>, LanguageServerId, Model<Buffer>)>
+) -> Option<(Anchor, Arc<Language>, LanguageServerId, Entity<Buffer>)>
 where
     F: Fn(&Language) -> bool,
 {

crates/editor/src/mouse_context_menu.rs 🔗

@@ -6,7 +6,7 @@ use crate::{
     SelectMode, ToDisplayPoint, ToggleCodeActions,
 };
 use gpui::prelude::FluentBuilder;
-use gpui::{DismissEvent, Pixels, Point, Subscription, View, ViewContext};
+use gpui::{Context, DismissEvent, Entity, Focusable as _, Pixels, Point, Subscription, Window};
 use std::ops::Range;
 use text::PointUtf16;
 use workspace::OpenInTerminal;
@@ -26,7 +26,7 @@ pub enum MenuPosition {
 
 pub struct MouseContextMenu {
     pub(crate) position: MenuPosition,
-    pub(crate) context_menu: View<ui::ContextMenu>,
+    pub(crate) context_menu: Entity<ui::ContextMenu>,
     _subscription: Subscription,
 }
 
@@ -44,37 +44,45 @@ impl MouseContextMenu {
         editor: &mut Editor,
         source: multi_buffer::Anchor,
         position: Point<Pixels>,
-        context_menu: View<ui::ContextMenu>,
-        cx: &mut ViewContext<Editor>,
+        context_menu: Entity<ui::ContextMenu>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) -> Option<Self> {
-        let editor_snapshot = editor.snapshot(cx);
+        let editor_snapshot = editor.snapshot(window, cx);
         let content_origin = editor.last_bounds?.origin
             + Point {
                 x: editor.gutter_dimensions.width,
                 y: Pixels(0.0),
             };
-        let source_position = editor.to_pixel_point(source, &editor_snapshot, cx)?;
+        let source_position = editor.to_pixel_point(source, &editor_snapshot, window)?;
         let menu_position = MenuPosition::PinnedToEditor {
             source,
             offset: position - (source_position + content_origin),
         };
-        return Some(MouseContextMenu::new(menu_position, context_menu, cx));
+        return Some(MouseContextMenu::new(
+            menu_position,
+            context_menu,
+            window,
+            cx,
+        ));
     }
 
     pub(crate) fn new(
         position: MenuPosition,
-        context_menu: View<ui::ContextMenu>,
-        cx: &mut ViewContext<Editor>,
+        context_menu: Entity<ui::ContextMenu>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) -> Self {
         let context_menu_focus = context_menu.focus_handle(cx);
-        cx.focus(&context_menu_focus);
+        window.focus(&context_menu_focus);
 
-        let _subscription = cx.subscribe(
+        let _subscription = cx.subscribe_in(
             &context_menu,
-            move |editor, _, _event: &DismissEvent, cx| {
+            window,
+            move |editor, _, _event: &DismissEvent, window, cx| {
                 editor.mouse_context_menu.take();
-                if context_menu_focus.contains_focused(cx) {
-                    editor.focus(cx);
+                if context_menu_focus.contains_focused(window, cx) {
+                    window.focus(&editor.focus_handle(cx));
                 }
             },
         );
@@ -106,10 +114,11 @@ pub fn deploy_context_menu(
     editor: &mut Editor,
     position: Option<Point<Pixels>>,
     point: DisplayPoint,
-    cx: &mut ViewContext<Editor>,
+    window: &mut Window,
+    cx: &mut Context<Editor>,
 ) {
-    if !editor.is_focused(cx) {
-        editor.focus(cx);
+    if !editor.is_focused(window) {
+        window.focus(&editor.focus_handle(cx));
     }
 
     // Don't show context menu for inline editors
@@ -120,7 +129,7 @@ pub fn deploy_context_menu(
     let display_map = editor.selections.display_map(cx);
     let source_anchor = display_map.display_point_to_anchor(point, text::Bias::Right);
     let context_menu = if let Some(custom) = editor.custom_context_menu.take() {
-        let menu = custom(editor, point, cx);
+        let menu = custom(editor, point, window, cx);
         editor.custom_context_menu = Some(custom);
         let Some(menu) = menu else {
             return;
@@ -133,17 +142,17 @@ pub fn deploy_context_menu(
         }
 
         let display_map = editor.selections.display_map(cx);
-        let buffer = &editor.snapshot(cx).buffer_snapshot;
+        let buffer = &editor.snapshot(window, cx).buffer_snapshot;
         let anchor = buffer.anchor_before(point.to_point(&display_map));
         if !display_ranges(&display_map, &editor.selections).any(|r| r.contains(&point)) {
             // Move the cursor to the clicked location so that dispatched actions make sense
-            editor.change_selections(None, cx, |s| {
+            editor.change_selections(None, window, cx, |s| {
                 s.clear_disjoint();
                 s.set_pending_anchor_range(anchor..anchor, SelectMode::Character);
             });
         }
 
-        let focus = cx.focused();
+        let focus = window.focused(cx);
         let has_reveal_target = editor.target_file(cx).is_some();
         let reveal_in_finder_label = if cfg!(target_os = "macos") {
             "Reveal in Finder"
@@ -161,7 +170,7 @@ pub fn deploy_context_menu(
             })
         });
 
-        ui::ContextMenu::build(cx, |menu, _cx| {
+        ui::ContextMenu::build(window, cx, |menu, _window, _cx| {
             let builder = menu
                 .on_blur_subscription(Subscription::new(|| {}))
                 .action("Go to Definition", Box::new(GoToDefinition))
@@ -211,15 +220,25 @@ pub fn deploy_context_menu(
     };
 
     editor.mouse_context_menu = match position {
-        Some(position) => {
-            MouseContextMenu::pinned_to_editor(editor, source_anchor, position, context_menu, cx)
-        }
+        Some(position) => MouseContextMenu::pinned_to_editor(
+            editor,
+            source_anchor,
+            position,
+            context_menu,
+            window,
+            cx,
+        ),
         None => {
             let menu_position = MenuPosition::PinnedToEditor {
                 source: source_anchor,
-                offset: editor.character_size(cx),
+                offset: editor.character_size(window),
             };
-            Some(MouseContextMenu::new(menu_position, context_menu, cx))
+            Some(MouseContextMenu::new(
+                menu_position,
+                context_menu,
+                window,
+                cx,
+            ))
         }
     };
     cx.notify();
@@ -254,9 +273,9 @@ mod tests {
                 do_wˇork();
             }
         "});
-        cx.editor(|editor, _app| assert!(editor.mouse_context_menu.is_none()));
-        cx.update_editor(|editor, cx| {
-            deploy_context_menu(editor, Some(Default::default()), point, cx)
+        cx.editor(|editor, _window, _app| assert!(editor.mouse_context_menu.is_none()));
+        cx.update_editor(|editor, window, cx| {
+            deploy_context_menu(editor, Some(Default::default()), point, window, cx)
         });
 
         cx.assert_editor_state(indoc! {"
@@ -264,6 +283,6 @@ mod tests {
                 do_wˇork();
             }
         "});
-        cx.editor(|editor, _app| assert!(editor.mouse_context_menu.is_some()));
+        cx.editor(|editor, _window, _app| assert!(editor.mouse_context_menu.is_some()));
     }
 }

crates/editor/src/movement.rs 🔗

@@ -703,17 +703,17 @@ mod tests {
         test::{editor_test_context::EditorTestContext, marked_display_snapshot},
         Buffer, DisplayMap, DisplayRow, ExcerptRange, FoldPlaceholder, InlayId, MultiBuffer,
     };
-    use gpui::{font, px, Context as _};
+    use gpui::{font, px, AppContext as _};
     use language::Capability;
     use project::Project;
     use settings::SettingsStore;
     use util::post_inc;
 
     #[gpui::test]
-    fn test_previous_word_start(cx: &mut gpui::AppContext) {
+    fn test_previous_word_start(cx: &mut gpui::App) {
         init_test(cx);
 
-        fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
+        fn assert(marked_text: &str, cx: &mut gpui::App) {
             let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
             assert_eq!(
                 previous_word_start(&snapshot, display_points[1]),
@@ -738,10 +738,10 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_previous_subword_start(cx: &mut gpui::AppContext) {
+    fn test_previous_subword_start(cx: &mut gpui::App) {
         init_test(cx);
 
-        fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
+        fn assert(marked_text: &str, cx: &mut gpui::App) {
             let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
             assert_eq!(
                 previous_subword_start(&snapshot, display_points[1]),
@@ -773,12 +773,12 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_find_preceding_boundary(cx: &mut gpui::AppContext) {
+    fn test_find_preceding_boundary(cx: &mut gpui::App) {
         init_test(cx);
 
         fn assert(
             marked_text: &str,
-            cx: &mut gpui::AppContext,
+            cx: &mut gpui::App,
             is_boundary: impl FnMut(char, char) -> bool,
         ) {
             let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
@@ -811,7 +811,7 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_find_preceding_boundary_with_inlays(cx: &mut gpui::AppContext) {
+    fn test_find_preceding_boundary_with_inlays(cx: &mut gpui::App) {
         init_test(cx);
 
         let input_text = "abcdefghijklmnopqrstuvwxys";
@@ -820,7 +820,7 @@ mod tests {
         let buffer = MultiBuffer::build_simple(input_text, cx);
         let buffer_snapshot = buffer.read(cx).snapshot(cx);
 
-        let display_map = cx.new_model(|cx| {
+        let display_map = cx.new(|cx| {
             DisplayMap::new(
                 buffer,
                 font,
@@ -884,10 +884,10 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_next_word_end(cx: &mut gpui::AppContext) {
+    fn test_next_word_end(cx: &mut gpui::App) {
         init_test(cx);
 
-        fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
+        fn assert(marked_text: &str, cx: &mut gpui::App) {
             let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
             assert_eq!(
                 next_word_end(&snapshot, display_points[0]),
@@ -909,10 +909,10 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_next_subword_end(cx: &mut gpui::AppContext) {
+    fn test_next_subword_end(cx: &mut gpui::App) {
         init_test(cx);
 
-        fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
+        fn assert(marked_text: &str, cx: &mut gpui::App) {
             let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
             assert_eq!(
                 next_subword_end(&snapshot, display_points[0]),
@@ -943,12 +943,12 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_find_boundary(cx: &mut gpui::AppContext) {
+    fn test_find_boundary(cx: &mut gpui::App) {
         init_test(cx);
 
         fn assert(
             marked_text: &str,
-            cx: &mut gpui::AppContext,
+            cx: &mut gpui::App,
             is_boundary: impl FnMut(char, char) -> bool,
         ) {
             let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
@@ -981,10 +981,10 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_surrounding_word(cx: &mut gpui::AppContext) {
+    fn test_surrounding_word(cx: &mut gpui::App) {
         init_test(cx);
 
-        fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
+        fn assert(marked_text: &str, cx: &mut gpui::App) {
             let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
             assert_eq!(
                 surrounding_word(&snapshot, display_points[1]),
@@ -1013,14 +1013,13 @@ mod tests {
         let mut cx = EditorTestContext::new(cx).await;
         let editor = cx.editor.clone();
         let window = cx.window;
-        _ = cx.update_window(window, |_, cx| {
-            let text_layout_details =
-                editor.update(cx, |editor, cx| editor.text_layout_details(cx));
+        _ = cx.update_window(window, |_, window, cx| {
+            let text_layout_details = editor.read(cx).text_layout_details(window);
 
             let font = font("Helvetica");
 
-            let buffer = cx.new_model(|cx| Buffer::local("abc\ndefg\nhijkl\nmn", cx));
-            let multibuffer = cx.new_model(|cx| {
+            let buffer = cx.new(|cx| Buffer::local("abc\ndefg\nhijkl\nmn", cx));
+            let multibuffer = cx.new(|cx| {
                 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
                 multibuffer.push_excerpts(
                     buffer.clone(),
@@ -1038,7 +1037,7 @@ mod tests {
                 );
                 multibuffer
             });
-            let display_map = cx.new_model(|cx| {
+            let display_map = cx.new(|cx| {
                 DisplayMap::new(
                     multibuffer,
                     font,
@@ -1182,7 +1181,7 @@ mod tests {
         });
     }
 
-    fn init_test(cx: &mut gpui::AppContext) {
+    fn init_test(cx: &mut gpui::App) {
         let settings_store = SettingsStore::test(cx);
         cx.set_global(settings_store);
         theme::init(theme::LoadThemes::JustBase, cx);

crates/editor/src/proposed_changes_editor.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{ApplyAllDiffHunks, Editor, EditorEvent, SemanticsProvider};
 use collections::HashSet;
 use futures::{channel::mpsc, future::join_all};
-use gpui::{AppContext, EventEmitter, FocusableView, Model, Render, Subscription, Task, View};
+use gpui::{App, Entity, EventEmitter, Focusable, Render, Subscription, Task};
 use language::{Buffer, BufferEvent, Capability};
 use multi_buffer::{ExcerptRange, MultiBuffer};
 use project::{buffer_store::BufferChangeSet, Project};
@@ -15,8 +15,8 @@ use workspace::{
 };
 
 pub struct ProposedChangesEditor {
-    editor: View<Editor>,
-    multibuffer: Model<MultiBuffer>,
+    editor: Entity<Editor>,
+    multibuffer: Entity<MultiBuffer>,
     title: SharedString,
     buffer_entries: Vec<BufferEntry>,
     _recalculate_diffs_task: Task<Option<()>>,
@@ -24,22 +24,22 @@ pub struct ProposedChangesEditor {
 }
 
 pub struct ProposedChangeLocation<T> {
-    pub buffer: Model<Buffer>,
+    pub buffer: Entity<Buffer>,
     pub ranges: Vec<Range<T>>,
 }
 
 struct BufferEntry {
-    base: Model<Buffer>,
-    branch: Model<Buffer>,
+    base: Entity<Buffer>,
+    branch: Entity<Buffer>,
     _subscription: Subscription,
 }
 
 pub struct ProposedChangesEditorToolbar {
-    current_editor: Option<View<ProposedChangesEditor>>,
+    current_editor: Option<Entity<ProposedChangesEditor>>,
 }
 
 struct RecalculateDiff {
-    buffer: Model<Buffer>,
+    buffer: Entity<Buffer>,
     debounce: bool,
 }
 
@@ -53,14 +53,16 @@ impl ProposedChangesEditor {
     pub fn new<T: ToOffset>(
         title: impl Into<SharedString>,
         locations: Vec<ProposedChangeLocation<T>>,
-        project: Option<Model<Project>>,
-        cx: &mut ViewContext<Self>,
+        project: Option<Entity<Project>>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
-        let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
+        let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
         let (recalculate_diffs_tx, mut recalculate_diffs_rx) = mpsc::unbounded();
         let mut this = Self {
-            editor: cx.new_view(|cx| {
-                let mut editor = Editor::for_multibuffer(multibuffer.clone(), project, true, cx);
+            editor: cx.new(|cx| {
+                let mut editor =
+                    Editor::for_multibuffer(multibuffer.clone(), project, true, window, cx);
                 editor.set_expand_all_diff_hunks(cx);
                 editor.set_completion_provider(None);
                 editor.clear_code_action_providers();
@@ -75,7 +77,7 @@ impl ProposedChangesEditor {
             title: title.into(),
             buffer_entries: Vec::new(),
             recalculate_diffs_tx,
-            _recalculate_diffs_task: cx.spawn(|this, mut cx| async move {
+            _recalculate_diffs_task: cx.spawn_in(window, |this, mut cx| async move {
                 let mut buffers_to_diff = HashSet::default();
                 while let Some(mut recalculate_diff) = recalculate_diffs_rx.next().await {
                     buffers_to_diff.insert(recalculate_diff.buffer);
@@ -125,11 +127,11 @@ impl ProposedChangesEditor {
                 None
             }),
         };
-        this.reset_locations(locations, cx);
+        this.reset_locations(locations, window, cx);
         this
     }
 
-    pub fn branch_buffer_for_base(&self, base_buffer: &Model<Buffer>) -> Option<Model<Buffer>> {
+    pub fn branch_buffer_for_base(&self, base_buffer: &Entity<Buffer>) -> Option<Entity<Buffer>> {
         self.buffer_entries.iter().find_map(|entry| {
             if &entry.base == base_buffer {
                 Some(entry.branch.clone())
@@ -139,7 +141,7 @@ impl ProposedChangesEditor {
         })
     }
 
-    pub fn set_title(&mut self, title: SharedString, cx: &mut ViewContext<Self>) {
+    pub fn set_title(&mut self, title: SharedString, cx: &mut Context<Self>) {
         self.title = title;
         cx.notify();
     }
@@ -147,7 +149,8 @@ impl ProposedChangesEditor {
     pub fn reset_locations<T: ToOffset>(
         &mut self,
         locations: Vec<ProposedChangeLocation<T>>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         // Undo all branch changes
         for entry in &self.buffer_entries {
@@ -186,7 +189,7 @@ impl ProposedChangesEditor {
                 buffer_entries.push(entry);
             } else {
                 branch_buffer = location.buffer.update(cx, |buffer, cx| buffer.branch(cx));
-                new_change_sets.push(cx.new_model(|cx| {
+                new_change_sets.push(cx.new(|cx| {
                     let mut change_set = BufferChangeSet::new(&branch_buffer, cx);
                     let _ = change_set.set_base_text(
                         location.buffer.read(cx).text(),
@@ -216,7 +219,7 @@ impl ProposedChangesEditor {
 
         self.buffer_entries = buffer_entries;
         self.editor.update(cx, |editor, cx| {
-            editor.change_selections(None, cx, |selections| selections.refresh());
+            editor.change_selections(None, window, cx, |selections| selections.refresh());
             editor.buffer.update(cx, |buffer, cx| {
                 for change_set in new_change_sets {
                     buffer.add_change_set(change_set, cx)
@@ -238,9 +241,9 @@ impl ProposedChangesEditor {
 
     fn on_buffer_event(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         event: &BufferEvent,
-        _cx: &mut ViewContext<Self>,
+        _cx: &mut Context<Self>,
     ) {
         match event {
             BufferEvent::Operation { .. } => {
@@ -265,7 +268,7 @@ impl ProposedChangesEditor {
 }
 
 impl Render for ProposedChangesEditor {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         div()
             .size_full()
             .key_context("ProposedChangesEditor")
@@ -273,8 +276,8 @@ impl Render for ProposedChangesEditor {
     }
 }
 
-impl FocusableView for ProposedChangesEditor {
-    fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
+impl Focusable for ProposedChangesEditor {
+    fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
         self.editor.focus_handle(cx)
     }
 }
@@ -284,23 +287,23 @@ impl EventEmitter<EditorEvent> for ProposedChangesEditor {}
 impl Item for ProposedChangesEditor {
     type Event = EditorEvent;
 
-    fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
+    fn tab_icon(&self, _window: &Window, _cx: &App) -> Option<Icon> {
         Some(Icon::new(IconName::Diff))
     }
 
-    fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
+    fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option<SharedString> {
         Some(self.title.clone())
     }
 
-    fn as_searchable(&self, _: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
+    fn as_searchable(&self, _: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {
         Some(Box::new(self.editor.clone()))
     }
 
     fn act_as_type<'a>(
         &'a self,
         type_id: TypeId,
-        self_handle: &'a View<Self>,
-        _: &'a AppContext,
+        self_handle: &'a Entity<Self>,
+        _: &'a App,
     ) -> Option<gpui::AnyView> {
         if type_id == TypeId::of::<Self>() {
             Some(self_handle.to_any())
@@ -311,43 +314,57 @@ impl Item for ProposedChangesEditor {
         }
     }
 
-    fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
+    fn added_to_workspace(
+        &mut self,
+        workspace: &mut Workspace,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.editor.update(cx, |editor, cx| {
-            Item::added_to_workspace(editor, workspace, cx)
+            Item::added_to_workspace(editor, workspace, window, cx)
         });
     }
 
-    fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
-        self.editor.update(cx, Item::deactivated);
+    fn deactivated(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        self.editor
+            .update(cx, |editor, cx| editor.deactivated(window, cx));
     }
 
-    fn navigate(&mut self, data: Box<dyn std::any::Any>, cx: &mut ViewContext<Self>) -> bool {
+    fn navigate(
+        &mut self,
+        data: Box<dyn std::any::Any>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> bool {
         self.editor
-            .update(cx, |editor, cx| Item::navigate(editor, data, cx))
+            .update(cx, |editor, cx| Item::navigate(editor, data, window, cx))
     }
 
     fn set_nav_history(
         &mut self,
         nav_history: workspace::ItemNavHistory,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.editor.update(cx, |editor, cx| {
-            Item::set_nav_history(editor, nav_history, cx)
+            Item::set_nav_history(editor, nav_history, window, cx)
         });
     }
 
-    fn can_save(&self, cx: &AppContext) -> bool {
+    fn can_save(&self, cx: &App) -> bool {
         self.editor.read(cx).can_save(cx)
     }
 
     fn save(
         &mut self,
         format: bool,
-        project: Model<Project>,
-        cx: &mut ViewContext<Self>,
+        project: Entity<Project>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<gpui::Result<()>> {
-        self.editor
-            .update(cx, |editor, cx| Item::save(editor, format, project, cx))
+        self.editor.update(cx, |editor, cx| {
+            Item::save(editor, format, project, window, cx)
+        })
     }
 }
 
@@ -368,17 +385,20 @@ impl ProposedChangesEditorToolbar {
 }
 
 impl Render for ProposedChangesEditorToolbar {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let button_like = ButtonLike::new("apply-changes").child(Label::new("Apply All"));
 
         match &self.current_editor {
             Some(editor) => {
                 let focus_handle = editor.focus_handle(cx);
-                let keybinding = KeyBinding::for_action_in(&ApplyAllDiffHunks, &focus_handle, cx)
-                    .map(|binding| binding.into_any_element());
+                let keybinding =
+                    KeyBinding::for_action_in(&ApplyAllDiffHunks, &focus_handle, window)
+                        .map(|binding| binding.into_any_element());
 
                 button_like.children(keybinding).on_click({
-                    move |_event, cx| focus_handle.dispatch_action(&ApplyAllDiffHunks, cx)
+                    move |_event, window, cx| {
+                        focus_handle.dispatch_action(&ApplyAllDiffHunks, window, cx)
+                    }
                 })
             }
             None => button_like.disabled(true),
@@ -392,7 +412,8 @@ impl ToolbarItemView for ProposedChangesEditorToolbar {
     fn set_active_pane_item(
         &mut self,
         active_pane_item: Option<&dyn workspace::ItemHandle>,
-        _cx: &mut ViewContext<Self>,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
     ) -> workspace::ToolbarItemLocation {
         self.current_editor =
             active_pane_item.and_then(|item| item.downcast::<ProposedChangesEditor>());
@@ -403,10 +424,10 @@ impl ToolbarItemView for ProposedChangesEditorToolbar {
 impl BranchBufferSemanticsProvider {
     fn to_base(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         positions: &[text::Anchor],
-        cx: &AppContext,
-    ) -> Option<Model<Buffer>> {
+        cx: &App,
+    ) -> Option<Entity<Buffer>> {
         let base_buffer = buffer.read(cx).base_buffer()?;
         let version = base_buffer.read(cx).version();
         if positions
@@ -422,9 +443,9 @@ impl BranchBufferSemanticsProvider {
 impl SemanticsProvider for BranchBufferSemanticsProvider {
     fn hover(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         position: text::Anchor,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Option<Task<Vec<project::Hover>>> {
         let buffer = self.to_base(buffer, &[position], cx)?;
         self.0.hover(&buffer, position, cx)
@@ -432,9 +453,9 @@ impl SemanticsProvider for BranchBufferSemanticsProvider {
 
     fn inlay_hints(
         &self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         range: Range<text::Anchor>,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Option<Task<anyhow::Result<Vec<project::InlayHint>>>> {
         let buffer = self.to_base(&buffer, &[range.start, range.end], cx)?;
         self.0.inlay_hints(buffer, range, cx)
@@ -443,15 +464,15 @@ impl SemanticsProvider for BranchBufferSemanticsProvider {
     fn resolve_inlay_hint(
         &self,
         hint: project::InlayHint,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         server_id: lsp::LanguageServerId,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Option<Task<anyhow::Result<project::InlayHint>>> {
         let buffer = self.to_base(&buffer, &[], cx)?;
         self.0.resolve_inlay_hint(hint, buffer, server_id, cx)
     }
 
-    fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool {
+    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &App) -> bool {
         if let Some(buffer) = self.to_base(&buffer, &[], cx) {
             self.0.supports_inlay_hints(&buffer, cx)
         } else {
@@ -461,9 +482,9 @@ impl SemanticsProvider for BranchBufferSemanticsProvider {
 
     fn document_highlights(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         position: text::Anchor,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Option<Task<gpui::Result<Vec<project::DocumentHighlight>>>> {
         let buffer = self.to_base(&buffer, &[position], cx)?;
         self.0.document_highlights(&buffer, position, cx)
@@ -471,10 +492,10 @@ impl SemanticsProvider for BranchBufferSemanticsProvider {
 
     fn definitions(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         position: text::Anchor,
         kind: crate::GotoDefinitionKind,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Option<Task<gpui::Result<Vec<project::LocationLink>>>> {
         let buffer = self.to_base(&buffer, &[position], cx)?;
         self.0.definitions(&buffer, position, kind, cx)
@@ -482,19 +503,19 @@ impl SemanticsProvider for BranchBufferSemanticsProvider {
 
     fn range_for_rename(
         &self,
-        _: &Model<Buffer>,
+        _: &Entity<Buffer>,
         _: text::Anchor,
-        _: &mut AppContext,
+        _: &mut App,
     ) -> Option<Task<gpui::Result<Option<Range<text::Anchor>>>>> {
         None
     }
 
     fn perform_rename(
         &self,
-        _: &Model<Buffer>,
+        _: &Entity<Buffer>,
         _: text::Anchor,
         _: String,
-        _: &mut AppContext,
+        _: &mut App,
     ) -> Option<Task<gpui::Result<project::ProjectTransaction>>> {
         None
     }

crates/editor/src/rust_analyzer_ext.rs 🔗

@@ -1,7 +1,7 @@
 use std::{fs, path::Path};
 
 use anyhow::Context as _;
-use gpui::{Context, View, ViewContext, VisualContext, WindowContext};
+use gpui::{App, AppContext as _, Context, Entity, Window};
 use language::Language;
 use multi_buffer::MultiBuffer;
 use project::lsp_ext_command::ExpandMacro;
@@ -18,22 +18,23 @@ fn is_rust_language(language: &Language) -> bool {
     language.name() == "Rust".into()
 }
 
-pub fn apply_related_actions(editor: &View<Editor>, cx: &mut WindowContext) {
+pub fn apply_related_actions(editor: &Entity<Editor>, window: &mut Window, cx: &mut App) {
     if editor
         .update(cx, |e, cx| {
             find_specific_language_server_in_selection(e, cx, is_rust_language, RUST_ANALYZER_NAME)
         })
         .is_some()
     {
-        register_action(editor, cx, expand_macro_recursively);
-        register_action(editor, cx, open_docs);
+        register_action(editor, window, expand_macro_recursively);
+        register_action(editor, window, open_docs);
     }
 }
 
 pub fn expand_macro_recursively(
     editor: &mut Editor,
     _: &ExpandMacroRecursively,
-    cx: &mut ViewContext<Editor>,
+    window: &mut Window,
+    cx: &mut Context<Editor>,
 ) {
     if editor.selections.count() == 0 {
         return;
@@ -67,7 +68,7 @@ pub fn expand_macro_recursively(
             cx,
         )
     });
-    cx.spawn(|_editor, mut cx| async move {
+    cx.spawn_in(window, |_editor, mut cx| async move {
         let macro_expansion = expand_macro_task.await.context("expand macro")?;
         if macro_expansion.is_empty() {
             log::info!("Empty macro expansion for position {position:?}");
@@ -77,20 +78,20 @@ pub fn expand_macro_recursively(
         let buffer = project
             .update(&mut cx, |project, cx| project.create_buffer(cx))?
             .await?;
-        workspace.update(&mut cx, |workspace, cx| {
+        workspace.update_in(&mut cx, |workspace, window, cx| {
             buffer.update(cx, |buffer, cx| {
                 buffer.edit([(0..0, macro_expansion.expansion)], None, cx);
                 buffer.set_language(Some(rust_language), cx)
             });
-            let multibuffer = cx.new_model(|cx| {
-                MultiBuffer::singleton(buffer, cx).with_title(macro_expansion.name)
-            });
+            let multibuffer =
+                cx.new(|cx| MultiBuffer::singleton(buffer, cx).with_title(macro_expansion.name));
             workspace.add_item_to_active_pane(
-                Box::new(
-                    cx.new_view(|cx| Editor::for_multibuffer(multibuffer, Some(project), true, cx)),
-                ),
+                Box::new(cx.new(|cx| {
+                    Editor::for_multibuffer(multibuffer, Some(project), true, window, cx)
+                })),
                 None,
                 true,
+                window,
                 cx,
             );
         })
@@ -98,7 +99,7 @@ pub fn expand_macro_recursively(
     .detach_and_log_err(cx);
 }
 
-pub fn open_docs(editor: &mut Editor, _: &OpenDocs, cx: &mut ViewContext<Editor>) {
+pub fn open_docs(editor: &mut Editor, _: &OpenDocs, window: &mut Window, cx: &mut Context<Editor>) {
     if editor.selections.count() == 0 {
         return;
     }
@@ -132,7 +133,7 @@ pub fn open_docs(editor: &mut Editor, _: &OpenDocs, cx: &mut ViewContext<Editor>
         )
     });
 
-    cx.spawn(|_editor, mut cx| async move {
+    cx.spawn_in(window, |_editor, mut cx| async move {
         let docs_urls = open_docs_task.await.context("open docs")?;
         if docs_urls.is_empty() {
             log::debug!("Empty docs urls for position {position:?}");

crates/editor/src/scroll.rs 🔗

@@ -12,9 +12,7 @@ use crate::{
 };
 pub use autoscroll::{Autoscroll, AutoscrollStrategy};
 use core::fmt::Debug;
-use gpui::{
-    point, px, Along, AppContext, Axis, Entity, Global, Pixels, Task, ViewContext, WindowContext,
-};
+use gpui::{point, px, Along, App, Axis, Context, Global, Pixels, Task, Window};
 use language::{Bias, Point};
 pub use scroll_amount::ScrollAmount;
 use settings::Settings;
@@ -188,7 +186,7 @@ pub struct ScrollManager {
 }
 
 impl ScrollManager {
-    pub fn new(cx: &mut WindowContext) -> Self {
+    pub fn new(cx: &mut App) -> Self {
         ScrollManager {
             vertical_scroll_margin: EditorSettings::get_global(cx).vertical_scroll_margin,
             anchor: ScrollAnchor::new(),
@@ -225,6 +223,7 @@ impl ScrollManager {
         self.anchor.scroll_position(snapshot)
     }
 
+    #[allow(clippy::too_many_arguments)]
     fn set_scroll_position(
         &mut self,
         scroll_position: gpui::Point<f32>,
@@ -232,7 +231,8 @@ impl ScrollManager {
         local: bool,
         autoscroll: bool,
         workspace_id: Option<WorkspaceId>,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) {
         if self.forbid_vertical_scroll {
             return;
@@ -287,9 +287,18 @@ impl ScrollManager {
             )
         };
 
-        self.set_anchor(new_anchor, top_row, local, autoscroll, workspace_id, cx);
+        self.set_anchor(
+            new_anchor,
+            top_row,
+            local,
+            autoscroll,
+            workspace_id,
+            window,
+            cx,
+        );
     }
 
+    #[allow(clippy::too_many_arguments)]
     fn set_anchor(
         &mut self,
         anchor: ScrollAnchor,
@@ -297,17 +306,18 @@ impl ScrollManager {
         local: bool,
         autoscroll: bool,
         workspace_id: Option<WorkspaceId>,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) {
         if self.forbid_vertical_scroll {
             return;
         }
         self.anchor = anchor;
         cx.emit(EditorEvent::ScrollPositionChanged { local, autoscroll });
-        self.show_scrollbar(cx);
+        self.show_scrollbar(window, cx);
         self.autoscroll_request.take();
         if let Some(workspace_id) = workspace_id {
-            let item_id = cx.view().entity_id().as_u64() as ItemId;
+            let item_id = cx.model().entity_id().as_u64() as ItemId;
 
             cx.foreground_executor()
                 .spawn(async move {
@@ -326,14 +336,14 @@ impl ScrollManager {
         cx.notify();
     }
 
-    pub fn show_scrollbar(&mut self, cx: &mut ViewContext<Editor>) {
+    pub fn show_scrollbar(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
         if !self.show_scrollbars {
             self.show_scrollbars = true;
             cx.notify();
         }
 
         if cx.default_global::<ScrollbarAutoHide>().0 {
-            self.hide_scrollbar_task = Some(cx.spawn(|editor, mut cx| async move {
+            self.hide_scrollbar_task = Some(cx.spawn_in(window, |editor, mut cx| async move {
                 cx.background_executor()
                     .timer(SCROLLBAR_SHOW_INTERVAL)
                     .await;
@@ -365,7 +375,7 @@ impl ScrollManager {
         &mut self,
         axis: Axis,
         dragging: bool,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) {
         self.dragging_scrollbar = self.dragging_scrollbar.apply_along(axis, |_| dragging);
         cx.notify();
@@ -394,7 +404,7 @@ impl Editor {
         self.scroll_manager.vertical_scroll_margin as usize
     }
 
-    pub fn set_vertical_scroll_margin(&mut self, margin_rows: usize, cx: &mut ViewContext<Self>) {
+    pub fn set_vertical_scroll_margin(&mut self, margin_rows: usize, cx: &mut Context<Self>) {
         self.scroll_manager.vertical_scroll_margin = margin_rows as f32;
         cx.notify();
     }
@@ -408,11 +418,16 @@ impl Editor {
             .map(|line_count| line_count as u32 - 1)
     }
 
-    pub(crate) fn set_visible_line_count(&mut self, lines: f32, cx: &mut ViewContext<Self>) {
+    pub(crate) fn set_visible_line_count(
+        &mut self,
+        lines: f32,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let opened_first_time = self.scroll_manager.visible_line_count.is_none();
         self.scroll_manager.visible_line_count = Some(lines);
         if opened_first_time {
-            cx.spawn(|editor, mut cx| async move {
+            cx.spawn_in(window, |editor, mut cx| async move {
                 editor
                     .update(&mut cx, |editor, cx| {
                         editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx)
@@ -426,25 +441,27 @@ impl Editor {
     pub fn apply_scroll_delta(
         &mut self,
         scroll_delta: gpui::Point<f32>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         if self.scroll_manager.forbid_vertical_scroll {
             return;
         }
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let position = self.scroll_manager.anchor.scroll_position(&display_map) + scroll_delta;
-        self.set_scroll_position_taking_display_map(position, true, false, display_map, cx);
+        self.set_scroll_position_taking_display_map(position, true, false, display_map, window, cx);
     }
 
     pub fn set_scroll_position(
         &mut self,
         scroll_position: gpui::Point<f32>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         if self.scroll_manager.forbid_vertical_scroll {
             return;
         }
-        self.set_scroll_position_internal(scroll_position, true, false, cx);
+        self.set_scroll_position_internal(scroll_position, true, false, window, cx);
     }
 
     pub(crate) fn set_scroll_position_internal(
@@ -452,10 +469,18 @@ impl Editor {
         scroll_position: gpui::Point<f32>,
         local: bool,
         autoscroll: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        self.set_scroll_position_taking_display_map(scroll_position, local, autoscroll, map, cx);
+        self.set_scroll_position_taking_display_map(
+            scroll_position,
+            local,
+            autoscroll,
+            map,
+            window,
+            cx,
+        );
     }
 
     fn set_scroll_position_taking_display_map(
@@ -464,7 +489,8 @@ impl Editor {
         local: bool,
         autoscroll: bool,
         display_map: DisplaySnapshot,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         hide_hover(self, cx);
         let workspace_id = self.workspace.as_ref().and_then(|workspace| workspace.1);
@@ -475,32 +501,46 @@ impl Editor {
             local,
             autoscroll,
             workspace_id,
+            window,
             cx,
         );
 
         self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
     }
 
-    pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> gpui::Point<f32> {
+    pub fn scroll_position(&self, cx: &mut Context<Self>) -> gpui::Point<f32> {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         self.scroll_manager.anchor.scroll_position(&display_map)
     }
 
-    pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext<Self>) {
+    pub fn set_scroll_anchor(
+        &mut self,
+        scroll_anchor: ScrollAnchor,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         hide_hover(self, cx);
         let workspace_id = self.workspace.as_ref().and_then(|workspace| workspace.1);
         let top_row = scroll_anchor
             .anchor
             .to_point(&self.buffer().read(cx).snapshot(cx))
             .row;
-        self.scroll_manager
-            .set_anchor(scroll_anchor, top_row, true, false, workspace_id, cx);
+        self.scroll_manager.set_anchor(
+            scroll_anchor,
+            top_row,
+            true,
+            false,
+            workspace_id,
+            window,
+            cx,
+        );
     }
 
     pub(crate) fn set_scroll_anchor_remote(
         &mut self,
         scroll_anchor: ScrollAnchor,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         hide_hover(self, cx);
         let workspace_id = self.workspace.as_ref().and_then(|workspace| workspace.1);
@@ -510,17 +550,29 @@ impl Editor {
             return;
         }
         let top_row = scroll_anchor.anchor.to_point(snapshot).row;
-        self.scroll_manager
-            .set_anchor(scroll_anchor, top_row, false, false, workspace_id, cx);
+        self.scroll_manager.set_anchor(
+            scroll_anchor,
+            top_row,
+            false,
+            false,
+            workspace_id,
+            window,
+            cx,
+        );
     }
 
-    pub fn scroll_screen(&mut self, amount: &ScrollAmount, cx: &mut ViewContext<Self>) {
+    pub fn scroll_screen(
+        &mut self,
+        amount: &ScrollAmount,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if matches!(self.mode, EditorMode::SingleLine { .. }) {
             cx.propagate();
             return;
         }
 
-        if self.take_rename(true, cx).is_some() {
+        if self.take_rename(true, window, cx).is_some() {
             return;
         }
 
@@ -529,14 +581,14 @@ impl Editor {
             return;
         };
         let new_pos = cur_position + point(0., amount.lines(visible_line_count));
-        self.set_scroll_position(new_pos, cx);
+        self.set_scroll_position(new_pos, window, cx);
     }
 
     /// Returns an ordering. The newest selection is:
     ///     Ordering::Equal => on screen
     ///     Ordering::Less => above the screen
     ///     Ordering::Greater => below the screen
-    pub fn newest_selection_on_screen(&self, cx: &mut AppContext) -> Ordering {
+    pub fn newest_selection_on_screen(&self, cx: &mut App) -> Ordering {
         let snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let newest_head = self
             .selections
@@ -566,7 +618,8 @@ impl Editor {
         &mut self,
         item_id: u64,
         workspace_id: WorkspaceId,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) {
         let scroll_position = DB.get_scroll_position(item_id, workspace_id);
         if let Ok(Some((top_row, x, y))) = scroll_position {
@@ -579,7 +632,7 @@ impl Editor {
                 offset: gpui::Point::new(x, y),
                 anchor: top_anchor,
             };
-            self.set_scroll_anchor(scroll_anchor, cx);
+            self.set_scroll_anchor(scroll_anchor, window, cx);
         }
     }
 }

crates/editor/src/scroll/actions.rs 🔗

@@ -4,11 +4,11 @@ use crate::{
     ScrollAnchor, ScrollCursorBottom, ScrollCursorCenter, ScrollCursorCenterTopBottom,
     ScrollCursorTop, SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT,
 };
-use gpui::{AsyncWindowContext, Point, ViewContext};
+use gpui::{Context, Point, Window};
 
 impl Editor {
-    pub fn next_screen(&mut self, _: &NextScreen, cx: &mut ViewContext<Editor>) {
-        if self.take_rename(true, cx).is_some() {
+    pub fn next_screen(&mut self, _: &NextScreen, window: &mut Window, cx: &mut Context<Editor>) {
+        if self.take_rename(true, window, cx).is_some() {
             return;
         }
 
@@ -27,18 +27,20 @@ impl Editor {
         &mut self,
         scroll_position: Point<f32>,
         axis: Option<Axis>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.scroll_manager.update_ongoing_scroll(axis);
-        self.set_scroll_position(scroll_position, cx);
+        self.set_scroll_position(scroll_position, window, cx);
     }
 
     pub fn scroll_cursor_center_top_bottom(
         &mut self,
         _: &ScrollCursorCenterTopBottom,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
-        let snapshot = self.snapshot(cx).display_snapshot;
+        let snapshot = self.snapshot(window, cx).display_snapshot;
         let visible_rows = if let Some(visible_rows) = self.visible_line_count() {
             visible_rows as u32
         } else {
@@ -70,25 +72,30 @@ impl Editor {
                     .anchor_before(new_screen_top.to_offset(&snapshot, Bias::Left)),
                 offset: Default::default(),
             },
+            window,
             cx,
         );
 
         self.next_scroll_position = self.next_scroll_position.next();
-        self._scroll_cursor_center_top_bottom_task =
-            cx.spawn(|editor, mut cx: AsyncWindowContext| async move {
-                cx.background_executor()
-                    .timer(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT)
-                    .await;
-                editor
-                    .update(&mut cx, |editor, _| {
-                        editor.next_scroll_position = NextScrollCursorCenterTopBottom::default();
-                    })
-                    .ok();
-            });
+        self._scroll_cursor_center_top_bottom_task = cx.spawn(|editor, mut cx| async move {
+            cx.background_executor()
+                .timer(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT)
+                .await;
+            editor
+                .update(&mut cx, |editor, _| {
+                    editor.next_scroll_position = NextScrollCursorCenterTopBottom::default();
+                })
+                .ok();
+        });
     }
 
-    pub fn scroll_cursor_top(&mut self, _: &ScrollCursorTop, cx: &mut ViewContext<Editor>) {
-        let snapshot = self.snapshot(cx).display_snapshot;
+    pub fn scroll_cursor_top(
+        &mut self,
+        _: &ScrollCursorTop,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
+    ) {
+        let snapshot = self.snapshot(window, cx).display_snapshot;
         let scroll_margin_rows = self.vertical_scroll_margin() as u32;
 
         let mut new_screen_top = self.selections.newest_display(cx).head();
@@ -102,12 +109,18 @@ impl Editor {
                 anchor: new_anchor,
                 offset: Default::default(),
             },
+            window,
             cx,
         )
     }
 
-    pub fn scroll_cursor_center(&mut self, _: &ScrollCursorCenter, cx: &mut ViewContext<Editor>) {
-        let snapshot = self.snapshot(cx).display_snapshot;
+    pub fn scroll_cursor_center(
+        &mut self,
+        _: &ScrollCursorCenter,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
+    ) {
+        let snapshot = self.snapshot(window, cx).display_snapshot;
         let visible_rows = if let Some(visible_rows) = self.visible_line_count() {
             visible_rows as u32
         } else {
@@ -125,12 +138,18 @@ impl Editor {
                 anchor: new_anchor,
                 offset: Default::default(),
             },
+            window,
             cx,
         )
     }
 
-    pub fn scroll_cursor_bottom(&mut self, _: &ScrollCursorBottom, cx: &mut ViewContext<Editor>) {
-        let snapshot = self.snapshot(cx).display_snapshot;
+    pub fn scroll_cursor_bottom(
+        &mut self,
+        _: &ScrollCursorBottom,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
+    ) {
+        let snapshot = self.snapshot(window, cx).display_snapshot;
         let scroll_margin_rows = self.vertical_scroll_margin() as u32;
         let visible_rows = if let Some(visible_rows) = self.visible_line_count() {
             visible_rows as u32
@@ -152,6 +171,7 @@ impl Editor {
                 anchor: new_anchor,
                 offset: Default::default(),
             },
+            window,
             cx,
         )
     }

crates/editor/src/scroll/autoscroll.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
     display_map::ToDisplayPoint, DisplayRow, Editor, EditorMode, LineWithInvisibles, RowExt,
 };
-use gpui::{px, Bounds, Pixels, ViewContext};
+use gpui::{px, Bounds, Context, Pixels, Window};
 use language::Point;
 use std::{cmp, f32};
 
@@ -76,7 +76,8 @@ impl Editor {
         bounds: Bounds<Pixels>,
         line_height: Pixels,
         max_scroll_top: f32,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
     ) -> bool {
         let viewport_height = bounds.size.height;
         let visible_lines = viewport_height / line_height;
@@ -96,7 +97,7 @@ impl Editor {
         }
 
         if original_y != scroll_position.y {
-            self.set_scroll_position(scroll_position, cx);
+            self.set_scroll_position(scroll_position, window, cx);
         }
 
         let Some((autoscroll, local)) = self.scroll_manager.autoscroll_request.take() else {
@@ -183,33 +184,33 @@ impl Editor {
 
                 if needs_scroll_up && !needs_scroll_down {
                     scroll_position.y = target_top;
-                    self.set_scroll_position_internal(scroll_position, local, true, cx);
+                    self.set_scroll_position_internal(scroll_position, local, true, window, cx);
                 }
                 if !needs_scroll_up && needs_scroll_down {
                     scroll_position.y = target_bottom - visible_lines;
-                    self.set_scroll_position_internal(scroll_position, local, true, cx);
+                    self.set_scroll_position_internal(scroll_position, local, true, window, cx);
                 }
             }
             AutoscrollStrategy::Center => {
                 scroll_position.y = (target_top - margin).max(0.0);
-                self.set_scroll_position_internal(scroll_position, local, true, cx);
+                self.set_scroll_position_internal(scroll_position, local, true, window, cx);
             }
             AutoscrollStrategy::Focused => {
                 let margin = margin.min(self.scroll_manager.vertical_scroll_margin);
                 scroll_position.y = (target_top - margin).max(0.0);
-                self.set_scroll_position_internal(scroll_position, local, true, cx);
+                self.set_scroll_position_internal(scroll_position, local, true, window, cx);
             }
             AutoscrollStrategy::Top => {
                 scroll_position.y = (target_top).max(0.0);
-                self.set_scroll_position_internal(scroll_position, local, true, cx);
+                self.set_scroll_position_internal(scroll_position, local, true, window, cx);
             }
             AutoscrollStrategy::Bottom => {
                 scroll_position.y = (target_bottom - visible_lines).max(0.0);
-                self.set_scroll_position_internal(scroll_position, local, true, cx);
+                self.set_scroll_position_internal(scroll_position, local, true, window, cx);
             }
             AutoscrollStrategy::TopRelative(lines) => {
                 scroll_position.y = target_top - lines as f32;
-                self.set_scroll_position_internal(scroll_position, local, true, cx);
+                self.set_scroll_position_internal(scroll_position, local, true, window, cx);
             }
         }
 
@@ -230,7 +231,7 @@ impl Editor {
         scroll_width: Pixels,
         max_glyph_width: Pixels,
         layouts: &[LineWithInvisibles],
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> bool {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let selections = self.selections.all::<Point>(cx);
@@ -287,7 +288,7 @@ impl Editor {
         }
     }
 
-    pub fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
+    pub fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut Context<Self>) {
         self.scroll_manager.autoscroll_request = Some((autoscroll, true));
         cx.notify();
     }
@@ -295,7 +296,7 @@ impl Editor {
     pub(crate) fn request_autoscroll_remotely(
         &mut self,
         autoscroll: Autoscroll,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.scroll_manager.autoscroll_request = Some((autoscroll, false));
         cx.notify();

crates/editor/src/selections_collection.rs 🔗

@@ -6,7 +6,7 @@ use std::{
 };
 
 use collections::HashMap;
-use gpui::{AppContext, Model, Pixels};
+use gpui::{App, Entity, Pixels};
 use itertools::Itertools;
 use language::{Bias, Point, Selection, SelectionGoal, TextDimension};
 use util::post_inc;
@@ -26,8 +26,8 @@ pub struct PendingSelection {
 
 #[derive(Debug, Clone)]
 pub struct SelectionsCollection {
-    display_map: Model<DisplayMap>,
-    buffer: Model<MultiBuffer>,
+    display_map: Entity<DisplayMap>,
+    buffer: Entity<MultiBuffer>,
     pub next_selection_id: usize,
     pub line_mode: bool,
     /// The non-pending, non-overlapping selections.
@@ -38,7 +38,7 @@ pub struct SelectionsCollection {
 }
 
 impl SelectionsCollection {
-    pub fn new(display_map: Model<DisplayMap>, buffer: Model<MultiBuffer>) -> Self {
+    pub fn new(display_map: Entity<DisplayMap>, buffer: Entity<MultiBuffer>) -> Self {
         Self {
             display_map,
             buffer,
@@ -58,11 +58,11 @@ impl SelectionsCollection {
         }
     }
 
-    pub fn display_map(&self, cx: &mut AppContext) -> DisplaySnapshot {
+    pub fn display_map(&self, cx: &mut App) -> DisplaySnapshot {
         self.display_map.update(cx, |map, cx| map.snapshot(cx))
     }
 
-    fn buffer<'a>(&self, cx: &'a AppContext) -> Ref<'a, MultiBufferSnapshot> {
+    fn buffer<'a>(&self, cx: &'a App) -> Ref<'a, MultiBufferSnapshot> {
         self.buffer.read(cx).read(cx)
     }
 
@@ -102,7 +102,7 @@ impl SelectionsCollection {
 
     pub fn pending<D: TextDimension + Ord + Sub<D, Output = D>>(
         &self,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Option<Selection<D>> {
         let map = self.display_map(cx);
         let selection = resolve_selections(self.pending_anchor().as_ref(), &map).next();
@@ -113,7 +113,7 @@ impl SelectionsCollection {
         self.pending.as_ref().map(|pending| pending.mode.clone())
     }
 
-    pub fn all<'a, D>(&self, cx: &mut AppContext) -> Vec<Selection<D>>
+    pub fn all<'a, D>(&self, cx: &mut App) -> Vec<Selection<D>>
     where
         D: 'a + TextDimension + Ord + Sub<D, Output = D>,
     {
@@ -148,7 +148,7 @@ impl SelectionsCollection {
     }
 
     /// Returns all of the selections, adjusted to take into account the selection line_mode
-    pub fn all_adjusted(&self, cx: &mut AppContext) -> Vec<Selection<Point>> {
+    pub fn all_adjusted(&self, cx: &mut App) -> Vec<Selection<Point>> {
         let mut selections = self.all::<Point>(cx);
         if self.line_mode {
             let map = self.display_map(cx);
@@ -162,7 +162,7 @@ impl SelectionsCollection {
     }
 
     /// Returns the newest selection, adjusted to take into account the selection line_mode
-    pub fn newest_adjusted(&self, cx: &mut AppContext) -> Selection<Point> {
+    pub fn newest_adjusted(&self, cx: &mut App) -> Selection<Point> {
         let mut selection = self.newest::<Point>(cx);
         if self.line_mode {
             let map = self.display_map(cx);
@@ -175,7 +175,7 @@ impl SelectionsCollection {
 
     pub fn all_adjusted_display(
         &self,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> (DisplaySnapshot, Vec<Selection<DisplayPoint>>) {
         if self.line_mode {
             let selections = self.all::<Point>(cx);
@@ -195,11 +195,7 @@ impl SelectionsCollection {
         }
     }
 
-    pub fn disjoint_in_range<'a, D>(
-        &self,
-        range: Range<Anchor>,
-        cx: &mut AppContext,
-    ) -> Vec<Selection<D>>
+    pub fn disjoint_in_range<'a, D>(&self, range: Range<Anchor>, cx: &mut App) -> Vec<Selection<D>>
     where
         D: 'a + TextDimension + Ord + Sub<D, Output = D> + std::fmt::Debug,
     {
@@ -220,10 +216,7 @@ impl SelectionsCollection {
         resolve_selections(&self.disjoint[start_ix..end_ix], &map).collect()
     }
 
-    pub fn all_display(
-        &self,
-        cx: &mut AppContext,
-    ) -> (DisplaySnapshot, Vec<Selection<DisplayPoint>>) {
+    pub fn all_display(&self, cx: &mut App) -> (DisplaySnapshot, Vec<Selection<DisplayPoint>>) {
         let map = self.display_map(cx);
         let disjoint_anchors = &self.disjoint;
         let mut disjoint = resolve_selections_display(disjoint_anchors.iter(), &map).peekable();
@@ -266,7 +259,7 @@ impl SelectionsCollection {
 
     pub fn newest<D: TextDimension + Ord + Sub<D, Output = D>>(
         &self,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Selection<D> {
         let map = self.display_map(cx);
         let selection = resolve_selections([self.newest_anchor()], &map)
@@ -275,7 +268,7 @@ impl SelectionsCollection {
         selection
     }
 
-    pub fn newest_display(&self, cx: &mut AppContext) -> Selection<DisplayPoint> {
+    pub fn newest_display(&self, cx: &mut App) -> Selection<DisplayPoint> {
         let map = self.display_map(cx);
         let selection = resolve_selections_display([self.newest_anchor()], &map)
             .next()
@@ -293,7 +286,7 @@ impl SelectionsCollection {
 
     pub fn oldest<D: TextDimension + Ord + Sub<D, Output = D>>(
         &self,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Selection<D> {
         let map = self.display_map(cx);
         let selection = resolve_selections([self.oldest_anchor()], &map)
@@ -309,23 +302,17 @@ impl SelectionsCollection {
             .unwrap_or_else(|| self.disjoint.first().cloned().unwrap())
     }
 
-    pub fn first<D: TextDimension + Ord + Sub<D, Output = D>>(
-        &self,
-        cx: &mut AppContext,
-    ) -> Selection<D> {
+    pub fn first<D: TextDimension + Ord + Sub<D, Output = D>>(&self, cx: &mut App) -> Selection<D> {
         self.all(cx).first().unwrap().clone()
     }
 
-    pub fn last<D: TextDimension + Ord + Sub<D, Output = D>>(
-        &self,
-        cx: &mut AppContext,
-    ) -> Selection<D> {
+    pub fn last<D: TextDimension + Ord + Sub<D, Output = D>>(&self, cx: &mut App) -> Selection<D> {
         self.all(cx).last().unwrap().clone()
     }
 
     pub fn ranges<D: TextDimension + Ord + Sub<D, Output = D>>(
         &self,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Vec<Range<D>> {
         self.all::<D>(cx)
             .iter()
@@ -340,7 +327,7 @@ impl SelectionsCollection {
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn display_ranges(&self, cx: &mut AppContext) -> Vec<Range<DisplayPoint>> {
+    pub fn display_ranges(&self, cx: &mut App) -> Vec<Range<DisplayPoint>> {
         let display_map = self.display_map(cx);
         self.disjoint_anchors()
             .iter()
@@ -391,7 +378,7 @@ impl SelectionsCollection {
 
     pub fn change_with<R>(
         &mut self,
-        cx: &mut AppContext,
+        cx: &mut App,
         change: impl FnOnce(&mut MutableSelectionsCollection) -> R,
     ) -> (bool, R) {
         let mut mutable_collection = MutableSelectionsCollection {
@@ -412,7 +399,7 @@ impl SelectionsCollection {
 pub struct MutableSelectionsCollection<'a> {
     collection: &'a mut SelectionsCollection,
     selections_changed: bool,
-    cx: &'a mut AppContext,
+    cx: &'a mut App,
 }
 
 impl<'a> MutableSelectionsCollection<'a> {

crates/editor/src/signature_help.rs 🔗

@@ -3,7 +3,7 @@ mod state;
 
 use crate::actions::ShowSignatureHelp;
 use crate::{Editor, EditorSettings, ToggleAutoSignatureHelp};
-use gpui::{AppContext, ViewContext};
+use gpui::{App, Context, Window};
 use language::markdown::parse_markdown;
 use language::BufferSnapshot;
 use multi_buffer::{Anchor, ToOffset};
@@ -27,7 +27,8 @@ impl Editor {
     pub fn toggle_auto_signature_help_menu(
         &mut self,
         _: &ToggleAutoSignatureHelp,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.auto_signature_help = self
             .auto_signature_help
@@ -35,7 +36,7 @@ impl Editor {
             .or_else(|| Some(!EditorSettings::get_global(cx).auto_signature_help));
         match self.auto_signature_help {
             Some(auto_signature_help) if auto_signature_help => {
-                self.show_signature_help(&ShowSignatureHelp, cx);
+                self.show_signature_help(&ShowSignatureHelp, window, cx);
             }
             Some(_) => {
                 self.hide_signature_help(cx, SignatureHelpHiddenBy::AutoClose);
@@ -47,7 +48,7 @@ impl Editor {
 
     pub(super) fn hide_signature_help(
         &mut self,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
         signature_help_hidden_by: SignatureHelpHiddenBy,
     ) -> bool {
         if self.signature_help_state.is_shown() {
@@ -60,7 +61,7 @@ impl Editor {
         }
     }
 
-    pub fn auto_signature_help_enabled(&self, cx: &AppContext) -> bool {
+    pub fn auto_signature_help_enabled(&self, cx: &App) -> bool {
         if let Some(auto_signature_help) = self.auto_signature_help {
             auto_signature_help
         } else {
@@ -72,7 +73,8 @@ impl Editor {
         &mut self,
         old_cursor_position: &Anchor,
         backspace_pressed: bool,
-        cx: &mut ViewContext<Self>,
+
+        cx: &mut Context<Self>,
     ) -> bool {
         if !(self.signature_help_state.is_shown() || self.auto_signature_help_enabled(cx)) {
             return false;
@@ -150,7 +152,12 @@ impl Editor {
         }
     }
 
-    pub fn show_signature_help(&mut self, _: &ShowSignatureHelp, cx: &mut ViewContext<Self>) {
+    pub fn show_signature_help(
+        &mut self,
+        _: &ShowSignatureHelp,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if self.pending_rename.is_some() || self.has_active_completions_menu() {
             return;
         }
@@ -163,7 +170,7 @@ impl Editor {
         };
 
         self.signature_help_state
-            .set_task(cx.spawn(move |editor, mut cx| async move {
+            .set_task(cx.spawn_in(window, move |editor, mut cx| async move {
                 let signature_help = editor
                     .update(&mut cx, |editor, cx| {
                         let language = editor.language_at(position, cx);

crates/editor/src/signature_help/popover.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{Editor, EditorStyle};
 use gpui::{
-    div, AnyElement, InteractiveElement, IntoElement, MouseButton, ParentElement, Pixels, Size,
-    StatefulInteractiveElement, Styled, ViewContext, WeakView,
+    div, AnyElement, Context, InteractiveElement, IntoElement, MouseButton, ParentElement, Pixels,
+    Size, StatefulInteractiveElement, Styled, WeakEntity,
 };
 use language::ParsedMarkdown;
 use ui::StyledExt;
@@ -25,8 +25,8 @@ impl SignatureHelpPopover {
         &mut self,
         style: &EditorStyle,
         max_size: Size<Pixels>,
-        workspace: Option<WeakView<Workspace>>,
-        cx: &mut ViewContext<Editor>,
+        workspace: Option<WeakEntity<Workspace>>,
+        cx: &mut Context<Editor>,
     ) -> AnyElement {
         div()
             .id("signature_help_popover")
@@ -34,8 +34,8 @@ impl SignatureHelpPopover {
             .overflow_y_scroll()
             .max_w(max_size.width)
             .max_h(max_size.height)
-            .on_mouse_move(|_, cx| cx.stop_propagation())
-            .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
+            .on_mouse_move(|_, _, cx| cx.stop_propagation())
+            .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
             .child(div().p_2().child(crate::render_parsed_markdown(
                 "signature_help_popover_content",
                 &self.parsed_content,

crates/editor/src/tasks.rs 🔗

@@ -1,6 +1,6 @@
 use crate::Editor;
 
-use gpui::{Task as AsyncTask, WindowContext};
+use gpui::{App, Task as AsyncTask, Window};
 use project::Location;
 use task::{TaskContext, TaskVariables, VariableName};
 use text::{ToOffset, ToPoint};
@@ -8,7 +8,8 @@ use workspace::Workspace;
 
 fn task_context_with_editor(
     editor: &mut Editor,
-    cx: &mut WindowContext,
+    window: &mut Window,
+    cx: &mut App,
 ) -> AsyncTask<Option<TaskContext>> {
     let Some(project) = editor.project.clone() else {
         return AsyncTask::ready(None);
@@ -22,7 +23,7 @@ fn task_context_with_editor(
         else {
             return AsyncTask::ready(None);
         };
-        let snapshot = editor.snapshot(cx);
+        let snapshot = editor.snapshot(window, cx);
         (selection, buffer, snapshot)
     };
     let selection_range = selection.range();
@@ -74,7 +75,11 @@ fn task_context_with_editor(
     })
 }
 
-pub fn task_context(workspace: &Workspace, cx: &mut WindowContext) -> AsyncTask<TaskContext> {
+pub fn task_context(
+    workspace: &Workspace,
+    window: &mut Window,
+    cx: &mut App,
+) -> AsyncTask<TaskContext> {
     let Some(editor) = workspace
         .active_item(cx)
         .and_then(|item| item.act_as::<Editor>(cx))
@@ -82,7 +87,7 @@ pub fn task_context(workspace: &Workspace, cx: &mut WindowContext) -> AsyncTask<
         return AsyncTask::ready(TaskContext::default());
     };
     editor.update(cx, |editor, cx| {
-        let context_task = task_context_with_editor(editor, cx);
+        let context_task = task_context_with_editor(editor, window, cx);
         cx.background_executor()
             .spawn(async move { context_task.await.unwrap_or_default() })
     })

crates/editor/src/test.rs 🔗

@@ -5,7 +5,9 @@ use crate::{
     display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
     DisplayPoint, Editor, EditorMode, FoldPlaceholder, MultiBuffer,
 };
-use gpui::{Context, Font, FontFeatures, FontStyle, FontWeight, Model, Pixels, ViewContext};
+use gpui::{
+    AppContext as _, Context, Entity, Font, FontFeatures, FontStyle, FontWeight, Pixels, Window,
+};
 use project::Project;
 use util::test::{marked_text_offsets, marked_text_ranges};
 
@@ -20,7 +22,7 @@ fn init_logger() {
 // Returns a snapshot from text containing '|' character markers with the markers removed, and DisplayPoints for each one.
 pub fn marked_display_snapshot(
     text: &str,
-    cx: &mut gpui::AppContext,
+    cx: &mut gpui::App,
 ) -> (DisplaySnapshot, Vec<DisplayPoint>) {
     let (unmarked_text, markers) = marked_text_offsets(text);
 
@@ -34,7 +36,7 @@ pub fn marked_display_snapshot(
     let font_size: Pixels = 14usize.into();
 
     let buffer = MultiBuffer::build_simple(&unmarked_text, cx);
-    let display_map = cx.new_model(|cx| {
+    let display_map = cx.new(|cx| {
         DisplayMap::new(
             buffer,
             font,
@@ -57,17 +59,22 @@ pub fn marked_display_snapshot(
     (snapshot, markers)
 }
 
-pub fn select_ranges(editor: &mut Editor, marked_text: &str, cx: &mut ViewContext<Editor>) {
+pub fn select_ranges(
+    editor: &mut Editor,
+    marked_text: &str,
+    window: &mut Window,
+    cx: &mut Context<Editor>,
+) {
     let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true);
     assert_eq!(editor.text(cx), unmarked_text);
-    editor.change_selections(None, cx, |s| s.select_ranges(text_ranges));
+    editor.change_selections(None, window, cx, |s| s.select_ranges(text_ranges));
 }
 
 #[track_caller]
 pub fn assert_text_with_selections(
     editor: &mut Editor,
     marked_text: &str,
-    cx: &mut ViewContext<Editor>,
+    cx: &mut Context<Editor>,
 ) {
     let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true);
     assert_eq!(editor.text(cx), unmarked_text);
@@ -77,14 +84,19 @@ pub fn assert_text_with_selections(
 // RA thinks this is dead code even though it is used in a whole lot of tests
 #[allow(dead_code)]
 #[cfg(any(test, feature = "test-support"))]
-pub(crate) fn build_editor(buffer: Model<MultiBuffer>, cx: &mut ViewContext<Editor>) -> Editor {
-    Editor::new(EditorMode::Full, buffer, None, true, cx)
+pub(crate) fn build_editor(
+    buffer: Entity<MultiBuffer>,
+    window: &mut Window,
+    cx: &mut Context<Editor>,
+) -> Editor {
+    Editor::new(EditorMode::Full, buffer, None, true, window, cx)
 }
 
 pub(crate) fn build_editor_with_project(
-    project: Model<Project>,
-    buffer: Model<MultiBuffer>,
-    cx: &mut ViewContext<Editor>,
+    project: Entity<Project>,
+    buffer: Entity<MultiBuffer>,
+    window: &mut Window,
+    cx: &mut Context<Editor>,
 ) -> Editor {
-    Editor::new(EditorMode::Full, buffer, Some(project), true, cx)
+    Editor::new(EditorMode::Full, buffer, Some(project), true, window, cx)
 }

crates/editor/src/test/editor_lsp_test_context.rs 🔗

@@ -11,7 +11,7 @@ use serde_json::json;
 use crate::{Editor, ToPoint};
 use collections::HashSet;
 use futures::Future;
-use gpui::{View, ViewContext, VisualTestContext};
+use gpui::{Context, Entity, Focusable as _, VisualTestContext, Window};
 use indoc::indoc;
 use language::{
     point_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageMatcher, LanguageQueries,
@@ -27,7 +27,7 @@ use super::editor_test_context::{AssertionContextManager, EditorTestContext};
 pub struct EditorLspTestContext {
     pub cx: EditorTestContext,
     pub lsp: lsp::FakeLanguageServer,
-    pub workspace: View<Workspace>,
+    pub workspace: Entity<Workspace>,
     pub buffer_lsp_url: lsp::Url,
 }
 
@@ -131,9 +131,9 @@ impl EditorLspTestContext {
             )
             .await;
 
-        let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
+        let window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
-        let workspace = window.root_view(cx).unwrap();
+        let workspace = window.root_model(cx).unwrap();
 
         let mut cx = VisualTestContext::from_window(*window.deref(), cx);
         project
@@ -146,16 +146,18 @@ impl EditorLspTestContext {
             .await;
         let file = cx.read(|cx| workspace.file_project_paths(cx)[0].clone());
         let item = workspace
-            .update(&mut cx, |workspace, cx| {
-                workspace.open_path(file, None, true, cx)
+            .update_in(&mut cx, |workspace, window, cx| {
+                workspace.open_path(file, None, true, window, cx)
             })
             .await
             .expect("Could not open test file");
-        let editor = cx.update(|cx| {
+        let editor = cx.update(|_, cx| {
             item.act_as::<Editor>(cx)
                 .expect("Opened test file wasn't an editor")
         });
-        editor.update(&mut cx, |editor, cx| editor.focus(cx));
+        editor.update_in(&mut cx, |editor, window, cx| {
+            window.focus(&editor.focus_handle(cx))
+        });
 
         let lsp = fake_servers.next().await.unwrap();
         Self {
@@ -272,11 +274,11 @@ impl EditorLspTestContext {
     }
 
     pub fn to_lsp_range(&mut self, range: Range<usize>) -> lsp::Range {
-        let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
+        let snapshot = self.update_editor(|editor, window, cx| editor.snapshot(window, cx));
         let start_point = range.start.to_point(&snapshot.buffer_snapshot);
         let end_point = range.end.to_point(&snapshot.buffer_snapshot);
 
-        self.editor(|editor, cx| {
+        self.editor(|editor, _, cx| {
             let buffer = editor.buffer().read(cx);
             let start = point_to_lsp(
                 buffer
@@ -298,10 +300,10 @@ impl EditorLspTestContext {
     }
 
     pub fn to_lsp(&mut self, offset: usize) -> lsp::Position {
-        let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
+        let snapshot = self.update_editor(|editor, window, cx| editor.snapshot(window, cx));
         let point = offset.to_point(&snapshot.buffer_snapshot);
 
-        self.editor(|editor, cx| {
+        self.editor(|editor, _, cx| {
             let buffer = editor.buffer().read(cx);
             point_to_lsp(
                 buffer
@@ -315,9 +317,9 @@ impl EditorLspTestContext {
 
     pub fn update_workspace<F, T>(&mut self, update: F) -> T
     where
-        F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
+        F: FnOnce(&mut Workspace, &mut Window, &mut Context<Workspace>) -> T,
     {
-        self.workspace.update(&mut self.cx.cx, update)
+        self.workspace.update_in(&mut self.cx.cx, update)
     }
 
     pub fn handle_request<T, F, Fut>(

crates/editor/src/test/editor_test_context.rs 🔗

@@ -6,8 +6,8 @@ use collections::BTreeMap;
 use futures::Future;
 use git::diff::DiffHunkStatus;
 use gpui::{
-    prelude::*, AnyWindowHandle, AppContext, Keystroke, ModelContext, Pixels, Point, View,
-    ViewContext, VisualTestContext, WindowHandle,
+    prelude::*, AnyWindowHandle, App, Context, Entity, Focusable as _, Keystroke, Pixels, Point,
+    VisualTestContext, Window, WindowHandle,
 };
 use itertools::Itertools;
 use language::{Buffer, BufferSnapshot, LanguageRegistry};
@@ -33,7 +33,7 @@ use super::{build_editor, build_editor_with_project};
 pub struct EditorTestContext {
     pub cx: gpui::VisualTestContext,
     pub window: AnyWindowHandle,
-    pub editor: View<Editor>,
+    pub editor: Entity<Editor>,
     pub assertion_cx: AssertionContextManager,
 }
 
@@ -56,13 +56,18 @@ impl EditorTestContext {
             })
             .await
             .unwrap();
-        let editor = cx.add_window(|cx| {
-            let editor =
-                build_editor_with_project(project, MultiBuffer::build_from_buffer(buffer, cx), cx);
-            editor.focus(cx);
+        let editor = cx.add_window(|window, cx| {
+            let editor = build_editor_with_project(
+                project,
+                MultiBuffer::build_from_buffer(buffer, cx),
+                window,
+                cx,
+            );
+
+            window.focus(&editor.focus_handle(cx));
             editor
         });
-        let editor_view = editor.root_view(cx).unwrap();
+        let editor_view = editor.root_model(cx).unwrap();
 
         cx.run_until_parked();
         Self {
@@ -84,7 +89,7 @@ impl EditorTestContext {
     }
 
     pub async fn for_editor(editor: WindowHandle<Editor>, cx: &mut gpui::TestAppContext) -> Self {
-        let editor_view = editor.root_view(cx).unwrap();
+        let editor_view = editor.root_model(cx).unwrap();
         Self {
             cx: VisualTestContext::from_window(*editor.deref(), cx),
             window: editor.into(),
@@ -98,10 +103,10 @@ impl EditorTestContext {
         excerpts: [&str; COUNT],
     ) -> EditorTestContext {
         let mut multibuffer = MultiBuffer::new(language::Capability::ReadWrite);
-        let buffer = cx.new_model(|cx| {
+        let buffer = cx.new(|cx| {
             for excerpt in excerpts.into_iter() {
                 let (text, ranges) = marked_text_ranges(excerpt, false);
-                let buffer = cx.new_model(|cx| Buffer::local(text, cx));
+                let buffer = cx.new(|cx| Buffer::local(text, cx));
                 multibuffer.push_excerpts(
                     buffer,
                     ranges.into_iter().map(|range| ExcerptRange {
@@ -114,13 +119,14 @@ impl EditorTestContext {
             multibuffer
         });
 
-        let editor = cx.add_window(|cx| {
-            let editor = build_editor(buffer, cx);
-            editor.focus(cx);
+        let editor = cx.add_window(|window, cx| {
+            let editor = build_editor(buffer, window, cx);
+            window.focus(&editor.focus_handle(cx));
+
             editor
         });
 
-        let editor_view = editor.root_view(cx).unwrap();
+        let editor_view = editor.root_model(cx).unwrap();
         Self {
             cx: VisualTestContext::from_window(*editor.deref(), cx),
             window: editor.into(),
@@ -131,7 +137,7 @@ impl EditorTestContext {
 
     pub fn condition(
         &self,
-        predicate: impl FnMut(&Editor, &AppContext) -> bool,
+        predicate: impl FnMut(&Editor, &App) -> bool,
     ) -> impl Future<Output = ()> {
         self.editor
             .condition::<crate::EditorEvent>(&self.cx, predicate)
@@ -140,31 +146,32 @@ impl EditorTestContext {
     #[track_caller]
     pub fn editor<F, T>(&mut self, read: F) -> T
     where
-        F: FnOnce(&Editor, &ViewContext<Editor>) -> T,
+        F: FnOnce(&Editor, &Window, &mut Context<Editor>) -> T,
     {
-        self.editor.update(&mut self.cx, |this, cx| read(this, cx))
+        self.editor
+            .update_in(&mut self.cx, |this, window, cx| read(this, window, cx))
     }
 
     #[track_caller]
     pub fn update_editor<F, T>(&mut self, update: F) -> T
     where
-        F: FnOnce(&mut Editor, &mut ViewContext<Editor>) -> T,
+        F: FnOnce(&mut Editor, &mut Window, &mut Context<Editor>) -> T,
     {
-        self.editor.update(&mut self.cx, update)
+        self.editor.update_in(&mut self.cx, update)
     }
 
     pub fn multibuffer<F, T>(&mut self, read: F) -> T
     where
-        F: FnOnce(&MultiBuffer, &AppContext) -> T,
+        F: FnOnce(&MultiBuffer, &App) -> T,
     {
-        self.editor(|editor, cx| read(editor.buffer().read(cx), cx))
+        self.editor(|editor, _, cx| read(editor.buffer().read(cx), cx))
     }
 
     pub fn update_multibuffer<F, T>(&mut self, update: F) -> T
     where
-        F: FnOnce(&mut MultiBuffer, &mut ModelContext<MultiBuffer>) -> T,
+        F: FnOnce(&mut MultiBuffer, &mut Context<MultiBuffer>) -> T,
     {
-        self.update_editor(|editor, cx| editor.buffer().update(cx, update))
+        self.update_editor(|editor, _, cx| editor.buffer().update(cx, update))
     }
 
     pub fn buffer_text(&mut self) -> String {
@@ -172,12 +179,12 @@ impl EditorTestContext {
     }
 
     pub fn display_text(&mut self) -> String {
-        self.update_editor(|editor, cx| editor.display_text(cx))
+        self.update_editor(|editor, _, cx| editor.display_text(cx))
     }
 
     pub fn buffer<F, T>(&mut self, read: F) -> T
     where
-        F: FnOnce(&Buffer, &AppContext) -> T,
+        F: FnOnce(&Buffer, &App) -> T,
     {
         self.multibuffer(|multibuffer, cx| {
             let buffer = multibuffer.as_singleton().unwrap().read(cx);
@@ -186,7 +193,7 @@ impl EditorTestContext {
     }
 
     pub fn language_registry(&mut self) -> Arc<LanguageRegistry> {
-        self.editor(|editor, cx| {
+        self.editor(|editor, _, cx| {
             editor
                 .project
                 .as_ref()
@@ -199,7 +206,7 @@ impl EditorTestContext {
 
     pub fn update_buffer<F, T>(&mut self, update: F) -> T
     where
-        F: FnOnce(&mut Buffer, &mut ModelContext<Buffer>) -> T,
+        F: FnOnce(&mut Buffer, &mut Context<Buffer>) -> T,
     {
         self.update_multibuffer(|multibuffer, cx| {
             let buffer = multibuffer.as_singleton().unwrap();
@@ -239,9 +246,9 @@ impl EditorTestContext {
 
     pub fn display_point(&mut self, marked_text: &str) -> DisplayPoint {
         let ranges = self.ranges(marked_text);
-        let snapshot = self
-            .editor
-            .update(&mut self.cx, |editor, cx| editor.snapshot(cx));
+        let snapshot = self.editor.update_in(&mut self.cx, |editor, window, cx| {
+            editor.snapshot(window, cx)
+        });
         ranges[0].start.to_display_point(&snapshot)
     }
 
@@ -251,16 +258,16 @@ impl EditorTestContext {
     }
 
     pub fn pixel_position_for(&mut self, display_point: DisplayPoint) -> Point<Pixels> {
-        self.update_editor(|editor, cx| {
+        self.update_editor(|editor, window, cx| {
             let newest_point = editor.selections.newest_display(cx).head();
             let pixel_position = editor.pixel_position_of_newest_cursor.unwrap();
             let line_height = editor
                 .style()
                 .unwrap()
                 .text
-                .line_height_in_pixels(cx.rem_size());
-            let snapshot = editor.snapshot(cx);
-            let details = editor.text_layout_details(cx);
+                .line_height_in_pixels(window.rem_size());
+            let snapshot = editor.snapshot(window, cx);
+            let details = editor.text_layout_details(window);
 
             let y = pixel_position.y
                 + line_height * (display_point.row().as_f32() - newest_point.row().as_f32());
@@ -279,8 +286,9 @@ impl EditorTestContext {
 
     pub fn set_diff_base(&mut self, diff_base: &str) {
         self.cx.run_until_parked();
-        let fs = self
-            .update_editor(|editor, cx| editor.project.as_ref().unwrap().read(cx).fs().as_fake());
+        let fs = self.update_editor(|editor, _, cx| {
+            editor.project.as_ref().unwrap().read(cx).fs().as_fake()
+        });
         let path = self.update_buffer(|buffer, _| buffer.file().unwrap().path().clone());
         fs.set_index_for_repo(
             &Self::root_path().join(".git"),
@@ -303,9 +311,9 @@ impl EditorTestContext {
             marked_text.escape_debug()
         ));
         let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
-        self.editor.update(&mut self.cx, |editor, cx| {
-            editor.set_text(unmarked_text, cx);
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+        self.editor.update_in(&mut self.cx, |editor, window, cx| {
+            editor.set_text(unmarked_text, window, cx);
+            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                 s.select_ranges(selection_ranges)
             })
         });
@@ -319,9 +327,9 @@ impl EditorTestContext {
             marked_text.escape_debug()
         ));
         let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
-        self.editor.update(&mut self.cx, |editor, cx| {
+        self.editor.update_in(&mut self.cx, |editor, window, cx| {
             assert_eq!(editor.text(cx), unmarked_text);
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                 s.select_ranges(selection_ranges)
             })
         });
@@ -355,8 +363,8 @@ impl EditorTestContext {
     #[track_caller]
     pub fn assert_editor_background_highlights<Tag: 'static>(&mut self, marked_text: &str) {
         let expected_ranges = self.ranges(marked_text);
-        let actual_ranges: Vec<Range<usize>> = self.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
+        let actual_ranges: Vec<Range<usize>> = self.update_editor(|editor, window, cx| {
+            let snapshot = editor.snapshot(window, cx);
             editor
                 .background_highlights
                 .get(&TypeId::of::<Tag>())
@@ -372,7 +380,7 @@ impl EditorTestContext {
     #[track_caller]
     pub fn assert_editor_text_highlights<Tag: ?Sized + 'static>(&mut self, marked_text: &str) {
         let expected_ranges = self.ranges(marked_text);
-        let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
+        let snapshot = self.update_editor(|editor, window, cx| editor.snapshot(window, cx));
         let actual_ranges: Vec<Range<usize>> = snapshot
             .text_highlight_ranges::<Tag>()
             .map(|ranges| ranges.as_ref().clone().1)
@@ -429,13 +437,13 @@ impl EditorTestContext {
 
 #[track_caller]
 pub fn assert_state_with_diff(
-    editor: &View<Editor>,
+    editor: &Entity<Editor>,
     cx: &mut VisualTestContext,
     expected_diff_text: &str,
 ) {
-    let (snapshot, selections) = editor.update(cx, |editor, cx| {
+    let (snapshot, selections) = editor.update_in(cx, |editor, window, cx| {
         (
-            editor.snapshot(cx).buffer_snapshot.clone(),
+            editor.snapshot(window, cx).buffer_snapshot.clone(),
             editor.selections.ranges::<usize>(cx),
         )
     });

crates/evals/src/eval.rs 🔗

@@ -6,7 +6,7 @@ use clock::RealSystemClock;
 use collections::BTreeMap;
 use feature_flags::FeatureFlagAppExt as _;
 use git::GitHostingProviderRegistry;
-use gpui::{AsyncAppContext, BackgroundExecutor, Context, Model};
+use gpui::{AppContext as _, AsyncAppContext, BackgroundExecutor, Entity};
 use http_client::{HttpClient, Method};
 use language::LanguageRegistry;
 use node_runtime::NodeRuntime;
@@ -99,7 +99,7 @@ fn main() -> Result<()> {
     let cli = Cli::parse();
     env_logger::init();
 
-    gpui::App::headless().run(move |cx| {
+    gpui::Application::headless().run(move |cx| {
         let executor = cx.background_executor().clone();
         let client = Arc::new(ReqwestClient::user_agent("Zed LLM evals").unwrap());
         cx.set_http_client(client.clone());
@@ -290,9 +290,7 @@ async fn run_evaluation(
             )
         })
         .unwrap();
-    let user_store = cx
-        .new_model(|cx| UserStore::new(client.clone(), cx))
-        .unwrap();
+    let user_store = cx.new(|cx| UserStore::new(client.clone(), cx)).unwrap();
     let node_runtime = NodeRuntime::unavailable();
 
     let evaluations = fs::read(&evaluations_path).expect("failed to read evaluations.json");
@@ -404,11 +402,11 @@ async fn run_evaluation(
 #[allow(clippy::too_many_arguments)]
 async fn run_eval_project(
     evaluation_project: EvaluationProject,
-    user_store: &Model<UserStore>,
+    user_store: &Entity<UserStore>,
     repo_db_path: PathBuf,
     repo_dir: &Path,
     counts: &mut Counts,
-    project: Model<Project>,
+    project: Entity<Project>,
     embedding_provider: Arc<dyn EmbeddingProvider>,
     fs: Arc<dyn Fs>,
     cx: &mut AsyncAppContext,
@@ -547,7 +545,7 @@ async fn run_eval_project(
 }
 
 async fn wait_for_indexing_complete(
-    project_index: &Model<ProjectIndex>,
+    project_index: &Entity<ProjectIndex>,
     cx: &mut AsyncAppContext,
     timeout: Option<Duration>,
 ) {

crates/extension/src/extension.rs 🔗

@@ -10,7 +10,7 @@ use ::lsp::LanguageServerName;
 use anyhow::{anyhow, bail, Context as _, Result};
 use async_trait::async_trait;
 use fs::normalize_path;
-use gpui::{AppContext, Task};
+use gpui::{App, Task};
 use language::LanguageName;
 use semantic_version::SemanticVersion;
 
@@ -19,7 +19,7 @@ pub use crate::extension_manifest::*;
 pub use crate::types::*;
 
 /// Initializes the `extension` crate.
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     ExtensionHostProxy::default_global(cx);
 }
 

crates/extension/src/extension_host_proxy.rs 🔗

@@ -3,7 +3,7 @@ use std::sync::Arc;
 
 use anyhow::Result;
 use fs::Fs;
-use gpui::{AppContext, Global, ReadGlobal, SharedString, Task};
+use gpui::{App, Global, ReadGlobal, SharedString, Task};
 use language::{LanguageMatcher, LanguageName, LanguageServerBinaryStatus, LoadedLanguage};
 use lsp::LanguageServerName;
 use parking_lot::RwLock;
@@ -33,14 +33,14 @@ pub struct ExtensionHostProxy {
 
 impl ExtensionHostProxy {
     /// Returns the global [`ExtensionHostProxy`].
-    pub fn global(cx: &AppContext) -> Arc<Self> {
+    pub fn global(cx: &App) -> Arc<Self> {
         GlobalExtensionHostProxy::global(cx).0.clone()
     }
 
     /// Returns the global [`ExtensionHostProxy`].
     ///
     /// Inserts a default [`ExtensionHostProxy`] if one does not yet exist.
-    pub fn default_global(cx: &mut AppContext) -> Arc<Self> {
+    pub fn default_global(cx: &mut App) -> Arc<Self> {
         cx.default_global::<GlobalExtensionHostProxy>().0.clone()
     }
 
@@ -102,7 +102,7 @@ pub trait ExtensionThemeProxy: Send + Sync + 'static {
 
     fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<()>>;
 
-    fn reload_current_theme(&self, cx: &mut AppContext);
+    fn reload_current_theme(&self, cx: &mut App);
 
     fn list_icon_theme_names(
         &self,
@@ -145,7 +145,7 @@ impl ExtensionThemeProxy for ExtensionHostProxy {
         proxy.load_user_theme(theme_path, fs)
     }
 
-    fn reload_current_theme(&self, cx: &mut AppContext) {
+    fn reload_current_theme(&self, cx: &mut App) {
         let Some(proxy) = self.theme_proxy.read().clone() else {
             return;
         };
@@ -340,7 +340,7 @@ pub trait ExtensionContextServerProxy: Send + Sync + 'static {
         &self,
         extension: Arc<dyn Extension>,
         server_id: Arc<str>,
-        cx: &mut AppContext,
+        cx: &mut App,
     );
 }
 
@@ -349,7 +349,7 @@ impl ExtensionContextServerProxy for ExtensionHostProxy {
         &self,
         extension: Arc<dyn Extension>,
         server_id: Arc<str>,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) {
         let Some(proxy) = self.context_server_proxy.read().clone() else {
             return;

crates/extension_host/src/extension_host.rs 🔗

@@ -27,8 +27,8 @@ use futures::{
     select_biased, AsyncReadExt as _, Future, FutureExt as _, StreamExt as _,
 };
 use gpui::{
-    actions, AppContext, AsyncAppContext, Context, EventEmitter, Global, Model, ModelContext, Task,
-    WeakModel,
+    actions, App, AppContext as _, AsyncAppContext, Context, Entity, EventEmitter, Global, Task,
+    WeakEntity,
 };
 use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
 use language::{
@@ -113,7 +113,7 @@ pub struct ExtensionStore {
     pub wasm_host: Arc<WasmHost>,
     pub wasm_extensions: Vec<(Arc<ExtensionManifest>, WasmExtension)>,
     pub tasks: Vec<Task<()>>,
-    pub ssh_clients: HashMap<String, WeakModel<SshRemoteClient>>,
+    pub ssh_clients: HashMap<String, WeakEntity<SshRemoteClient>>,
     pub ssh_registered_tx: UnboundedSender<()>,
 }
 
@@ -134,7 +134,7 @@ pub enum Event {
 
 impl EventEmitter<Event> for ExtensionStore {}
 
-struct GlobalExtensionStore(Model<ExtensionStore>);
+struct GlobalExtensionStore(Entity<ExtensionStore>);
 
 impl Global for GlobalExtensionStore {}
 
@@ -181,11 +181,11 @@ pub fn init(
     fs: Arc<dyn Fs>,
     client: Arc<Client>,
     node_runtime: NodeRuntime,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) {
     ExtensionSettings::register(cx);
 
-    let store = cx.new_model(move |cx| {
+    let store = cx.new(move |cx| {
         ExtensionStore::new(
             paths::extensions_dir().clone(),
             None,
@@ -208,12 +208,12 @@ pub fn init(
 }
 
 impl ExtensionStore {
-    pub fn try_global(cx: &AppContext) -> Option<Model<Self>> {
+    pub fn try_global(cx: &App) -> Option<Entity<Self>> {
         cx.try_global::<GlobalExtensionStore>()
             .map(|store| store.0.clone())
     }
 
-    pub fn global(cx: &AppContext) -> Model<Self> {
+    pub fn global(cx: &App) -> Entity<Self> {
         cx.global::<GlobalExtensionStore>().0.clone()
     }
 
@@ -227,7 +227,7 @@ impl ExtensionStore {
         builder_client: Arc<dyn HttpClient>,
         telemetry: Option<Arc<Telemetry>>,
         node_runtime: NodeRuntime,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Self {
         let work_dir = extensions_dir.join("work");
         let build_dir = build_dir.unwrap_or_else(|| extensions_dir.join("build"));
@@ -400,7 +400,7 @@ impl ExtensionStore {
     pub fn reload(
         &mut self,
         modified_extension: Option<Arc<str>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> impl Future<Output = ()> {
         let (tx, rx) = oneshot::channel();
         self.reload_complete_senders.push(tx);
@@ -447,7 +447,7 @@ impl ExtensionStore {
     pub fn fetch_extensions(
         &self,
         search: Option<&str>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<Vec<ExtensionMetadata>>> {
         let version = CURRENT_SCHEMA_VERSION.to_string();
         let mut query = vec![("max_schema_version", version.as_str())];
@@ -460,7 +460,7 @@ impl ExtensionStore {
 
     pub fn fetch_extensions_with_update_available(
         &mut self,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<Vec<ExtensionMetadata>>> {
         let schema_versions = schema_version_range();
         let wasm_api_versions = wasm_api_version_range(ReleaseChannel::global(cx));
@@ -508,7 +508,7 @@ impl ExtensionStore {
     pub fn fetch_extension_versions(
         &self,
         extension_id: &str,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<Vec<ExtensionMetadata>>> {
         self.fetch_extensions_from_api(&format!("/extensions/{extension_id}"), &[], cx)
     }
@@ -517,7 +517,7 @@ impl ExtensionStore {
     ///
     /// This can be used to make certain functionality provided by extensions
     /// available out-of-the-box.
-    pub fn auto_install_extensions(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn auto_install_extensions(&mut self, cx: &mut Context<Self>) {
         let extension_settings = ExtensionSettings::get_global(cx);
 
         let extensions_to_install = extension_settings
@@ -545,7 +545,7 @@ impl ExtensionStore {
         .detach();
     }
 
-    pub fn check_for_updates(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn check_for_updates(&mut self, cx: &mut Context<Self>) {
         let task = self.fetch_extensions_with_update_available(cx);
         cx.spawn(move |this, mut cx| async move {
             Self::upgrade_extensions(this, task.await?, &mut cx).await
@@ -554,7 +554,7 @@ impl ExtensionStore {
     }
 
     async fn upgrade_extensions(
-        this: WeakModel<Self>,
+        this: WeakEntity<Self>,
         extensions: Vec<ExtensionMetadata>,
         cx: &mut AsyncAppContext,
     ) -> Result<()> {
@@ -587,7 +587,7 @@ impl ExtensionStore {
         &self,
         path: &str,
         query: &[(&str, &str)],
-        cx: &mut ModelContext<'_, ExtensionStore>,
+        cx: &mut Context<'_, ExtensionStore>,
     ) -> Task<Result<Vec<ExtensionMetadata>>> {
         let url = self.http_client.build_zed_api_url(path, query);
         let http_client = self.http_client.clone();
@@ -620,7 +620,7 @@ impl ExtensionStore {
         &mut self,
         extension_id: Arc<str>,
         version: Arc<str>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.install_or_upgrade_extension(extension_id, version, ExtensionOperation::Install, cx)
             .detach_and_log_err(cx);
@@ -631,7 +631,7 @@ impl ExtensionStore {
         extension_id: Arc<str>,
         url: Url,
         operation: ExtensionOperation,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let extension_dir = self.installed_dir.join(extension_id.as_ref());
         let http_client = self.http_client.clone();
@@ -705,11 +705,7 @@ impl ExtensionStore {
         })
     }
 
-    pub fn install_latest_extension(
-        &mut self,
-        extension_id: Arc<str>,
-        cx: &mut ModelContext<Self>,
-    ) {
+    pub fn install_latest_extension(&mut self, extension_id: Arc<str>, cx: &mut Context<Self>) {
         log::info!("installing extension {extension_id} latest version");
 
         let schema_versions = schema_version_range();
@@ -747,7 +743,7 @@ impl ExtensionStore {
         &mut self,
         extension_id: Arc<str>,
         version: Arc<str>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         self.install_or_upgrade_extension(extension_id, version, ExtensionOperation::Upgrade, cx)
     }
@@ -757,7 +753,7 @@ impl ExtensionStore {
         extension_id: Arc<str>,
         version: Arc<str>,
         operation: ExtensionOperation,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         log::info!("installing extension {extension_id} {version}");
         let Some(url) = self
@@ -774,7 +770,7 @@ impl ExtensionStore {
         self.install_or_upgrade_extension_at_endpoint(extension_id, url, operation, cx)
     }
 
-    pub fn uninstall_extension(&mut self, extension_id: Arc<str>, cx: &mut ModelContext<Self>) {
+    pub fn uninstall_extension(&mut self, extension_id: Arc<str>, cx: &mut Context<Self>) {
         let extension_dir = self.installed_dir.join(extension_id.as_ref());
         let work_dir = self.wasm_host.work_dir.join(extension_id.as_ref());
         let fs = self.fs.clone();
@@ -826,7 +822,7 @@ impl ExtensionStore {
     pub fn install_dev_extension(
         &mut self,
         extension_source_path: PathBuf,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let extensions_dir = self.extensions_dir();
         let fs = self.fs.clone();
@@ -901,7 +897,7 @@ impl ExtensionStore {
         })
     }
 
-    pub fn rebuild_dev_extension(&mut self, extension_id: Arc<str>, cx: &mut ModelContext<Self>) {
+    pub fn rebuild_dev_extension(&mut self, extension_id: Arc<str>, cx: &mut Context<Self>) {
         let path = self.installed_dir.join(extension_id.as_ref());
         let builder = self.builder.clone();
         let fs = self.fs.clone();
@@ -950,7 +946,7 @@ impl ExtensionStore {
     fn extensions_updated(
         &mut self,
         new_index: ExtensionIndex,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<()> {
         let old_index = &self.extension_index;
 
@@ -1271,7 +1267,7 @@ impl ExtensionStore {
         })
     }
 
-    fn rebuild_extension_index(&self, cx: &mut ModelContext<Self>) -> Task<ExtensionIndex> {
+    fn rebuild_extension_index(&self, cx: &mut Context<Self>) -> Task<ExtensionIndex> {
         let fs = self.fs.clone();
         let work_dir = self.wasm_host.work_dir.clone();
         let extensions_dir = self.installed_dir.clone();
@@ -1459,7 +1455,7 @@ impl ExtensionStore {
         extension_id: Arc<str>,
         is_dev: bool,
         tmp_dir: PathBuf,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let src_dir = self.extensions_dir().join(extension_id.as_ref());
         let Some(loaded_extension) = self.extension_index.extensions.get(&extension_id).cloned()
@@ -1518,8 +1514,8 @@ impl ExtensionStore {
     }
 
     async fn sync_extensions_over_ssh(
-        this: &WeakModel<Self>,
-        client: WeakModel<SshRemoteClient>,
+        this: &WeakEntity<Self>,
+        client: WeakEntity<SshRemoteClient>,
         cx: &mut AsyncAppContext,
     ) -> Result<()> {
         let extensions = this.update(cx, |this, _cx| {
@@ -1586,7 +1582,7 @@ impl ExtensionStore {
     }
 
     pub async fn update_ssh_clients(
-        this: &WeakModel<Self>,
+        this: &WeakEntity<Self>,
         cx: &mut AsyncAppContext,
     ) -> Result<()> {
         let clients = this.update(cx, |this, _cx| {
@@ -1603,11 +1599,7 @@ impl ExtensionStore {
         anyhow::Ok(())
     }
 
-    pub fn register_ssh_client(
-        &mut self,
-        client: Model<SshRemoteClient>,
-        cx: &mut ModelContext<Self>,
-    ) {
+    pub fn register_ssh_client(&mut self, client: Entity<SshRemoteClient>, cx: &mut Context<Self>) {
         let connection_options = client.read(cx).connection_options();
         if self.ssh_clients.contains_key(&connection_options.ssh_url()) {
             return;

crates/extension_host/src/extension_settings.rs 🔗

@@ -1,6 +1,6 @@
 use anyhow::Result;
 use collections::HashMap;
-use gpui::AppContext;
+use gpui::App;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsSources};
@@ -40,7 +40,7 @@ impl Settings for ExtensionSettings {
 
     type FileContent = Self;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _cx: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _cx: &mut App) -> Result<Self> {
         SettingsSources::<Self::FileContent>::json_merge_with(
             [sources.default]
                 .into_iter()

crates/extension_host/src/extension_store_test.rs 🔗

@@ -8,7 +8,7 @@ use collections::BTreeMap;
 use extension::ExtensionHostProxy;
 use fs::{FakeFs, Fs, RealFs};
 use futures::{io::BufReader, AsyncReadExt, StreamExt};
-use gpui::{Context, SemanticVersion, TestAppContext};
+use gpui::{AppContext as _, SemanticVersion, TestAppContext};
 use http_client::{FakeHttpClient, Response};
 use language::{LanguageMatcher, LanguageRegistry, LanguageServerBinaryStatus};
 use lsp::LanguageServerName;
@@ -270,7 +270,7 @@ async fn test_extension_store(cx: &mut TestAppContext) {
     language_extension::init(proxy.clone(), language_registry.clone());
     let node_runtime = NodeRuntime::unavailable();
 
-    let store = cx.new_model(|cx| {
+    let store = cx.new(|cx| {
         ExtensionStore::new(
             PathBuf::from("/the-extension-dir"),
             None,
@@ -396,7 +396,7 @@ async fn test_extension_store(cx: &mut TestAppContext) {
 
     // Create new extension store, as if Zed were restarting.
     drop(store);
-    let store = cx.new_model(|cx| {
+    let store = cx.new(|cx| {
         ExtensionStore::new(
             PathBuf::from("/the-extension-dir"),
             None,
@@ -577,7 +577,7 @@ async fn test_extension_store_with_test_extension(cx: &mut TestAppContext) {
     let builder_client =
         Arc::new(ReqwestClient::user_agent(&user_agent).expect("Could not create HTTP client"));
 
-    let extension_store = cx.new_model(|cx| {
+    let extension_store = cx.new(|cx| {
         ExtensionStore::new(
             extensions_dir.clone(),
             Some(cache_dir),

crates/extension_host/src/headless_host.rs 🔗

@@ -8,7 +8,7 @@ use extension::{
     ExtensionManifest,
 };
 use fs::{Fs, RemoveOptions, RenameOptions};
-use gpui::{AppContext, AsyncAppContext, Context, Model, ModelContext, Task, WeakModel};
+use gpui::{App, AppContext as _, AsyncAppContext, Context, Entity, Task, WeakEntity};
 use http_client::HttpClient;
 use language::{LanguageConfig, LanguageName, LanguageQueries, LoadedLanguage};
 use lsp::LanguageServerName;
@@ -40,9 +40,9 @@ impl HeadlessExtensionStore {
         extension_dir: PathBuf,
         extension_host_proxy: Arc<ExtensionHostProxy>,
         node_runtime: NodeRuntime,
-        cx: &mut AppContext,
-    ) -> Model<Self> {
-        cx.new_model(|cx| Self {
+        cx: &mut App,
+    ) -> Entity<Self> {
+        cx.new(|cx| Self {
             fs: fs.clone(),
             wasm_host: WasmHost::new(
                 fs.clone(),
@@ -63,7 +63,7 @@ impl HeadlessExtensionStore {
     pub fn sync_extensions(
         &mut self,
         extensions: Vec<ExtensionVersion>,
-        cx: &ModelContext<Self>,
+        cx: &Context<Self>,
     ) -> Task<Result<Vec<ExtensionVersion>>> {
         let on_client = HashSet::from_iter(extensions.iter().map(|e| e.id.as_str()));
         let to_remove: Vec<Arc<str>> = self
@@ -111,7 +111,7 @@ impl HeadlessExtensionStore {
     }
 
     pub async fn load_extension(
-        this: WeakModel<Self>,
+        this: WeakEntity<Self>,
         extension: ExtensionVersion,
         cx: &mut AsyncAppContext,
     ) -> Result<()> {
@@ -198,7 +198,7 @@ impl HeadlessExtensionStore {
     fn uninstall_extension(
         &mut self,
         extension_id: &Arc<str>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         self.loaded_extensions.remove(extension_id);
 
@@ -235,7 +235,7 @@ impl HeadlessExtensionStore {
         &mut self,
         extension: ExtensionVersion,
         tmp_path: PathBuf,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let path = self.extension_dir.join(&extension.id);
         let fs = self.fs.clone();
@@ -256,7 +256,7 @@ impl HeadlessExtensionStore {
     }
 
     pub async fn handle_sync_extensions(
-        extension_store: Model<HeadlessExtensionStore>,
+        extension_store: Entity<HeadlessExtensionStore>,
         envelope: TypedEnvelope<proto::SyncExtensions>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::SyncExtensionsResponse> {
@@ -292,7 +292,7 @@ impl HeadlessExtensionStore {
     }
 
     pub async fn handle_install_extension(
-        extensions: Model<HeadlessExtensionStore>,
+        extensions: Entity<HeadlessExtensionStore>,
         envelope: TypedEnvelope<proto::InstallExtension>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::Ack> {

crates/extension_host/src/wasm_host.rs 🔗

@@ -17,7 +17,7 @@ use futures::{
     future::BoxFuture,
     Future, FutureExt, StreamExt as _,
 };
-use gpui::{AppContext, AsyncAppContext, BackgroundExecutor, Task};
+use gpui::{App, AsyncAppContext, BackgroundExecutor, Task};
 use http_client::HttpClient;
 use language::LanguageName;
 use lsp::LanguageServerName;
@@ -332,7 +332,7 @@ impl WasmHost {
         node_runtime: NodeRuntime,
         proxy: Arc<ExtensionHostProxy>,
         work_dir: PathBuf,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Arc<Self> {
         let (tx, mut rx) = mpsc::unbounded::<MainThreadCall>();
         let task = cx.spawn(|mut cx| async move {

crates/extension_host/src/wasm_host/wit.rs 🔗

@@ -10,7 +10,7 @@ use release_channel::ReleaseChannel;
 use since_v0_2_0 as latest;
 
 use super::{wasm_engine, WasmState};
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use semantic_version::SemanticVersion;
 use std::{ops::RangeInclusive, sync::Arc};
 use wasmtime::{

crates/extensions_ui/src/components/extension_card.rs 🔗

@@ -29,7 +29,7 @@ impl ParentElement for ExtensionCard {
 }
 
 impl RenderOnce for ExtensionCard {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         div().w_full().child(
             v_flex()
                 .w_full()

crates/extensions_ui/src/components/feature_upsell.rs 🔗

@@ -44,7 +44,7 @@ impl FeatureUpsell {
 }
 
 impl RenderOnce for FeatureUpsell {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         self.base
             .p_4()
             .justify_between()
@@ -63,7 +63,7 @@ impl RenderOnce for FeatureUpsell {
                             )
                             .on_click({
                                 let docs_url = docs_url.clone();
-                                move |_event, cx| {
+                                move |_event, _window, cx| {
                                     telemetry::event!(
                                         "Documentation Viewed",
                                         source = "Feature Upsell",

crates/extensions_ui/src/extension_suggest.rs 🔗

@@ -5,13 +5,10 @@ use std::sync::{Arc, OnceLock};
 use db::kvp::KEY_VALUE_STORE;
 use editor::Editor;
 use extension_host::ExtensionStore;
-use gpui::{Model, VisualContext};
+use gpui::{AppContext as _, Context, Entity, SharedString, Window};
 use language::Buffer;
-use ui::{SharedString, ViewContext};
-use workspace::{
-    notifications::{simple_message_notification, NotificationId},
-    Workspace,
-};
+use workspace::notifications::simple_message_notification::MessageNotification;
+use workspace::{notifications::NotificationId, Workspace};
 
 const SUGGESTIONS_BY_EXTENSION_ID: &[(&str, &[&str])] = &[
     ("astro", &["astro"]),
@@ -136,7 +133,7 @@ fn language_extension_key(extension_id: &str) -> String {
     format!("{}_extension_suggest", extension_id)
 }
 
-pub(crate) fn suggest(buffer: Model<Buffer>, cx: &mut ViewContext<Workspace>) {
+pub(crate) fn suggest(buffer: Entity<Buffer>, window: &mut Window, cx: &mut Context<Workspace>) {
     let Some(file) = buffer.read(cx).file().cloned() else {
         return;
     };
@@ -154,7 +151,7 @@ pub(crate) fn suggest(buffer: Model<Buffer>, cx: &mut ViewContext<Workspace>) {
         return;
     };
 
-    cx.on_next_frame(move |workspace, cx| {
+    cx.on_next_frame(window, move |workspace, _, cx| {
         let Some(editor) = workspace.active_item_as::<Editor>(cx) else {
             return;
         };
@@ -170,15 +167,15 @@ pub(crate) fn suggest(buffer: Model<Buffer>, cx: &mut ViewContext<Workspace>) {
         );
 
         workspace.show_notification(notification_id, cx, |cx| {
-            cx.new_view(move |_cx| {
-                simple_message_notification::MessageNotification::new(format!(
+            cx.new(move |_cx| {
+                MessageNotification::new(format!(
                     "Do you want to install the recommended '{}' extension for '{}' files?",
                     extension_id, file_name_or_extension
                 ))
                 .with_click_message("Yes, install extension")
                 .on_click({
                     let extension_id = extension_id.clone();
-                    move |cx| {
+                    move |_window, cx| {
                         let extension_id = extension_id.clone();
                         let extension_store = ExtensionStore::global(cx);
                         extension_store.update(cx, move |store, cx| {
@@ -187,7 +184,7 @@ pub(crate) fn suggest(buffer: Model<Buffer>, cx: &mut ViewContext<Workspace>) {
                     }
                 })
                 .with_secondary_click_message("No, don't install it")
-                .on_secondary_click(move |cx| {
+                .on_secondary_click(move |_window, cx| {
                     let key = language_extension_key(&extension_id);
                     db::write_and_log(cx, move || {
                         KEY_VALUE_STORE.write_kvp(key, "dismissed".to_string())

crates/extensions_ui/src/extension_version_selector.rs 🔗

@@ -5,9 +5,7 @@ use client::ExtensionMetadata;
 use extension_host::{ExtensionSettings, ExtensionStore};
 use fs::Fs;
 use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
-use gpui::{
-    prelude::*, AppContext, DismissEvent, EventEmitter, FocusableView, Task, View, WeakView,
-};
+use gpui::{prelude::*, App, DismissEvent, Entity, EventEmitter, Focusable, Task, WeakEntity};
 use picker::{Picker, PickerDelegate};
 use release_channel::ReleaseChannel;
 use semantic_version::SemanticVersion;
@@ -17,35 +15,39 @@ use util::ResultExt;
 use workspace::ModalView;
 
 pub struct ExtensionVersionSelector {
-    picker: View<Picker<ExtensionVersionSelectorDelegate>>,
+    picker: Entity<Picker<ExtensionVersionSelectorDelegate>>,
 }
 
 impl ModalView for ExtensionVersionSelector {}
 
 impl EventEmitter<DismissEvent> for ExtensionVersionSelector {}
 
-impl FocusableView for ExtensionVersionSelector {
-    fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
+impl Focusable for ExtensionVersionSelector {
+    fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
 
 impl Render for ExtensionVersionSelector {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         v_flex().w(rems(34.)).child(self.picker.clone())
     }
 }
 
 impl ExtensionVersionSelector {
-    pub fn new(delegate: ExtensionVersionSelectorDelegate, cx: &mut ViewContext<Self>) -> Self {
-        let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
+    pub fn new(
+        delegate: ExtensionVersionSelectorDelegate,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Self {
+        let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
         Self { picker }
     }
 }
 
 pub struct ExtensionVersionSelectorDelegate {
     fs: Arc<dyn Fs>,
-    view: WeakView<ExtensionVersionSelector>,
+    selector: WeakEntity<ExtensionVersionSelector>,
     extension_versions: Vec<ExtensionMetadata>,
     selected_index: usize,
     matches: Vec<StringMatch>,
@@ -54,7 +56,7 @@ pub struct ExtensionVersionSelectorDelegate {
 impl ExtensionVersionSelectorDelegate {
     pub fn new(
         fs: Arc<dyn Fs>,
-        weak_view: WeakView<ExtensionVersionSelector>,
+        selector: WeakEntity<ExtensionVersionSelector>,
         mut extension_versions: Vec<ExtensionMetadata>,
     ) -> Self {
         extension_versions.sort_unstable_by(|a, b| {
@@ -79,7 +81,7 @@ impl ExtensionVersionSelectorDelegate {
 
         Self {
             fs,
-            view: weak_view,
+            selector,
             extension_versions,
             selected_index: 0,
             matches,
@@ -90,7 +92,7 @@ impl ExtensionVersionSelectorDelegate {
 impl PickerDelegate for ExtensionVersionSelectorDelegate {
     type ListItem = ui::ListItem;
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Select extension version...".into()
     }
 
@@ -102,11 +104,21 @@ impl PickerDelegate for ExtensionVersionSelectorDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, _cx: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
+    ) {
         self.selected_index = ix;
     }
 
-    fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
+    fn update_matches(
+        &mut self,
+        query: String,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Task<()> {
         let background_executor = cx.background_executor().clone();
         let candidates = self
             .extension_versions
@@ -117,7 +129,7 @@ impl PickerDelegate for ExtensionVersionSelectorDelegate {
             })
             .collect::<Vec<_>>();
 
-        cx.spawn(move |this, mut cx| async move {
+        cx.spawn_in(window, move |this, mut cx| async move {
             let matches = if query.is_empty() {
                 candidates
                     .into_iter()
@@ -152,9 +164,9 @@ impl PickerDelegate for ExtensionVersionSelectorDelegate {
         })
     }
 
-    fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
         if self.matches.is_empty() {
-            self.dismissed(cx);
+            self.dismissed(window, cx);
             return;
         }
 
@@ -181,8 +193,8 @@ impl PickerDelegate for ExtensionVersionSelectorDelegate {
         });
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
-        self.view
+    fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<Self>>) {
+        self.selector
             .update(cx, |_, cx| cx.emit(DismissEvent))
             .log_err();
     }
@@ -191,7 +203,8 @@ impl PickerDelegate for ExtensionVersionSelectorDelegate {
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let version_match = &self.matches[ix];
         let extension_version = &self.extension_versions[version_match.candidate_id];

crates/extensions_ui/src/extensions_ui.rs 🔗

@@ -2,7 +2,6 @@ mod components;
 mod extension_suggest;
 mod extension_version_selector;
 
-use std::ops::DerefMut;
 use std::sync::OnceLock;
 use std::time::Duration;
 use std::{ops::Range, sync::Arc};
@@ -13,9 +12,9 @@ use editor::{Editor, EditorElement, EditorStyle};
 use extension_host::{ExtensionManifest, ExtensionOperation, ExtensionStore};
 use fuzzy::{match_strings, StringMatchCandidate};
 use gpui::{
-    actions, uniform_list, Action, AppContext, ClipboardItem, EventEmitter, Flatten, FocusableView,
-    InteractiveElement, KeyContext, ParentElement, Render, Styled, Task, TextStyle,
-    UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WindowContext,
+    actions, uniform_list, Action, App, ClipboardItem, Context, Entity, EventEmitter, Flatten,
+    Focusable, InteractiveElement, KeyContext, ParentElement, Render, Styled, Task, TextStyle,
+    UniformListScrollHandle, WeakEntity, Window,
 };
 use num_format::{Locale, ToFormattedString};
 use project::DirectoryLister;
@@ -36,10 +35,13 @@ use crate::extension_version_selector::{
 
 actions!(zed, [InstallDevExtension]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(move |workspace: &mut Workspace, cx| {
+pub fn init(cx: &mut App) {
+    cx.observe_new(move |workspace: &mut Workspace, window, cx| {
+        let Some(window) = window else {
+            return;
+        };
         workspace
-            .register_action(move |workspace, _: &zed_actions::Extensions, cx| {
+            .register_action(move |workspace, _: &zed_actions::Extensions, window, cx| {
                 let existing = workspace
                     .active_pane()
                     .read(cx)
@@ -47,13 +49,19 @@ pub fn init(cx: &mut AppContext) {
                     .find_map(|item| item.downcast::<ExtensionsPage>());
 
                 if let Some(existing) = existing {
-                    workspace.activate_item(&existing, true, true, cx);
+                    workspace.activate_item(&existing, true, true, window, cx);
                 } else {
-                    let extensions_page = ExtensionsPage::new(workspace, cx);
-                    workspace.add_item_to_active_pane(Box::new(extensions_page), None, true, cx)
+                    let extensions_page = ExtensionsPage::new(workspace, window, cx);
+                    workspace.add_item_to_active_pane(
+                        Box::new(extensions_page),
+                        None,
+                        true,
+                        window,
+                        cx,
+                    )
                 }
             })
-            .register_action(move |workspace, _: &InstallDevExtension, cx| {
+            .register_action(move |workspace, _: &InstallDevExtension, window, cx| {
                 let store = ExtensionStore::global(cx);
                 let prompt = workspace.prompt_for_open_path(
                     gpui::PathPromptOptions {
@@ -62,12 +70,13 @@ pub fn init(cx: &mut AppContext) {
                         multiple: false,
                     },
                     DirectoryLister::Local(workspace.app_state().fs.clone()),
+                    window,
                     cx,
                 );
 
-                let workspace_handle = cx.view().downgrade();
-                cx.deref_mut()
-                    .spawn(|mut cx| async move {
+                let workspace_handle = cx.model().downgrade();
+                window
+                    .spawn(cx, |mut cx| async move {
                         let extension_path =
                             match Flatten::flatten(prompt.await.map_err(|e| e.into())) {
                                 Ok(Some(mut paths)) => paths.pop()?,
@@ -107,9 +116,9 @@ pub fn init(cx: &mut AppContext) {
                     .detach();
             });
 
-        cx.subscribe(workspace.project(), |_, _, event, cx| {
+        cx.subscribe_in(workspace.project(), window, |_, _, event, window, cx| {
             if let project::Event::LanguageNotFound(buffer) = event {
-                extension_suggest::suggest(buffer.clone(), cx);
+                extension_suggest::suggest(buffer.clone(), window, cx);
             }
         })
         .detach();
@@ -192,14 +201,14 @@ fn keywords_by_feature() -> &'static BTreeMap<Feature, Vec<&'static str>> {
 }
 
 pub struct ExtensionsPage {
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     list: UniformListScrollHandle,
     is_fetching_extensions: bool,
     filter: ExtensionFilter,
     remote_extension_entries: Vec<ExtensionMetadata>,
     dev_extension_entries: Vec<Arc<ExtensionManifest>>,
     filtered_remote_extension_indices: Vec<usize>,
-    query_editor: View<Editor>,
+    query_editor: Entity<Editor>,
     query_contains_error: bool,
     _subscriptions: [gpui::Subscription; 2],
     extension_fetch_task: Option<Task<()>>,
@@ -207,23 +216,37 @@ pub struct ExtensionsPage {
 }
 
 impl ExtensionsPage {
-    pub fn new(workspace: &Workspace, cx: &mut ViewContext<Workspace>) -> View<Self> {
-        cx.new_view(|cx: &mut ViewContext<Self>| {
+    pub fn new(
+        workspace: &Workspace,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Entity<Self> {
+        cx.new(|cx| {
             let store = ExtensionStore::global(cx);
             let workspace_handle = workspace.weak_handle();
             let subscriptions = [
-                cx.observe(&store, |_, _, cx| cx.notify()),
-                cx.subscribe(&store, move |this, _, event, cx| match event {
-                    extension_host::Event::ExtensionsUpdated => this.fetch_extensions_debounced(cx),
-                    extension_host::Event::ExtensionInstalled(extension_id) => {
-                        this.on_extension_installed(workspace_handle.clone(), extension_id, cx)
-                    }
-                    _ => {}
-                }),
+                cx.observe(&store, |_: &mut Self, _, cx| cx.notify()),
+                cx.subscribe_in(
+                    &store,
+                    window,
+                    move |this, _, event, window, cx| match event {
+                        extension_host::Event::ExtensionsUpdated => {
+                            this.fetch_extensions_debounced(cx)
+                        }
+                        extension_host::Event::ExtensionInstalled(extension_id) => this
+                            .on_extension_installed(
+                                workspace_handle.clone(),
+                                extension_id,
+                                window,
+                                cx,
+                            ),
+                        _ => {}
+                    },
+                ),
             ];
 
-            let query_editor = cx.new_view(|cx| {
-                let mut input = Editor::single_line(cx);
+            let query_editor = cx.new(|cx| {
+                let mut input = Editor::single_line(window, cx);
                 input.set_placeholder_text("Search extensions...", cx);
                 input
             });
@@ -250,9 +273,10 @@ impl ExtensionsPage {
 
     fn on_extension_installed(
         &mut self,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         extension_id: &str,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let extension_store = ExtensionStore::global(cx).read(cx);
         let themes = extension_store
@@ -262,11 +286,12 @@ impl ExtensionsPage {
         if !themes.is_empty() {
             workspace
                 .update(cx, |_workspace, cx| {
-                    cx.dispatch_action(
+                    window.dispatch_action(
                         zed_actions::theme_selector::Toggle {
                             themes_filter: Some(themes),
                         }
                         .boxed_clone(),
+                        cx,
                     );
                 })
                 .ok();
@@ -274,7 +299,7 @@ impl ExtensionsPage {
     }
 
     /// Returns whether a dev extension currently exists for the extension with the given ID.
-    fn dev_extension_exists(extension_id: &str, cx: &mut ViewContext<Self>) -> bool {
+    fn dev_extension_exists(extension_id: &str, cx: &mut Context<Self>) -> bool {
         let extension_store = ExtensionStore::global(cx).read(cx);
 
         extension_store
@@ -282,7 +307,7 @@ impl ExtensionsPage {
             .any(|dev_extension| dev_extension.id.as_ref() == extension_id)
     }
 
-    fn extension_status(extension_id: &str, cx: &mut ViewContext<Self>) -> ExtensionStatus {
+    fn extension_status(extension_id: &str, cx: &mut Context<Self>) -> ExtensionStatus {
         let extension_store = ExtensionStore::global(cx).read(cx);
 
         match extension_store.outstanding_operations().get(extension_id) {
@@ -296,7 +321,7 @@ impl ExtensionsPage {
         }
     }
 
-    fn filter_extension_entries(&mut self, cx: &mut ViewContext<Self>) {
+    fn filter_extension_entries(&mut self, cx: &mut Context<Self>) {
         self.filtered_remote_extension_indices.clear();
         self.filtered_remote_extension_indices.extend(
             self.remote_extension_entries
@@ -319,7 +344,7 @@ impl ExtensionsPage {
         cx.notify();
     }
 
-    fn fetch_extensions(&mut self, search: Option<String>, cx: &mut ViewContext<Self>) {
+    fn fetch_extensions(&mut self, search: Option<String>, cx: &mut Context<Self>) {
         self.is_fetching_extensions = true;
         cx.notify();
 
@@ -374,7 +399,8 @@ impl ExtensionsPage {
     fn render_extensions(
         &mut self,
         range: Range<usize>,
-        cx: &mut ViewContext<Self>,
+        _: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Vec<ExtensionCard> {
         let dev_extension_entries_len = if self.filter.include_dev_extensions() {
             self.dev_extension_entries.len()
@@ -399,7 +425,7 @@ impl ExtensionsPage {
     fn render_dev_extension(
         &self,
         extension: &ExtensionManifest,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> ExtensionCard {
         let status = Self::extension_status(&extension.id, cx);
 
@@ -430,7 +456,7 @@ impl ExtensionsPage {
                                 )
                                 .on_click({
                                     let extension_id = extension.id.clone();
-                                    move |_, cx| {
+                                    move |_, _, cx| {
                                         ExtensionStore::global(cx).update(cx, |store, cx| {
                                             store.rebuild_dev_extension(extension_id.clone(), cx)
                                         });
@@ -443,7 +469,7 @@ impl ExtensionsPage {
                                 Button::new(SharedString::from(extension.id.clone()), "Uninstall")
                                     .on_click({
                                         let extension_id = extension.id.clone();
-                                        move |_, cx| {
+                                        move |_, _, cx| {
                                             ExtensionStore::global(cx).update(cx, |store, cx| {
                                                 store.uninstall_extension(extension_id.clone(), cx)
                                             });
@@ -493,11 +519,11 @@ impl ExtensionsPage {
                         .style(ButtonStyle::Filled)
                         .on_click(cx.listener({
                             let repository_url = repository_url.clone();
-                            move |_, _, cx| {
+                            move |_, _, _, cx| {
                                 cx.open_url(&repository_url);
                             }
                         }))
-                        .tooltip(move |cx| Tooltip::text(repository_url.clone(), cx))
+                        .tooltip(Tooltip::text(repository_url.clone()))
                     })),
             )
     }
@@ -505,9 +531,9 @@ impl ExtensionsPage {
     fn render_remote_extension(
         &self,
         extension: &ExtensionMetadata,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> ExtensionCard {
-        let this = cx.view().clone();
+        let this = cx.model().clone();
         let status = Self::extension_status(&extension.id, cx);
         let has_dev_extension = Self::dev_extension_exists(&extension.id, cx);
 
@@ -601,11 +627,11 @@ impl ExtensionsPage {
                                 .style(ButtonStyle::Filled)
                                 .on_click(cx.listener({
                                     let repository_url = repository_url.clone();
-                                    move |_, _, cx| {
+                                    move |_, _, _, cx| {
                                         cx.open_url(&repository_url);
                                     }
                                 }))
-                                .tooltip(move |cx| Tooltip::text(repository_url.clone(), cx)),
+                                .tooltip(Tooltip::text(repository_url.clone())),
                             )
                             .child(
                                 PopoverMenu::new(SharedString::from(format!(
@@ -621,10 +647,11 @@ impl ExtensionsPage {
                                     .icon_size(IconSize::Small)
                                     .style(ButtonStyle::Filled),
                                 )
-                                .menu(move |cx| {
+                                .menu(move |window, cx| {
                                     Some(Self::render_remote_extension_context_menu(
                                         &this,
                                         extension_id.clone(),
+                                        window,
                                         cx,
                                     ))
                                 }),
@@ -634,23 +661,26 @@ impl ExtensionsPage {
     }
 
     fn render_remote_extension_context_menu(
-        this: &View<Self>,
+        this: &Entity<Self>,
         extension_id: Arc<str>,
-        cx: &mut WindowContext,
-    ) -> View<ContextMenu> {
-        let context_menu = ContextMenu::build(cx, |context_menu, cx| {
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Entity<ContextMenu> {
+        let context_menu = ContextMenu::build(window, cx, |context_menu, window, _| {
             context_menu
                 .entry(
                     "Install Another Version...",
                     None,
-                    cx.handler_for(this, {
+                    window.handler_for(this, {
                         let extension_id = extension_id.clone();
-                        move |this, cx| this.show_extension_version_list(extension_id.clone(), cx)
+                        move |this, window, cx| {
+                            this.show_extension_version_list(extension_id.clone(), window, cx)
+                        }
                     }),
                 )
                 .entry("Copy Extension ID", None, {
                     let extension_id = extension_id.clone();
-                    move |cx| {
+                    move |_, cx| {
                         cx.write_to_clipboard(ClipboardItem::new_string(extension_id.to_string()));
                     }
                 })
@@ -659,12 +689,17 @@ impl ExtensionsPage {
         context_menu
     }
 
-    fn show_extension_version_list(&mut self, extension_id: Arc<str>, cx: &mut ViewContext<Self>) {
+    fn show_extension_version_list(
+        &mut self,
+        extension_id: Arc<str>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let Some(workspace) = self.workspace.upgrade() else {
             return;
         };
 
-        cx.spawn(move |this, mut cx| async move {
+        cx.spawn_in(window, move |this, mut cx| async move {
             let extension_versions_task = this.update(&mut cx, |_, cx| {
                 let extension_store = ExtensionStore::global(cx);
 
@@ -675,16 +710,16 @@ impl ExtensionsPage {
 
             let extension_versions = extension_versions_task.await?;
 
-            workspace.update(&mut cx, |workspace, cx| {
+            workspace.update_in(&mut cx, |workspace, window, cx| {
                 let fs = workspace.project().read(cx).fs().clone();
-                workspace.toggle_modal(cx, |cx| {
+                workspace.toggle_modal(window, cx, |window, cx| {
                     let delegate = ExtensionVersionSelectorDelegate::new(
                         fs,
-                        cx.view().downgrade(),
+                        cx.model().downgrade(),
                         extension_versions,
                     );
 
-                    ExtensionVersionSelector::new(delegate, cx)
+                    ExtensionVersionSelector::new(delegate, window, cx)
                 });
             })?;
 
@@ -698,7 +733,7 @@ impl ExtensionsPage {
         extension: &ExtensionMetadata,
         status: &ExtensionStatus,
         has_dev_extension: bool,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> (Button, Option<Button>) {
         let is_compatible =
             extension_host::is_version_compatible(ReleaseChannel::global(cx), extension);
@@ -716,7 +751,7 @@ impl ExtensionsPage {
             ExtensionStatus::NotInstalled => (
                 Button::new(SharedString::from(extension.id.clone()), "Install").on_click({
                     let extension_id = extension.id.clone();
-                    move |_, cx| {
+                    move |_, _, cx| {
                         telemetry::event!("Extension Installed");
                         ExtensionStore::global(cx).update(cx, |store, cx| {
                             store.install_latest_extension(extension_id.clone(), cx)
@@ -738,7 +773,7 @@ impl ExtensionsPage {
             ExtensionStatus::Installed(installed_version) => (
                 Button::new(SharedString::from(extension.id.clone()), "Uninstall").on_click({
                     let extension_id = extension.id.clone();
-                    move |_, cx| {
+                    move |_, _, cx| {
                         telemetry::event!("Extension Uninstalled", extension_id);
                         ExtensionStore::global(cx).update(cx, |store, cx| {
                             store.uninstall_extension(extension_id.clone(), cx)
@@ -753,12 +788,12 @@ impl ExtensionsPage {
                             .when(!is_compatible, |upgrade_button| {
                                 upgrade_button.disabled(true).tooltip({
                                     let version = extension.manifest.version.clone();
-                                    move |cx| {
-                                        Tooltip::text(
+                                    move |_, cx| {
+                                        Tooltip::simple(
                                             format!(
                                                 "v{version} is not compatible with this version of Zed.",
                                             ),
-                                            cx,
+                                             cx,
                                         )
                                     }
                                 })
@@ -767,7 +802,7 @@ impl ExtensionsPage {
                             .on_click({
                                 let extension_id = extension.id.clone();
                                 let version = extension.manifest.version.clone();
-                                move |_, cx| {
+                                move |_, _, cx| {
                                     telemetry::event!("Extension Installed", extension_id, version);
                                     ExtensionStore::global(cx).update(cx, |store, cx| {
                                         store
@@ -790,7 +825,7 @@ impl ExtensionsPage {
         }
     }
 
-    fn render_search(&self, cx: &mut ViewContext<Self>) -> Div {
+    fn render_search(&self, cx: &mut Context<Self>) -> Div {
         let mut key_context = KeyContext::new_with_defaults();
         key_context.add("BufferSearchBar");
 
@@ -815,7 +850,11 @@ impl ExtensionsPage {
         )
     }
 
-    fn render_text_input(&self, editor: &View<Editor>, cx: &ViewContext<Self>) -> impl IntoElement {
+    fn render_text_input(
+        &self,
+        editor: &Entity<Editor>,
+        cx: &mut Context<Self>,
+    ) -> impl IntoElement {
         let settings = ThemeSettings::get_global(cx);
         let text_style = TextStyle {
             color: if editor.read(cx).read_only(cx) {
@@ -845,9 +884,9 @@ impl ExtensionsPage {
 
     fn on_query_change(
         &mut self,
-        _: View<Editor>,
+        _: Entity<Editor>,
         event: &editor::EditorEvent,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if let editor::EditorEvent::Edited { .. } = event {
             self.query_contains_error = false;
@@ -856,7 +895,7 @@ impl ExtensionsPage {
         }
     }
 
-    fn fetch_extensions_debounced(&mut self, cx: &mut ViewContext<ExtensionsPage>) {
+    fn fetch_extensions_debounced(&mut self, cx: &mut Context<ExtensionsPage>) {
         self.extension_fetch_task = Some(cx.spawn(|this, mut cx| async move {
             let search = this
                 .update(&mut cx, |this, cx| this.search_query(cx))
@@ -882,7 +921,7 @@ impl ExtensionsPage {
         }));
     }
 
-    pub fn search_query(&self, cx: &WindowContext) -> Option<String> {
+    pub fn search_query(&self, cx: &mut App) -> Option<String> {
         let search = self.query_editor.read(cx).text(cx);
         if search.trim().is_empty() {
             None
@@ -891,7 +930,7 @@ impl ExtensionsPage {
         }
     }
 
-    fn render_empty_state(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render_empty_state(&self, cx: &mut Context<Self>) -> impl IntoElement {
         let has_search = self.search_query(cx).is_some();
 
         let message = if self.is_fetching_extensions {
@@ -928,7 +967,8 @@ impl ExtensionsPage {
     fn update_settings<T: Settings>(
         &mut self,
         selection: &ToggleState,
-        cx: &mut ViewContext<Self>,
+
+        cx: &mut Context<Self>,
         callback: impl 'static + Send + Fn(&mut T::FileContent, bool),
     ) {
         if let Some(workspace) = self.workspace.upgrade() {
@@ -946,7 +986,7 @@ impl ExtensionsPage {
         }
     }
 
-    fn refresh_feature_upsells(&mut self, cx: &mut ViewContext<Self>) {
+    fn refresh_feature_upsells(&mut self, cx: &mut Context<Self>) {
         let Some(search) = self.search_query(cx) else {
             self.upsells.clear();
             return;
@@ -970,7 +1010,7 @@ impl ExtensionsPage {
         }
     }
 
-    fn render_feature_upsells(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render_feature_upsells(&self, cx: &mut Context<Self>) -> impl IntoElement {
         let upsells_count = self.upsells.len();
 
         v_flex().children(self.upsells.iter().enumerate().map(|(ix, feature)| {
@@ -993,7 +1033,7 @@ impl ExtensionsPage {
                         } else {
                             ui::ToggleState::Unselected
                         },
-                        cx.listener(move |this, selection, cx| {
+                        cx.listener(move |this, selection, _, cx| {
                             telemetry::event!("Vim Mode Toggled", source = "Feature Upsell");
                             this.update_settings::<VimModeSetting>(
                                 selection,
@@ -1028,7 +1068,7 @@ impl ExtensionsPage {
 }
 
 impl Render for ExtensionsPage {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         v_flex()
             .size_full()
             .bg(cx.theme().colors().editor_background)
@@ -1049,8 +1089,8 @@ impl Render for ExtensionsPage {
                                 Button::new("install-dev-extension", "Install Dev Extension")
                                     .style(ButtonStyle::Filled)
                                     .size(ButtonSize::Large)
-                                    .on_click(|_event, cx| {
-                                        cx.dispatch_action(Box::new(InstallDevExtension))
+                                    .on_click(|_event, window, cx| {
+                                        window.dispatch_action(Box::new(InstallDevExtension), cx)
                                     }),
                             ),
                     )
@@ -1067,12 +1107,12 @@ impl Render for ExtensionsPage {
                                             .style(ButtonStyle::Filled)
                                             .size(ButtonSize::Large)
                                             .toggle_state(self.filter == ExtensionFilter::All)
-                                            .on_click(cx.listener(|this, _event, cx| {
+                                            .on_click(cx.listener(|this, _event, _, cx| {
                                                 this.filter = ExtensionFilter::All;
                                                 this.filter_extension_entries(cx);
                                             }))
-                                            .tooltip(move |cx| {
-                                                Tooltip::text("Show all extensions", cx)
+                                            .tooltip(move |_, cx| {
+                                                Tooltip::simple("Show all extensions", cx)
                                             })
                                             .first(),
                                     )
@@ -1081,12 +1121,12 @@ impl Render for ExtensionsPage {
                                             .style(ButtonStyle::Filled)
                                             .size(ButtonSize::Large)
                                             .toggle_state(self.filter == ExtensionFilter::Installed)
-                                            .on_click(cx.listener(|this, _event, cx| {
+                                            .on_click(cx.listener(|this, _event, _, cx| {
                                                 this.filter = ExtensionFilter::Installed;
                                                 this.filter_extension_entries(cx);
                                             }))
-                                            .tooltip(move |cx| {
-                                                Tooltip::text("Show installed extensions", cx)
+                                            .tooltip(move |_, cx| {
+                                                Tooltip::simple("Show installed extensions", cx)
                                             })
                                             .middle(),
                                     )
@@ -1097,12 +1137,12 @@ impl Render for ExtensionsPage {
                                             .toggle_state(
                                                 self.filter == ExtensionFilter::NotInstalled,
                                             )
-                                            .on_click(cx.listener(|this, _event, cx| {
+                                            .on_click(cx.listener(|this, _event, _, cx| {
                                                 this.filter = ExtensionFilter::NotInstalled;
                                                 this.filter_extension_entries(cx);
                                             }))
-                                            .tooltip(move |cx| {
-                                                Tooltip::text("Show not installed extensions", cx)
+                                            .tooltip(move |_, cx| {
+                                                Tooltip::simple("Show not installed extensions", cx)
                                             })
                                             .last(),
                                     ),
@@ -1120,10 +1160,10 @@ impl Render for ExtensionsPage {
                     return this.py_4().child(self.render_empty_state(cx));
                 }
 
-                let view = cx.view().clone();
+                let extensions_page = cx.model().clone();
                 let scroll_handle = self.list.clone();
                 this.child(
-                    uniform_list(view, "entries", count, Self::render_extensions)
+                    uniform_list(extensions_page, "entries", count, Self::render_extensions)
                         .flex_grow()
                         .pb_4()
                         .track_scroll(scroll_handle),
@@ -1134,8 +1174,8 @@ impl Render for ExtensionsPage {
 
 impl EventEmitter<ItemEvent> for ExtensionsPage {}
 
-impl FocusableView for ExtensionsPage {
-    fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
+impl Focusable for ExtensionsPage {
+    fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
         self.query_editor.read(cx).focus_handle(cx)
     }
 }
@@ -1143,7 +1183,7 @@ impl FocusableView for ExtensionsPage {
 impl Item for ExtensionsPage {
     type Event = ItemEvent;
 
-    fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
+    fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option<SharedString> {
         Some("Extensions".into())
     }
 
@@ -1158,8 +1198,9 @@ impl Item for ExtensionsPage {
     fn clone_on_split(
         &self,
         _workspace_id: Option<WorkspaceId>,
-        _: &mut ViewContext<Self>,
-    ) -> Option<View<Self>> {
+        _window: &mut Window,
+        _: &mut Context<Self>,
+    ) -> Option<Entity<Self>> {
         None
     }
 

crates/feature_flags/src/feature_flags.rs 🔗

@@ -1,10 +1,6 @@
 use futures::{channel::oneshot, FutureExt as _};
-use gpui::{AppContext, Global, Subscription, ViewContext};
-use std::{
-    future::Future,
-    pin::Pin,
-    task::{Context, Poll},
-};
+use gpui::{App, Context, Global, Subscription, Window};
+use std::{future::Future, pin::Pin, task::Poll};
 
 #[derive(Default)]
 struct FeatureFlags {
@@ -101,22 +97,22 @@ impl FeatureFlag for AutoCommand {
 }
 
 pub trait FeatureFlagViewExt<V: 'static> {
-    fn observe_flag<T: FeatureFlag, F>(&mut self, callback: F) -> Subscription
+    fn observe_flag<T: FeatureFlag, F>(&mut self, window: &Window, callback: F) -> Subscription
     where
-        F: Fn(bool, &mut V, &mut ViewContext<V>) + Send + Sync + 'static;
+        F: Fn(bool, &mut V, &mut Window, &mut Context<V>) + Send + Sync + 'static;
 }
 
-impl<V> FeatureFlagViewExt<V> for ViewContext<'_, V>
+impl<V> FeatureFlagViewExt<V> for Context<'_, V>
 where
     V: 'static,
 {
-    fn observe_flag<T: FeatureFlag, F>(&mut self, callback: F) -> Subscription
+    fn observe_flag<T: FeatureFlag, F>(&mut self, window: &Window, callback: F) -> Subscription
     where
-        F: Fn(bool, &mut V, &mut ViewContext<V>) + 'static,
+        F: Fn(bool, &mut V, &mut Window, &mut Context<V>) + 'static,
     {
-        self.observe_global::<FeatureFlags>(move |v, cx| {
+        self.observe_global_in::<FeatureFlags>(window, move |v, window, cx| {
             let feature_flags = cx.global::<FeatureFlags>();
-            callback(feature_flags.has_flag::<T>(), v, cx);
+            callback(feature_flags.has_flag::<T>(), v, window, cx);
         })
     }
 }
@@ -130,10 +126,10 @@ pub trait FeatureFlagAppExt {
 
     fn observe_flag<T: FeatureFlag, F>(&mut self, callback: F) -> Subscription
     where
-        F: FnMut(bool, &mut AppContext) + 'static;
+        F: FnMut(bool, &mut App) + 'static;
 }
 
-impl FeatureFlagAppExt for AppContext {
+impl FeatureFlagAppExt for App {
     fn update_flags(&mut self, staff: bool, flags: Vec<String>) {
         let feature_flags = self.default_global::<FeatureFlags>();
         feature_flags.staff = staff;
@@ -159,7 +155,7 @@ impl FeatureFlagAppExt for AppContext {
 
     fn observe_flag<T: FeatureFlag, F>(&mut self, mut callback: F) -> Subscription
     where
-        F: FnMut(bool, &mut AppContext) + 'static,
+        F: FnMut(bool, &mut App) + 'static,
     {
         self.observe_global::<FeatureFlags>(move |cx| {
             let feature_flags = cx.global::<FeatureFlags>();
@@ -196,7 +192,7 @@ pub struct WaitForFlag(oneshot::Receiver<bool>, Option<Subscription>);
 impl Future for WaitForFlag {
     type Output = bool;
 
-    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+    fn poll(mut self: Pin<&mut Self>, cx: &mut core::task::Context<'_>) -> Poll<Self::Output> {
         self.0.poll_unpin(cx).map(|result| {
             self.1.take();
             result.unwrap_or(false)

crates/feedback/src/feedback.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{actions, AppContext, ClipboardItem, PromptLevel};
+use gpui::{actions, App, ClipboardItem, PromptLevel};
 use system_specs::SystemSpecs;
 use util::ResultExt;
 use workspace::Workspace;
@@ -45,18 +45,23 @@ fn file_bug_report_url(specs: &SystemSpecs) -> String {
     )
 }
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(|workspace: &mut Workspace, cx| {
-        feedback_modal::FeedbackModal::register(workspace, cx);
+pub fn init(cx: &mut App) {
+    cx.observe_new(|workspace: &mut Workspace, window, cx| {
+        let Some(window) = window else {
+            return;
+        };
+        feedback_modal::FeedbackModal::register(workspace, window, cx);
         workspace
-            .register_action(|_, _: &CopySystemSpecsIntoClipboard, cx| {
-                let specs = SystemSpecs::new(cx);
+            .register_action(|_, _: &CopySystemSpecsIntoClipboard, window, cx| {
+                let specs = SystemSpecs::new(window, cx);
 
-                cx.spawn(|_, mut cx| async move {
+                cx.spawn_in(window, |_, mut cx| async move {
                     let specs = specs.await.to_string();
 
-                    cx.update(|cx| cx.write_to_clipboard(ClipboardItem::new_string(specs.clone())))
-                        .log_err();
+                    cx.update(|_, cx| {
+                        cx.write_to_clipboard(ClipboardItem::new_string(specs.clone()))
+                    })
+                    .log_err();
 
                     cx.prompt(
                         PromptLevel::Info,
@@ -65,33 +70,32 @@ pub fn init(cx: &mut AppContext) {
                         &["OK"],
                     )
                     .await
-                    .ok();
                 })
                 .detach();
             })
-            .register_action(|_, _: &RequestFeature, cx| {
-                let specs = SystemSpecs::new(cx);
-                cx.spawn(|_, mut cx| async move {
+            .register_action(|_, _: &RequestFeature, window, cx| {
+                let specs = SystemSpecs::new(window, cx);
+                cx.spawn_in(window, |_, mut cx| async move {
                     let specs = specs.await;
-                    cx.update(|cx| {
+                    cx.update(|_, cx| {
                         cx.open_url(&request_feature_url(&specs));
                     })
                     .log_err();
                 })
                 .detach();
             })
-            .register_action(move |_, _: &FileBugReport, cx| {
-                let specs = SystemSpecs::new(cx);
-                cx.spawn(|_, mut cx| async move {
+            .register_action(move |_, _: &FileBugReport, window, cx| {
+                let specs = SystemSpecs::new(window, cx);
+                cx.spawn_in(window, |_, mut cx| async move {
                     let specs = specs.await;
-                    cx.update(|cx| {
+                    cx.update(|_, cx| {
                         cx.open_url(&file_bug_report_url(&specs));
                     })
                     .log_err();
                 })
                 .detach();
             })
-            .register_action(move |_, _: &OpenZedRepo, cx| {
+            .register_action(move |_, _: &OpenZedRepo, _, cx| {
                 cx.open_url(zed_repo_url());
             });
     })

crates/feedback/src/feedback_modal.rs 🔗

@@ -11,8 +11,8 @@ use db::kvp::KEY_VALUE_STORE;
 use editor::{Editor, EditorEvent};
 use futures::AsyncReadExt;
 use gpui::{
-    div, rems, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Model,
-    PromptLevel, Render, Task, View, ViewContext,
+    div, rems, App, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable,
+    PromptLevel, Render, Task, Window,
 };
 use http_client::HttpClient;
 use language::Buffer;
@@ -76,23 +76,27 @@ enum SubmissionState {
 
 pub struct FeedbackModal {
     system_specs: SystemSpecs,
-    feedback_editor: View<Editor>,
-    email_address_editor: View<Editor>,
+    feedback_editor: Entity<Editor>,
+    email_address_editor: Entity<Editor>,
     submission_state: Option<SubmissionState>,
     dismiss_modal: bool,
     character_count: i32,
 }
 
-impl FocusableView for FeedbackModal {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for FeedbackModal {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.feedback_editor.focus_handle(cx)
     }
 }
 impl EventEmitter<DismissEvent> for FeedbackModal {}
 
 impl ModalView for FeedbackModal {
-    fn on_before_dismiss(&mut self, cx: &mut ViewContext<Self>) -> DismissDecision {
-        self.update_email_in_store(cx);
+    fn on_before_dismiss(
+        &mut self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> DismissDecision {
+        self.update_email_in_store(window, cx);
 
         if self.dismiss_modal {
             return DismissDecision::Dismiss(true);
@@ -103,9 +107,15 @@ impl ModalView for FeedbackModal {
             return DismissDecision::Dismiss(true);
         }
 
-        let answer = cx.prompt(PromptLevel::Info, "Discard feedback?", None, &["Yes", "No"]);
+        let answer = window.prompt(
+            PromptLevel::Info,
+            "Discard feedback?",
+            None,
+            &["Yes", "No"],
+            cx,
+        );
 
-        cx.spawn(move |this, mut cx| async move {
+        cx.spawn_in(window, move |this, mut cx| async move {
             if answer.await.ok() == Some(0) {
                 this.update(&mut cx, |this, cx| {
                     this.dismiss_modal = true;
@@ -121,11 +131,11 @@ impl ModalView for FeedbackModal {
 }
 
 impl FeedbackModal {
-    pub fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
-        let _handle = cx.view().downgrade();
-        workspace.register_action(move |workspace, _: &GiveFeedback, cx| {
+    pub fn register(workspace: &mut Workspace, _: &mut Window, cx: &mut Context<Workspace>) {
+        let _handle = cx.model().downgrade();
+        workspace.register_action(move |workspace, _: &GiveFeedback, window, cx| {
             workspace
-                .with_local_workspace(cx, |workspace, cx| {
+                .with_local_workspace(window, cx, |workspace, window, cx| {
                     let markdown = workspace
                         .app_state()
                         .languages
@@ -133,17 +143,17 @@ impl FeedbackModal {
 
                     let project = workspace.project().clone();
 
-                    let system_specs = SystemSpecs::new(cx);
-                    cx.spawn(|workspace, mut cx| async move {
+                    let system_specs = SystemSpecs::new(window, cx);
+                    cx.spawn_in(window, |workspace, mut cx| async move {
                         let markdown = markdown.await.log_err();
                         let buffer = project.update(&mut cx, |project, cx| {
                             project.create_local_buffer("", markdown, cx)
                         })?;
                         let system_specs = system_specs.await;
 
-                        workspace.update(&mut cx, |workspace, cx| {
-                            workspace.toggle_modal(cx, move |cx| {
-                                FeedbackModal::new(system_specs, project, buffer, cx)
+                        workspace.update_in(&mut cx, |workspace, window, cx| {
+                            workspace.toggle_modal(window, cx, move |window, cx| {
+                                FeedbackModal::new(system_specs, project, buffer, window, cx)
                             });
                         })?;
 
@@ -157,30 +167,31 @@ impl FeedbackModal {
 
     pub fn new(
         system_specs: SystemSpecs,
-        project: Model<Project>,
-        buffer: Model<Buffer>,
-        cx: &mut ViewContext<Self>,
+        project: Entity<Project>,
+        buffer: Entity<Buffer>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
-        let email_address_editor = cx.new_view(|cx| {
-            let mut editor = Editor::single_line(cx);
+        let email_address_editor = cx.new(|cx| {
+            let mut editor = Editor::single_line(window, cx);
             editor.set_placeholder_text("Email address (optional)", cx);
 
             if let Ok(Some(email_address)) = KEY_VALUE_STORE.read_kvp(DATABASE_KEY_NAME) {
-                editor.set_text(email_address, cx)
+                editor.set_text(email_address, window, cx)
             }
 
             editor
         });
 
-        let feedback_editor = cx.new_view(|cx| {
-            let mut editor = Editor::for_buffer(buffer, Some(project.clone()), cx);
+        let feedback_editor = cx.new(|cx| {
+            let mut editor = Editor::for_buffer(buffer, Some(project.clone()), window, cx);
             editor.set_placeholder_text(
                 "You can use markdown to organize your feedback with code and links.",
                 cx,
             );
             editor.set_show_gutter(false, cx);
             editor.set_show_indent_guides(false, cx);
-            editor.set_show_inline_completions(Some(false), cx);
+            editor.set_show_inline_completions(Some(false), window, cx);
             editor.set_vertical_scroll_margin(5, cx);
             editor.set_use_modal_editing(false);
             editor
@@ -211,19 +222,24 @@ impl FeedbackModal {
         }
     }
 
-    pub fn submit(&mut self, cx: &mut ViewContext<Self>) -> Task<anyhow::Result<()>> {
+    pub fn submit(
+        &mut self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Task<anyhow::Result<()>> {
         let feedback_text = self.feedback_editor.read(cx).text(cx).trim().to_string();
         let email = self.email_address_editor.read(cx).text_option(cx);
 
-        let answer = cx.prompt(
+        let answer = window.prompt(
             PromptLevel::Info,
             "Ready to submit your feedback?",
             None,
             &["Yes, Submit!", "No"],
+            cx,
         );
         let client = Client::global(cx).clone();
         let specs = self.system_specs.clone();
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let answer = answer.await.ok();
             if answer == Some(0) {
                 this.update(&mut cx, |this, cx| {
@@ -248,14 +264,15 @@ impl FeedbackModal {
                     }
                     Err(error) => {
                         log::error!("{}", error);
-                        this.update(&mut cx, |this, cx| {
-                            let prompt = cx.prompt(
+                        this.update_in(&mut cx, |this, window, cx| {
+                            let prompt = window.prompt(
                                 PromptLevel::Critical,
                                 FEEDBACK_SUBMISSION_ERROR_TEXT,
                                 None,
                                 &["OK"],
+                                cx,
                             );
-                            cx.spawn(|_, _cx| async move {
+                            cx.spawn_in(window, |_, _cx| async move {
                                 prompt.await.ok();
                             })
                             .detach();
@@ -317,7 +334,7 @@ impl FeedbackModal {
         Ok(())
     }
 
-    fn update_submission_state(&mut self, cx: &mut ViewContext<Self>) {
+    fn update_submission_state(&mut self, cx: &mut Context<Self>) {
         if self.awaiting_submission() {
             return;
         }
@@ -348,10 +365,10 @@ impl FeedbackModal {
         }
     }
 
-    fn update_email_in_store(&self, cx: &mut ViewContext<Self>) {
+    fn update_email_in_store(&self, window: &mut Window, cx: &mut Context<Self>) {
         let email = self.email_address_editor.read(cx).text_option(cx);
 
-        cx.spawn(|_, _| async move {
+        cx.spawn_in(window, |_, _| async move {
             match email {
                 Some(email) => {
                     KEY_VALUE_STORE
@@ -400,13 +417,13 @@ impl FeedbackModal {
         matches!(self.submission_state, Some(SubmissionState::CanSubmit))
     }
 
-    fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
+    fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {
         cx.emit(DismissEvent)
     }
 }
 
 impl Render for FeedbackModal {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         self.update_submission_state(cx);
 
         let submit_button_text = if self.awaiting_submission() {
@@ -415,7 +432,8 @@ impl Render for FeedbackModal {
             "Submit"
         };
 
-        let open_zed_repo = cx.listener(|_, _, cx| cx.dispatch_action(Box::new(OpenZedRepo)));
+        let open_zed_repo =
+            cx.listener(|_, _, window, cx| window.dispatch_action(Box::new(OpenZedRepo), cx));
 
         v_flex()
             .elevation_3(cx)
@@ -496,8 +514,8 @@ impl Render for FeedbackModal {
                                 Button::new("cancel_feedback", "Cancel")
                                     .style(ButtonStyle::Subtle)
                                     .color(Color::Muted)
-                                    .on_click(cx.listener(move |_, _, cx| {
-                                        cx.spawn(|this, mut cx| async move {
+                                    .on_click(cx.listener(move |_, _, window, cx| {
+                                        cx.spawn_in(window, |this, mut cx| async move {
                                             this.update(&mut cx, |_, cx| cx.emit(DismissEvent))
                                                 .ok();
                                         })
@@ -508,11 +526,11 @@ impl Render for FeedbackModal {
                                 Button::new("submit_feedback", submit_button_text)
                                     .color(Color::Accent)
                                     .style(ButtonStyle::Filled)
-                                    .on_click(cx.listener(|this, _, cx| {
-                                        this.submit(cx).detach();
+                                    .on_click(cx.listener(|this, _, window, cx| {
+                                        this.submit(window, cx).detach();
                                     }))
-                                    .tooltip(move |cx| {
-                                        Tooltip::text("Submit feedback to the Zed team.", cx)
+                                    .tooltip(move |_, cx| {
+                                        Tooltip::simple("Submit feedback to the Zed team.", cx)
                                     })
                                     .when(!self.can_submit(), |this| this.disabled(true)),
                             ),

crates/feedback/src/system_specs.rs 🔗

@@ -1,5 +1,5 @@
 use client::telemetry;
-use gpui::{Task, WindowContext};
+use gpui::{App, Task, Window};
 use human_bytes::human_bytes;
 use release_channel::{AppCommitSha, AppVersion, ReleaseChannel};
 use serde::Serialize;
@@ -19,7 +19,7 @@ pub struct SystemSpecs {
 }
 
 impl SystemSpecs {
-    pub fn new(cx: &WindowContext) -> Task<Self> {
+    pub fn new(window: &mut Window, cx: &mut App) -> Task<Self> {
         let app_version = AppVersion::global(cx).to_string();
         let release_channel = ReleaseChannel::global(cx);
         let os_name = telemetry::os_name();
@@ -35,7 +35,7 @@ impl SystemSpecs {
             _ => None,
         };
 
-        let gpu_specs = if let Some(specs) = cx.gpu_specs() {
+        let gpu_specs = if let Some(specs) = window.gpu_specs() {
             Some(format!(
                 "{} || {} || {}",
                 specs.device_name, specs.driver_name, specs.driver_info

crates/file_finder/src/file_finder.rs 🔗

@@ -14,9 +14,9 @@ use file_finder_settings::{FileFinderSettings, FileFinderWidth};
 use file_icons::FileIcons;
 use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
 use gpui::{
-    actions, Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle,
-    FocusableView, KeyContext, Model, Modifiers, ModifiersChangedEvent, ParentElement, Render,
-    Styled, Task, View, ViewContext, VisualContext, WeakView,
+    actions, Action, AnyElement, App, Context, DismissEvent, Entity, EventEmitter, FocusHandle,
+    Focusable, KeyContext, Modifiers, ModifiersChangedEvent, ParentElement, Render, Styled, Task,
+    WeakEntity, Window,
 };
 use new_path_prompt::NewPathPrompt;
 use open_path_prompt::OpenPathPrompt;
@@ -45,52 +45,63 @@ use workspace::{
 actions!(file_finder, [SelectPrev, ToggleMenu]);
 
 impl ModalView for FileFinder {
-    fn on_before_dismiss(&mut self, cx: &mut ViewContext<Self>) -> workspace::DismissDecision {
+    fn on_before_dismiss(
+        &mut self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> workspace::DismissDecision {
         let submenu_focused = self.picker.update(cx, |picker, cx| {
-            picker.delegate.popover_menu_handle.is_focused(cx)
+            picker.delegate.popover_menu_handle.is_focused(window, cx)
         });
         workspace::DismissDecision::Dismiss(!submenu_focused)
     }
 }
 
 pub struct FileFinder {
-    picker: View<Picker<FileFinderDelegate>>,
+    picker: Entity<Picker<FileFinderDelegate>>,
     picker_focus_handle: FocusHandle,
     init_modifiers: Option<Modifiers>,
 }
 
-pub fn init_settings(cx: &mut AppContext) {
+pub fn init_settings(cx: &mut App) {
     FileFinderSettings::register(cx);
 }
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     init_settings(cx);
-    cx.observe_new_views(FileFinder::register).detach();
-    cx.observe_new_views(NewPathPrompt::register).detach();
-    cx.observe_new_views(OpenPathPrompt::register).detach();
+    cx.observe_new(FileFinder::register).detach();
+    cx.observe_new(NewPathPrompt::register).detach();
+    cx.observe_new(OpenPathPrompt::register).detach();
 }
 
 impl FileFinder {
-    fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-        workspace.register_action(|workspace, action: &workspace::ToggleFileFinder, cx| {
-            let Some(file_finder) = workspace.active_modal::<Self>(cx) else {
-                Self::open(workspace, action.separate_history, cx).detach();
-                return;
-            };
+    fn register(
+        workspace: &mut Workspace,
+        _window: Option<&mut Window>,
+        _: &mut Context<Workspace>,
+    ) {
+        workspace.register_action(
+            |workspace, action: &workspace::ToggleFileFinder, window, cx| {
+                let Some(file_finder) = workspace.active_modal::<Self>(cx) else {
+                    Self::open(workspace, action.separate_history, window, cx).detach();
+                    return;
+                };
 
-            file_finder.update(cx, |file_finder, cx| {
-                file_finder.init_modifiers = Some(cx.modifiers());
-                file_finder.picker.update(cx, |picker, cx| {
-                    picker.cycle_selection(cx);
+                file_finder.update(cx, |file_finder, cx| {
+                    file_finder.init_modifiers = Some(window.modifiers());
+                    file_finder.picker.update(cx, |picker, cx| {
+                        picker.cycle_selection(window, cx);
+                    });
                 });
-            });
-        });
+            },
+        );
     }
 
     fn open(
         workspace: &mut Workspace,
         separate_history: bool,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) -> Task<()> {
         let project = workspace.project().read(cx);
         let fs = project.fs();
@@ -130,33 +141,34 @@ impl FileFinder {
                 }
             })
             .collect::<Vec<_>>();
-        cx.spawn(move |workspace, mut cx| async move {
+        cx.spawn_in(window, move |workspace, mut cx| async move {
             let history_items = join_all(history_items).await.into_iter().flatten();
 
             workspace
-                .update(&mut cx, |workspace, cx| {
+                .update_in(&mut cx, |workspace, window, cx| {
                     let project = workspace.project().clone();
-                    let weak_workspace = cx.view().downgrade();
-                    workspace.toggle_modal(cx, |cx| {
+                    let weak_workspace = cx.model().downgrade();
+                    workspace.toggle_modal(window, cx, |window, cx| {
                         let delegate = FileFinderDelegate::new(
-                            cx.view().downgrade(),
+                            cx.model().downgrade(),
                             weak_workspace,
                             project,
                             currently_opened_path,
                             history_items.collect(),
                             separate_history,
+                            window,
                             cx,
                         );
 
-                        FileFinder::new(delegate, cx)
+                        FileFinder::new(delegate, window, cx)
                     });
                 })
                 .ok();
         })
     }
 
-    fn new(delegate: FileFinderDelegate, cx: &mut ViewContext<Self>) -> Self {
-        let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
+    fn new(delegate: FileFinderDelegate, window: &mut Window, cx: &mut Context<Self>) -> Self {
+        let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
         let picker_focus_handle = picker.focus_handle(cx);
         picker.update(cx, |picker, _| {
             picker.delegate.focus_handle = picker_focus_handle.clone();
@@ -164,14 +176,15 @@ impl FileFinder {
         Self {
             picker,
             picker_focus_handle,
-            init_modifiers: cx.modifiers().modified().then_some(cx.modifiers()),
+            init_modifiers: window.modifiers().modified().then_some(window.modifiers()),
         }
     }
 
     fn handle_modifiers_changed(
         &mut self,
         event: &ModifiersChangedEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let Some(init_modifiers) = self.init_modifiers.take() else {
             return;
@@ -179,47 +192,68 @@ impl FileFinder {
         if self.picker.read(cx).delegate.has_changed_selected_index {
             if !event.modified() || !init_modifiers.is_subset_of(&event) {
                 self.init_modifiers = None;
-                cx.dispatch_action(menu::Confirm.boxed_clone());
+                window.dispatch_action(menu::Confirm.boxed_clone(), cx);
             }
         }
     }
 
-    fn handle_select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext<Self>) {
-        self.init_modifiers = Some(cx.modifiers());
-        cx.dispatch_action(Box::new(menu::SelectPrev));
+    fn handle_select_prev(&mut self, _: &SelectPrev, window: &mut Window, cx: &mut Context<Self>) {
+        self.init_modifiers = Some(window.modifiers());
+        window.dispatch_action(Box::new(menu::SelectPrev), cx);
     }
 
-    fn handle_toggle_menu(&mut self, _: &ToggleMenu, cx: &mut ViewContext<Self>) {
+    fn handle_toggle_menu(&mut self, _: &ToggleMenu, window: &mut Window, cx: &mut Context<Self>) {
         self.picker.update(cx, |picker, cx| {
             let menu_handle = &picker.delegate.popover_menu_handle;
             if menu_handle.is_deployed() {
                 menu_handle.hide(cx);
             } else {
-                menu_handle.show(cx);
+                menu_handle.show(window, cx);
             }
         });
     }
 
-    fn go_to_file_split_left(&mut self, _: &pane::SplitLeft, cx: &mut ViewContext<Self>) {
-        self.go_to_file_split_inner(SplitDirection::Left, cx)
+    fn go_to_file_split_left(
+        &mut self,
+        _: &pane::SplitLeft,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.go_to_file_split_inner(SplitDirection::Left, window, cx)
     }
 
-    fn go_to_file_split_right(&mut self, _: &pane::SplitRight, cx: &mut ViewContext<Self>) {
-        self.go_to_file_split_inner(SplitDirection::Right, cx)
+    fn go_to_file_split_right(
+        &mut self,
+        _: &pane::SplitRight,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.go_to_file_split_inner(SplitDirection::Right, window, cx)
     }
 
-    fn go_to_file_split_up(&mut self, _: &pane::SplitUp, cx: &mut ViewContext<Self>) {
-        self.go_to_file_split_inner(SplitDirection::Up, cx)
+    fn go_to_file_split_up(
+        &mut self,
+        _: &pane::SplitUp,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.go_to_file_split_inner(SplitDirection::Up, window, cx)
     }
 
-    fn go_to_file_split_down(&mut self, _: &pane::SplitDown, cx: &mut ViewContext<Self>) {
-        self.go_to_file_split_inner(SplitDirection::Down, cx)
+    fn go_to_file_split_down(
+        &mut self,
+        _: &pane::SplitDown,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.go_to_file_split_inner(SplitDirection::Down, window, cx)
     }
 
     fn go_to_file_split_inner(
         &mut self,
         split_direction: SplitDirection,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.picker.update(cx, |picker, cx| {
             let delegate = &mut picker.delegate;
@@ -239,7 +273,7 @@ impl FileFinder {
                         },
                     };
                     let open_task = workspace.update(cx, move |workspace, cx| {
-                        workspace.split_path_preview(path, false, Some(split_direction), cx)
+                        workspace.split_path_preview(path, false, Some(split_direction), window, cx)
                     });
                     open_task.detach_and_log_err(cx);
                 }
@@ -247,11 +281,8 @@ impl FileFinder {
         })
     }
 
-    pub fn modal_max_width(
-        width_setting: Option<FileFinderWidth>,
-        cx: &mut ViewContext<Self>,
-    ) -> Pixels {
-        let window_width = cx.viewport_size().width;
+    pub fn modal_max_width(width_setting: Option<FileFinderWidth>, window: &mut Window) -> Pixels {
+        let window_width = window.viewport_size().width;
         let small_width = Pixels(545.);
 
         match width_setting {
@@ -266,18 +297,18 @@ impl FileFinder {
 
 impl EventEmitter<DismissEvent> for FileFinder {}
 
-impl FocusableView for FileFinder {
-    fn focus_handle(&self, _: &AppContext) -> FocusHandle {
+impl Focusable for FileFinder {
+    fn focus_handle(&self, _: &App) -> FocusHandle {
         self.picker_focus_handle.clone()
     }
 }
 
 impl Render for FileFinder {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let key_context = self.picker.read(cx).delegate.key_context(cx);
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+        let key_context = self.picker.read(cx).delegate.key_context(window, cx);
 
         let file_finder_settings = FileFinderSettings::get_global(cx);
-        let modal_max_width = Self::modal_max_width(file_finder_settings.modal_max_width, cx);
+        let modal_max_width = Self::modal_max_width(file_finder_settings.modal_max_width, window);
 
         v_flex()
             .key_context(key_context)
@@ -294,9 +325,9 @@ impl Render for FileFinder {
 }
 
 pub struct FileFinderDelegate {
-    file_finder: WeakView<FileFinder>,
-    workspace: WeakView<Workspace>,
-    project: Model<Project>,
+    file_finder: WeakEntity<FileFinder>,
+    workspace: WeakEntity<Workspace>,
+    project: Entity<Project>,
     search_count: usize,
     latest_search_id: usize,
     latest_search_did_cancel: bool,
@@ -613,16 +644,18 @@ impl FileSearchQuery {
 }
 
 impl FileFinderDelegate {
+    #[allow(clippy::too_many_arguments)]
     fn new(
-        file_finder: WeakView<FileFinder>,
-        workspace: WeakView<Workspace>,
-        project: Model<Project>,
+        file_finder: WeakEntity<FileFinder>,
+        workspace: WeakEntity<Workspace>,
+        project: Entity<Project>,
         currently_opened_path: Option<FoundPath>,
         history_items: Vec<FoundPath>,
         separate_history: bool,
-        cx: &mut ViewContext<FileFinder>,
+        window: &mut Window,
+        cx: &mut Context<FileFinder>,
     ) -> Self {
-        Self::subscribe_to_updates(&project, cx);
+        Self::subscribe_to_updates(&project, window, cx);
         Self {
             file_finder,
             workspace,
@@ -644,14 +677,18 @@ impl FileFinderDelegate {
         }
     }
 
-    fn subscribe_to_updates(project: &Model<Project>, cx: &mut ViewContext<FileFinder>) {
-        cx.subscribe(project, |file_finder, _, event, cx| {
+    fn subscribe_to_updates(
+        project: &Entity<Project>,
+        window: &mut Window,
+        cx: &mut Context<FileFinder>,
+    ) {
+        cx.subscribe_in(project, window, |file_finder, _, event, window, cx| {
             match event {
                 project::Event::WorktreeUpdatedEntries(_, _)
                 | project::Event::WorktreeAdded(_)
                 | project::Event::WorktreeRemoved(_) => file_finder
                     .picker
-                    .update(cx, |picker, cx| picker.refresh(cx)),
+                    .update(cx, |picker, cx| picker.refresh(window, cx)),
                 _ => {}
             };
         })
@@ -661,7 +698,8 @@ impl FileFinderDelegate {
     fn spawn_search(
         &mut self,
         query: FileSearchQuery,
-        cx: &mut ViewContext<Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Task<()> {
         let relative_to = self
             .currently_opened_path
@@ -692,7 +730,7 @@ impl FileFinderDelegate {
         self.cancel_flag.store(true, atomic::Ordering::Relaxed);
         self.cancel_flag = Arc::new(AtomicBool::new(false));
         let cancel_flag = self.cancel_flag.clone();
-        cx.spawn(|picker, mut cx| async move {
+        cx.spawn_in(window, |picker, mut cx| async move {
             let matches = fuzzy::match_path_sets(
                 candidate_sets.as_slice(),
                 query.path_query(),
@@ -722,7 +760,8 @@ impl FileFinderDelegate {
         did_cancel: bool,
         query: FileSearchQuery,
         matches: impl IntoIterator<Item = ProjectPanelOrdMatch>,
-        cx: &mut ViewContext<Picker<Self>>,
+
+        cx: &mut Context<Picker<Self>>,
     ) {
         if search_id >= self.latest_search_id {
             self.latest_search_id = search_id;
@@ -766,7 +805,7 @@ impl FileFinderDelegate {
     fn labels_for_match(
         &self,
         path_match: &Match,
-        cx: &AppContext,
+        cx: &App,
         ix: usize,
     ) -> (String, Vec<usize>, String, Vec<usize>) {
         let (file_name, file_name_positions, full_path, full_path_positions) = match &path_match {
@@ -884,9 +923,10 @@ impl FileFinderDelegate {
     fn lookup_absolute_path(
         &self,
         query: FileSearchQuery,
-        cx: &mut ViewContext<Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Task<()> {
-        cx.spawn(|picker, mut cx| async move {
+        cx.spawn_in(window, |picker, mut cx| async move {
             let Some(project) = picker
                 .update(&mut cx, |picker, _| picker.delegate.project.clone())
                 .log_err()
@@ -929,7 +969,7 @@ impl FileFinderDelegate {
             }
 
             picker
-                .update(&mut cx, |picker, cx| {
+                .update_in(&mut cx, |picker, _, cx| {
                     let picker_delegate = &mut picker.delegate;
                     let search_id = util::post_inc(&mut picker_delegate.search_count);
                     picker_delegate.set_search_matches(search_id, false, query, path_matches, cx);
@@ -954,10 +994,10 @@ impl FileFinderDelegate {
         0
     }
 
-    fn key_context(&self, cx: &WindowContext) -> KeyContext {
+    fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
         let mut key_context = KeyContext::new_with_defaults();
         key_context.add("FileFinder");
-        if self.popover_menu_handle.is_focused(cx) {
+        if self.popover_menu_handle.is_focused(window, cx) {
             key_context.add("menu_open");
         }
         key_context
@@ -967,7 +1007,7 @@ impl FileFinderDelegate {
 impl PickerDelegate for FileFinderDelegate {
     type ListItem = ListItem;
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Search project files...".into()
     }
 
@@ -979,7 +1019,7 @@ impl PickerDelegate for FileFinderDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(&mut self, ix: usize, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.has_changed_selected_index = true;
         self.selected_index = ix;
         cx.notify();
@@ -1006,7 +1046,8 @@ impl PickerDelegate for FileFinderDelegate {
     fn update_matches(
         &mut self,
         raw_query: String,
-        cx: &mut ViewContext<Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Task<()> {
         let raw_query = raw_query.replace(' ', "");
         let raw_query = raw_query.trim();
@@ -1057,31 +1098,44 @@ impl PickerDelegate for FileFinderDelegate {
             };
 
             if Path::new(query.path_query()).is_absolute() {
-                self.lookup_absolute_path(query, cx)
+                self.lookup_absolute_path(query, window, cx)
             } else {
-                self.spawn_search(query, cx)
+                self.spawn_search(query, window, cx)
             }
         }
     }
 
-    fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<Picker<FileFinderDelegate>>) {
+    fn confirm(
+        &mut self,
+        secondary: bool,
+        window: &mut Window,
+        cx: &mut Context<Picker<FileFinderDelegate>>,
+    ) {
         if let Some(m) = self.matches.get(self.selected_index()) {
             if let Some(workspace) = self.workspace.upgrade() {
-                let open_task = workspace.update(cx, move |workspace, cx| {
+                let open_task = workspace.update(cx, |workspace, cx| {
                     let split_or_open =
                         |workspace: &mut Workspace,
                          project_path,
-                         cx: &mut ViewContext<Workspace>| {
+                         window: &mut Window,
+                         cx: &mut Context<Workspace>| {
                             let allow_preview =
                                 PreviewTabsSettings::get_global(cx).enable_preview_from_file_finder;
                             if secondary {
-                                workspace.split_path_preview(project_path, allow_preview, None, cx)
+                                workspace.split_path_preview(
+                                    project_path,
+                                    allow_preview,
+                                    None,
+                                    window,
+                                    cx,
+                                )
                             } else {
                                 workspace.open_path_preview(
                                     project_path,
                                     None,
                                     true,
                                     allow_preview,
+                                    window,
                                     cx,
                                 )
                             }
@@ -1101,6 +1155,7 @@ impl PickerDelegate for FileFinderDelegate {
                                         worktree_id,
                                         path: Arc::clone(&path.project.path),
                                     },
+                                    window,
                                     cx,
                                 )
                             } else {
@@ -1110,12 +1165,14 @@ impl PickerDelegate for FileFinderDelegate {
                                             workspace.split_abs_path(
                                                 abs_path.to_path_buf(),
                                                 false,
+                                                window,
                                                 cx,
                                             )
                                         } else {
                                             workspace.open_abs_path(
                                                 abs_path.to_path_buf(),
                                                 false,
+                                                window,
                                                 cx,
                                             )
                                         }
@@ -1126,6 +1183,7 @@ impl PickerDelegate for FileFinderDelegate {
                                             worktree_id,
                                             path: Arc::clone(&path.project.path),
                                         },
+                                        window,
                                         cx,
                                     ),
                                 }
@@ -1137,6 +1195,7 @@ impl PickerDelegate for FileFinderDelegate {
                                 worktree_id: WorktreeId::from_usize(m.0.worktree_id),
                                 path: m.0.path.clone(),
                             },
+                            window,
                             cx,
                         ),
                     }
@@ -1155,14 +1214,18 @@ impl PickerDelegate for FileFinderDelegate {
                     .saturating_sub(1);
                 let finder = self.file_finder.clone();
 
-                cx.spawn(|_, mut cx| async move {
+                cx.spawn_in(window, |_, mut cx| async move {
                     let item = open_task.await.notify_async_err(&mut cx)?;
                     if let Some(row) = row {
                         if let Some(active_editor) = item.downcast::<Editor>() {
                             active_editor
                                 .downgrade()
-                                .update(&mut cx, |editor, cx| {
-                                    editor.go_to_singleton_buffer_point(Point::new(row, col), cx);
+                                .update_in(&mut cx, |editor, window, cx| {
+                                    editor.go_to_singleton_buffer_point(
+                                        Point::new(row, col),
+                                        window,
+                                        cx,
+                                    );
                                 })
                                 .log_err();
                         }
@@ -1176,7 +1239,7 @@ impl PickerDelegate for FileFinderDelegate {
         }
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<FileFinderDelegate>>) {
+    fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<FileFinderDelegate>>) {
         self.file_finder
             .update(cx, |_, cx| cx.emit(DismissEvent))
             .log_err();
@@ -1186,7 +1249,8 @@ impl PickerDelegate for FileFinderDelegate {
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let settings = FileFinderSettings::get_global(cx);
 
@@ -1237,7 +1301,11 @@ impl PickerDelegate for FileFinderDelegate {
         )
     }
 
-    fn render_footer(&self, cx: &mut ViewContext<Picker<Self>>) -> Option<AnyElement> {
+    fn render_footer(
+        &self,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Option<AnyElement> {
         let context = self.focus_handle.clone();
         Some(
             h_flex()
@@ -1249,8 +1317,10 @@ impl PickerDelegate for FileFinderDelegate {
                 .border_color(cx.theme().colors().border_variant)
                 .child(
                     Button::new("open-selection", "Open")
-                        .key_binding(KeyBinding::for_action(&menu::Confirm, cx))
-                        .on_click(|_, cx| cx.dispatch_action(menu::Confirm.boxed_clone())),
+                        .key_binding(KeyBinding::for_action(&menu::Confirm, window))
+                        .on_click(|_, window, cx| {
+                            window.dispatch_action(menu::Confirm.boxed_clone(), cx)
+                        }),
                 )
                 .child(
                     PopoverMenu::new("menu-popover")
@@ -1260,13 +1330,17 @@ impl PickerDelegate for FileFinderDelegate {
                         .trigger(
                             Button::new("actions-trigger", "Split Options")
                                 .selected_label_color(Color::Accent)
-                                .key_binding(KeyBinding::for_action_in(&ToggleMenu, &context, cx)),
+                                .key_binding(KeyBinding::for_action_in(
+                                    &ToggleMenu,
+                                    &context,
+                                    window,
+                                )),
                         )
                         .menu({
-                            move |cx| {
-                                Some(ContextMenu::build(cx, {
+                            move |window, cx| {
+                                Some(ContextMenu::build(window, cx, {
                                     let context = context.clone();
-                                    move |menu, _| {
+                                    move |menu, _, _| {
                                         menu.context(context)
                                             .action("Split Left", pane::SplitLeft.boxed_clone())
                                             .action("Split Right", pane::SplitRight.boxed_clone())

crates/file_finder/src/file_finder_settings.rs 🔗

@@ -26,7 +26,7 @@ impl Settings for FileFinderSettings {
 
     type FileContent = FileFinderSettingsContent;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _: &mut gpui::AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut gpui::App) -> Result<Self> {
         sources.json_merge()
     }
 }

crates/file_finder/src/file_finder_tests.rs 🔗

@@ -57,10 +57,10 @@ async fn test_matching_paths(cx: &mut TestAppContext) {
         "a bandana",
     ] {
         picker
-            .update(cx, |picker, cx| {
+            .update_in(cx, |picker, window, cx| {
                 picker
                     .delegate
-                    .update_matches(bandana_query.to_string(), cx)
+                    .update_matches(bandana_query.to_string(), window, cx)
             })
             .await;
         picker.update(cx, |picker, _| {
@@ -108,10 +108,10 @@ async fn test_absolute_paths(cx: &mut TestAppContext) {
 
     let matching_abs_path = "/root/a/b/file2.txt";
     picker
-        .update(cx, |picker, cx| {
+        .update_in(cx, |picker, window, cx| {
             picker
                 .delegate
-                .update_matches(matching_abs_path.to_string(), cx)
+                .update_matches(matching_abs_path.to_string(), window, cx)
         })
         .await;
     picker.update(cx, |picker, _| {
@@ -130,10 +130,10 @@ async fn test_absolute_paths(cx: &mut TestAppContext) {
 
     let mismatching_abs_path = "/root/a/b/file1.txt";
     picker
-        .update(cx, |picker, cx| {
+        .update_in(cx, |picker, window, cx| {
             picker
                 .delegate
-                .update_matches(mismatching_abs_path.to_string(), cx)
+                .update_matches(mismatching_abs_path.to_string(), window, cx)
         })
         .await;
     picker.update(cx, |picker, _| {
@@ -213,10 +213,10 @@ async fn test_row_column_numbers_query_inside_file(cx: &mut TestAppContext) {
     assert!(file_column <= first_file_contents.len());
     let query_inside_file = format!("{file_query}:{file_row}:{file_column}");
     picker
-        .update(cx, |finder, cx| {
+        .update_in(cx, |finder, window, cx| {
             finder
                 .delegate
-                .update_matches(query_inside_file.to_string(), cx)
+                .update_matches(query_inside_file.to_string(), window, cx)
         })
         .await;
     picker.update(cx, |finder, _| {
@@ -238,7 +238,7 @@ async fn test_row_column_numbers_query_inside_file(cx: &mut TestAppContext) {
     cx.dispatch_action(SelectNext);
     cx.dispatch_action(Confirm);
 
-    let editor = cx.update(|cx| workspace.read(cx).active_item_as::<Editor>(cx).unwrap());
+    let editor = cx.update(|_, cx| workspace.read(cx).active_item_as::<Editor>(cx).unwrap());
     cx.executor().advance_clock(Duration::from_secs(2));
 
     editor.update(cx, |editor, cx| {
@@ -288,10 +288,10 @@ async fn test_row_column_numbers_query_outside_file(cx: &mut TestAppContext) {
     assert!(file_column > first_file_contents.len());
     let query_outside_file = format!("{file_query}:{file_row}:{file_column}");
     picker
-        .update(cx, |picker, cx| {
+        .update_in(cx, |picker, window, cx| {
             picker
                 .delegate
-                .update_matches(query_outside_file.to_string(), cx)
+                .update_matches(query_outside_file.to_string(), window, cx)
         })
         .await;
     picker.update(cx, |finder, _| {
@@ -313,7 +313,7 @@ async fn test_row_column_numbers_query_outside_file(cx: &mut TestAppContext) {
     cx.dispatch_action(SelectNext);
     cx.dispatch_action(Confirm);
 
-    let editor = cx.update(|cx| workspace.read(cx).active_item_as::<Editor>(cx).unwrap());
+    let editor = cx.update(|_, cx| workspace.read(cx).active_item_as::<Editor>(cx).unwrap());
     cx.executor().advance_clock(Duration::from_secs(2));
 
     editor.update(cx, |editor, cx| {
@@ -359,8 +359,8 @@ async fn test_matching_cancellation(cx: &mut TestAppContext) {
 
     let query = test_path_position("hi");
     picker
-        .update(cx, |picker, cx| {
-            picker.delegate.spawn_search(query.clone(), cx)
+        .update_in(cx, |picker, window, cx| {
+            picker.delegate.spawn_search(query.clone(), window, cx)
         })
         .await;
 
@@ -368,13 +368,13 @@ async fn test_matching_cancellation(cx: &mut TestAppContext) {
         assert_eq!(picker.delegate.matches.len(), 5)
     });
 
-    picker.update(cx, |picker, cx| {
+    picker.update_in(cx, |picker, window, cx| {
         let matches = collect_search_matches(picker).search_matches_only();
         let delegate = &mut picker.delegate;
 
         // Simulate a search being cancelled after the time limit,
         // returning only a subset of the matches that would have been found.
-        drop(delegate.spawn_search(query.clone(), cx));
+        drop(delegate.spawn_search(query.clone(), window, cx));
         delegate.set_search_matches(
             delegate.latest_search_id,
             true, // did-cancel
@@ -387,7 +387,7 @@ async fn test_matching_cancellation(cx: &mut TestAppContext) {
         );
 
         // Simulate another cancellation.
-        drop(delegate.spawn_search(query.clone(), cx));
+        drop(delegate.spawn_search(query.clone(), window, cx));
         delegate.set_search_matches(
             delegate.latest_search_id,
             true, // did-cancel
@@ -449,8 +449,10 @@ async fn test_ignored_root(cx: &mut TestAppContext) {
     let (picker, _, cx) = build_find_picker(project, cx);
 
     picker
-        .update(cx, |picker, cx| {
-            picker.delegate.spawn_search(test_path_position("hi"), cx)
+        .update_in(cx, |picker, window, cx| {
+            picker
+                .delegate
+                .spawn_search(test_path_position("hi"), window, cx)
         })
         .await;
     picker.update(cx, |picker, _| assert_eq!(picker.delegate.matches.len(), 7));
@@ -477,8 +479,10 @@ async fn test_single_file_worktrees(cx: &mut TestAppContext) {
     // Even though there is only one worktree, that worktree's filename
     // is included in the matching, because the worktree is a single file.
     picker
-        .update(cx, |picker, cx| {
-            picker.delegate.spawn_search(test_path_position("thf"), cx)
+        .update_in(cx, |picker, window, cx| {
+            picker
+                .delegate
+                .spawn_search(test_path_position("thf"), window, cx)
         })
         .await;
     cx.read(|cx| {
@@ -498,8 +502,10 @@ async fn test_single_file_worktrees(cx: &mut TestAppContext) {
     // Since the worktree root is a file, searching for its name followed by a slash does
     // not match anything.
     picker
-        .update(cx, |f, cx| {
-            f.delegate.spawn_search(test_path_position("thf/"), cx)
+        .update_in(cx, |picker, window, cx| {
+            picker
+                .delegate
+                .spawn_search(test_path_position("thf/"), window, cx)
         })
         .await;
     picker.update(cx, |f, _| assert_eq!(f.delegate.matches.len(), 0));
@@ -524,7 +530,7 @@ async fn test_path_distance_ordering(cx: &mut TestAppContext) {
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
 
     let worktree_id = cx.read(|cx| {
         let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
@@ -540,15 +546,16 @@ async fn test_path_distance_ordering(cx: &mut TestAppContext) {
         path: Arc::from(Path::new("dir2/b.txt")),
     };
     workspace
-        .update(cx, |workspace, cx| {
-            workspace.open_path(b_path, None, true, cx)
+        .update_in(cx, |workspace, window, cx| {
+            workspace.open_path(b_path, None, true, window, cx)
         })
         .await
         .unwrap();
     let finder = open_file_picker(&workspace, cx);
     finder
-        .update(cx, |f, cx| {
-            f.delegate.spawn_search(test_path_position("a.txt"), cx)
+        .update_in(cx, |f, window, cx| {
+            f.delegate
+                .spawn_search(test_path_position("a.txt"), window, cx)
         })
         .await;
 
@@ -580,8 +587,9 @@ async fn test_search_worktree_without_files(cx: &mut TestAppContext) {
     let (picker, _workspace, cx) = build_find_picker(project, cx);
 
     picker
-        .update(cx, |f, cx| {
-            f.delegate.spawn_search(test_path_position("dir"), cx)
+        .update_in(cx, |f, window, cx| {
+            f.delegate
+                .spawn_search(test_path_position("dir"), window, cx)
         })
         .await;
     cx.read(|cx| {
@@ -610,7 +618,7 @@ async fn test_query_history(cx: &mut gpui::TestAppContext) {
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
     let worktree_id = cx.read(|cx| {
         let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
         assert_eq!(worktrees.len(), 1);
@@ -623,7 +631,7 @@ async fn test_query_history(cx: &mut gpui::TestAppContext) {
     //
     // TODO: without closing, the opened items do not propagate their history changes for some reason
     // it does work in real app though, only tests do not propagate.
-    workspace.update(cx, |_, cx| cx.focused());
+    workspace.update_in(cx, |_workspace, window, cx| window.focused(cx));
 
     let initial_history = open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await;
     assert!(
@@ -773,7 +781,7 @@ async fn test_external_files_history(cx: &mut gpui::TestAppContext) {
     .detach();
     cx.background_executor.run_until_parked();
 
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
     let worktree_id = cx.read(|cx| {
         let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
         assert_eq!(worktrees.len(), 1,);
@@ -781,8 +789,13 @@ async fn test_external_files_history(cx: &mut gpui::TestAppContext) {
         WorktreeId::from_usize(worktrees[0].entity_id().as_u64() as usize)
     });
     workspace
-        .update(cx, |workspace, cx| {
-            workspace.open_abs_path(PathBuf::from("/external-src/test/third.rs"), false, cx)
+        .update_in(cx, |workspace, window, cx| {
+            workspace.open_abs_path(
+                PathBuf::from("/external-src/test/third.rs"),
+                false,
+                window,
+                cx,
+            )
         })
         .detach();
     cx.background_executor.run_until_parked();
@@ -863,7 +876,7 @@ async fn test_toggle_panel_new_selections(cx: &mut gpui::TestAppContext) {
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
 
     // generate some history to select from
     open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await;
@@ -919,7 +932,7 @@ async fn test_search_preserves_history_items(cx: &mut gpui::TestAppContext) {
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
     let worktree_id = cx.read(|cx| {
         let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
         assert_eq!(worktrees.len(), 1,);
@@ -936,8 +949,10 @@ async fn test_search_preserves_history_items(cx: &mut gpui::TestAppContext) {
     let finder = open_file_picker(&workspace, cx);
     let first_query = "f";
     finder
-        .update(cx, |finder, cx| {
-            finder.delegate.update_matches(first_query.to_string(), cx)
+        .update_in(cx, |finder, window, cx| {
+            finder
+                .delegate
+                .update_matches(first_query.to_string(), window, cx)
         })
         .await;
     finder.update(cx, |picker, _| {
@@ -958,8 +973,10 @@ async fn test_search_preserves_history_items(cx: &mut gpui::TestAppContext) {
     let second_query = "fsdasdsa";
     let finder = active_file_picker(&workspace, cx);
     finder
-        .update(cx, |finder, cx| {
-            finder.delegate.update_matches(second_query.to_string(), cx)
+        .update_in(cx, |finder, window, cx| {
+            finder
+                .delegate
+                .update_matches(second_query.to_string(), window, cx)
         })
         .await;
     finder.update(cx, |picker, _| {
@@ -975,10 +992,10 @@ async fn test_search_preserves_history_items(cx: &mut gpui::TestAppContext) {
 
     let finder = active_file_picker(&workspace, cx);
     finder
-        .update(cx, |finder, cx| {
+        .update_in(cx, |finder, window, cx| {
             finder
                 .delegate
-                .update_matches(first_query_again.to_string(), cx)
+                .update_matches(first_query_again.to_string(), window, cx)
         })
         .await;
     finder.update(cx, |picker, _| {
@@ -1021,7 +1038,7 @@ async fn test_search_sorts_history_items(cx: &mut gpui::TestAppContext) {
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
     // generate some history to select from
     open_close_queried_buffer("1", 1, "1_qw", &workspace, cx).await;
     open_close_queried_buffer("2", 1, "2_second", &workspace, cx).await;
@@ -1032,8 +1049,10 @@ async fn test_search_sorts_history_items(cx: &mut gpui::TestAppContext) {
     let finder = open_file_picker(&workspace, cx);
     let query = "qw";
     finder
-        .update(cx, |finder, cx| {
-            finder.delegate.update_matches(query.to_string(), cx)
+        .update_in(cx, |finder, window, cx| {
+            finder
+                .delegate
+                .update_matches(query.to_string(), window, cx)
         })
         .await;
     finder.update(cx, |finder, _| {
@@ -1070,7 +1089,7 @@ async fn test_select_current_open_file_when_no_history(cx: &mut gpui::TestAppCon
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
     // Open new buffer
     open_queried_buffer("1", 1, "1_qw", &workspace, cx).await;
 
@@ -1104,7 +1123,7 @@ async fn test_keep_opened_file_on_top_of_search_results_and_select_next_one(
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
 
     open_close_queried_buffer("bar", 1, "bar.rs", &workspace, cx).await;
     open_close_queried_buffer("lib", 1, "lib.rs", &workspace, cx).await;
@@ -1121,8 +1140,10 @@ async fn test_keep_opened_file_on_top_of_search_results_and_select_next_one(
 
     // all files match, main.rs is still on top, but the second item is selected
     picker
-        .update(cx, |finder, cx| {
-            finder.delegate.update_matches(".rs".to_string(), cx)
+        .update_in(cx, |finder, window, cx| {
+            finder
+                .delegate
+                .update_matches(".rs".to_string(), window, cx)
         })
         .await;
     picker.update(cx, |finder, _| {
@@ -1136,8 +1157,8 @@ async fn test_keep_opened_file_on_top_of_search_results_and_select_next_one(
 
     // main.rs is not among matches, select top item
     picker
-        .update(cx, |finder, cx| {
-            finder.delegate.update_matches("b".to_string(), cx)
+        .update_in(cx, |finder, window, cx| {
+            finder.delegate.update_matches("b".to_string(), window, cx)
         })
         .await;
     picker.update(cx, |finder, _| {
@@ -1148,8 +1169,8 @@ async fn test_keep_opened_file_on_top_of_search_results_and_select_next_one(
 
     // main.rs is back, put it on top and select next item
     picker
-        .update(cx, |finder, cx| {
-            finder.delegate.update_matches("m".to_string(), cx)
+        .update_in(cx, |finder, window, cx| {
+            finder.delegate.update_matches("m".to_string(), window, cx)
         })
         .await;
     picker.update(cx, |finder, _| {
@@ -1161,8 +1182,8 @@ async fn test_keep_opened_file_on_top_of_search_results_and_select_next_one(
 
     // get back to the initial state
     picker
-        .update(cx, |finder, cx| {
-            finder.delegate.update_matches("".to_string(), cx)
+        .update_in(cx, |finder, window, cx| {
+            finder.delegate.update_matches("".to_string(), window, cx)
         })
         .await;
     picker.update(cx, |finder, _| {
@@ -1195,7 +1216,7 @@ async fn test_non_separate_history_items(cx: &mut TestAppContext) {
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
 
     open_close_queried_buffer("bar", 1, "bar.rs", &workspace, cx).await;
     open_close_queried_buffer("lib", 1, "lib.rs", &workspace, cx).await;
@@ -1213,8 +1234,10 @@ async fn test_non_separate_history_items(cx: &mut TestAppContext) {
 
     // all files match, main.rs is still on top, but the second item is selected
     picker
-        .update(cx, |finder, cx| {
-            finder.delegate.update_matches(".rs".to_string(), cx)
+        .update_in(cx, |finder, window, cx| {
+            finder
+                .delegate
+                .update_matches(".rs".to_string(), window, cx)
         })
         .await;
     picker.update(cx, |finder, _| {
@@ -1228,8 +1251,8 @@ async fn test_non_separate_history_items(cx: &mut TestAppContext) {
 
     // main.rs is not among matches, select top item
     picker
-        .update(cx, |finder, cx| {
-            finder.delegate.update_matches("b".to_string(), cx)
+        .update_in(cx, |finder, window, cx| {
+            finder.delegate.update_matches("b".to_string(), window, cx)
         })
         .await;
     picker.update(cx, |finder, _| {
@@ -1240,8 +1263,8 @@ async fn test_non_separate_history_items(cx: &mut TestAppContext) {
 
     // main.rs is back, put it on top and select next item
     picker
-        .update(cx, |finder, cx| {
-            finder.delegate.update_matches("m".to_string(), cx)
+        .update_in(cx, |finder, window, cx| {
+            finder.delegate.update_matches("m".to_string(), window, cx)
         })
         .await;
     picker.update(cx, |finder, _| {
@@ -1253,8 +1276,8 @@ async fn test_non_separate_history_items(cx: &mut TestAppContext) {
 
     // get back to the initial state
     picker
-        .update(cx, |finder, cx| {
-            finder.delegate.update_matches("".to_string(), cx)
+        .update_in(cx, |finder, window, cx| {
+            finder.delegate.update_matches("".to_string(), window, cx)
         })
         .await;
     picker.update(cx, |finder, _| {
@@ -1285,7 +1308,7 @@ async fn test_history_items_shown_in_order_of_open(cx: &mut TestAppContext) {
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
 
     open_queried_buffer("1", 1, "1.txt", &workspace, cx).await;
     open_queried_buffer("2", 1, "2.txt", &workspace, cx).await;
@@ -1343,7 +1366,7 @@ async fn test_selected_history_item_stays_selected_on_worktree_updated(cx: &mut
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
 
     open_close_queried_buffer("1", 1, "1.txt", &workspace, cx).await;
     open_close_queried_buffer("2", 1, "2.txt", &workspace, cx).await;
@@ -1400,7 +1423,7 @@ async fn test_history_items_vs_very_good_external_match(cx: &mut gpui::TestAppCo
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
     // generate some history to select from
     open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await;
     open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await;
@@ -1445,7 +1468,7 @@ async fn test_nonexistent_history_items_not_shown(cx: &mut gpui::TestAppContext)
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); // generate some history to select from
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); // generate some history to select from
     open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await;
     open_close_queried_buffer("non", 1, "nonexistent.rs", &workspace, cx).await;
     open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await;
@@ -1493,7 +1516,8 @@ async fn test_search_results_refreshed_on_worktree_updates(cx: &mut gpui::TestAp
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+    let (workspace, cx) =
+        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
     // Initial state
     let picker = open_file_picker(&workspace, cx);
@@ -1559,7 +1583,8 @@ async fn test_search_results_refreshed_on_adding_and_removing_worktrees(
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/test/project_1".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+    let (workspace, cx) =
+        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
     let worktree_1_id = project.update(cx, |project, cx| {
         let worktree = project.worktrees(cx).last().expect("worktree not found");
         worktree.read(cx).id()
@@ -1629,7 +1654,8 @@ async fn test_selected_match_stays_selected_after_matches_refreshed(cx: &mut gpu
     }
 
     let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+    let (workspace, cx) =
+        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
     // Initial state
     let picker = open_file_picker(&workspace, cx);
@@ -1685,7 +1711,8 @@ async fn test_first_match_selected_if_previous_one_is_not_in_the_match_list(
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+    let (workspace, cx) =
+        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
     // Initial state
     let picker = open_file_picker(&workspace, cx);
@@ -1723,7 +1750,7 @@ async fn test_keeps_file_finder_open_after_modifier_keys_release(cx: &mut gpui::
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
 
     open_queried_buffer("1", 1, "1.txt", &workspace, cx).await;
 
@@ -1751,7 +1778,7 @@ async fn test_opens_file_on_modifier_keys_release(cx: &mut gpui::TestAppContext)
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
 
     open_queried_buffer("1", 1, "1.txt", &workspace, cx).await;
     open_queried_buffer("2", 1, "2.txt", &workspace, cx).await;
@@ -1791,7 +1818,7 @@ async fn test_switches_between_release_norelease_modes_on_forward_nav(
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
 
     open_queried_buffer("1", 1, "1.txt", &workspace, cx).await;
     open_queried_buffer("2", 1, "2.txt", &workspace, cx).await;
@@ -1847,7 +1874,7 @@ async fn test_switches_between_release_norelease_modes_on_backward_nav(
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
 
     open_queried_buffer("1", 1, "1.txt", &workspace, cx).await;
     open_queried_buffer("2", 1, "2.txt", &workspace, cx).await;
@@ -1902,7 +1929,7 @@ async fn test_extending_modifiers_does_not_confirm_selection(cx: &mut gpui::Test
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
 
     open_queried_buffer("1", 1, "1.txt", &workspace, cx).await;
 
@@ -1933,7 +1960,7 @@ async fn test_repeat_toggle_action(cx: &mut gpui::TestAppContext) {
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
 
     cx.dispatch_action(ToggleFileFinder::default());
     let picker = active_file_picker(&workspace, cx);
@@ -1956,7 +1983,7 @@ async fn open_close_queried_buffer(
     input: &str,
     expected_matches: usize,
     expected_editor_title: &str,
-    workspace: &View<Workspace>,
+    workspace: &Entity<Workspace>,
     cx: &mut gpui::VisualTestContext,
 ) -> Vec<FoundPath> {
     let history_items = open_queried_buffer(
@@ -1977,7 +2004,7 @@ async fn open_queried_buffer(
     input: &str,
     expected_matches: usize,
     expected_editor_title: &str,
-    workspace: &View<Workspace>,
+    workspace: &Entity<Workspace>,
     cx: &mut gpui::VisualTestContext,
 ) -> Vec<FoundPath> {
     let picker = open_file_picker(&workspace, cx);
@@ -2035,23 +2062,23 @@ fn test_path_position(test_str: &str) -> FileSearchQuery {
 }
 
 fn build_find_picker(
-    project: Model<Project>,
+    project: Entity<Project>,
     cx: &mut TestAppContext,
 ) -> (
-    View<Picker<FileFinderDelegate>>,
-    View<Workspace>,
+    Entity<Picker<FileFinderDelegate>>,
+    Entity<Workspace>,
     &mut VisualTestContext,
 ) {
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+    let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
     let picker = open_file_picker(&workspace, cx);
     (picker, workspace, cx)
 }
 
 #[track_caller]
 fn open_file_picker(
-    workspace: &View<Workspace>,
+    workspace: &Entity<Workspace>,
     cx: &mut VisualTestContext,
-) -> View<Picker<FileFinderDelegate>> {
+) -> Entity<Picker<FileFinderDelegate>> {
     cx.dispatch_action(ToggleFileFinder {
         separate_history: true,
     });
@@ -2060,9 +2087,9 @@ fn open_file_picker(
 
 #[track_caller]
 fn active_file_picker(
-    workspace: &View<Workspace>,
+    workspace: &Entity<Workspace>,
     cx: &mut VisualTestContext,
-) -> View<Picker<FileFinderDelegate>> {
+) -> Entity<Picker<FileFinderDelegate>> {
     workspace.update(cx, |workspace, cx| {
         workspace
             .active_modal::<FileFinder>(cx)

crates/file_finder/src/new_path_prompt.rs 🔗

@@ -1,6 +1,6 @@
 use futures::channel::oneshot;
 use fuzzy::PathMatch;
-use gpui::{HighlightStyle, Model, StyledText};
+use gpui::{Entity, HighlightStyle, StyledText};
 use picker::{Picker, PickerDelegate};
 use project::{Entry, PathMatchCandidateSet, Project, ProjectPath, WorktreeId};
 use std::{
@@ -11,7 +11,7 @@ use std::{
     },
 };
 use ui::{highlight_ranges, prelude::*, LabelLike, ListItemSpacing};
-use ui::{ListItem, ViewContext};
+use ui::{Context, ListItem, Window};
 use util::ResultExt;
 use workspace::Workspace;
 
@@ -24,7 +24,7 @@ struct Match {
 }
 
 impl Match {
-    fn entry<'a>(&'a self, project: &'a Project, cx: &'a WindowContext) -> Option<&'a Entry> {
+    fn entry<'a>(&'a self, project: &'a Project, cx: &'a App) -> Option<&'a Entry> {
         if let Some(suffix) = &self.suffix {
             let (worktree, path) = if let Some(path_match) = &self.path_match {
                 (
@@ -45,7 +45,7 @@ impl Match {
         }
     }
 
-    fn is_dir(&self, project: &Project, cx: &WindowContext) -> bool {
+    fn is_dir(&self, project: &Project, cx: &App) -> bool {
         self.entry(project, cx).is_some_and(|e| e.is_dir())
             || self.suffix.as_ref().is_some_and(|s| s.ends_with('/'))
     }
@@ -68,7 +68,7 @@ impl Match {
         }
     }
 
-    fn project_path(&self, project: &Project, cx: &WindowContext) -> Option<ProjectPath> {
+    fn project_path(&self, project: &Project, cx: &App) -> Option<ProjectPath> {
         let worktree_id = if let Some(path_match) = &self.path_match {
             WorktreeId::from_usize(path_match.worktree_id)
         } else if let Some(worktree) = project.visible_worktrees(cx).find(|worktree| {
@@ -91,7 +91,7 @@ impl Match {
         })
     }
 
-    fn existing_prefix(&self, project: &Project, cx: &WindowContext) -> Option<PathBuf> {
+    fn existing_prefix(&self, project: &Project, cx: &App) -> Option<PathBuf> {
         let worktree = project.worktrees(cx).next()?.read(cx);
         let mut prefix = PathBuf::new();
         let parts = self.suffix.as_ref()?.split('/');
@@ -105,7 +105,7 @@ impl Match {
         None
     }
 
-    fn styled_text(&self, project: &Project, cx: &WindowContext) -> StyledText {
+    fn styled_text(&self, project: &Project, window: &Window, cx: &App) -> StyledText {
         let mut text = "./".to_string();
         let mut highlights = Vec::new();
         let mut offset = text.as_bytes().len();
@@ -192,12 +192,12 @@ impl Match {
             }
         }
 
-        StyledText::new(text).with_highlights(&cx.text_style().clone(), highlights)
+        StyledText::new(text).with_highlights(&window.text_style().clone(), highlights)
     }
 }
 
 pub struct NewPathDelegate {
-    project: Model<Project>,
+    project: Entity<Project>,
     tx: Option<oneshot::Sender<Option<ProjectPath>>>,
     selected_index: usize,
     matches: Vec<Match>,
@@ -207,10 +207,14 @@ pub struct NewPathDelegate {
 }
 
 impl NewPathPrompt {
-    pub(crate) fn register(workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>) {
-        workspace.set_prompt_for_new_path(Box::new(|workspace, cx| {
+    pub(crate) fn register(
+        workspace: &mut Workspace,
+        _window: Option<&mut Window>,
+        _cx: &mut Context<Workspace>,
+    ) {
+        workspace.set_prompt_for_new_path(Box::new(|workspace, window, cx| {
             let (tx, rx) = futures::channel::oneshot::channel();
-            Self::prompt_for_new_path(workspace, tx, cx);
+            Self::prompt_for_new_path(workspace, tx, window, cx);
             rx
         }));
     }
@@ -218,10 +222,11 @@ impl NewPathPrompt {
     fn prompt_for_new_path(
         workspace: &mut Workspace,
         tx: oneshot::Sender<Option<ProjectPath>>,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
         let project = workspace.project().clone();
-        workspace.toggle_modal(cx, |cx| {
+        workspace.toggle_modal(window, cx, |window, cx| {
             let delegate = NewPathDelegate {
                 project,
                 tx: Some(tx),
@@ -232,7 +237,7 @@ impl NewPathPrompt {
                 should_dismiss: true,
             };
 
-            Picker::uniform_list(delegate, cx).width(rems(34.))
+            Picker::uniform_list(delegate, window, cx).width(rems(34.))
         });
     }
 }
@@ -248,7 +253,12 @@ impl PickerDelegate for NewPathDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<picker::Picker<Self>>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        _: &mut Window,
+        cx: &mut Context<picker::Picker<Self>>,
+    ) {
         self.selected_index = ix;
         cx.notify();
     }
@@ -256,7 +266,8 @@ impl PickerDelegate for NewPathDelegate {
     fn update_matches(
         &mut self,
         query: String,
-        cx: &mut ViewContext<picker::Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<picker::Picker<Self>>,
     ) -> gpui::Task<()> {
         let query = query
             .trim()
@@ -301,7 +312,7 @@ impl PickerDelegate for NewPathDelegate {
         let cancel_flag = self.cancel_flag.clone();
         let query = query.to_string();
         let prefix = dir.clone();
-        cx.spawn(|picker, mut cx| async move {
+        cx.spawn_in(window, |picker, mut cx| async move {
             let matches = fuzzy::match_path_sets(
                 candidate_sets.as_slice(),
                 &dir,
@@ -329,12 +340,17 @@ impl PickerDelegate for NewPathDelegate {
     fn confirm_completion(
         &mut self,
         _: String,
-        cx: &mut ViewContext<Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<String> {
-        self.confirm_update_query(cx)
+        self.confirm_update_query(window, cx)
     }
 
-    fn confirm_update_query(&mut self, cx: &mut ViewContext<Picker<Self>>) -> Option<String> {
+    fn confirm_update_query(
+        &mut self,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Option<String> {
         let m = self.matches.get(self.selected_index)?;
         if m.is_dir(self.project.read(cx), cx) {
             let path = m.relative_path();
@@ -345,7 +361,7 @@ impl PickerDelegate for NewPathDelegate {
         }
     }
 
-    fn confirm(&mut self, _: bool, cx: &mut ViewContext<picker::Picker<Self>>) {
+    fn confirm(&mut self, _: bool, window: &mut Window, cx: &mut Context<picker::Picker<Self>>) {
         let Some(m) = self.matches.get(self.selected_index) else {
             return;
         };
@@ -353,16 +369,16 @@ impl PickerDelegate for NewPathDelegate {
         let exists = m.entry(self.project.read(cx), cx).is_some();
         if exists {
             self.should_dismiss = false;
-            let answer = cx.prompt(
+            let answer = window.prompt(
                 gpui::PromptLevel::Critical,
                 &format!("{} already exists. Do you want to replace it?", m.relative_path()),
                 Some(
                     "A file or folder with the same name already exists. Replacing it will overwrite its current contents.",
                 ),
                 &["Replace", "Cancel"],
-            );
+            cx);
             let m = m.clone();
-            cx.spawn(|picker, mut cx| async move {
+            cx.spawn_in(window, |picker, mut cx| async move {
                 let answer = answer.await.ok();
                 picker
                     .update(&mut cx, |picker, cx| {
@@ -395,7 +411,7 @@ impl PickerDelegate for NewPathDelegate {
         self.should_dismiss
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<picker::Picker<Self>>) {
+    fn dismissed(&mut self, _: &mut Window, cx: &mut Context<picker::Picker<Self>>) {
         if let Some(tx) = self.tx.take() {
             tx.send(None).ok();
         }
@@ -406,7 +422,8 @@ impl PickerDelegate for NewPathDelegate {
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<picker::Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<picker::Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let m = self.matches.get(ix)?;
 
@@ -415,15 +432,15 @@ impl PickerDelegate for NewPathDelegate {
                 .spacing(ListItemSpacing::Sparse)
                 .inset(true)
                 .toggle_state(selected)
-                .child(LabelLike::new().child(m.styled_text(self.project.read(cx), cx))),
+                .child(LabelLike::new().child(m.styled_text(self.project.read(cx), window, cx))),
         )
     }
 
-    fn no_matches_text(&self, _cx: &mut WindowContext) -> SharedString {
+    fn no_matches_text(&self, _window: &mut Window, _cx: &mut App) -> SharedString {
         "Type a path...".into()
     }
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         Arc::from("[directory/]filename.ext")
     }
 }
@@ -435,7 +452,7 @@ impl NewPathDelegate {
         prefix: String,
         suffix: Option<String>,
         matches: Vec<PathMatch>,
-        cx: &mut ViewContext<Picker<Self>>,
+        cx: &mut Context<Picker<Self>>,
     ) {
         cx.notify();
         if query.is_empty() {

crates/file_finder/src/open_path_prompt.rs 🔗

@@ -10,7 +10,7 @@ use std::{
     },
 };
 use ui::{prelude::*, LabelLike, ListItemSpacing};
-use ui::{ListItem, ViewContext};
+use ui::{Context, ListItem, Window};
 use util::{maybe, paths::compare_paths};
 use workspace::Workspace;
 
@@ -47,10 +47,14 @@ struct DirectoryState {
 }
 
 impl OpenPathPrompt {
-    pub(crate) fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-        workspace.set_prompt_for_open_path(Box::new(|workspace, lister, cx| {
+    pub(crate) fn register(
+        workspace: &mut Workspace,
+        _window: Option<&mut Window>,
+        _: &mut Context<Workspace>,
+    ) {
+        workspace.set_prompt_for_open_path(Box::new(|workspace, lister, window, cx| {
             let (tx, rx) = futures::channel::oneshot::channel();
-            Self::prompt_for_open_path(workspace, lister, tx, cx);
+            Self::prompt_for_open_path(workspace, lister, tx, window, cx);
             rx
         }));
     }
@@ -59,14 +63,15 @@ impl OpenPathPrompt {
         workspace: &mut Workspace,
         lister: DirectoryLister,
         tx: oneshot::Sender<Option<Vec<PathBuf>>>,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
-        workspace.toggle_modal(cx, |cx| {
+        workspace.toggle_modal(window, cx, |window, cx| {
             let delegate = OpenPathDelegate::new(tx, lister.clone());
 
-            let picker = Picker::uniform_list(delegate, cx).width(rems(34.));
+            let picker = Picker::uniform_list(delegate, window, cx).width(rems(34.));
             let query = lister.default_query(cx);
-            picker.set_query(query, cx);
+            picker.set_query(query, window, cx);
             picker
         });
     }
@@ -83,7 +88,7 @@ impl PickerDelegate for OpenPathDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(&mut self, ix: usize, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.selected_index = ix;
         cx.notify();
     }
@@ -91,7 +96,8 @@ impl PickerDelegate for OpenPathDelegate {
     fn update_matches(
         &mut self,
         query: String,
-        cx: &mut ViewContext<Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> gpui::Task<()> {
         let lister = self.lister.clone();
         let (mut dir, suffix) = if let Some(index) = query.rfind('/') {
@@ -116,7 +122,7 @@ impl PickerDelegate for OpenPathDelegate {
         self.cancel_flag = Arc::new(AtomicBool::new(false));
         let cancel_flag = self.cancel_flag.clone();
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             if let Some(query) = query {
                 let paths = query.await;
                 if cancel_flag.load(atomic::Ordering::Relaxed) {
@@ -223,7 +229,8 @@ impl PickerDelegate for OpenPathDelegate {
     fn confirm_completion(
         &mut self,
         query: String,
-        _: &mut ViewContext<Picker<Self>>,
+        _window: &mut Window,
+        _: &mut Context<Picker<Self>>,
     ) -> Option<String> {
         Some(
             maybe!({
@@ -236,7 +243,7 @@ impl PickerDelegate for OpenPathDelegate {
         )
     }
 
-    fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _: bool, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         let Some(m) = self.matches.get(self.selected_index) else {
             return;
         };
@@ -262,7 +269,7 @@ impl PickerDelegate for OpenPathDelegate {
         self.should_dismiss
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+    fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         if let Some(tx) = self.tx.take() {
             tx.send(None).ok();
         }
@@ -273,7 +280,8 @@ impl PickerDelegate for OpenPathDelegate {
         &self,
         ix: usize,
         selected: bool,
-        _: &mut ViewContext<Picker<Self>>,
+        _window: &mut Window,
+        _: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let m = self.matches.get(ix)?;
         let directory_state = self.directory_state.as_ref()?;
@@ -288,7 +296,7 @@ impl PickerDelegate for OpenPathDelegate {
         )
     }
 
-    fn no_matches_text(&self, _cx: &mut WindowContext) -> SharedString {
+    fn no_matches_text(&self, _window: &mut Window, _cx: &mut App) -> SharedString {
         if let Some(error) = self.directory_state.as_ref().and_then(|s| s.error.clone()) {
             error
         } else {
@@ -296,7 +304,7 @@ impl PickerDelegate for OpenPathDelegate {
         }
     }
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         Arc::from("[directory/]filename.ext")
     }
 }

crates/file_icons/src/file_icons.rs 🔗

@@ -3,7 +3,7 @@ use std::{path::Path, str};
 
 use collections::HashMap;
 
-use gpui::{AppContext, AssetSource, Global, SharedString};
+use gpui::{App, AssetSource, Global, SharedString};
 use serde_derive::Deserialize;
 use settings::Settings;
 use theme::{IconTheme, ThemeRegistry, ThemeSettings};
@@ -19,12 +19,12 @@ impl Global for FileIcons {}
 
 pub const FILE_TYPES_ASSET: &str = "icons/file_icons/file_types.json";
 
-pub fn init(assets: impl AssetSource, cx: &mut AppContext) {
+pub fn init(assets: impl AssetSource, cx: &mut App) {
     cx.set_global(FileIcons::new(assets))
 }
 
 impl FileIcons {
-    pub fn get(cx: &AppContext) -> &Self {
+    pub fn get(cx: &App) -> &Self {
         cx.global::<FileIcons>()
     }
 
@@ -40,7 +40,7 @@ impl FileIcons {
             })
     }
 
-    pub fn get_icon(path: &Path, cx: &AppContext) -> Option<SharedString> {
+    pub fn get_icon(path: &Path, cx: &App) -> Option<SharedString> {
         let this = cx.try_global::<Self>()?;
 
         // TODO: Associate a type with the languages and have the file's language
@@ -59,12 +59,12 @@ impl FileIcons {
         .or_else(|| this.get_icon_for_type("default", cx))
     }
 
-    fn default_icon_theme(cx: &AppContext) -> Option<Arc<IconTheme>> {
+    fn default_icon_theme(cx: &App) -> Option<Arc<IconTheme>> {
         let theme_registry = ThemeRegistry::global(cx);
         theme_registry.default_icon_theme().ok()
     }
 
-    pub fn get_icon_for_type(&self, typ: &str, cx: &AppContext) -> Option<SharedString> {
+    pub fn get_icon_for_type(&self, typ: &str, cx: &App) -> Option<SharedString> {
         fn get_icon_for_type(icon_theme: &Arc<IconTheme>, typ: &str) -> Option<SharedString> {
             icon_theme
                 .file_icons
@@ -77,7 +77,7 @@ impl FileIcons {
         })
     }
 
-    pub fn get_folder_icon(expanded: bool, cx: &AppContext) -> Option<SharedString> {
+    pub fn get_folder_icon(expanded: bool, cx: &App) -> Option<SharedString> {
         fn get_folder_icon(icon_theme: &Arc<IconTheme>, expanded: bool) -> Option<SharedString> {
             if expanded {
                 icon_theme.directory_icons.expanded.clone()
@@ -92,7 +92,7 @@ impl FileIcons {
         })
     }
 
-    pub fn get_chevron_icon(expanded: bool, cx: &AppContext) -> Option<SharedString> {
+    pub fn get_chevron_icon(expanded: bool, cx: &App) -> Option<SharedString> {
         fn get_chevron_icon(icon_theme: &Arc<IconTheme>, expanded: bool) -> Option<SharedString> {
             if expanded {
                 icon_theme.chevron_icons.expanded.clone()

crates/fs/src/fs.rs 🔗

@@ -4,7 +4,7 @@ mod mac_watcher;
 #[cfg(not(target_os = "macos"))]
 pub mod fs_watcher;
 
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 #[cfg(any(test, feature = "test-support"))]
 use git::status::FileStatus;
 use git::GitHostingProviderRegistry;
@@ -25,7 +25,7 @@ use std::os::unix::fs::FileTypeExt;
 use async_tar::Archive;
 use futures::{future::BoxFuture, AsyncRead, Stream, StreamExt};
 use git::repository::{GitRepository, RealGitRepository};
-use gpui::{AppContext, Global, ReadGlobal};
+use gpui::{App, Global, ReadGlobal};
 use rope::Rope;
 use serde::{Deserialize, Serialize};
 use smol::io::AsyncWriteExt;
@@ -143,12 +143,12 @@ impl Global for GlobalFs {}
 
 impl dyn Fs {
     /// Returns the global [`Fs`].
-    pub fn global(cx: &AppContext) -> Arc<Self> {
+    pub fn global(cx: &App) -> Arc<Self> {
         GlobalFs::global(cx).0.clone()
     }
 
     /// Sets the global [`Fs`].
-    pub fn set_global(fs: Arc<Self>, cx: &mut AppContext) {
+    pub fn set_global(fs: Arc<Self>, cx: &mut App) {
         cx.set_global(GlobalFs(fs));
     }
 }

crates/git/src/blame.rs 🔗

@@ -1,6 +1,6 @@
 use crate::commit::get_messages;
 use crate::{parse_git_remote_url, BuildCommitPermalinkParams, GitHostingProviderRegistry, Oid};
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use collections::{HashMap, HashSet};
 use serde::{Deserialize, Serialize};
 use std::io::Write;

crates/git/src/git.rs 🔗

@@ -6,7 +6,7 @@ mod remote;
 pub mod repository;
 pub mod status;
 
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use gpui::actions;
 use serde::{Deserialize, Serialize};
 use std::ffi::OsStr;

crates/git/src/hosting_provider.rs 🔗

@@ -4,7 +4,7 @@ use anyhow::Result;
 use async_trait::async_trait;
 use collections::BTreeMap;
 use derive_more::{Deref, DerefMut};
-use gpui::{AppContext, Global};
+use gpui::{App, Global};
 use http_client::HttpClient;
 use parking_lot::RwLock;
 use url::Url;
@@ -107,12 +107,12 @@ pub struct GitHostingProviderRegistry {
 
 impl GitHostingProviderRegistry {
     /// Returns the global [`GitHostingProviderRegistry`].
-    pub fn global(cx: &AppContext) -> Arc<Self> {
+    pub fn global(cx: &App) -> Arc<Self> {
         cx.global::<GlobalGitHostingProviderRegistry>().0.clone()
     }
 
     /// Returns the global [`GitHostingProviderRegistry`], if one is set.
-    pub fn try_global(cx: &AppContext) -> Option<Arc<Self>> {
+    pub fn try_global(cx: &App) -> Option<Arc<Self>> {
         cx.try_global::<GlobalGitHostingProviderRegistry>()
             .map(|registry| registry.0.clone())
     }
@@ -120,14 +120,14 @@ impl GitHostingProviderRegistry {
     /// Returns the global [`GitHostingProviderRegistry`].
     ///
     /// Inserts a default [`GitHostingProviderRegistry`] if one does not yet exist.
-    pub fn default_global(cx: &mut AppContext) -> Arc<Self> {
+    pub fn default_global(cx: &mut App) -> Arc<Self> {
         cx.default_global::<GlobalGitHostingProviderRegistry>()
             .0
             .clone()
     }
 
     /// Sets the global [`GitHostingProviderRegistry`].
-    pub fn set_global(registry: Arc<GitHostingProviderRegistry>, cx: &mut AppContext) {
+    pub fn set_global(registry: Arc<GitHostingProviderRegistry>, cx: &mut App) {
         cx.set_global(GlobalGitHostingProviderRegistry(registry));
     }
 

crates/git/src/repository.rs 🔗

@@ -1,7 +1,7 @@
 use crate::status::FileStatus;
 use crate::GitHostingProviderRegistry;
 use crate::{blame::Blame, status::GitStatus};
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use collections::{HashMap, HashSet};
 use git2::BranchType;
 use gpui::SharedString;

crates/git_hosting_providers/src/git_hosting_providers.rs 🔗

@@ -4,12 +4,12 @@ use std::sync::Arc;
 
 use git::repository::GitRepository;
 use git::GitHostingProviderRegistry;
-use gpui::AppContext;
+use gpui::App;
 
 pub use crate::providers::*;
 
 /// Initializes the Git hosting providers.
-pub fn init(cx: &AppContext) {
+pub fn init(cx: &App) {
     let provider_registry = GitHostingProviderRegistry::global(cx);
     provider_registry.register_hosting_provider(Arc::new(Bitbucket));
     provider_registry.register_hosting_provider(Arc::new(Codeberg));

crates/git_ui/src/git_panel.rs 🔗

@@ -46,11 +46,11 @@ const GIT_PANEL_KEY: &str = "GitPanel";
 
 const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(
-        |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
-            workspace.register_action(|workspace, _: &ToggleFocus, cx| {
-                workspace.toggle_panel_focus::<GitPanel>(cx);
+pub fn init(cx: &mut App) {
+    cx.observe_new(
+        |workspace: &mut Workspace, _window, _cx: &mut Context<Workspace>| {
+            workspace.register_action(|workspace, _: &ToggleFocus, window, cx| {
+                workspace.toggle_panel_focus::<GitPanel>(window, cx);
             });
         },
     )
@@ -83,15 +83,15 @@ pub struct GitPanel {
     fs: Arc<dyn Fs>,
     hide_scrollbar_task: Option<Task<()>>,
     pending_serialization: Task<Option<()>>,
-    workspace: WeakView<Workspace>,
-    project: Model<Project>,
+    workspace: WeakEntity<Workspace>,
+    project: Entity<Project>,
     active_repository: Option<RepositoryHandle>,
     scroll_handle: UniformListScrollHandle,
     scrollbar_state: ScrollbarState,
     selected_entry: Option<usize>,
     show_scrollbar: bool,
     update_visible_entries_task: Task<()>,
-    commit_editor: View<Editor>,
+    commit_editor: Entity<Editor>,
     visible_entries: Vec<GitListEntry>,
     all_staged: Option<bool>,
     width: Option<Pixels>,
@@ -100,11 +100,12 @@ pub struct GitPanel {
 
 fn commit_message_editor(
     active_repository: Option<&RepositoryHandle>,
-    cx: &mut ViewContext<'_, Editor>,
+    window: &mut Window,
+    cx: &mut Context<'_, Editor>,
 ) -> Editor {
     let theme = ThemeSettings::get_global(cx);
 
-    let mut text_style = cx.text_style();
+    let mut text_style = window.text_style();
     let refinement = TextStyleRefinement {
         font_family: Some(theme.buffer_font.family.clone()),
         font_features: Some(FontFeatures::disable_ligatures()),
@@ -116,17 +117,17 @@ fn commit_message_editor(
     text_style.refine(&refinement);
 
     let mut commit_editor = if let Some(active_repository) = active_repository.as_ref() {
-        let buffer =
-            cx.new_model(|cx| MultiBuffer::singleton(active_repository.commit_message(), cx));
+        let buffer = cx.new(|cx| MultiBuffer::singleton(active_repository.commit_message(), cx));
         Editor::new(
             EditorMode::AutoHeight { max_lines: 10 },
             buffer,
             None,
             false,
+            window,
             cx,
         )
     } else {
-        Editor::auto_height(10, cx)
+        Editor::auto_height(10, window, cx)
     };
     commit_editor.set_use_autoclose(false);
     commit_editor.set_show_gutter(false, cx);
@@ -139,40 +140,48 @@ fn commit_message_editor(
 
 impl GitPanel {
     pub fn load(
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         cx: AsyncWindowContext,
-    ) -> Task<Result<View<Self>>> {
-        cx.spawn(|mut cx| async move { workspace.update(&mut cx, Self::new) })
+    ) -> Task<Result<Entity<Self>>> {
+        cx.spawn(|mut cx| async move { workspace.update_in(&mut cx, Self::new) })
     }
 
-    pub fn new(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) -> View<Self> {
+    pub fn new(
+        workspace: &mut Workspace,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Entity<Self> {
         let fs = workspace.app_state().fs.clone();
         let project = workspace.project().clone();
         let git_state = project.read(cx).git_state().cloned();
         let active_repository = project.read(cx).active_repository(cx);
         let (err_sender, mut err_receiver) = mpsc::channel(1);
-        let workspace = cx.view().downgrade();
+        let workspace = cx.model().downgrade();
 
-        let git_panel = cx.new_view(|cx: &mut ViewContext<Self>| {
+        let git_panel = cx.new(|cx| {
             let focus_handle = cx.focus_handle();
-            cx.on_focus(&focus_handle, Self::focus_in).detach();
-            cx.on_focus_out(&focus_handle, |this, _, cx| {
-                this.hide_scrollbar(cx);
+            cx.on_focus(&focus_handle, window, Self::focus_in).detach();
+            cx.on_focus_out(&focus_handle, window, |this, _, window, cx| {
+                this.hide_scrollbar(window, cx);
             })
             .detach();
 
             let commit_editor =
-                cx.new_view(|cx| commit_message_editor(active_repository.as_ref(), cx));
+                cx.new(|cx| commit_message_editor(active_repository.as_ref(), window, cx));
 
             let scroll_handle = UniformListScrollHandle::new();
 
             if let Some(git_state) = git_state {
-                cx.subscribe(&git_state, move |this, git_state, event, cx| match event {
-                    project::git::Event::RepositoriesUpdated => {
-                        this.active_repository = git_state.read(cx).active_repository();
-                        this.schedule_update(cx);
-                    }
-                })
+                cx.subscribe_in(
+                    &git_state,
+                    window,
+                    move |this, git_state, event, window, cx| match event {
+                        project::git::Event::RepositoriesUpdated => {
+                            this.active_repository = git_state.read(cx).active_repository();
+                            this.schedule_update(window, cx);
+                        }
+                    },
+                )
                 .detach();
             }
 
@@ -181,9 +190,10 @@ impl GitPanel {
                 pending_serialization: Task::ready(None),
                 visible_entries: Vec::new(),
                 all_staged: None,
-                current_modifiers: cx.modifiers(),
+                current_modifiers: window.modifiers(),
                 width: Some(px(360.)),
-                scrollbar_state: ScrollbarState::new(scroll_handle.clone()).parent_view(cx.view()),
+                scrollbar_state: ScrollbarState::new(scroll_handle.clone())
+                    .parent_model(&cx.model()),
                 selected_entry: None,
                 show_scrollbar: false,
                 hide_scrollbar_task: None,
@@ -196,7 +206,7 @@ impl GitPanel {
                 err_sender,
                 workspace,
             };
-            git_panel.schedule_update(cx);
+            git_panel.schedule_update(window, cx);
             git_panel.show_scrollbar = git_panel.should_show_scrollbar(cx);
             git_panel
         });
@@ -219,13 +229,14 @@ impl GitPanel {
         })
         .detach();
 
-        cx.subscribe(
+        cx.subscribe_in(
             &git_panel,
-            move |workspace, _, event: &Event, cx| match event.clone() {
+            window,
+            move |workspace, _, event: &Event, window, cx| match event.clone() {
                 Event::OpenedEntry { path } => {
                     workspace
-                        .open_path_preview(path, None, false, false, cx)
-                        .detach_and_prompt_err("Failed to open file", cx, |e, _| {
+                        .open_path_preview(path, None, false, false, window, cx)
+                        .detach_and_prompt_err("Failed to open file", window, cx, |e, _, _| {
                             Some(format!("{e}"))
                         });
                 }
@@ -237,7 +248,7 @@ impl GitPanel {
         git_panel
     }
 
-    fn serialize(&mut self, cx: &mut ViewContext<Self>) {
+    fn serialize(&mut self, cx: &mut Context<Self>) {
         // TODO: we can store stage status here
         let width = self.width;
         self.pending_serialization = cx.background_executor().spawn(
@@ -254,45 +265,46 @@ impl GitPanel {
         );
     }
 
-    fn dispatch_context(&self, cx: &ViewContext<Self>) -> KeyContext {
+    fn dispatch_context(&self, window: &mut Window, cx: &Context<Self>) -> KeyContext {
         let mut dispatch_context = KeyContext::new_with_defaults();
         dispatch_context.add("GitPanel");
 
-        if self.is_focused(cx) {
+        if self.is_focused(window, cx) {
             dispatch_context.add("menu");
             dispatch_context.add("ChangesList");
         }
 
-        if self.commit_editor.read(cx).is_focused(cx) {
+        if self.commit_editor.read(cx).is_focused(window) {
             dispatch_context.add("CommitEditor");
         }
 
         dispatch_context
     }
 
-    fn is_focused(&self, cx: &ViewContext<Self>) -> bool {
-        cx.focused()
+    fn is_focused(&self, window: &Window, cx: &Context<Self>) -> bool {
+        window
+            .focused(cx)
             .map_or(false, |focused| self.focus_handle == focused)
     }
 
-    fn close_panel(&mut self, _: &Close, cx: &mut ViewContext<Self>) {
+    fn close_panel(&mut self, _: &Close, _window: &mut Window, cx: &mut Context<Self>) {
         cx.emit(PanelEvent::Close);
     }
 
-    fn focus_in(&mut self, cx: &mut ViewContext<Self>) {
-        if !self.focus_handle.contains_focused(cx) {
+    fn focus_in(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        if !self.focus_handle.contains_focused(window, cx) {
             cx.emit(Event::Focus);
         }
     }
 
-    fn show_scrollbar(&self, cx: &mut ViewContext<Self>) -> ShowScrollbar {
+    fn show_scrollbar(&self, cx: &mut Context<Self>) -> ShowScrollbar {
         GitPanelSettings::get_global(cx)
             .scrollbar
             .show
             .unwrap_or_else(|| EditorSettings::get_global(cx).scrollbar.show)
     }
 
-    fn should_show_scrollbar(&self, cx: &mut ViewContext<Self>) -> bool {
+    fn should_show_scrollbar(&self, cx: &mut Context<Self>) -> bool {
         let show = self.show_scrollbar(cx);
         match show {
             ShowScrollbar::Auto => true,
@@ -302,7 +314,7 @@ impl GitPanel {
         }
     }
 
-    fn should_autohide_scrollbar(&self, cx: &mut ViewContext<Self>) -> bool {
+    fn should_autohide_scrollbar(&self, cx: &mut Context<Self>) -> bool {
         let show = self.show_scrollbar(cx);
         match show {
             ShowScrollbar::Auto => true,
@@ -314,12 +326,12 @@ impl GitPanel {
         }
     }
 
-    fn hide_scrollbar(&mut self, cx: &mut ViewContext<Self>) {
+    fn hide_scrollbar(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1);
         if !self.should_autohide_scrollbar(cx) {
             return;
         }
-        self.hide_scrollbar_task = Some(cx.spawn(|panel, mut cx| async move {
+        self.hide_scrollbar_task = Some(cx.spawn_in(window, |panel, mut cx| async move {
             cx.background_executor()
                 .timer(SCROLLBAR_SHOW_INTERVAL)
                 .await;
@@ -335,7 +347,8 @@ impl GitPanel {
     fn handle_modifiers_changed(
         &mut self,
         event: &ModifiersChangedEvent,
-        cx: &mut ViewContext<Self>,
+        _: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.current_modifiers = event.modifiers;
         cx.notify();
@@ -366,7 +379,7 @@ impl GitPanel {
         (0, 0)
     }
 
-    fn scroll_to_selected_entry(&mut self, cx: &mut ViewContext<Self>) {
+    fn scroll_to_selected_entry(&mut self, cx: &mut Context<Self>) {
         if let Some(selected_entry) = self.selected_entry {
             self.scroll_handle
                 .scroll_to_item(selected_entry, ScrollStrategy::Center);
@@ -375,14 +388,14 @@ impl GitPanel {
         cx.notify();
     }
 
-    fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext<Self>) {
+    fn select_first(&mut self, _: &SelectFirst, _window: &mut Window, cx: &mut Context<Self>) {
         if self.visible_entries.first().is_some() {
             self.selected_entry = Some(0);
             self.scroll_to_selected_entry(cx);
         }
     }
 
-    fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext<Self>) {
+    fn select_prev(&mut self, _: &SelectPrev, _window: &mut Window, cx: &mut Context<Self>) {
         let item_count = self.visible_entries.len();
         if item_count == 0 {
             return;
@@ -403,7 +416,7 @@ impl GitPanel {
         cx.notify();
     }
 
-    fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
+    fn select_next(&mut self, _: &SelectNext, _window: &mut Window, cx: &mut Context<Self>) {
         let item_count = self.visible_entries.len();
         if item_count == 0 {
             return;
@@ -424,21 +437,21 @@ impl GitPanel {
         cx.notify();
     }
 
-    fn select_last(&mut self, _: &SelectLast, cx: &mut ViewContext<Self>) {
+    fn select_last(&mut self, _: &SelectLast, _window: &mut Window, cx: &mut Context<Self>) {
         if self.visible_entries.last().is_some() {
             self.selected_entry = Some(self.visible_entries.len() - 1);
             self.scroll_to_selected_entry(cx);
         }
     }
 
-    fn focus_editor(&mut self, _: &FocusEditor, cx: &mut ViewContext<Self>) {
+    fn focus_editor(&mut self, _: &FocusEditor, window: &mut Window, cx: &mut Context<Self>) {
         self.commit_editor.update(cx, |editor, cx| {
-            editor.focus(cx);
+            window.focus(&editor.focus_handle(cx));
         });
         cx.notify();
     }
 
-    fn select_first_entry_if_none(&mut self, cx: &mut ViewContext<Self>) {
+    fn select_first_entry_if_none(&mut self, cx: &mut Context<Self>) {
         let have_entries = self
             .active_repository
             .as_ref()
@@ -452,10 +465,15 @@ impl GitPanel {
         }
     }
 
-    fn focus_changes_list(&mut self, _: &FocusChanges, cx: &mut ViewContext<Self>) {
+    fn focus_changes_list(
+        &mut self,
+        _: &FocusChanges,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.select_first_entry_if_none(cx);
 
-        cx.focus_self();
+        cx.focus_self(window);
         cx.notify();
     }
 
@@ -464,7 +482,7 @@ impl GitPanel {
             .and_then(|i| self.visible_entries.get(i))
     }
 
-    fn open_selected(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
+    fn open_selected(&mut self, _: &menu::Confirm, _window: &mut Window, cx: &mut Context<Self>) {
         if let Some(entry) = self
             .selected_entry
             .and_then(|i| self.visible_entries.get(i))
@@ -473,7 +491,12 @@ impl GitPanel {
         }
     }
 
-    fn toggle_staged_for_entry(&mut self, entry: &GitListEntry, cx: &mut ViewContext<Self>) {
+    fn toggle_staged_for_entry(
+        &mut self,
+        entry: &GitListEntry,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let Some(active_repository) = self.active_repository.as_ref() else {
             return;
         };
@@ -489,13 +512,18 @@ impl GitPanel {
         cx.notify();
     }
 
-    fn toggle_staged_for_selected(&mut self, _: &git::ToggleStaged, cx: &mut ViewContext<Self>) {
+    fn toggle_staged_for_selected(
+        &mut self,
+        _: &git::ToggleStaged,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(selected_entry) = self.get_selected_entry().cloned() {
-            self.toggle_staged_for_entry(&selected_entry, cx);
+            self.toggle_staged_for_entry(&selected_entry, window, cx);
         }
     }
 
-    fn open_entry(&self, entry: &GitListEntry, cx: &mut ViewContext<Self>) {
+    fn open_entry(&self, entry: &GitListEntry, cx: &mut Context<Self>) {
         let Some(active_repository) = self.active_repository.as_ref() else {
             return;
         };
@@ -512,7 +540,7 @@ impl GitPanel {
         cx.emit(Event::OpenedEntry { path });
     }
 
-    fn stage_all(&mut self, _: &git::StageAll, cx: &mut ViewContext<Self>) {
+    fn stage_all(&mut self, _: &git::StageAll, _window: &mut Window, cx: &mut Context<Self>) {
         let Some(active_repository) = self.active_repository.as_ref() else {
             return;
         };
@@ -526,7 +554,7 @@ impl GitPanel {
         };
     }
 
-    fn unstage_all(&mut self, _: &git::UnstageAll, cx: &mut ViewContext<Self>) {
+    fn unstage_all(&mut self, _: &git::UnstageAll, _window: &mut Window, cx: &mut Context<Self>) {
         let Some(active_repository) = self.active_repository.as_ref() else {
             return;
         };
@@ -539,13 +567,18 @@ impl GitPanel {
         };
     }
 
-    fn discard_all(&mut self, _: &git::RevertAll, _cx: &mut ViewContext<Self>) {
+    fn discard_all(&mut self, _: &git::RevertAll, _window: &mut Window, _cx: &mut Context<Self>) {
         // TODO: Implement discard all
         println!("Discard all triggered");
     }
 
     /// Commit all staged changes
-    fn commit_changes(&mut self, _: &git::CommitChanges, cx: &mut ViewContext<Self>) {
+    fn commit_changes(
+        &mut self,
+        _: &git::CommitChanges,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let Some(active_repository) = self.active_repository.as_ref() else {
             return;
         };
@@ -556,7 +589,12 @@ impl GitPanel {
     }
 
     /// Commit all changes, regardless of whether they are staged or not
-    fn commit_all_changes(&mut self, _: &git::CommitAllChanges, cx: &mut ViewContext<Self>) {
+    fn commit_all_changes(
+        &mut self,
+        _: &git::CommitAllChanges,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let Some(active_repository) = self.active_repository.as_ref() else {
             return;
         };
@@ -566,7 +604,7 @@ impl GitPanel {
         active_repository.commit_all(self.err_sender.clone(), cx);
     }
 
-    fn fill_co_authors(&mut self, _: &FillCoAuthors, cx: &mut ViewContext<Self>) {
+    fn fill_co_authors(&mut self, _: &FillCoAuthors, window: &mut Window, cx: &mut Context<Self>) {
         const CO_AUTHOR_PREFIX: &str = "Co-authored-by: ";
 
         let Some(room) = self
@@ -626,16 +664,16 @@ impl GitPanel {
             }
 
             editor.edit(Some((editor_end..editor_end, edit)), cx);
-            editor.move_to_end(&MoveToEnd, cx);
-            editor.focus(cx);
+            editor.move_to_end(&MoveToEnd, window, cx);
+            editor.focus_handle(cx).focus(window);
         });
     }
 
     fn for_each_visible_entry(
         &self,
         range: Range<usize>,
-        cx: &mut ViewContext<Self>,
-        mut callback: impl FnMut(usize, GitListEntry, &mut ViewContext<Self>),
+        cx: &mut Context<Self>,
+        mut callback: impl FnMut(usize, GitListEntry, &mut Context<Self>),
     ) {
         let visible_entries = &self.visible_entries;
 
@@ -664,23 +702,23 @@ impl GitPanel {
         }
     }
 
-    fn schedule_update(&mut self, cx: &mut ViewContext<Self>) {
-        let handle = cx.view().downgrade();
-        self.update_visible_entries_task = cx.spawn(|_, mut cx| async move {
+    fn schedule_update(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        let handle = cx.model().downgrade();
+        self.update_visible_entries_task = cx.spawn_in(window, |_, mut cx| async move {
             cx.background_executor().timer(UPDATE_DEBOUNCE).await;
             if let Some(this) = handle.upgrade() {
-                this.update(&mut cx, |this, cx| {
+                this.update_in(&mut cx, |this, window, cx| {
                     this.update_visible_entries(cx);
                     let active_repository = this.active_repository.as_ref();
                     this.commit_editor =
-                        cx.new_view(|cx| commit_message_editor(active_repository, cx));
+                        cx.new(|cx| commit_message_editor(active_repository, window, cx));
                 })
                 .ok();
             }
         });
     }
 
-    fn update_visible_entries(&mut self, cx: &mut ViewContext<Self>) {
+    fn update_visible_entries(&mut self, cx: &mut Context<Self>) {
         self.visible_entries.clear();
 
         let Some(repo) = self.active_repository.as_ref() else {
@@ -747,15 +785,15 @@ impl GitPanel {
         cx.notify();
     }
 
-    fn show_err_toast(&self, id: &'static str, e: anyhow::Error, cx: &mut ViewContext<Self>) {
+    fn show_err_toast(&self, id: &'static str, e: anyhow::Error, cx: &mut Context<Self>) {
         let Some(workspace) = self.workspace.upgrade() else {
             return;
         };
         let notif_id = NotificationId::Named(id.into());
         let message = e.to_string();
         workspace.update(cx, |workspace, cx| {
-            let toast = Toast::new(notif_id, message).on_click("Open Zed Log", |cx| {
-                cx.dispatch_action(workspace::OpenLog.boxed_clone());
+            let toast = Toast::new(notif_id, message).on_click("Open Zed Log", |window, cx| {
+                window.dispatch_action(workspace::OpenLog.boxed_clone(), cx);
             });
             workspace.show_toast(toast, cx);
         });
@@ -779,14 +817,18 @@ impl GitPanel {
             .style(ButtonStyle::Filled)
     }
 
-    pub fn render_divider(&self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    pub fn render_divider(&self, _cx: &mut Context<Self>) -> impl IntoElement {
         h_flex()
             .items_center()
             .h(px(8.))
             .child(Divider::horizontal_dashed().color(DividerColor::Border))
     }
 
-    pub fn render_panel_header(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    pub fn render_panel_header(
+        &self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> impl IntoElement {
         let focus_handle = self.focus_handle(cx).clone();
         let entry_count = self
             .active_repository
@@ -822,18 +864,18 @@ impl GitPanel {
                         )
                         .fill()
                         .elevation(ElevationIndex::Surface)
-                        .tooltip(move |cx| {
-                            if all_staged {
-                                Tooltip::text("Unstage all changes", cx)
-                            } else {
-                                Tooltip::text("Stage all changes", cx)
-                            }
+                        .tooltip(if all_staged {
+                            Tooltip::text("Unstage all changes")
+                        } else {
+                            Tooltip::text("Stage all changes")
                         })
                         .disabled(entry_count == 0)
-                        .on_click(cx.listener(move |git_panel, _, cx| match all_staged {
-                            true => git_panel.unstage_all(&UnstageAll, cx),
-                            false => git_panel.stage_all(&StageAll, cx),
-                        })),
+                        .on_click(cx.listener(
+                            move |git_panel, _, window, cx| match all_staged {
+                                true => git_panel.unstage_all(&UnstageAll, window, cx),
+                                false => git_panel.stage_all(&StageAll, window, cx),
+                            },
+                        )),
                     )
                     .child(
                         div()
@@ -841,10 +883,12 @@ impl GitPanel {
                             .text_buffer(cx)
                             .text_ui_sm(cx)
                             .child(changes_string)
-                            .on_click(cx.listener(move |git_panel, _, cx| match all_staged {
-                                true => git_panel.unstage_all(&UnstageAll, cx),
-                                false => git_panel.stage_all(&StageAll, cx),
-                            })),
+                            .on_click(cx.listener(
+                                move |git_panel, _, window, cx| match all_staged {
+                                    true => git_panel.unstage_all(&UnstageAll, window, cx),
+                                    false => git_panel.stage_all(&StageAll, window, cx),
+                                },
+                            )),
                     ),
             )
             .child(div().flex_grow())
@@ -872,11 +916,12 @@ impl GitPanel {
                         self.panel_button("unstage-all", "Unstage All")
                             .tooltip({
                                 let focus_handle = focus_handle.clone();
-                                move |cx| {
+                                move |window, cx| {
                                     Tooltip::for_action_in(
                                         "Unstage all changes",
                                         &UnstageAll,
                                         &focus_handle,
+                                        window,
                                         cx,
                                     )
                                 }
@@ -884,20 +929,21 @@ impl GitPanel {
                             .key_binding(ui::KeyBinding::for_action_in(
                                 &UnstageAll,
                                 &focus_handle,
-                                cx,
+                                window,
                             ))
-                            .on_click(
-                                cx.listener(move |this, _, cx| this.unstage_all(&UnstageAll, cx)),
-                            )
+                            .on_click(cx.listener(move |this, _, window, cx| {
+                                this.unstage_all(&UnstageAll, window, cx)
+                            }))
                     } else {
                         self.panel_button("stage-all", "Stage All")
                             .tooltip({
                                 let focus_handle = focus_handle.clone();
-                                move |cx| {
+                                move |window, cx| {
                                     Tooltip::for_action_in(
                                         "Stage all changes",
                                         &StageAll,
                                         &focus_handle,
+                                        window,
                                         cx,
                                     )
                                 }
@@ -905,14 +951,16 @@ impl GitPanel {
                             .key_binding(ui::KeyBinding::for_action_in(
                                 &StageAll,
                                 &focus_handle,
-                                cx,
+                                window,
                             ))
-                            .on_click(cx.listener(move |this, _, cx| this.stage_all(&StageAll, cx)))
+                            .on_click(cx.listener(move |this, _, window, cx| {
+                                this.stage_all(&StageAll, window, cx)
+                            }))
                     }),
             )
     }
 
-    pub fn render_commit_editor(&self, cx: &ViewContext<Self>) -> impl IntoElement {
+    pub fn render_commit_editor(&self, cx: &Context<Self>) -> impl IntoElement {
         let editor = self.commit_editor.clone();
         let editor_focus_handle = editor.read(cx).focus_handle(cx).clone();
         let (can_commit, can_commit_all) =
@@ -930,34 +978,36 @@ impl GitPanel {
 
         let commit_staged_button = self
             .panel_button("commit-staged-changes", "Commit")
-            .tooltip(move |cx| {
+            .tooltip(move |window, cx| {
                 let focus_handle = focus_handle_1.clone();
                 Tooltip::for_action_in(
                     "Commit all staged changes",
                     &CommitChanges,
                     &focus_handle,
+                    window,
                     cx,
                 )
             })
             .disabled(!can_commit)
-            .on_click(
-                cx.listener(|this, _: &ClickEvent, cx| this.commit_changes(&CommitChanges, cx)),
-            );
+            .on_click(cx.listener(|this, _: &ClickEvent, window, cx| {
+                this.commit_changes(&CommitChanges, window, cx)
+            }));
 
         let commit_all_button = self
             .panel_button("commit-all-changes", "Commit All")
-            .tooltip(move |cx| {
+            .tooltip(move |window, cx| {
                 let focus_handle = focus_handle_2.clone();
                 Tooltip::for_action_in(
                     "Commit all changes, including unstaged changes",
                     &CommitAllChanges,
                     &focus_handle,
+                    window,
                     cx,
                 )
             })
             .disabled(!can_commit_all)
-            .on_click(cx.listener(|this, _: &ClickEvent, cx| {
-                this.commit_all_changes(&CommitAllChanges, cx)
+            .on_click(cx.listener(|this, _: &ClickEvent, window, cx| {
+                this.commit_all_changes(&CommitAllChanges, window, cx)
             }));
 
         div().w_full().h(px(140.)).px_2().pt_1().pb_2().child(
@@ -968,7 +1018,9 @@ impl GitPanel {
                 .py_2p5()
                 .px_3()
                 .bg(cx.theme().colors().editor_background)
-                .on_click(cx.listener(move |_, _: &ClickEvent, cx| cx.focus(&editor_focus_handle)))
+                .on_click(cx.listener(move |_, _: &ClickEvent, window, _cx| {
+                    window.focus(&editor_focus_handle);
+                }))
                 .child(self.commit_editor.clone())
                 .child(
                     h_flex()
@@ -985,7 +1037,7 @@ impl GitPanel {
         )
     }
 
-    fn render_empty_state(&self, cx: &ViewContext<Self>) -> impl IntoElement {
+    fn render_empty_state(&self, cx: &mut Context<Self>) -> impl IntoElement {
         h_flex()
             .h_full()
             .flex_1()
@@ -1001,7 +1053,7 @@ impl GitPanel {
             )
     }
 
-    fn render_scrollbar(&self, cx: &mut ViewContext<Self>) -> Option<Stateful<Div>> {
+    fn render_scrollbar(&self, cx: &mut Context<Self>) -> Option<Stateful<Div>> {
         let scroll_bar_style = self.show_scrollbar(cx);
         let show_container = matches!(scroll_bar_style, ShowScrollbar::Always);
 
@@ -1022,30 +1074,30 @@ impl GitPanel {
                 .when(!show_container, |this| {
                     this.absolute().right_1().top_1().bottom_1().w(px(12.))
                 })
-                .on_mouse_move(cx.listener(|_, _, cx| {
+                .on_mouse_move(cx.listener(|_, _, _, cx| {
                     cx.notify();
                     cx.stop_propagation()
                 }))
-                .on_hover(|_, cx| {
+                .on_hover(|_, _, cx| {
                     cx.stop_propagation();
                 })
-                .on_any_mouse_down(|_, cx| {
+                .on_any_mouse_down(|_, _, cx| {
                     cx.stop_propagation();
                 })
                 .on_mouse_up(
                     MouseButton::Left,
-                    cx.listener(|this, _, cx| {
+                    cx.listener(|this, _, window, cx| {
                         if !this.scrollbar_state.is_dragging()
-                            && !this.focus_handle.contains_focused(cx)
+                            && !this.focus_handle.contains_focused(window, cx)
                         {
-                            this.hide_scrollbar(cx);
+                            this.hide_scrollbar(window, cx);
                             cx.notify();
                         }
 
                         cx.stop_propagation();
                     }),
                 )
-                .on_scroll_wheel(cx.listener(|_, _, cx| {
+                .on_scroll_wheel(cx.listener(|_, _, _, cx| {
                     cx.notify();
                 }))
                 .children(Scrollbar::vertical(
@@ -1055,15 +1107,15 @@ impl GitPanel {
         )
     }
 
-    fn render_entries(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render_entries(&self, cx: &mut Context<Self>) -> impl IntoElement {
         let entry_count = self.visible_entries.len();
 
         h_flex()
             .size_full()
             .overflow_hidden()
             .child(
-                uniform_list(cx.view().clone(), "entries", entry_count, {
-                    move |git_panel, range, cx| {
+                uniform_list(cx.model().clone(), "entries", entry_count, {
+                    move |git_panel, range, _window, cx| {
                         let mut items = Vec::with_capacity(range.end - range.start);
                         git_panel.for_each_visible_entry(range, cx, |ix, details, cx| {
                             items.push(git_panel.render_entry(ix, details, cx));
@@ -1084,7 +1136,7 @@ impl GitPanel {
         &self,
         ix: usize,
         entry_details: GitListEntry,
-        cx: &ViewContext<Self>,
+        cx: &Context<Self>,
     ) -> impl IntoElement {
         let repo_path = entry_details.repo_path.clone();
         let selected = self.selected_entry == Some(ix);
@@ -1115,7 +1167,7 @@ impl GitPanel {
         let checkbox_id =
             ElementId::Name(format!("checkbox_{}", entry_details.display_name).into());
         let is_tree_view = false;
-        let handle = cx.view().downgrade();
+        let handle = cx.model().downgrade();
 
         let end_slot = h_flex()
             .invisible()
@@ -1168,7 +1220,7 @@ impl GitPanel {
                 .on_click({
                     let handle = handle.clone();
                     let repo_path = repo_path.clone();
-                    move |toggle, cx| {
+                    move |toggle, _window, cx| {
                         let Some(this) = handle.upgrade() else {
                             return;
                         };
@@ -1220,9 +1272,9 @@ impl GitPanel {
             )
             .child(div().flex_1())
             .child(end_slot)
-            .on_click(move |_, cx| {
+            .on_click(move |_, window, cx| {
                 // TODO: add `select_entry` method then do after that
-                cx.dispatch_action(Box::new(OpenSelected));
+                window.dispatch_action(Box::new(OpenSelected), cx);
 
                 handle
                     .update(cx, |git_panel, _| {
@@ -1236,7 +1288,7 @@ impl GitPanel {
 }
 
 impl Render for GitPanel {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let project = self.project.read(cx);
         let has_entries = self
             .active_repository
@@ -1260,24 +1312,32 @@ impl Render for GitPanel {
 
         v_flex()
             .id("git_panel")
-            .key_context(self.dispatch_context(cx))
+            .key_context(self.dispatch_context(window, cx))
             .track_focus(&self.focus_handle)
             .on_modifiers_changed(cx.listener(Self::handle_modifiers_changed))
             .when(!project.is_read_only(cx), |this| {
-                this.on_action(cx.listener(|this, &ToggleStaged, cx| {
-                    this.toggle_staged_for_selected(&ToggleStaged, cx)
+                this.on_action(cx.listener(|this, &ToggleStaged, window, cx| {
+                    this.toggle_staged_for_selected(&ToggleStaged, window, cx)
                 }))
-                .on_action(cx.listener(|this, &StageAll, cx| this.stage_all(&StageAll, cx)))
-                .on_action(cx.listener(|this, &UnstageAll, cx| this.unstage_all(&UnstageAll, cx)))
-                .on_action(cx.listener(|this, &RevertAll, cx| this.discard_all(&RevertAll, cx)))
                 .on_action(
-                    cx.listener(|this, &CommitChanges, cx| this.commit_changes(&CommitChanges, cx)),
+                    cx.listener(|this, &StageAll, window, cx| {
+                        this.stage_all(&StageAll, window, cx)
+                    }),
                 )
-                .on_action(cx.listener(|this, &CommitAllChanges, cx| {
-                    this.commit_all_changes(&CommitAllChanges, cx)
+                .on_action(cx.listener(|this, &UnstageAll, window, cx| {
+                    this.unstage_all(&UnstageAll, window, cx)
+                }))
+                .on_action(cx.listener(|this, &RevertAll, window, cx| {
+                    this.discard_all(&RevertAll, window, cx)
+                }))
+                .on_action(cx.listener(|this, &CommitChanges, window, cx| {
+                    this.commit_changes(&CommitChanges, window, cx)
+                }))
+                .on_action(cx.listener(|this, &CommitAllChanges, window, cx| {
+                    this.commit_all_changes(&CommitAllChanges, window, cx)
                 }))
             })
-            .when(self.is_focused(cx), |this| {
+            .when(self.is_focused(window, cx), |this| {
                 this.on_action(cx.listener(Self::select_first))
                     .on_action(cx.listener(Self::select_next))
                     .on_action(cx.listener(Self::select_prev))
@@ -1292,13 +1352,13 @@ impl Render for GitPanel {
                 git_panel.on_action(cx.listener(Self::fill_co_authors))
             })
             // .on_action(cx.listener(|this, &OpenSelected, cx| this.open_selected(&OpenSelected, cx)))
-            .on_hover(cx.listener(|this, hovered, cx| {
+            .on_hover(cx.listener(|this, hovered, window, cx| {
                 if *hovered {
                     this.show_scrollbar = true;
                     this.hide_scrollbar_task.take();
                     cx.notify();
-                } else if !this.focus_handle.contains_focused(cx) {
-                    this.hide_scrollbar(cx);
+                } else if !this.focus_handle.contains_focused(window, cx) {
+                    this.hide_scrollbar(window, cx);
                 }
             }))
             .size_full()
@@ -1306,7 +1366,7 @@ impl Render for GitPanel {
             .font_buffer(cx)
             .py_1()
             .bg(ElevationIndex::Surface.bg(cx))
-            .child(self.render_panel_header(cx))
+            .child(self.render_panel_header(window, cx))
             .child(self.render_divider(cx))
             .child(if has_entries {
                 self.render_entries(cx).into_any_element()
@@ -1318,8 +1378,8 @@ impl Render for GitPanel {
     }
 }
 
-impl FocusableView for GitPanel {
-    fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
+impl Focusable for GitPanel {
+    fn focus_handle(&self, _: &App) -> gpui::FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -1333,7 +1393,7 @@ impl Panel for GitPanel {
         "GitPanel"
     }
 
-    fn position(&self, cx: &WindowContext) -> DockPosition {
+    fn position(&self, _: &Window, cx: &App) -> DockPosition {
         GitPanelSettings::get_global(cx).dock
     }
 
@@ -1341,7 +1401,7 @@ impl Panel for GitPanel {
         matches!(position, DockPosition::Left | DockPosition::Right)
     }
 
-    fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) {
+    fn set_position(&mut self, position: DockPosition, _: &mut Window, cx: &mut Context<Self>) {
         settings::update_settings_file::<GitPanelSettings>(
             self.fs.clone(),
             cx,
@@ -1349,22 +1409,22 @@ impl Panel for GitPanel {
         );
     }
 
-    fn size(&self, cx: &WindowContext) -> Pixels {
+    fn size(&self, _: &Window, cx: &App) -> Pixels {
         self.width
             .unwrap_or_else(|| GitPanelSettings::get_global(cx).default_width)
     }
 
-    fn set_size(&mut self, size: Option<Pixels>, cx: &mut ViewContext<Self>) {
+    fn set_size(&mut self, size: Option<Pixels>, _: &mut Window, cx: &mut Context<Self>) {
         self.width = size;
         self.serialize(cx);
         cx.notify();
     }
 
-    fn icon(&self, cx: &WindowContext) -> Option<ui::IconName> {
+    fn icon(&self, _: &Window, cx: &App) -> Option<ui::IconName> {
         Some(ui::IconName::GitBranch).filter(|_| GitPanelSettings::get_global(cx).button)
     }
 
-    fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> {
+    fn icon_tooltip(&self, _window: &Window, _cx: &App) -> Option<&'static str> {
         Some("Git Panel")
     }
 

crates/git_ui/src/git_panel_settings.rs 🔗

@@ -76,7 +76,7 @@ impl Settings for GitPanelSettings {
 
     fn load(
         sources: SettingsSources<Self::FileContent>,
-        _: &mut gpui::AppContext,
+        _: &mut gpui::App,
     ) -> anyhow::Result<Self> {
         sources.json_merge()
     }

crates/git_ui/src/git_ui.rs 🔗

@@ -1,19 +1,19 @@
 use ::settings::Settings;
 use git::status::FileStatus;
 use git_panel_settings::GitPanelSettings;
-use gpui::AppContext;
-use ui::{ActiveTheme, Color, Icon, IconName, IntoElement, WindowContext};
+use gpui::App;
+use ui::{ActiveTheme, Color, Icon, IconName, IntoElement};
 
 pub mod git_panel;
 mod git_panel_settings;
 pub mod repository_selector;
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     GitPanelSettings::register(cx);
 }
 
 // TODO: Add updated status colors to theme
-pub fn git_status_icon(status: FileStatus, cx: &WindowContext) -> impl IntoElement {
+pub fn git_status_icon(status: FileStatus, cx: &App) -> impl IntoElement {
     let (icon_name, color) = if status.is_conflicted() {
         (
             IconName::Warning,

crates/git_ui/src/repository_selector.rs 🔗

@@ -1,6 +1,6 @@
 use gpui::{
-    AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Model,
-    Subscription, Task, View, WeakModel, WeakView,
+    AnyElement, App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Subscription,
+    Task, WeakEntity,
 };
 use picker::{Picker, PickerDelegate};
 use project::{
@@ -11,7 +11,7 @@ use std::sync::Arc;
 use ui::{prelude::*, ListItem, ListItemSpacing, PopoverMenu, PopoverMenuHandle, PopoverTrigger};
 
 pub struct RepositorySelector {
-    picker: View<Picker<RepositorySelectorDelegate>>,
+    picker: Entity<Picker<RepositorySelectorDelegate>>,
     /// The task used to update the picker's matches when there is a change to
     /// the repository list.
     update_matches_task: Option<Task<()>>,
@@ -19,7 +19,7 @@ pub struct RepositorySelector {
 }
 
 impl RepositorySelector {
-    pub fn new(project: Model<Project>, cx: &mut ViewContext<Self>) -> Self {
+    pub fn new(project: Entity<Project>, window: &mut Window, cx: &mut Context<Self>) -> Self {
         let git_state = project.read(cx).git_state().cloned();
         let all_repositories = git_state
             .as_ref()
@@ -27,17 +27,18 @@ impl RepositorySelector {
         let filtered_repositories = all_repositories.clone();
         let delegate = RepositorySelectorDelegate {
             project: project.downgrade(),
-            repository_selector: cx.view().downgrade(),
+            repository_selector: cx.model().downgrade(),
             repository_entries: all_repositories,
             filtered_repositories,
             selected_index: 0,
         };
 
-        let picker =
-            cx.new_view(|cx| Picker::uniform_list(delegate, cx).max_height(Some(rems(20.).into())));
+        let picker = cx.new(|cx| {
+            Picker::uniform_list(delegate, window, cx).max_height(Some(rems(20.).into()))
+        });
 
         let _subscriptions = if let Some(git_state) = git_state {
-            vec![cx.subscribe(&git_state, Self::handle_project_git_event)]
+            vec![cx.subscribe_in(&git_state, window, Self::handle_project_git_event)]
         } else {
             Vec::new()
         };
@@ -51,15 +52,16 @@ impl RepositorySelector {
 
     fn handle_project_git_event(
         &mut self,
-        git_state: Model<GitState>,
+        git_state: &Entity<GitState>,
         _event: &project::git::Event,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         // TODO handle events individually
         let task = self.picker.update(cx, |this, cx| {
             let query = this.query(cx);
             this.delegate.repository_entries = git_state.read(cx).all_repositories();
-            this.delegate.update_matches(query, cx)
+            this.delegate.update_matches(query, window, cx)
         });
         self.update_matches_task = Some(task);
     }
@@ -67,14 +69,14 @@ impl RepositorySelector {
 
 impl EventEmitter<DismissEvent> for RepositorySelector {}
 
-impl FocusableView for RepositorySelector {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for RepositorySelector {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
 
 impl Render for RepositorySelector {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         self.picker.clone()
     }
 }
@@ -84,13 +86,13 @@ pub struct RepositorySelectorPopoverMenu<T>
 where
     T: PopoverTrigger,
 {
-    repository_selector: View<RepositorySelector>,
+    repository_selector: Entity<RepositorySelector>,
     trigger: T,
     handle: Option<PopoverMenuHandle<RepositorySelector>>,
 }
 
 impl<T: PopoverTrigger> RepositorySelectorPopoverMenu<T> {
-    pub fn new(repository_selector: View<RepositorySelector>, trigger: T) -> Self {
+    pub fn new(repository_selector: Entity<RepositorySelector>, trigger: T) -> Self {
         Self {
             repository_selector,
             trigger,
@@ -105,11 +107,11 @@ impl<T: PopoverTrigger> RepositorySelectorPopoverMenu<T> {
 }
 
 impl<T: PopoverTrigger> RenderOnce for RepositorySelectorPopoverMenu<T> {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         let repository_selector = self.repository_selector.clone();
 
         PopoverMenu::new("repository-switcher")
-            .menu(move |_cx| Some(repository_selector.clone()))
+            .menu(move |_window, _cx| Some(repository_selector.clone()))
             .trigger(self.trigger)
             .attach(gpui::Corner::BottomLeft)
             .when_some(self.handle.clone(), |menu, handle| menu.with_handle(handle))
@@ -117,8 +119,8 @@ impl<T: PopoverTrigger> RenderOnce for RepositorySelectorPopoverMenu<T> {
 }
 
 pub struct RepositorySelectorDelegate {
-    project: WeakModel<Project>,
-    repository_selector: WeakView<RepositorySelector>,
+    project: WeakEntity<Project>,
+    repository_selector: WeakEntity<RepositorySelector>,
     repository_entries: Vec<RepositoryHandle>,
     filtered_repositories: Vec<RepositoryHandle>,
     selected_index: usize,
@@ -143,19 +145,29 @@ impl PickerDelegate for RepositorySelectorDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        _window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) {
         self.selected_index = ix.min(self.filtered_repositories.len().saturating_sub(1));
         cx.notify();
     }
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Select a repository...".into()
     }
 
-    fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
+    fn update_matches(
+        &mut self,
+        query: String,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Task<()> {
         let all_repositories = self.repository_entries.clone();
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let filtered_repositories = cx
                 .background_executor()
                 .spawn(async move {
@@ -173,30 +185,34 @@ impl PickerDelegate for RepositorySelectorDelegate {
                 })
                 .await;
 
-            this.update(&mut cx, |this, cx| {
+            this.update_in(&mut cx, |this, window, cx| {
                 this.delegate.filtered_repositories = filtered_repositories;
-                this.delegate.set_selected_index(0, cx);
+                this.delegate.set_selected_index(0, window, cx);
                 cx.notify();
             })
             .ok();
         })
     }
 
-    fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
         let Some(selected_repo) = self.filtered_repositories.get(self.selected_index) else {
             return;
         };
         selected_repo.activate(cx);
-        self.dismissed(cx);
+        self.dismissed(window, cx);
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+    fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.repository_selector
             .update(cx, |_this, cx| cx.emit(DismissEvent))
             .ok();
     }
 
-    fn render_header(&self, _cx: &mut ViewContext<Picker<Self>>) -> Option<AnyElement> {
+    fn render_header(
+        &self,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
+    ) -> Option<AnyElement> {
         // TODO: Implement header rendering if needed
         None
     }
@@ -205,7 +221,8 @@ impl PickerDelegate for RepositorySelectorDelegate {
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        _window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let project = self.project.upgrade()?;
         let repo_info = self.filtered_repositories.get(ix)?;
@@ -220,7 +237,11 @@ impl PickerDelegate for RepositorySelectorDelegate {
         )
     }
 
-    fn render_footer(&self, cx: &mut ViewContext<Picker<Self>>) -> Option<gpui::AnyElement> {
+    fn render_footer(
+        &self,
+        _window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Option<gpui::AnyElement> {
         // TODO: Implement footer rendering if needed
         Some(
             div()

crates/go_to_line/src/cursor_position.rs 🔗

@@ -1,13 +1,13 @@
 use editor::{Editor, MultiBufferSnapshot};
-use gpui::{AppContext, FocusHandle, FocusableView, Subscription, Task, View, WeakView};
+use gpui::{App, Entity, FocusHandle, Focusable, Subscription, Task, WeakEntity};
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsSources};
 use std::{fmt::Write, num::NonZeroU32, time::Duration};
 use text::{Point, Selection};
 use ui::{
-    div, Button, ButtonCommon, Clickable, FluentBuilder, IntoElement, LabelSize, ParentElement,
-    Render, Tooltip, ViewContext,
+    div, Button, ButtonCommon, Clickable, Context, FluentBuilder, IntoElement, LabelSize,
+    ParentElement, Render, Tooltip, Window,
 };
 use util::paths::FILE_ROW_COLUMN_DELIMITER;
 use workspace::{item::ItemHandle, StatusItemView, Workspace};
@@ -23,7 +23,7 @@ pub struct CursorPosition {
     position: Option<UserCaretPosition>,
     selected_count: SelectionStats,
     context: Option<FocusHandle>,
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     update_position: Task<()>,
     _observe_active_editor: Option<Subscription>,
 }
@@ -66,12 +66,13 @@ impl CursorPosition {
 
     fn update_position(
         &mut self,
-        editor: View<Editor>,
+        editor: Entity<Editor>,
         debounce: Option<Duration>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let editor = editor.downgrade();
-        self.update_position = cx.spawn(|cursor_position, mut cx| async move {
+        self.update_position = cx.spawn_in(window, |cursor_position, mut cx| async move {
             let is_singleton = editor
                 .update(&mut cx, |editor, cx| {
                     editor.buffer().read(cx).is_singleton()
@@ -137,7 +138,7 @@ impl CursorPosition {
         });
     }
 
-    fn write_position(&self, text: &mut String, cx: &AppContext) {
+    fn write_position(&self, text: &mut String, cx: &App) {
         if self.selected_count
             <= (SelectionStats {
                 selections: 1,
@@ -191,7 +192,7 @@ impl CursorPosition {
 }
 
 impl Render for CursorPosition {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         div().when_some(self.position, |el, position| {
             let mut text = format!(
                 "{}{FILE_ROW_COLUMN_DELIMITER}{}",
@@ -204,7 +205,7 @@ impl Render for CursorPosition {
             el.child(
                 Button::new("go-to-line-column", text)
                     .label_size(LabelSize::Small)
-                    .on_click(cx.listener(|this, _, cx| {
+                    .on_click(cx.listener(|this, _, window, cx| {
                         if let Some(workspace) = this.workspace.upgrade() {
                             workspace.update(cx, |workspace, cx| {
                                 if let Some(editor) = workspace
@@ -213,24 +214,26 @@ impl Render for CursorPosition {
                                 {
                                     if let Some((_, buffer, _)) = editor.read(cx).active_excerpt(cx)
                                     {
-                                        workspace.toggle_modal(cx, |cx| {
-                                            crate::GoToLine::new(editor, buffer, cx)
+                                        workspace.toggle_modal(window, cx, |window, cx| {
+                                            crate::GoToLine::new(editor, buffer, window, cx)
                                         })
                                     }
                                 }
                             });
                         }
                     }))
-                    .tooltip(move |cx| match context.as_ref() {
+                    .tooltip(move |window, cx| match context.as_ref() {
                         Some(context) => Tooltip::for_action_in(
                             "Go to Line/Column",
                             &editor::actions::ToggleGoToLine,
                             context,
+                            window,
                             cx,
                         ),
                         None => Tooltip::for_action(
                             "Go to Line/Column",
                             &editor::actions::ToggleGoToLine,
+                            window,
                             cx,
                         ),
                     }),
@@ -245,14 +248,23 @@ impl StatusItemView for CursorPosition {
     fn set_active_pane_item(
         &mut self,
         active_pane_item: Option<&dyn ItemHandle>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         if let Some(editor) = active_pane_item.and_then(|item| item.act_as::<Editor>(cx)) {
             self._observe_active_editor =
-                Some(cx.observe(&editor, |cursor_position, editor, cx| {
-                    Self::update_position(cursor_position, editor, Some(UPDATE_DEBOUNCE), cx)
-                }));
-            self.update_position(editor, None, cx);
+                Some(
+                    cx.observe_in(&editor, window, |cursor_position, editor, window, cx| {
+                        Self::update_position(
+                            cursor_position,
+                            editor,
+                            Some(UPDATE_DEBOUNCE),
+                            window,
+                            cx,
+                        )
+                    }),
+                );
+            self.update_position(editor, None, window, cx);
         } else {
             self.position = None;
             self._observe_active_editor = None;
@@ -283,10 +295,7 @@ impl Settings for LineIndicatorFormat {
 
     type FileContent = Option<LineIndicatorFormatContent>;
 
-    fn load(
-        sources: SettingsSources<Self::FileContent>,
-        _: &mut AppContext,
-    ) -> anyhow::Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> anyhow::Result<Self> {
         let format = [sources.release_channel, sources.user]
             .into_iter()
             .find_map(|value| value.copied().flatten())

crates/go_to_line/src/go_to_line.rs 🔗

@@ -5,9 +5,8 @@ use editor::{
     actions::Tab, scroll::Autoscroll, Anchor, Editor, MultiBufferSnapshot, ToOffset, ToPoint,
 };
 use gpui::{
-    div, prelude::*, AnyWindowHandle, AppContext, DismissEvent, EventEmitter, FocusHandle,
-    FocusableView, Model, Render, SharedString, Styled, Subscription, View, ViewContext,
-    VisualContext,
+    div, prelude::*, AnyWindowHandle, App, DismissEvent, Entity, EventEmitter, FocusHandle,
+    Focusable, Render, SharedString, Styled, Subscription,
 };
 use language::Buffer;
 use settings::Settings;
@@ -17,14 +16,14 @@ use ui::prelude::*;
 use util::paths::FILE_ROW_COLUMN_DELIMITER;
 use workspace::ModalView;
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     LineIndicatorFormat::register(cx);
-    cx.observe_new_views(GoToLine::register).detach();
+    cx.observe_new(GoToLine::register).detach();
 }
 
 pub struct GoToLine {
-    line_editor: View<Editor>,
-    active_editor: View<Editor>,
+    line_editor: Entity<Editor>,
+    active_editor: Entity<Editor>,
     current_text: SharedString,
     prev_scroll_position: Option<gpui::Point<f32>>,
     _subscriptions: Vec<Subscription>,
@@ -32,8 +31,8 @@ pub struct GoToLine {
 
 impl ModalView for GoToLine {}
 
-impl FocusableView for GoToLine {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for GoToLine {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.line_editor.focus_handle(cx)
     }
 }
@@ -42,10 +41,10 @@ impl EventEmitter<DismissEvent> for GoToLine {}
 enum GoToLineRowHighlights {}
 
 impl GoToLine {
-    fn register(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
-        let handle = cx.view().downgrade();
+    fn register(editor: &mut Editor, _window: Option<&mut Window>, cx: &mut Context<Editor>) {
+        let handle = cx.model().downgrade();
         editor
-            .register_action(move |_: &editor::actions::ToggleGoToLine, cx| {
+            .register_action(move |_: &editor::actions::ToggleGoToLine, window, cx| {
                 let Some(editor_handle) = handle.upgrade() else {
                     return;
                 };
@@ -57,16 +56,19 @@ impl GoToLine {
                     return;
                 };
                 workspace.update(cx, |workspace, cx| {
-                    workspace.toggle_modal(cx, move |cx| GoToLine::new(editor_handle, buffer, cx));
+                    workspace.toggle_modal(window, cx, move |window, cx| {
+                        GoToLine::new(editor_handle, buffer, window, cx)
+                    });
                 })
             })
             .detach();
     }
 
     pub fn new(
-        active_editor: View<Editor>,
-        active_buffer: Model<Buffer>,
-        cx: &mut ViewContext<Self>,
+        active_editor: Entity<Editor>,
+        active_buffer: Entity<Buffer>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let (user_caret, last_line, scroll_position) = active_editor.update(cx, |editor, cx| {
             let user_caret = UserCaretPosition::at_selection_end(
@@ -90,20 +92,20 @@ impl GoToLine {
         let line = user_caret.line.get();
         let column = user_caret.character.get();
 
-        let line_editor = cx.new_view(|cx| {
-            let mut editor = Editor::single_line(cx);
-            let editor_handle = cx.view().downgrade();
+        let line_editor = cx.new(|cx| {
+            let mut editor = Editor::single_line(window, cx);
+            let editor_handle = cx.model().downgrade();
             editor
                 .register_action::<Tab>({
-                    move |_, cx| {
+                    move |_, window, cx| {
                         let Some(editor) = editor_handle.upgrade() else {
                             return;
                         };
                         editor.update(cx, |editor, cx| {
-                            if let Some(placeholder_text) = editor.placeholder_text(cx) {
+                            if let Some(placeholder_text) = editor.placeholder_text() {
                                 if editor.text(cx).is_empty() {
                                     let placeholder_text = placeholder_text.to_string();
-                                    editor.set_text(placeholder_text, cx);
+                                    editor.set_text(placeholder_text, window, cx);
                                 }
                             }
                         });
@@ -113,7 +115,7 @@ impl GoToLine {
             editor.set_placeholder_text(format!("{line}{FILE_ROW_COLUMN_DELIMITER}{column}"), cx);
             editor
         });
-        let line_editor_change = cx.subscribe(&line_editor, Self::on_line_editor_event);
+        let line_editor_change = cx.subscribe_in(&line_editor, window, Self::on_line_editor_event);
 
         let current_text = format!(
             "Current Line: {} of {} (column {})",
@@ -127,18 +129,18 @@ impl GoToLine {
             active_editor,
             current_text: current_text.into(),
             prev_scroll_position: Some(scroll_position),
-            _subscriptions: vec![line_editor_change, cx.on_release(Self::release)],
+            _subscriptions: vec![line_editor_change, cx.on_release_in(window, Self::release)],
         }
     }
 
-    fn release(&mut self, window: AnyWindowHandle, cx: &mut AppContext) {
+    fn release(&mut self, window: AnyWindowHandle, cx: &mut App) {
         window
-            .update(cx, |_, cx| {
+            .update(cx, |_, window, cx| {
                 let scroll_position = self.prev_scroll_position.take();
                 self.active_editor.update(cx, |editor, cx| {
                     editor.clear_row_highlights::<GoToLineRowHighlights>();
                     if let Some(scroll_position) = scroll_position {
-                        editor.set_scroll_position(scroll_position, cx);
+                        editor.set_scroll_position(scroll_position, window, cx);
                     }
                     cx.notify();
                 })
@@ -148,9 +150,10 @@ impl GoToLine {
 
     fn on_line_editor_event(
         &mut self,
-        _: View<Editor>,
+        _: &Entity<Editor>,
         event: &editor::EditorEvent,
-        cx: &mut ViewContext<Self>,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match event {
             editor::EditorEvent::Blurred => cx.emit(DismissEvent),
@@ -159,7 +162,7 @@ impl GoToLine {
         }
     }
 
-    fn highlight_current_line(&mut self, cx: &mut ViewContext<Self>) {
+    fn highlight_current_line(&mut self, cx: &mut Context<Self>) {
         self.active_editor.update(cx, |editor, cx| {
             editor.clear_row_highlights::<GoToLineRowHighlights>();
             let snapshot = editor.buffer().read(cx).snapshot(cx);
@@ -189,7 +192,7 @@ impl GoToLine {
     fn anchor_from_query(
         &self,
         snapshot: &MultiBufferSnapshot,
-        cx: &ViewContext<Editor>,
+        cx: &Context<Editor>,
     ) -> Option<Anchor> {
         let (query_row, query_char) = self.line_and_char_from_query(cx)?;
         let row = query_row.saturating_sub(1);
@@ -222,7 +225,7 @@ impl GoToLine {
         Some(snapshot.anchor_before(snapshot.clip_offset(end_offset, Bias::Left)))
     }
 
-    fn line_and_char_from_query(&self, cx: &AppContext) -> Option<(u32, Option<u32>)> {
+    fn line_and_char_from_query(&self, cx: &App) -> Option<(u32, Option<u32>)> {
         let input = self.line_editor.read(cx).text(cx);
         let mut components = input
             .splitn(2, FILE_ROW_COLUMN_DELIMITER)
@@ -233,20 +236,20 @@ impl GoToLine {
         Some((row, column))
     }
 
-    fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
+    fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {
         cx.emit(DismissEvent);
     }
 
-    fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
+    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
         self.active_editor.update(cx, |editor, cx| {
             let snapshot = editor.buffer().read(cx).snapshot(cx);
             let Some(start) = self.anchor_from_query(&snapshot, cx) else {
                 return;
             };
-            editor.change_selections(Some(Autoscroll::center()), cx, |s| {
+            editor.change_selections(Some(Autoscroll::center()), window, cx, |s| {
                 s.select_anchor_ranges([start..start])
             });
-            editor.focus(cx);
+            editor.focus_handle(cx).focus(window);
             cx.notify()
         });
         self.prev_scroll_position.take();
@@ -256,7 +259,7 @@ impl GoToLine {
 }
 
 impl Render for GoToLine {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let help_text = match self.line_and_char_from_query(cx) {
             Some((line, Some(character))) => {
                 format!("Go to line {line}, character {character}").into()
@@ -328,7 +331,8 @@ mod tests {
         .await;
 
         let project = Project::test(fs, ["/dir".as_ref()], cx).await;
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let (workspace, cx) =
+            cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
         let worktree_id = workspace.update(cx, |workspace, cx| {
             workspace.project().update(cx, |project, cx| {
                 project.worktrees(cx).next().unwrap().read(cx).id()
@@ -339,8 +343,8 @@ mod tests {
             .await
             .unwrap();
         let editor = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_path((worktree_id, "a.rs"), None, true, cx)
+            .update_in(cx, |workspace, window, cx| {
+                workspace.open_path((worktree_id, "a.rs"), None, true, window, cx)
             })
             .await
             .unwrap()
@@ -422,11 +426,12 @@ mod tests {
         .await;
 
         let project = Project::test(fs, ["/dir".as_ref()], cx).await;
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
-        workspace.update(cx, |workspace, cx| {
-            let cursor_position = cx.new_view(|_| CursorPosition::new(workspace));
+        let (workspace, cx) =
+            cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
+        workspace.update_in(cx, |workspace, window, cx| {
+            let cursor_position = cx.new(|_| CursorPosition::new(workspace));
             workspace.status_bar().update(cx, |status_bar, cx| {
-                status_bar.add_right_item(cursor_position, cx);
+                status_bar.add_right_item(cursor_position, window, cx);
             });
         });
 
@@ -440,8 +445,8 @@ mod tests {
             .await
             .unwrap();
         let editor = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_path((worktree_id, "a.rs"), None, true, cx)
+            .update_in(cx, |workspace, window, cx| {
+                workspace.open_path((worktree_id, "a.rs"), None, true, window, cx)
             })
             .await
             .unwrap()
@@ -466,7 +471,9 @@ mod tests {
                 "No selections should be initially"
             );
         });
-        editor.update(cx, |editor, cx| editor.select_all(&SelectAll, cx));
+        editor.update_in(cx, |editor, window, cx| {
+            editor.select_all(&SelectAll, window, cx)
+        });
         cx.executor().advance_clock(Duration::from_millis(200));
         workspace.update(cx, |workspace, cx| {
             assert_eq!(
@@ -502,11 +509,12 @@ mod tests {
         .await;
 
         let project = Project::test(fs, ["/dir".as_ref()], cx).await;
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
-        workspace.update(cx, |workspace, cx| {
-            let cursor_position = cx.new_view(|_| CursorPosition::new(workspace));
+        let (workspace, cx) =
+            cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
+        workspace.update_in(cx, |workspace, window, cx| {
+            let cursor_position = cx.new(|_| CursorPosition::new(workspace));
             workspace.status_bar().update(cx, |status_bar, cx| {
-                status_bar.add_right_item(cursor_position, cx);
+                status_bar.add_right_item(cursor_position, window, cx);
             });
         });
 
@@ -520,16 +528,16 @@ mod tests {
             .await
             .unwrap();
         let editor = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_path((worktree_id, "a.rs"), None, true, cx)
+            .update_in(cx, |workspace, window, cx| {
+                workspace.open_path((worktree_id, "a.rs"), None, true, window, cx)
             })
             .await
             .unwrap()
             .downcast::<Editor>()
             .unwrap();
 
-        editor.update(cx, |editor, cx| {
-            editor.move_to_beginning(&MoveToBeginning, cx)
+        editor.update_in(cx, |editor, window, cx| {
+            editor.move_to_beginning(&MoveToBeginning, window, cx)
         });
         cx.executor().advance_clock(Duration::from_millis(200));
         assert_eq!(
@@ -540,7 +548,9 @@ mod tests {
 
         for (i, c) in text.chars().enumerate() {
             let i = i as u32 + 1;
-            editor.update(cx, |editor, cx| editor.move_right(&MoveRight, cx));
+            editor.update_in(cx, |editor, window, cx| {
+                editor.move_right(&MoveRight, window, cx)
+            });
             cx.executor().advance_clock(Duration::from_millis(200));
             assert_eq!(
                 user_caret_position(1, i + 1),
@@ -549,7 +559,9 @@ mod tests {
             );
         }
 
-        editor.update(cx, |editor, cx| editor.move_right(&MoveRight, cx));
+        editor.update_in(cx, |editor, window, cx| {
+            editor.move_right(&MoveRight, window, cx)
+        });
         cx.executor().advance_clock(Duration::from_millis(200));
         assert_eq!(
             user_caret_position(1, text.chars().count() as u32 + 1),
@@ -573,11 +585,12 @@ mod tests {
         .await;
 
         let project = Project::test(fs, ["/dir".as_ref()], cx).await;
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
-        workspace.update(cx, |workspace, cx| {
-            let cursor_position = cx.new_view(|_| CursorPosition::new(workspace));
+        let (workspace, cx) =
+            cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
+        workspace.update_in(cx, |workspace, window, cx| {
+            let cursor_position = cx.new(|_| CursorPosition::new(workspace));
             workspace.status_bar().update(cx, |status_bar, cx| {
-                status_bar.add_right_item(cursor_position, cx);
+                status_bar.add_right_item(cursor_position, window, cx);
             });
         });
 
@@ -591,16 +604,16 @@ mod tests {
             .await
             .unwrap();
         let editor = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_path((worktree_id, "a.rs"), None, true, cx)
+            .update_in(cx, |workspace, window, cx| {
+                workspace.open_path((worktree_id, "a.rs"), None, true, window, cx)
             })
             .await
             .unwrap()
             .downcast::<Editor>()
             .unwrap();
 
-        editor.update(cx, |editor, cx| {
-            editor.move_to_beginning(&MoveToBeginning, cx)
+        editor.update_in(cx, |editor, window, cx| {
+            editor.move_to_beginning(&MoveToBeginning, window, cx)
         });
         cx.executor().advance_clock(Duration::from_millis(200));
         assert_eq!(user_caret_position(1, 1), current_position(&workspace, cx));
@@ -632,7 +645,7 @@ mod tests {
     }
 
     fn current_position(
-        workspace: &View<Workspace>,
+        workspace: &Entity<Workspace>,
         cx: &mut VisualTestContext,
     ) -> UserCaretPosition {
         workspace.update(cx, |workspace, cx| {
@@ -657,7 +670,7 @@ mod tests {
     fn go_to_point(
         new_point: UserCaretPosition,
         expected_placeholder: UserCaretPosition,
-        workspace: &View<Workspace>,
+        workspace: &Entity<Workspace>,
         cx: &mut VisualTestContext,
     ) {
         let go_to_line_view = open_go_to_line_view(workspace, cx);
@@ -666,7 +679,7 @@ mod tests {
                 go_to_line_view
                     .line_editor
                     .read(cx)
-                    .placeholder_text(cx)
+                    .placeholder_text()
                     .expect("No placeholder text"),
                 format!(
                     "{}:{}",
@@ -679,19 +692,19 @@ mod tests {
     }
 
     fn open_go_to_line_view(
-        workspace: &View<Workspace>,
+        workspace: &Entity<Workspace>,
         cx: &mut VisualTestContext,
-    ) -> View<GoToLine> {
+    ) -> Entity<GoToLine> {
         cx.dispatch_action(editor::actions::ToggleGoToLine);
         workspace.update(cx, |workspace, cx| {
             workspace.active_modal::<GoToLine>(cx).unwrap().clone()
         })
     }
 
-    fn highlighted_display_rows(editor: &View<Editor>, cx: &mut VisualTestContext) -> Vec<u32> {
-        editor.update(cx, |editor, cx| {
+    fn highlighted_display_rows(editor: &Entity<Editor>, cx: &mut VisualTestContext) -> Vec<u32> {
+        editor.update_in(cx, |editor, window, cx| {
             editor
-                .highlighted_display_rows(cx)
+                .highlighted_display_rows(window, cx)
                 .into_keys()
                 .map(|r| r.0)
                 .collect()
@@ -700,7 +713,7 @@ mod tests {
 
     #[track_caller]
     fn assert_single_caret_at_row(
-        editor: &View<Editor>,
+        editor: &Entity<Editor>,
         buffer_row: u32,
         cx: &mut VisualTestContext,
     ) {

crates/gpui/docs/contexts.md 🔗

@@ -2,7 +2,7 @@
 
 GPUI makes extensive use of _context parameters_, typically named `cx` and positioned at the end of the parameter list, unless they're before a final function parameter. A context reference provides access to application state and services.
 
-There are multiple kinds of contexts, and contexts implement the `Deref` trait so that a function taking `&mut AppContext` could be passed a `&mut WindowContext` or `&mut ViewContext` instead.
+There are multiple kinds of contexts, and contexts implement the `Deref` trait so that a function taking `&mut AppContext` could be passed a `&mut Window, &mut AppContext` or `&mut ViewContext` instead.
 
 ```
      AppContext

crates/gpui/docs/key_dispatch.md 🔗

@@ -42,9 +42,9 @@ To bind actions, chain `on_action` on to your element:
 
 ```rust
 impl Render for Menu {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Component {
+    fn render(&mut self, window: &mut Window, cx: &mut ModelContext<Self>) -> impl Component {
         div()
-            .on_action(|this: &mut Menu, move: &MoveUp, cx: &mut ViewContext<Menu>| {
+            .on_action(|this: &mut Menu, move: &MoveUp, window: &mut Window, cx: &mut ModelContext<Menu>| {
                 // ...
             })
             .on_action(|this, move: &MoveDown, cx| {
@@ -59,10 +59,10 @@ In order to bind keys to actions, you need to declare a _key context_ for part o
 
 ```rust
 impl Render for Menu {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Component {
+    fn render(&mut self, window: &mut Window, cx: &mut ModelContext<Self>) -> impl Component {
         div()
             .key_context("menu")
-            .on_action(|this: &mut Menu, move: &MoveUp, cx: &mut ViewContext<Menu>| {
+            .on_action(|this: &mut Menu, move: &MoveUp, window: &mut Window, cx: &mut ModelContext<Menu>| {
                 // ...
             })
             .on_action(|this, move: &MoveDown, cx| {

crates/gpui/examples/animation.rs 🔗

@@ -3,8 +3,8 @@ use std::time::Duration;
 use anyhow::Result;
 use gpui::{
     black, bounce, div, ease_in_out, percentage, prelude::*, px, rgb, size, svg, Animation,
-    AnimationExt as _, App, AppContext, AssetSource, Bounds, SharedString, Transformation,
-    ViewContext, WindowBounds, WindowOptions,
+    AnimationExt as _, App, Application, AssetSource, Bounds, Context, SharedString,
+    Transformation, Window, WindowBounds, WindowOptions,
 };
 
 struct Assets {}
@@ -36,7 +36,7 @@ const ARROW_CIRCLE_SVG: &str = concat!(
 struct AnimationExample {}
 
 impl Render for AnimationExample {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         div().flex().flex_col().size_full().justify_around().child(
             div().flex().flex_row().w_full().justify_around().child(
                 div()
@@ -72,9 +72,9 @@ impl Render for AnimationExample {
 }
 
 fn main() {
-    App::new()
+    Application::new()
         .with_assets(Assets {})
-        .run(|cx: &mut AppContext| {
+        .run(|cx: &mut App| {
             let options = WindowOptions {
                 window_bounds: Some(WindowBounds::Windowed(Bounds::centered(
                     None,
@@ -83,9 +83,9 @@ fn main() {
                 ))),
                 ..Default::default()
             };
-            cx.open_window(options, |cx| {
+            cx.open_window(options, |_, cx| {
                 cx.activate(false);
-                cx.new_view(|_cx| AnimationExample {})
+                cx.new(|_| AnimationExample {})
             })
             .unwrap();
         });

crates/gpui/examples/gif_viewer.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{div, img, prelude::*, App, AppContext, Render, ViewContext, WindowOptions};
+use gpui::{div, img, prelude::*, App, Application, Context, Render, Window, WindowOptions};
 use std::path::PathBuf;
 
 struct GifViewer {
@@ -12,7 +12,7 @@ impl GifViewer {
 }
 
 impl Render for GifViewer {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         div().size_full().child(
             img(self.gif_path.clone())
                 .size_full()
@@ -24,7 +24,7 @@ impl Render for GifViewer {
 
 fn main() {
     env_logger::init();
-    App::new().run(|cx: &mut AppContext| {
+    Application::new().run(|cx: &mut App| {
         let cwd = std::env::current_dir().expect("Failed to get current working directory");
         let gif_path = cwd.join("crates/gpui/examples/image/black-cat-typing.gif");
 
@@ -40,7 +40,7 @@ fn main() {
                 focus: true,
                 ..Default::default()
             },
-            |cx| cx.new_view(|_cx| GifViewer::new(gif_path)),
+            |_, cx| cx.new(|_| GifViewer::new(gif_path)),
         )
         .unwrap();
         cx.activate(true);

crates/gpui/examples/gradient.rs 🔗

@@ -1,6 +1,6 @@
 use gpui::{
-    canvas, div, linear_color_stop, linear_gradient, point, prelude::*, px, size, App, AppContext,
-    Bounds, ColorSpace, Half, Render, ViewContext, WindowOptions,
+    canvas, div, linear_color_stop, linear_gradient, point, prelude::*, px, size, App, Application,
+    Bounds, ColorSpace, Context, Half, Render, Window, WindowOptions,
 };
 
 struct GradientViewer {
@@ -16,7 +16,7 @@ impl GradientViewer {
 }
 
 impl Render for GradientViewer {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let color_space = self.color_space;
 
         div()
@@ -46,7 +46,7 @@ impl Render for GradientViewer {
                                 .text_color(gpui::white())
                                 .child(format!("{}", color_space))
                                 .active(|this| this.opacity(0.8))
-                                .on_click(cx.listener(move |this, _, cx| {
+                                .on_click(cx.listener(move |this, _, _, cx| {
                                     this.color_space = match this.color_space {
                                         ColorSpace::Oklab => ColorSpace::Srgb,
                                         ColorSpace::Srgb => ColorSpace::Oklab,
@@ -205,8 +205,8 @@ impl Render for GradientViewer {
                     ),
             )
             .child(div().h_24().child(canvas(
-                move |_, _| {},
-                move |bounds, _, cx| {
+                move |_, _, _| {},
+                move |bounds, _, window, _| {
                     let size = size(bounds.size.width * 0.8, px(80.));
                     let square_bounds = Bounds {
                         origin: point(
@@ -225,7 +225,7 @@ impl Render for GradientViewer {
                     );
                     path.line_to(square_bounds.bottom_right());
                     path.line_to(square_bounds.bottom_left());
-                    cx.paint_path(
+                    window.paint_path(
                         path,
                         linear_gradient(
                             180.,
@@ -240,13 +240,13 @@ impl Render for GradientViewer {
 }
 
 fn main() {
-    App::new().run(|cx: &mut AppContext| {
+    Application::new().run(|cx: &mut App| {
         cx.open_window(
             WindowOptions {
                 focus: true,
                 ..Default::default()
             },
-            |cx| cx.new_view(|_| GradientViewer::new()),
+            |_, cx| cx.new(|_| GradientViewer::new()),
         )
         .unwrap();
         cx.activate(true);

crates/gpui/examples/hello_world.rs 🔗

@@ -1,5 +1,5 @@
 use gpui::{
-    div, prelude::*, px, rgb, size, App, AppContext, Bounds, SharedString, ViewContext,
+    div, prelude::*, px, rgb, size, App, Application, Bounds, Context, SharedString, Window,
     WindowBounds, WindowOptions,
 };
 
@@ -8,7 +8,7 @@ struct HelloWorld {
 }
 
 impl Render for HelloWorld {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         div()
             .flex()
             .flex_col()
@@ -38,15 +38,15 @@ impl Render for HelloWorld {
 }
 
 fn main() {
-    App::new().run(|cx: &mut AppContext| {
+    Application::new().run(|cx: &mut App| {
         let bounds = Bounds::centered(None, size(px(500.), px(500.0)), cx);
         cx.open_window(
             WindowOptions {
                 window_bounds: Some(WindowBounds::Windowed(bounds)),
                 ..Default::default()
             },
-            |cx| {
-                cx.new_view(|_cx| HelloWorld {
+            |_, cx| {
+                cx.new(|_| HelloWorld {
                     text: "World".into(),
                 })
             },

crates/gpui/examples/image/image.rs 🔗

@@ -5,9 +5,9 @@ use std::sync::Arc;
 
 use anyhow::Result;
 use gpui::{
-    actions, div, img, prelude::*, px, rgb, size, App, AppContext, AssetSource, Bounds,
-    ImageSource, KeyBinding, Menu, MenuItem, Point, SharedString, SharedUri, TitlebarOptions,
-    ViewContext, WindowBounds, WindowContext, WindowOptions,
+    actions, div, img, prelude::*, px, rgb, size, App, AppContext, Application, AssetSource,
+    Bounds, Context, ImageSource, KeyBinding, Menu, MenuItem, Point, SharedString, SharedUri,
+    TitlebarOptions, Window, WindowBounds, WindowOptions,
 };
 
 struct Assets {
@@ -53,7 +53,7 @@ impl ImageContainer {
 }
 
 impl RenderOnce for ImageContainer {
-    fn render(self, _: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _: &mut App) -> impl IntoElement {
         div().child(
             div()
                 .flex_row()
@@ -72,7 +72,7 @@ struct ImageShowcase {
 }
 
 impl Render for ImageShowcase {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         div()
             .size_full()
             .flex()
@@ -127,11 +127,11 @@ actions!(image, [Quit]);
 fn main() {
     env_logger::init();
 
-    App::new()
+    Application::new()
         .with_assets(Assets {
             base: PathBuf::from("crates/gpui/examples"),
         })
-        .run(|cx: &mut AppContext| {
+        .run(|cx: &mut App| {
             cx.activate(true);
             cx.on_action(|_: &Quit, cx| cx.quit());
             cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]);
@@ -155,8 +155,8 @@ fn main() {
                 ..Default::default()
             };
 
-            cx.open_window(window_options, |cx| {
-                cx.new_view(|_cx| ImageShowcase {
+            cx.open_window(window_options, |_, cx| {
+                cx.new(|_| ImageShowcase {
                     // Relative path to your root project path
                     local_resource: PathBuf::from_str("crates/gpui/examples/image/app-icon.png")
                         .unwrap()

crates/gpui/examples/image_loading.rs 🔗

@@ -3,9 +3,9 @@ use std::{path::Path, sync::Arc, time::Duration};
 use anyhow::anyhow;
 use gpui::{
     black, div, img, prelude::*, pulsating_between, px, red, size, Animation, AnimationExt, App,
-    AppContext, Asset, AssetLogger, AssetSource, Bounds, Hsla, ImageAssetLoader, ImageCacheError,
-    ImgResourceLoader, Length, Pixels, RenderImage, Resource, SharedString, ViewContext,
-    WindowBounds, WindowContext, WindowOptions, LOADING_DELAY,
+    Application, Asset, AssetLogger, AssetSource, Bounds, Context, Hsla, ImageAssetLoader,
+    ImageCacheError, ImgResourceLoader, Length, Pixels, RenderImage, Resource, SharedString,
+    Window, WindowBounds, WindowOptions, LOADING_DELAY,
 };
 
 struct Assets {}
@@ -46,7 +46,7 @@ impl Asset for LoadImageWithParameters {
 
     fn load(
         parameters: Self::Source,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> impl std::future::Future<Output = Self::Output> + Send + 'static {
         let timer = cx.background_executor().timer(parameters.timeout);
         let data = AssetLogger::<ImageAssetLoader>::load(
@@ -100,7 +100,7 @@ impl ImageLoadingExample {
 }
 
 impl Render for ImageLoadingExample {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         div().flex().flex_col().size_full().justify_around().child(
             div().flex().flex_row().w_full().justify_around().child(
                 div()
@@ -116,8 +116,8 @@ impl Render for ImageLoadingExample {
                         };
 
                         // Load within the 'loading delay', should not show loading fallback
-                        img(move |cx: &mut WindowContext| {
-                            cx.use_asset::<LoadImageWithParameters>(&image_source)
+                        img(move |window: &mut Window, cx: &mut App| {
+                            window.use_asset::<LoadImageWithParameters>(&image_source, cx)
                         })
                         .id("image-1")
                         .border_1()
@@ -125,7 +125,7 @@ impl Render for ImageLoadingExample {
                         .with_fallback(|| Self::fallback_element().into_any_element())
                         .border_color(red())
                         .with_loading(|| Self::loading_element().into_any_element())
-                        .on_click(move |_, cx| {
+                        .on_click(move |_, _, cx| {
                             cx.remove_asset::<LoadImageWithParameters>(&image_source);
                         })
                     })
@@ -136,8 +136,8 @@ impl Render for ImageLoadingExample {
                             fail: false,
                         };
 
-                        img(move |cx: &mut WindowContext| {
-                            cx.use_asset::<LoadImageWithParameters>(&image_source)
+                        img(move |window: &mut Window, cx: &mut App| {
+                            window.use_asset::<LoadImageWithParameters>(&image_source, cx)
                         })
                         .id("image-2")
                         .with_fallback(|| Self::fallback_element().into_any_element())
@@ -145,7 +145,7 @@ impl Render for ImageLoadingExample {
                         .size_12()
                         .border_1()
                         .border_color(red())
-                        .on_click(move |_, cx| {
+                        .on_click(move |_, _, cx| {
                             cx.remove_asset::<LoadImageWithParameters>(&image_source);
                         })
                     })
@@ -157,8 +157,8 @@ impl Render for ImageLoadingExample {
                         };
 
                         // Fail to load after a long delay
-                        img(move |cx: &mut WindowContext| {
-                            cx.use_asset::<LoadImageWithParameters>(&image_source)
+                        img(move |window: &mut Window, cx: &mut App| {
+                            window.use_asset::<LoadImageWithParameters>(&image_source, cx)
                         })
                         .id("image-3")
                         .with_fallback(|| Self::fallback_element().into_any_element())
@@ -166,7 +166,7 @@ impl Render for ImageLoadingExample {
                         .size_12()
                         .border_1()
                         .border_color(red())
-                        .on_click(move |_, cx| {
+                        .on_click(move |_, _, cx| {
                             cx.remove_asset::<LoadImageWithParameters>(&image_source);
                         })
                     })
@@ -183,7 +183,7 @@ impl Render for ImageLoadingExample {
                             .with_fallback(|| Self::fallback_element().into_any_element())
                             .border_color(red())
                             .with_loading(|| Self::loading_element().into_any_element())
-                            .on_click(move |_, cx| {
+                            .on_click(move |_, _, cx| {
                                 cx.remove_asset::<ImgResourceLoader>(&image_source.clone().into());
                             })
                     }),
@@ -194,9 +194,9 @@ impl Render for ImageLoadingExample {
 
 fn main() {
     env_logger::init();
-    App::new()
+    Application::new()
         .with_assets(Assets {})
-        .run(|cx: &mut AppContext| {
+        .run(|cx: &mut App| {
             let options = WindowOptions {
                 window_bounds: Some(WindowBounds::Windowed(Bounds::centered(
                     None,
@@ -205,9 +205,9 @@ fn main() {
                 ))),
                 ..Default::default()
             };
-            cx.open_window(options, |cx| {
+            cx.open_window(options, |_, cx| {
                 cx.activate(false);
-                cx.new_view(|_cx| ImageLoadingExample {})
+                cx.new(|_| ImageLoadingExample {})
             })
             .unwrap();
         });

crates/gpui/examples/input.rs 🔗

@@ -2,11 +2,11 @@ use std::ops::Range;
 
 use gpui::{
     actions, black, div, fill, hsla, opaque_grey, point, prelude::*, px, relative, rgb, rgba, size,
-    white, yellow, App, AppContext, Bounds, ClipboardItem, CursorStyle, ElementId,
-    ElementInputHandler, FocusHandle, FocusableView, GlobalElementId, KeyBinding, Keystroke,
+    white, yellow, App, Application, Bounds, ClipboardItem, Context, CursorStyle, ElementId,
+    ElementInputHandler, Entity, FocusHandle, Focusable, GlobalElementId, KeyBinding, Keystroke,
     LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, Pixels, Point,
-    ShapedLine, SharedString, Style, TextRun, UTF16Selection, UnderlineStyle, View, ViewContext,
-    ViewInputHandler, WindowBounds, WindowContext, WindowOptions,
+    ShapedLine, SharedString, Style, TextRun, UTF16Selection, UnderlineStyle, ViewInputHandler,
+    Window, WindowBounds, WindowOptions,
 };
 use unicode_segmentation::*;
 
@@ -42,7 +42,7 @@ struct TextInput {
 }
 
 impl TextInput {
-    fn left(&mut self, _: &Left, cx: &mut ViewContext<Self>) {
+    fn left(&mut self, _: &Left, _: &mut Window, cx: &mut Context<Self>) {
         if self.selected_range.is_empty() {
             self.move_to(self.previous_boundary(self.cursor_offset()), cx);
         } else {
@@ -50,7 +50,7 @@ impl TextInput {
         }
     }
 
-    fn right(&mut self, _: &Right, cx: &mut ViewContext<Self>) {
+    fn right(&mut self, _: &Right, _: &mut Window, cx: &mut Context<Self>) {
         if self.selected_range.is_empty() {
             self.move_to(self.next_boundary(self.selected_range.end), cx);
         } else {
@@ -58,42 +58,47 @@ impl TextInput {
         }
     }
 
-    fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
+    fn select_left(&mut self, _: &SelectLeft, _: &mut Window, cx: &mut Context<Self>) {
         self.select_to(self.previous_boundary(self.cursor_offset()), cx);
     }
 
-    fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
+    fn select_right(&mut self, _: &SelectRight, _: &mut Window, cx: &mut Context<Self>) {
         self.select_to(self.next_boundary(self.cursor_offset()), cx);
     }
 
-    fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
+    fn select_all(&mut self, _: &SelectAll, _: &mut Window, cx: &mut Context<Self>) {
         self.move_to(0, cx);
         self.select_to(self.content.len(), cx)
     }
 
-    fn home(&mut self, _: &Home, cx: &mut ViewContext<Self>) {
+    fn home(&mut self, _: &Home, _: &mut Window, cx: &mut Context<Self>) {
         self.move_to(0, cx);
     }
 
-    fn end(&mut self, _: &End, cx: &mut ViewContext<Self>) {
+    fn end(&mut self, _: &End, _: &mut Window, cx: &mut Context<Self>) {
         self.move_to(self.content.len(), cx);
     }
 
-    fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
+    fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
         if self.selected_range.is_empty() {
             self.select_to(self.previous_boundary(self.cursor_offset()), cx)
         }
-        self.replace_text_in_range(None, "", cx)
+        self.replace_text_in_range(None, "", window, cx)
     }
 
-    fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
+    fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
         if self.selected_range.is_empty() {
             self.select_to(self.next_boundary(self.cursor_offset()), cx)
         }
-        self.replace_text_in_range(None, "", cx)
+        self.replace_text_in_range(None, "", window, cx)
     }
 
-    fn on_mouse_down(&mut self, event: &MouseDownEvent, cx: &mut ViewContext<Self>) {
+    fn on_mouse_down(
+        &mut self,
+        event: &MouseDownEvent,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.is_selecting = true;
 
         if event.modifiers.shift {
@@ -103,43 +108,48 @@ impl TextInput {
         }
     }
 
-    fn on_mouse_up(&mut self, _: &MouseUpEvent, _: &mut ViewContext<Self>) {
+    fn on_mouse_up(&mut self, _: &MouseUpEvent, _window: &mut Window, _: &mut Context<Self>) {
         self.is_selecting = false;
     }
 
-    fn on_mouse_move(&mut self, event: &MouseMoveEvent, cx: &mut ViewContext<Self>) {
+    fn on_mouse_move(&mut self, event: &MouseMoveEvent, _: &mut Window, cx: &mut Context<Self>) {
         if self.is_selecting {
             self.select_to(self.index_for_mouse_position(event.position), cx);
         }
     }
 
-    fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
-        cx.show_character_palette();
+    fn show_character_palette(
+        &mut self,
+        _: &ShowCharacterPalette,
+        window: &mut Window,
+        _: &mut Context<Self>,
+    ) {
+        window.show_character_palette();
     }
 
-    fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
+    fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(text) = cx.read_from_clipboard().and_then(|item| item.text()) {
-            self.replace_text_in_range(None, &text.replace("\n", " "), cx);
+            self.replace_text_in_range(None, &text.replace("\n", " "), window, cx);
         }
     }
 
-    fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
+    fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
         if !self.selected_range.is_empty() {
             cx.write_to_clipboard(ClipboardItem::new_string(
                 (&self.content[self.selected_range.clone()]).to_string(),
             ));
         }
     }
-    fn cut(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
+    fn cut(&mut self, _: &Copy, window: &mut Window, cx: &mut Context<Self>) {
         if !self.selected_range.is_empty() {
             cx.write_to_clipboard(ClipboardItem::new_string(
                 (&self.content[self.selected_range.clone()]).to_string(),
             ));
-            self.replace_text_in_range(None, "", cx)
+            self.replace_text_in_range(None, "", window, cx)
         }
     }
 
-    fn move_to(&mut self, offset: usize, cx: &mut ViewContext<Self>) {
+    fn move_to(&mut self, offset: usize, cx: &mut Context<Self>) {
         self.selected_range = offset..offset;
         cx.notify()
     }
@@ -170,7 +180,7 @@ impl TextInput {
         line.closest_index_for_x(position.x - bounds.left())
     }
 
-    fn select_to(&mut self, offset: usize, cx: &mut ViewContext<Self>) {
+    fn select_to(&mut self, offset: usize, cx: &mut Context<Self>) {
         if self.selection_reversed {
             self.selected_range.start = offset
         } else {
@@ -252,7 +262,8 @@ impl ViewInputHandler for TextInput {
         &mut self,
         range_utf16: Range<usize>,
         actual_range: &mut Option<Range<usize>>,
-        _cx: &mut ViewContext<Self>,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
     ) -> Option<String> {
         let range = self.range_from_utf16(&range_utf16);
         actual_range.replace(self.range_to_utf16(&range));
@@ -262,7 +273,8 @@ impl ViewInputHandler for TextInput {
     fn selected_text_range(
         &mut self,
         _ignore_disabled_input: bool,
-        _cx: &mut ViewContext<Self>,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
     ) -> Option<UTF16Selection> {
         Some(UTF16Selection {
             range: self.range_to_utf16(&self.selected_range),
@@ -270,13 +282,17 @@ impl ViewInputHandler for TextInput {
         })
     }
 
-    fn marked_text_range(&self, _cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
+    fn marked_text_range(
+        &self,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
+    ) -> Option<Range<usize>> {
         self.marked_range
             .as_ref()
             .map(|range| self.range_to_utf16(range))
     }
 
-    fn unmark_text(&mut self, _cx: &mut ViewContext<Self>) {
+    fn unmark_text(&mut self, _window: &mut Window, _cx: &mut Context<Self>) {
         self.marked_range = None;
     }
 
@@ -284,7 +300,8 @@ impl ViewInputHandler for TextInput {
         &mut self,
         range_utf16: Option<Range<usize>>,
         new_text: &str,
-        cx: &mut ViewContext<Self>,
+        _: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let range = range_utf16
             .as_ref()
@@ -305,7 +322,8 @@ impl ViewInputHandler for TextInput {
         range_utf16: Option<Range<usize>>,
         new_text: &str,
         new_selected_range_utf16: Option<Range<usize>>,
-        cx: &mut ViewContext<Self>,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let range = range_utf16
             .as_ref()
@@ -330,7 +348,8 @@ impl ViewInputHandler for TextInput {
         &mut self,
         range_utf16: Range<usize>,
         bounds: Bounds<Pixels>,
-        _cx: &mut ViewContext<Self>,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
     ) -> Option<Bounds<Pixels>> {
         let last_layout = self.last_layout.as_ref()?;
         let range = self.range_from_utf16(&range_utf16);
@@ -348,7 +367,7 @@ impl ViewInputHandler for TextInput {
 }
 
 struct TextElement {
-    input: View<TextInput>,
+    input: Entity<TextInput>,
 }
 
 struct PrepaintState {
@@ -377,12 +396,13 @@ impl Element for TextElement {
     fn request_layout(
         &mut self,
         _id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
         let mut style = Style::default();
         style.size.width = relative(1.).into();
-        style.size.height = cx.line_height().into();
-        (cx.request_layout(style, []), ())
+        style.size.height = window.line_height().into();
+        (window.request_layout(style, [], cx), ())
     }
 
     fn prepaint(
@@ -390,13 +410,14 @@ impl Element for TextElement {
         _id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Self::PrepaintState {
         let input = self.input.read(cx);
         let content = input.content.clone();
         let selected_range = input.selected_range.clone();
         let cursor = input.cursor_offset();
-        let style = cx.text_style();
+        let style = window.text_style();
 
         let (display_text, text_color) = if content.is_empty() {
             (input.placeholder.clone(), hsla(0., 0., 0., 0.2))
@@ -439,8 +460,8 @@ impl Element for TextElement {
             vec![run]
         };
 
-        let font_size = style.font_size.to_pixels(cx.rem_size());
-        let line = cx
+        let font_size = style.font_size.to_pixels(window.rem_size());
+        let line = window
             .text_system()
             .shape_line(display_text, font_size, &runs)
             .unwrap();
@@ -488,22 +509,25 @@ impl Element for TextElement {
         bounds: Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
         prepaint: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let focus_handle = self.input.read(cx).focus_handle.clone();
-        cx.handle_input(
+        window.handle_input(
             &focus_handle,
             ElementInputHandler::new(bounds, self.input.clone()),
+            cx,
         );
         if let Some(selection) = prepaint.selection.take() {
-            cx.paint_quad(selection)
+            window.paint_quad(selection)
         }
         let line = prepaint.line.take().unwrap();
-        line.paint(bounds.origin, cx.line_height(), cx).unwrap();
+        line.paint(bounds.origin, window.line_height(), window, cx)
+            .unwrap();
 
-        if focus_handle.is_focused(cx) {
+        if focus_handle.is_focused(window) {
             if let Some(cursor) = prepaint.cursor.take() {
-                cx.paint_quad(cursor);
+                window.paint_quad(cursor);
             }
         }
 
@@ -515,7 +539,7 @@ impl Element for TextElement {
 }
 
 impl Render for TextInput {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         div()
             .flex()
             .key_context("TextInput")
@@ -548,32 +572,32 @@ impl Render for TextInput {
                     .p(px(4.))
                     .bg(white())
                     .child(TextElement {
-                        input: cx.view().clone(),
+                        input: cx.model().clone(),
                     }),
             )
     }
 }
 
-impl FocusableView for TextInput {
-    fn focus_handle(&self, _: &AppContext) -> FocusHandle {
+impl Focusable for TextInput {
+    fn focus_handle(&self, _: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
 
 struct InputExample {
-    text_input: View<TextInput>,
+    text_input: Entity<TextInput>,
     recent_keystrokes: Vec<Keystroke>,
     focus_handle: FocusHandle,
 }
 
-impl FocusableView for InputExample {
-    fn focus_handle(&self, _: &AppContext) -> FocusHandle {
+impl Focusable for InputExample {
+    fn focus_handle(&self, _: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
 
 impl InputExample {
-    fn on_reset_click(&mut self, _: &MouseUpEvent, cx: &mut ViewContext<Self>) {
+    fn on_reset_click(&mut self, _: &MouseUpEvent, _window: &mut Window, cx: &mut Context<Self>) {
         self.recent_keystrokes.clear();
         self.text_input
             .update(cx, |text_input, _cx| text_input.reset());
@@ -582,7 +606,7 @@ impl InputExample {
 }
 
 impl Render for InputExample {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         div()
             .bg(rgb(0xaaaaaa))
             .track_focus(&self.focus_handle(cx))
@@ -629,7 +653,7 @@ impl Render for InputExample {
 }
 
 fn main() {
-    App::new().run(|cx: &mut AppContext| {
+    Application::new().run(|cx: &mut App| {
         let bounds = Bounds::centered(None, size(px(300.0), px(300.0)), cx);
         cx.bind_keys([
             KeyBinding::new("backspace", Backspace, None),
@@ -653,8 +677,8 @@ fn main() {
                     window_bounds: Some(WindowBounds::Windowed(bounds)),
                     ..Default::default()
                 },
-                |cx| {
-                    let text_input = cx.new_view(|cx| TextInput {
+                |_, cx| {
+                    let text_input = cx.new(|cx| TextInput {
                         focus_handle: cx.focus_handle(),
                         content: "".into(),
                         placeholder: "Type here...".into(),
@@ -665,7 +689,7 @@ fn main() {
                         last_bounds: None,
                         is_selecting: false,
                     });
-                    cx.new_view(|cx| InputExample {
+                    cx.new(|cx| InputExample {
                         text_input,
                         recent_keystrokes: vec![],
                         focus_handle: cx.focus_handle(),
@@ -673,25 +697,24 @@ fn main() {
                 },
             )
             .unwrap();
-        cx.observe_keystrokes(move |ev, cx| {
-            window
-                .update(cx, |view, cx| {
-                    view.recent_keystrokes.push(ev.keystroke.clone());
-                    cx.notify();
-                })
-                .unwrap();
+        let view = window.root_model(cx).unwrap();
+        cx.observe_keystrokes(move |ev, _, cx| {
+            view.update(cx, |view, cx| {
+                view.recent_keystrokes.push(ev.keystroke.clone());
+                cx.notify();
+            })
         })
         .detach();
         cx.on_keyboard_layout_change({
             move |cx| {
-                window.update(cx, |_, cx| cx.notify()).ok();
+                window.update(cx, |_, _, cx| cx.notify()).ok();
             }
         })
         .detach();
 
         window
-            .update(cx, |view, cx| {
-                cx.focus_view(&view.text_input);
+            .update(cx, |view, window, cx| {
+                window.focus(&view.text_input.focus_handle(cx));
                 cx.activate(true);
             })
             .unwrap();

crates/gpui/examples/opacity.rs 🔗

@@ -2,8 +2,8 @@ use std::{fs, path::PathBuf, time::Duration};
 
 use anyhow::Result;
 use gpui::{
-    div, hsla, img, point, prelude::*, px, rgb, size, svg, App, AppContext, AssetSource, Bounds,
-    BoxShadow, ClickEvent, SharedString, Task, Timer, ViewContext, WindowBounds, WindowOptions,
+    div, hsla, img, point, prelude::*, px, rgb, size, svg, App, Application, AssetSource, Bounds,
+    BoxShadow, ClickEvent, Context, SharedString, Task, Timer, Window, WindowBounds, WindowOptions,
 };
 
 struct Assets {
@@ -39,22 +39,22 @@ struct HelloWorld {
 }
 
 impl HelloWorld {
-    fn new(_: &mut ViewContext<Self>) -> Self {
+    fn new(_window: &mut Window, _: &mut Context<Self>) -> Self {
         Self {
             _task: None,
             opacity: 0.5,
         }
     }
 
-    fn change_opacity(&mut self, _: &ClickEvent, cx: &mut ViewContext<Self>) {
+    fn change_opacity(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context<Self>) {
         self.opacity = 0.0;
         cx.notify();
 
-        self._task = Some(cx.spawn(|view, mut cx| async move {
+        self._task = Some(cx.spawn_in(window, |view, mut cx| async move {
             loop {
                 Timer::after(Duration::from_secs_f32(0.05)).await;
                 let mut stop = false;
-                let _ = cx.update(|cx| {
+                let _ = cx.update(|_, cx| {
                     view.update(cx, |view, cx| {
                         if view.opacity >= 1.0 {
                             stop = true;
@@ -75,7 +75,7 @@ impl HelloWorld {
 }
 
 impl Render for HelloWorld {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         div()
             .flex()
             .flex_row()
@@ -157,18 +157,18 @@ impl Render for HelloWorld {
 }
 
 fn main() {
-    App::new()
+    Application::new()
         .with_assets(Assets {
             base: PathBuf::from("crates/gpui/examples"),
         })
-        .run(|cx: &mut AppContext| {
+        .run(|cx: &mut App| {
             let bounds = Bounds::centered(None, size(px(500.0), px(500.0)), cx);
             cx.open_window(
                 WindowOptions {
                     window_bounds: Some(WindowBounds::Windowed(bounds)),
                     ..Default::default()
                 },
-                |cx| cx.new_view(HelloWorld::new),
+                |window, cx| cx.new(|cx| HelloWorld::new(window, cx)),
             )
             .unwrap();
         });

crates/gpui/examples/ownership_post.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{prelude::*, App, AppContext, EventEmitter, Model, ModelContext};
+use gpui::{prelude::*, App, Application, Context, Entity, EventEmitter};
 
 struct Counter {
     count: usize,
@@ -11,9 +11,9 @@ struct Change {
 impl EventEmitter<Change> for Counter {}
 
 fn main() {
-    App::new().run(|cx: &mut AppContext| {
-        let counter: Model<Counter> = cx.new_model(|_cx| Counter { count: 0 });
-        let subscriber = cx.new_model(|cx: &mut ModelContext<Counter>| {
+    Application::new().run(|cx: &mut App| {
+        let counter: Entity<Counter> = cx.new(|_cx| Counter { count: 0 });
+        let subscriber = cx.new(|cx: &mut Context<Counter>| {
             cx.subscribe(&counter, |subscriber, _emitter, event, _cx| {
                 subscriber.count += event.increment * 2;
             })

crates/gpui/examples/painting.rs 🔗

@@ -1,6 +1,6 @@
 use gpui::{
-    canvas, div, point, prelude::*, px, size, App, AppContext, Bounds, MouseDownEvent, Path,
-    Pixels, Point, Render, ViewContext, WindowOptions,
+    canvas, div, point, prelude::*, px, size, App, Application, Bounds, Context, MouseDownEvent,
+    Path, Pixels, Point, Render, Window, WindowOptions,
 };
 struct PaintingViewer {
     default_lines: Vec<Path<Pixels>>,
@@ -70,13 +70,13 @@ impl PaintingViewer {
         }
     }
 
-    fn clear(&mut self, cx: &mut ViewContext<Self>) {
+    fn clear(&mut self, cx: &mut Context<Self>) {
         self.lines.clear();
         cx.notify();
     }
 }
 impl Render for PaintingViewer {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let default_lines = self.default_lines.clone();
         let lines = self.lines.clone();
         div()
@@ -103,7 +103,7 @@ impl Render for PaintingViewer {
                             .flex()
                             .px_3()
                             .py_1()
-                            .on_click(cx.listener(|this, _, cx| {
+                            .on_click(cx.listener(|this, _, _, cx| {
                                 this.clear(cx);
                             })),
                     ),
@@ -113,11 +113,11 @@ impl Render for PaintingViewer {
                     .size_full()
                     .child(
                         canvas(
-                            move |_, _| {},
-                            move |_, _, cx| {
+                            move |_, _, _| {},
+                            move |_, _, window, _| {
                                 const STROKE_WIDTH: Pixels = px(2.0);
                                 for path in default_lines {
-                                    cx.paint_path(path, gpui::black());
+                                    window.paint_path(path, gpui::black());
                                 }
                                 for points in lines {
                                     let mut path = Path::new(points[0]);
@@ -135,7 +135,7 @@ impl Render for PaintingViewer {
                                         last = p;
                                     }
 
-                                    cx.paint_path(path, gpui::black());
+                                    window.paint_path(path, gpui::black());
                                 }
                             },
                         )
@@ -143,14 +143,14 @@ impl Render for PaintingViewer {
                     )
                     .on_mouse_down(
                         gpui::MouseButton::Left,
-                        cx.listener(|this, ev: &MouseDownEvent, _| {
+                        cx.listener(|this, ev: &MouseDownEvent, _, _| {
                             this._painting = true;
                             this.start = ev.position;
                             let path = vec![ev.position];
                             this.lines.push(path);
                         }),
                     )
-                    .on_mouse_move(cx.listener(|this, ev: &gpui::MouseMoveEvent, cx| {
+                    .on_mouse_move(cx.listener(|this, ev: &gpui::MouseMoveEvent, _, cx| {
                         if !this._painting {
                             return;
                         }
@@ -176,7 +176,7 @@ impl Render for PaintingViewer {
                     }))
                     .on_mouse_up(
                         gpui::MouseButton::Left,
-                        cx.listener(|this, _, _| {
+                        cx.listener(|this, _, _, _| {
                             this._painting = false;
                         }),
                     ),
@@ -185,13 +185,13 @@ impl Render for PaintingViewer {
 }
 
 fn main() {
-    App::new().run(|cx: &mut AppContext| {
+    Application::new().run(|cx: &mut App| {
         cx.open_window(
             WindowOptions {
                 focus: true,
                 ..Default::default()
             },
-            |cx| cx.new_view(|_| PaintingViewer::new()),
+            |_, cx| cx.new(|_| PaintingViewer::new()),
         )
         .unwrap();
         cx.activate(true);

crates/gpui/examples/set_menus.rs 🔗

@@ -1,11 +1,11 @@
 use gpui::{
-    actions, div, prelude::*, rgb, App, AppContext, Menu, MenuItem, ViewContext, WindowOptions,
+    actions, div, prelude::*, rgb, App, Application, Context, Menu, MenuItem, Window, WindowOptions,
 };
 
 struct SetMenus;
 
 impl Render for SetMenus {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         div()
             .flex()
             .bg(rgb(0x2e7d32))
@@ -19,7 +19,7 @@ impl Render for SetMenus {
 }
 
 fn main() {
-    App::new().run(|cx: &mut AppContext| {
+    Application::new().run(|cx: &mut App| {
         // Bring the menu bar to the foreground (so you can see the menu bar)
         cx.activate(true);
         // Register the `quit` function so it can be referenced by the `MenuItem::action` in the menu bar
@@ -29,10 +29,8 @@ fn main() {
             name: "set_menus".into(),
             items: vec![MenuItem::action("Quit", Quit)],
         }]);
-        cx.open_window(WindowOptions::default(), |cx| {
-            cx.new_view(|_cx| SetMenus {})
-        })
-        .unwrap();
+        cx.open_window(WindowOptions::default(), |_, cx| cx.new(|_| SetMenus {}))
+            .unwrap();
     });
 }
 
@@ -40,7 +38,7 @@ fn main() {
 actions!(set_menus, [Quit]);
 
 // Define the quit function that is registered with the AppContext
-fn quit(_: &Quit, cx: &mut AppContext) {
+fn quit(_: &Quit, cx: &mut App) {
     println!("Gracefully quitting the application . . .");
     cx.quit();
 }

crates/gpui/examples/shadow.rs 🔗

@@ -1,6 +1,6 @@
 use gpui::{
-    div, hsla, point, prelude::*, px, relative, rgb, size, App, AppContext, Bounds, BoxShadow, Div,
-    SharedString, ViewContext, WindowBounds, WindowOptions,
+    div, hsla, point, prelude::*, px, relative, rgb, size, App, Application, Bounds, BoxShadow,
+    Context, Div, SharedString, Window, WindowBounds, WindowOptions,
 };
 
 use smallvec::smallvec;
@@ -86,7 +86,7 @@ fn example(label: impl Into<SharedString>, example: impl IntoElement) -> impl In
 }
 
 impl Render for Shadow {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         div()
             .id("shadow-example")
             .overflow_y_scroll()
@@ -567,14 +567,14 @@ impl Render for Shadow {
 }
 
 fn main() {
-    App::new().run(|cx: &mut AppContext| {
+    Application::new().run(|cx: &mut App| {
         let bounds = Bounds::centered(None, size(px(1000.0), px(800.0)), cx);
         cx.open_window(
             WindowOptions {
                 window_bounds: Some(WindowBounds::Windowed(bounds)),
                 ..Default::default()
             },
-            |cx| cx.new_view(|_cx| Shadow {}),
+            |_, cx| cx.new(|_| Shadow {}),
         )
         .unwrap();
 

crates/gpui/examples/svg/svg.rs 🔗

@@ -3,8 +3,8 @@ use std::path::PathBuf;
 
 use anyhow::Result;
 use gpui::{
-    div, prelude::*, px, rgb, size, svg, App, AppContext, AssetSource, Bounds, SharedString,
-    ViewContext, WindowBounds, WindowOptions,
+    div, prelude::*, px, rgb, size, svg, App, Application, AssetSource, Bounds, Context,
+    SharedString, Window, WindowBounds, WindowOptions,
 };
 
 struct Assets {
@@ -37,7 +37,7 @@ impl AssetSource for Assets {
 struct SvgExample;
 
 impl Render for SvgExample {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         div()
             .flex()
             .flex_row()
@@ -68,18 +68,18 @@ impl Render for SvgExample {
 }
 
 fn main() {
-    App::new()
+    Application::new()
         .with_assets(Assets {
             base: PathBuf::from("crates/gpui/examples"),
         })
-        .run(|cx: &mut AppContext| {
+        .run(|cx: &mut App| {
             let bounds = Bounds::centered(None, size(px(300.0), px(300.0)), cx);
             cx.open_window(
                 WindowOptions {
                     window_bounds: Some(WindowBounds::Windowed(bounds)),
                     ..Default::default()
                 },
-                |cx| cx.new_view(|_cx| SvgExample),
+                |_, cx| cx.new(|_| SvgExample),
             )
             .unwrap();
         });

crates/gpui/examples/text_wrapper.rs 🔗

@@ -1,11 +1,12 @@
 use gpui::{
-    div, prelude::*, px, size, App, AppContext, Bounds, ViewContext, WindowBounds, WindowOptions,
+    div, prelude::*, px, size, App, Application, Bounds, Context, Window, WindowBounds,
+    WindowOptions,
 };
 
 struct HelloWorld {}
 
 impl Render for HelloWorld {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         let text = "The longest word 你好世界这段是中文,こんにちはこの段落は日本語です in any of the major English language dictionaries is pneumonoultramicroscopicsilicovolcanoconiosis, a word that refers to a lung disease contracted from the inhalation of very fine silica particles, specifically from a volcano; medically, it is the same as silicosis.";
         div()
             .id("page")
@@ -78,14 +79,14 @@ impl Render for HelloWorld {
 }
 
 fn main() {
-    App::new().run(|cx: &mut AppContext| {
+    Application::new().run(|cx: &mut App| {
         let bounds = Bounds::centered(None, size(px(600.0), px(480.0)), cx);
         cx.open_window(
             WindowOptions {
                 window_bounds: Some(WindowBounds::Windowed(bounds)),
                 ..Default::default()
             },
-            |cx| cx.new_view(|_cx| HelloWorld {}),
+            |_, cx| cx.new(|_| HelloWorld {}),
         )
         .unwrap();
     });

crates/gpui/examples/uniform_list.rs 🔗

@@ -1,45 +1,50 @@
 use gpui::{
-    div, prelude::*, px, rgb, size, uniform_list, App, AppContext, Bounds, ViewContext,
+    div, prelude::*, px, rgb, size, uniform_list, App, Application, Bounds, Context, Window,
     WindowBounds, WindowOptions,
 };
 
 struct UniformListExample {}
 
 impl Render for UniformListExample {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         div().size_full().bg(rgb(0xffffff)).child(
-            uniform_list(cx.view().clone(), "entries", 50, |_this, range, _cx| {
-                let mut items = Vec::new();
-                for ix in range {
-                    let item = ix + 1;
+            uniform_list(
+                cx.model().clone(),
+                "entries",
+                50,
+                |_this, range, _window, _cx| {
+                    let mut items = Vec::new();
+                    for ix in range {
+                        let item = ix + 1;
 
-                    items.push(
-                        div()
-                            .id(ix)
-                            .px_2()
-                            .cursor_pointer()
-                            .on_click(move |_event, _cx| {
-                                println!("clicked Item {item:?}");
-                            })
-                            .child(format!("Item {item}")),
-                    );
-                }
-                items
-            })
+                        items.push(
+                            div()
+                                .id(ix)
+                                .px_2()
+                                .cursor_pointer()
+                                .on_click(move |_event, _window, _cx| {
+                                    println!("clicked Item {item:?}");
+                                })
+                                .child(format!("Item {item}")),
+                        );
+                    }
+                    items
+                },
+            )
             .h_full(),
         )
     }
 }
 
 fn main() {
-    App::new().run(|cx: &mut AppContext| {
+    Application::new().run(|cx: &mut App| {
         let bounds = Bounds::centered(None, size(px(300.0), px(300.0)), cx);
         cx.open_window(
             WindowOptions {
                 window_bounds: Some(WindowBounds::Windowed(bounds)),
                 ..Default::default()
             },
-            |cx| cx.new_view(|_cx| UniformListExample {}),
+            |_, cx| cx.new(|_| UniformListExample {}),
         )
         .unwrap();
     });

crates/gpui/examples/window.rs 🔗

@@ -1,13 +1,13 @@
 use gpui::{
-    div, prelude::*, px, rgb, size, App, AppContext, Bounds, SharedString, Timer, ViewContext,
-    WindowBounds, WindowContext, WindowKind, WindowOptions,
+    div, prelude::*, px, rgb, size, App, Application, Bounds, Context, SharedString, Timer, Window,
+    WindowBounds, WindowKind, WindowOptions,
 };
 
 struct SubWindow {
     custom_titlebar: bool,
 }
 
-fn button(text: &str, on_click: impl Fn(&mut WindowContext) + 'static) -> impl IntoElement {
+fn button(text: &str, on_click: impl Fn(&mut Window, &mut App) + 'static) -> impl IntoElement {
     div()
         .id(SharedString::from(text.to_string()))
         .flex_none()
@@ -19,11 +19,11 @@ fn button(text: &str, on_click: impl Fn(&mut WindowContext) + 'static) -> impl I
         .rounded_md()
         .cursor_pointer()
         .child(text.to_string())
-        .on_click(move |_, cx| on_click(cx))
+        .on_click(move |_, window, cx| on_click(window, cx))
 }
 
 impl Render for SubWindow {
-    fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
         div()
             .flex()
             .flex_col()
@@ -54,8 +54,8 @@ impl Render for SubWindow {
                     .p_8()
                     .gap_2()
                     .child("SubWindow")
-                    .child(button("Close", |cx| {
-                        cx.remove_window();
+                    .child(button("Close", |window, _| {
+                        window.remove_window();
                     })),
             )
     }
@@ -64,7 +64,7 @@ impl Render for SubWindow {
 struct WindowDemo {}
 
 impl Render for WindowDemo {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let window_bounds =
             WindowBounds::Windowed(Bounds::centered(None, size(px(300.0), px(300.0)), cx));
 
@@ -77,66 +77,66 @@ impl Render for WindowDemo {
             .justify_center()
             .items_center()
             .gap_2()
-            .child(button("Normal", move |cx| {
+            .child(button("Normal", move |_, cx| {
                 cx.open_window(
                     WindowOptions {
                         window_bounds: Some(window_bounds),
                         ..Default::default()
                     },
-                    |cx| {
-                        cx.new_view(|_cx| SubWindow {
+                    |_, cx| {
+                        cx.new(|_| SubWindow {
                             custom_titlebar: false,
                         })
                     },
                 )
                 .unwrap();
             }))
-            .child(button("Popup", move |cx| {
+            .child(button("Popup", move |_, cx| {
                 cx.open_window(
                     WindowOptions {
                         window_bounds: Some(window_bounds),
                         kind: WindowKind::PopUp,
                         ..Default::default()
                     },
-                    |cx| {
-                        cx.new_view(|_cx| SubWindow {
+                    |_, cx| {
+                        cx.new(|_| SubWindow {
                             custom_titlebar: false,
                         })
                     },
                 )
                 .unwrap();
             }))
-            .child(button("Custom Titlebar", move |cx| {
+            .child(button("Custom Titlebar", move |_, cx| {
                 cx.open_window(
                     WindowOptions {
                         titlebar: None,
                         window_bounds: Some(window_bounds),
                         ..Default::default()
                     },
-                    |cx| {
-                        cx.new_view(|_cx| SubWindow {
+                    |_, cx| {
+                        cx.new(|_| SubWindow {
                             custom_titlebar: true,
                         })
                     },
                 )
                 .unwrap();
             }))
-            .child(button("Invisible", move |cx| {
+            .child(button("Invisible", move |_, cx| {
                 cx.open_window(
                     WindowOptions {
                         show: false,
                         window_bounds: Some(window_bounds),
                         ..Default::default()
                     },
-                    |cx| {
-                        cx.new_view(|_cx| SubWindow {
+                    |_, cx| {
+                        cx.new(|_| SubWindow {
                             custom_titlebar: false,
                         })
                     },
                 )
                 .unwrap();
             }))
-            .child(button("Unmovable", move |cx| {
+            .child(button("Unmovable", move |_, cx| {
                 cx.open_window(
                     WindowOptions {
                         is_movable: false,
@@ -144,38 +144,39 @@ impl Render for WindowDemo {
                         window_bounds: Some(window_bounds),
                         ..Default::default()
                     },
-                    |cx| {
-                        cx.new_view(|_cx| SubWindow {
+                    |_, cx| {
+                        cx.new(|_| SubWindow {
                             custom_titlebar: false,
                         })
                     },
                 )
                 .unwrap();
             }))
-            .child(button("Hide Application", |cx| {
+            .child(button("Hide Application", |window, cx| {
                 cx.hide();
 
                 // Restore the application after 3 seconds
-                cx.spawn(|mut cx| async move {
-                    Timer::after(std::time::Duration::from_secs(3)).await;
-                    cx.update(|cx| {
-                        cx.activate(false);
+                window
+                    .spawn(cx, |mut cx| async move {
+                        Timer::after(std::time::Duration::from_secs(3)).await;
+                        cx.update(|_, cx| {
+                            cx.activate(false);
+                        })
                     })
-                })
-                .detach();
+                    .detach();
             }))
     }
 }
 
 fn main() {
-    App::new().run(|cx: &mut AppContext| {
+    Application::new().run(|cx: &mut App| {
         let bounds = Bounds::centered(None, size(px(800.0), px(600.0)), cx);
         cx.open_window(
             WindowOptions {
                 window_bounds: Some(WindowBounds::Windowed(bounds)),
                 ..Default::default()
             },
-            |cx| cx.new_view(|_cx| WindowDemo {}),
+            |_, cx| cx.new(|_| WindowDemo {}),
         )
         .unwrap();
     });

crates/gpui/examples/window_positioning.rs 🔗

@@ -1,6 +1,6 @@
 use gpui::{
-    div, point, prelude::*, px, rgb, App, AppContext, Bounds, DisplayId, Hsla, Pixels,
-    SharedString, Size, ViewContext, WindowBackgroundAppearance, WindowBounds, WindowKind,
+    div, point, prelude::*, px, rgb, App, Application, Bounds, Context, DisplayId, Hsla, Pixels,
+    SharedString, Size, Window, WindowBackgroundAppearance, WindowBounds, WindowKind,
     WindowOptions,
 };
 
@@ -11,8 +11,8 @@ struct WindowContent {
 }
 
 impl Render for WindowContent {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let window_bounds = cx.bounds();
+    fn render(&mut self, window: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
+        let window_bounds = window.bounds();
 
         div()
             .flex()
@@ -66,7 +66,7 @@ fn build_window_options(display_id: DisplayId, bounds: Bounds<Pixels>) -> Window
 }
 
 fn main() {
-    App::new().run(|cx: &mut AppContext| {
+    Application::new().run(|cx: &mut App| {
         // Create several new windows, positioned in the top right corner of each screen
         let size = Size {
             width: px(350.),
@@ -80,8 +80,8 @@ fn main() {
                 size,
             };
 
-            cx.open_window(build_window_options(screen.id(), bounds), |cx| {
-                cx.new_view(|_| WindowContent {
+            cx.open_window(build_window_options(screen.id(), bounds), |_, cx| {
+                cx.new(|_| WindowContent {
                     text: format!("Top Left {:?}", screen.id()).into(),
                     bg: gpui::red(),
                     bounds,
@@ -95,8 +95,8 @@ fn main() {
                 size,
             };
 
-            cx.open_window(build_window_options(screen.id(), bounds), |cx| {
-                cx.new_view(|_| WindowContent {
+            cx.open_window(build_window_options(screen.id(), bounds), |_, cx| {
+                cx.new(|_| WindowContent {
                     text: format!("Top Right {:?}", screen.id()).into(),
                     bg: gpui::red(),
                     bounds,
@@ -110,8 +110,8 @@ fn main() {
                 size,
             };
 
-            cx.open_window(build_window_options(screen.id(), bounds), |cx| {
-                cx.new_view(|_| WindowContent {
+            cx.open_window(build_window_options(screen.id(), bounds), |_, cx| {
+                cx.new(|_| WindowContent {
                     text: format!("Bottom Left {:?}", screen.id()).into(),
                     bg: gpui::blue(),
                     bounds,
@@ -125,8 +125,8 @@ fn main() {
                 size,
             };
 
-            cx.open_window(build_window_options(screen.id(), bounds), |cx| {
-                cx.new_view(|_| WindowContent {
+            cx.open_window(build_window_options(screen.id(), bounds), |_, cx| {
+                cx.new(|_| WindowContent {
                     text: format!("Bottom Right {:?}", screen.id()).into(),
                     bg: gpui::blue(),
                     bounds,
@@ -139,8 +139,8 @@ fn main() {
                 size,
             };
 
-            cx.open_window(build_window_options(screen.id(), bounds), |cx| {
-                cx.new_view(|_| WindowContent {
+            cx.open_window(build_window_options(screen.id(), bounds), |_, cx| {
+                cx.new(|_| WindowContent {
                     text: format!("Top Center {:?}", screen.id()).into(),
                     bg: gpui::black(),
                     bounds,
@@ -153,8 +153,8 @@ fn main() {
                 size,
             };
 
-            cx.open_window(build_window_options(screen.id(), bounds), |cx| {
-                cx.new_view(|_| WindowContent {
+            cx.open_window(build_window_options(screen.id(), bounds), |_, cx| {
+                cx.new(|_| WindowContent {
                     text: format!("Left Center {:?}", screen.id()).into(),
                     bg: gpui::black(),
                     bounds,
@@ -170,8 +170,8 @@ fn main() {
                 size,
             };
 
-            cx.open_window(build_window_options(screen.id(), bounds), |cx| {
-                cx.new_view(|_| WindowContent {
+            cx.open_window(build_window_options(screen.id(), bounds), |_, cx| {
+                cx.new(|_| WindowContent {
                     text: format!("Center {:?}", screen.id()).into(),
                     bg: gpui::black(),
                     bounds,
@@ -187,8 +187,8 @@ fn main() {
                 size,
             };
 
-            cx.open_window(build_window_options(screen.id(), bounds), |cx| {
-                cx.new_view(|_| WindowContent {
+            cx.open_window(build_window_options(screen.id(), bounds), |_, cx| {
+                cx.new(|_| WindowContent {
                     text: format!("Right Center {:?}", screen.id()).into(),
                     bg: gpui::black(),
                     bounds,
@@ -204,8 +204,8 @@ fn main() {
                 size,
             };
 
-            cx.open_window(build_window_options(screen.id(), bounds), |cx| {
-                cx.new_view(|_| WindowContent {
+            cx.open_window(build_window_options(screen.id(), bounds), |_, cx| {
+                cx.new(|_| WindowContent {
                     text: format!("Bottom Center {:?}", screen.id()).into(),
                     bg: gpui::black(),
                     bounds,

crates/gpui/examples/window_shadow.rs 🔗

@@ -1,7 +1,8 @@
 use gpui::{
     black, canvas, div, green, point, prelude::*, px, rgb, size, transparent_black, white, App,
-    AppContext, Bounds, CursorStyle, Decorations, Hsla, MouseButton, Pixels, Point, ResizeEdge,
-    Size, ViewContext, WindowBackgroundAppearance, WindowBounds, WindowDecorations, WindowOptions,
+    Application, Bounds, Context, CursorStyle, Decorations, Hsla, MouseButton, Pixels, Point,
+    ResizeEdge, Size, Window, WindowBackgroundAppearance, WindowBounds, WindowDecorations,
+    WindowOptions,
 };
 
 struct WindowShadow {}
@@ -13,13 +14,13 @@ struct WindowShadow {}
 // 3. We need to implement the techniques in here in Zed
 
 impl Render for WindowShadow {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let decorations = cx.window_decorations();
+    fn render(&mut self, window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
+        let decorations = window.window_decorations();
         let rounding = px(10.0);
         let shadow_size = px(10.0);
         let border_size = px(1.0);
         let grey = rgb(0x808080);
-        cx.set_client_inset(shadow_size);
+        window.set_client_inset(shadow_size);
 
         div()
             .id("window-backdrop")
@@ -30,22 +31,22 @@ impl Render for WindowShadow {
                     .bg(gpui::transparent_black())
                     .child(
                         canvas(
-                            |_bounds, cx| {
-                                cx.insert_hitbox(
+                            |_bounds, window, _cx| {
+                                window.insert_hitbox(
                                     Bounds::new(
                                         point(px(0.0), px(0.0)),
-                                        cx.window_bounds().get_bounds().size,
+                                        window.window_bounds().get_bounds().size,
                                     ),
                                     false,
                                 )
                             },
-                            move |_bounds, hitbox, cx| {
-                                let mouse = cx.mouse_position();
-                                let size = cx.window_bounds().get_bounds().size;
+                            move |_bounds, hitbox, window, _cx| {
+                                let mouse = window.mouse_position();
+                                let size = window.window_bounds().get_bounds().size;
                                 let Some(edge) = resize_edge(mouse, shadow_size, size) else {
                                     return;
                                 };
-                                cx.set_cursor_style(
+                                window.set_cursor_style(
                                     match edge {
                                         ResizeEdge::Top | ResizeEdge::Bottom => {
                                             CursorStyle::ResizeUpDown
@@ -75,14 +76,14 @@ impl Render for WindowShadow {
                     .when(!tiling.bottom, |div| div.pb(shadow_size))
                     .when(!tiling.left, |div| div.pl(shadow_size))
                     .when(!tiling.right, |div| div.pr(shadow_size))
-                    .on_mouse_move(|_e, cx| cx.refresh())
-                    .on_mouse_down(MouseButton::Left, move |e, cx| {
-                        let size = cx.window_bounds().get_bounds().size;
+                    .on_mouse_move(|_e, window, _cx| window.refresh())
+                    .on_mouse_down(MouseButton::Left, move |e, window, _cx| {
+                        let size = window.window_bounds().get_bounds().size;
                         let pos = e.position;
 
                         match resize_edge(pos, shadow_size, size) {
-                            Some(edge) => cx.start_window_resize(edge),
-                            None => cx.start_window_move(),
+                            Some(edge) => window.start_window_resize(edge),
+                            None => window.start_window_move(),
                         };
                     }),
             })
@@ -116,7 +117,7 @@ impl Render for WindowShadow {
                                 }])
                             }),
                     })
-                    .on_mouse_move(|_e, cx| {
+                    .on_mouse_move(|_e, _, cx| {
                         cx.stop_propagation();
                     })
                     .bg(gpui::rgb(0xCCCCFF))
@@ -157,12 +158,15 @@ impl Render for WindowShadow {
                                         .map(|div| match decorations {
                                             Decorations::Server => div,
                                             Decorations::Client { .. } => div
-                                                .on_mouse_down(MouseButton::Left, |_e, cx| {
-                                                    cx.start_window_move();
-                                                })
-                                                .on_click(|e, cx| {
+                                                .on_mouse_down(
+                                                    MouseButton::Left,
+                                                    |_e, window, _| {
+                                                        window.start_window_move();
+                                                    },
+                                                )
+                                                .on_click(|e, window, _| {
                                                     if e.down.button == MouseButton::Right {
-                                                        cx.show_window_menu(e.up.position);
+                                                        window.show_window_menu(e.up.position);
                                                     }
                                                 })
                                                 .text_color(black())
@@ -199,7 +203,7 @@ fn resize_edge(pos: Point<Pixels>, shadow_size: Pixels, size: Size<Pixels>) -> O
 }
 
 fn main() {
-    App::new().run(|cx: &mut AppContext| {
+    Application::new().run(|cx: &mut App| {
         let bounds = Bounds::centered(None, size(px(600.0), px(600.0)), cx);
         cx.open_window(
             WindowOptions {
@@ -208,10 +212,10 @@ fn main() {
                 window_decorations: Some(WindowDecorations::Client),
                 ..Default::default()
             },
-            |cx| {
-                cx.new_view(|cx| {
-                    cx.observe_window_appearance(|_, cx| {
-                        cx.refresh();
+            |window, cx| {
+                cx.new(|cx| {
+                    cx.observe_window_appearance(window, |_, window, _| {
+                        window.refresh();
                     })
                     .detach();
                     WindowShadow {}

crates/gpui/src/app.rs 🔗

@@ -2,13 +2,11 @@ use std::{
     any::{type_name, TypeId},
     cell::{Ref, RefCell, RefMut},
     marker::PhantomData,
+    mem,
     ops::{Deref, DerefMut},
     path::{Path, PathBuf},
     rc::{Rc, Weak},
-    sync::{
-        atomic::{AtomicUsize, Ordering::SeqCst},
-        Arc,
-    },
+    sync::{atomic::Ordering::SeqCst, Arc},
     time::Duration,
 };
 
@@ -33,13 +31,13 @@ use util::ResultExt;
 
 use crate::{
     current_platform, hash, init_app_menus, Action, ActionBuildError, ActionRegistry, Any, AnyView,
-    AnyWindowHandle, Asset, AssetSource, BackgroundExecutor, Bounds, ClipboardItem, Context,
-    DispatchPhase, DisplayId, Entity, EventEmitter, FocusHandle, FocusId, ForegroundExecutor,
-    Global, KeyBinding, Keymap, Keystroke, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions,
-    Pixels, Platform, PlatformDisplay, Point, PromptBuilder, PromptHandle, PromptLevel, Render,
+    AnyWindowHandle, AppContext, Asset, AssetSource, BackgroundExecutor, Bounds, ClipboardItem,
+    DispatchPhase, DisplayId, EventEmitter, FocusHandle, FocusMap, ForegroundExecutor, Global,
+    KeyBinding, Keymap, Keystroke, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels,
+    Platform, PlatformDisplay, Point, PromptBuilder, PromptHandle, PromptLevel, Render,
     RenderablePromptHandle, Reservation, ScreenCaptureSource, SharedString, SubscriberSet,
-    Subscription, SvgRenderer, Task, TextSystem, View, ViewContext, Window, WindowAppearance,
-    WindowContext, WindowHandle, WindowId,
+    Subscription, SvgRenderer, Task, TextSystem, Window, WindowAppearance, WindowHandle, WindowId,
+    WindowInvalidator,
 };
 
 mod async_context;
@@ -55,7 +53,7 @@ pub const SHUTDOWN_TIMEOUT: Duration = Duration::from_millis(100);
 /// Strongly consider removing after stabilization.
 #[doc(hidden)]
 pub struct AppCell {
-    app: RefCell<AppContext>,
+    app: RefCell<App>,
 }
 
 impl AppCell {
@@ -82,7 +80,7 @@ impl AppCell {
 
 #[doc(hidden)]
 #[derive(Deref, DerefMut)]
-pub struct AppRef<'a>(Ref<'a, AppContext>);
+pub struct AppRef<'a>(Ref<'a, App>);
 
 impl<'a> Drop for AppRef<'a> {
     fn drop(&mut self) {
@@ -95,7 +93,7 @@ impl<'a> Drop for AppRef<'a> {
 
 #[doc(hidden)]
 #[derive(Deref, DerefMut)]
-pub struct AppRefMut<'a>(RefMut<'a, AppContext>);
+pub struct AppRefMut<'a>(RefMut<'a, App>);
 
 impl<'a> Drop for AppRefMut<'a> {
     fn drop(&mut self) {
@@ -108,18 +106,18 @@ impl<'a> Drop for AppRefMut<'a> {
 
 /// A reference to a GPUI application, typically constructed in the `main` function of your app.
 /// You won't interact with this type much outside of initial configuration and startup.
-pub struct App(Rc<AppCell>);
+pub struct Application(Rc<AppCell>);
 
 /// Represents an application before it is fully launched. Once your app is
 /// configured, you'll start the app with `App::run`.
-impl App {
+impl Application {
     /// Builds an app with the given asset source.
     #[allow(clippy::new_without_default)]
     pub fn new() -> Self {
         #[cfg(any(test, feature = "test-support"))]
         log::info!("GPUI was compiled in test mode");
 
-        Self(AppContext::new(
+        Self(App::new_app(
             current_platform(false),
             Arc::new(()),
             Arc::new(NullHttpClient),
@@ -130,7 +128,7 @@ impl App {
     /// but makes it possible to run an application in an context like
     /// SSH, where GUI applications are not allowed.
     pub fn headless() -> Self {
-        Self(AppContext::new(
+        Self(App::new_app(
             current_platform(true),
             Arc::new(()),
             Arc::new(NullHttpClient),
@@ -159,7 +157,7 @@ impl App {
     /// app is fully launched.
     pub fn run<F>(self, on_finish_launching: F)
     where
-        F: 'static + FnOnce(&mut AppContext),
+        F: 'static + FnOnce(&mut App),
     {
         let this = self.0.clone();
         let platform = self.0.borrow().platform.clone();
@@ -183,7 +181,7 @@ impl App {
     /// On macOS, this can occur when the application icon is double-clicked or the app is launched via the dock.
     pub fn on_reopen<F>(&self, mut callback: F) -> &Self
     where
-        F: 'static + FnMut(&mut AppContext),
+        F: 'static + FnMut(&mut App),
     {
         let this = Rc::downgrade(&self.0);
         self.0.borrow_mut().platform.on_reopen(Box::new(move || {
@@ -215,19 +213,18 @@ impl App {
     }
 }
 
-type Handler = Box<dyn FnMut(&mut AppContext) -> bool + 'static>;
-type Listener = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool + 'static>;
+type Handler = Box<dyn FnMut(&mut App) -> bool + 'static>;
+type Listener = Box<dyn FnMut(&dyn Any, &mut App) -> bool + 'static>;
 pub(crate) type KeystrokeObserver =
-    Box<dyn FnMut(&KeystrokeEvent, &mut WindowContext) -> bool + 'static>;
-type QuitHandler = Box<dyn FnOnce(&mut AppContext) -> LocalBoxFuture<'static, ()> + 'static>;
-type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut AppContext) + 'static>;
-type NewViewListener = Box<dyn FnMut(AnyView, &mut WindowContext) + 'static>;
-type NewModelListener = Box<dyn FnMut(AnyModel, &mut AppContext) + 'static>;
+    Box<dyn FnMut(&KeystrokeEvent, &mut Window, &mut App) -> bool + 'static>;
+type QuitHandler = Box<dyn FnOnce(&mut App) -> LocalBoxFuture<'static, ()> + 'static>;
+type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut App) + 'static>;
+type NewModelListener = Box<dyn FnMut(AnyEntity, &mut Option<&mut Window>, &mut App) + 'static>;
 
 /// Contains the state of the full application, and passed as a reference to a variety of callbacks.
 /// Other contexts such as [ModelContext], [WindowContext], and [ViewContext] deref to this type, making it the most general context type.
 /// You need a reference to an `AppContext` to access the state of a [Model].
-pub struct AppContext {
+pub struct App {
     pub(crate) this: Weak<AppCell>,
     pub(crate) platform: Rc<dyn Platform>,
     text_system: Arc<TextSystem>,
@@ -243,11 +240,11 @@ pub struct AppContext {
     http_client: Arc<dyn HttpClient>,
     pub(crate) globals_by_type: FxHashMap<TypeId, Box<dyn Any>>,
     pub(crate) entities: EntityMap,
+    pub(crate) window_update_stack: Vec<WindowId>,
     pub(crate) new_model_observers: SubscriberSet<TypeId, NewModelListener>,
-    pub(crate) new_view_observers: SubscriberSet<TypeId, NewViewListener>,
     pub(crate) windows: SlotMap<WindowId, Option<Window>>,
     pub(crate) window_handles: FxHashMap<WindowId, AnyWindowHandle>,
-    pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
+    pub(crate) focus_handles: Arc<FocusMap>,
     pub(crate) keymap: Rc<RefCell<Keymap>>,
     pub(crate) keyboard_layout: SharedString,
     pub(crate) global_action_listeners:
@@ -266,14 +263,16 @@ pub struct AppContext {
     pub(crate) layout_id_buffer: Vec<LayoutId>, // We recycle this memory across layout requests.
     pub(crate) propagate_event: bool,
     pub(crate) prompt_builder: Option<PromptBuilder>,
-
+    pub(crate) window_invalidators_by_entity:
+        FxHashMap<EntityId, FxHashMap<WindowId, WindowInvalidator>>,
+    pub(crate) tracked_entities: FxHashMap<WindowId, FxHashSet<EntityId>>,
     #[cfg(any(test, feature = "test-support", debug_assertions))]
     pub(crate) name: Option<&'static str>,
 }
 
-impl AppContext {
+impl App {
     #[allow(clippy::new_ret_no_self)]
-    pub(crate) fn new(
+    pub(crate) fn new_app(
         platform: Rc<dyn Platform>,
         asset_source: Arc<dyn AssetSource>,
         http_client: Arc<dyn HttpClient>,
@@ -290,7 +289,7 @@ impl AppContext {
         let keyboard_layout = SharedString::from(platform.keyboard_layout());
 
         let app = Rc::new_cyclic(|this| AppCell {
-            app: RefCell::new(AppContext {
+            app: RefCell::new(App {
                 this: this.clone(),
                 platform: platform.clone(),
                 text_system,
@@ -306,9 +305,9 @@ impl AppContext {
                 http_client,
                 globals_by_type: FxHashMap::default(),
                 entities,
-                new_view_observers: SubscriberSet::new(),
                 new_model_observers: SubscriberSet::new(),
                 windows: SlotMap::with_key(),
+                window_update_stack: Vec::new(),
                 window_handles: FxHashMap::default(),
                 focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
                 keymap: Rc::new(RefCell::new(Keymap::default())),
@@ -318,6 +317,8 @@ impl AppContext {
                 pending_notifications: FxHashSet::default(),
                 pending_global_notifications: FxHashSet::default(),
                 observers: SubscriberSet::new(),
+                tracked_entities: FxHashMap::default(),
+                window_invalidators_by_entity: FxHashMap::default(),
                 event_listeners: SubscriberSet::new(),
                 release_listeners: SubscriberSet::new(),
                 keystroke_observers: SubscriberSet::new(),
@@ -389,7 +390,7 @@ impl AppContext {
     /// Invokes a handler when the current keyboard layout changes
     pub fn on_keyboard_layout_change<F>(&self, mut callback: F) -> Subscription
     where
-        F: 'static + FnMut(&mut AppContext),
+        F: 'static + FnMut(&mut App),
     {
         let (subscription, activate) = self.keyboard_layout_observers.insert(
             (),
@@ -409,8 +410,8 @@ impl AppContext {
 
     /// Schedules all windows in the application to be redrawn. This can be called
     /// multiple times in an update cycle and still result in a single redraw.
-    pub fn refresh(&mut self) {
-        self.pending_effects.push_back(Effect::Refresh);
+    pub fn refresh_windows(&mut self) {
+        self.pending_effects.push_back(Effect::RefreshWindows);
     }
 
     pub(crate) fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
@@ -426,14 +427,13 @@ impl AppContext {
     }
 
     /// Arrange a callback to be invoked when the given model or view calls `notify` on its respective context.
-    pub fn observe<W, E>(
+    pub fn observe<W>(
         &mut self,
-        entity: &E,
-        mut on_notify: impl FnMut(E, &mut AppContext) + 'static,
+        entity: &Entity<W>,
+        mut on_notify: impl FnMut(Entity<W>, &mut App) + 'static,
     ) -> Subscription
     where
         W: 'static,
-        E: Entity<W>,
     {
         self.observe_internal(entity, move |e, cx| {
             on_notify(e, cx);
@@ -441,27 +441,67 @@ impl AppContext {
         })
     }
 
+    pub(crate) fn detect_accessed_entities<R>(
+        &mut self,
+        callback: impl FnOnce(&mut App) -> R,
+    ) -> (R, FxHashSet<EntityId>) {
+        let accessed_entities_start = self.entities.accessed_entities.borrow().clone();
+        let result = callback(self);
+        let accessed_entities_end = self.entities.accessed_entities.borrow().clone();
+        let entities_accessed_in_callback = accessed_entities_end
+            .difference(&accessed_entities_start)
+            .copied()
+            .collect::<FxHashSet<EntityId>>();
+        (result, entities_accessed_in_callback)
+    }
+
+    pub(crate) fn record_entities_accessed(
+        &mut self,
+        window_handle: AnyWindowHandle,
+        invalidator: WindowInvalidator,
+        entities: &FxHashSet<EntityId>,
+    ) {
+        let mut tracked_entities =
+            std::mem::take(self.tracked_entities.entry(window_handle.id).or_default());
+        for entity in tracked_entities.iter() {
+            self.window_invalidators_by_entity
+                .entry(*entity)
+                .and_modify(|windows| {
+                    windows.remove(&window_handle.id);
+                });
+        }
+        for entity in entities.iter() {
+            self.window_invalidators_by_entity
+                .entry(*entity)
+                .or_default()
+                .insert(window_handle.id, invalidator.clone());
+        }
+        tracked_entities.clear();
+        tracked_entities.extend(entities.iter().copied());
+        self.tracked_entities
+            .insert(window_handle.id, tracked_entities);
+    }
+
     pub(crate) fn new_observer(&mut self, key: EntityId, value: Handler) -> Subscription {
         let (subscription, activate) = self.observers.insert(key, value);
         self.defer(move |_| activate());
         subscription
     }
 
-    pub(crate) fn observe_internal<W, E>(
+    pub(crate) fn observe_internal<W>(
         &mut self,
-        entity: &E,
-        mut on_notify: impl FnMut(E, &mut AppContext) -> bool + 'static,
+        entity: &Entity<W>,
+        mut on_notify: impl FnMut(Entity<W>, &mut App) -> bool + 'static,
     ) -> Subscription
     where
         W: 'static,
-        E: Entity<W>,
     {
         let entity_id = entity.entity_id();
         let handle = entity.downgrade();
         self.new_observer(
             entity_id,
             Box::new(move |cx| {
-                if let Some(handle) = E::upgrade_from(&handle) {
+                if let Some(handle) = Entity::<W>::upgrade_from(&handle) {
                     on_notify(handle, cx)
                 } else {
                     false
@@ -472,14 +512,13 @@ impl AppContext {
 
     /// Arrange for the given callback to be invoked whenever the given model or view emits an event of a given type.
     /// The callback is provided a handle to the emitting entity and a reference to the emitted event.
-    pub fn subscribe<T, E, Event>(
+    pub fn subscribe<T, Event>(
         &mut self,
-        entity: &E,
-        mut on_event: impl FnMut(E, &Event, &mut AppContext) + 'static,
+        entity: &Entity<T>,
+        mut on_event: impl FnMut(Entity<T>, &Event, &mut App) + 'static,
     ) -> Subscription
     where
         T: 'static + EventEmitter<Event>,
-        E: Entity<T>,
         Event: 'static,
     {
         self.subscribe_internal(entity, move |entity, event, cx| {
@@ -497,14 +536,13 @@ impl AppContext {
         self.defer(move |_| activate());
         subscription
     }
-    pub(crate) fn subscribe_internal<T, E, Evt>(
+    pub(crate) fn subscribe_internal<T, Evt>(
         &mut self,
-        entity: &E,
-        mut on_event: impl FnMut(E, &Evt, &mut AppContext) -> bool + 'static,
+        entity: &Entity<T>,
+        mut on_event: impl FnMut(Entity<T>, &Evt, &mut App) -> bool + 'static,
     ) -> Subscription
     where
         T: 'static + EventEmitter<Evt>,
-        E: Entity<T>,
         Evt: 'static,
     {
         let entity_id = entity.entity_id();
@@ -515,7 +553,7 @@ impl AppContext {
                 TypeId::of::<Evt>(),
                 Box::new(move |event, cx| {
                     let event: &Evt = event.downcast_ref().expect("invalid event type");
-                    if let Some(handle) = E::upgrade_from(&entity) {
+                    if let Some(handle) = Entity::<T>::upgrade_from(&entity) {
                         on_event(handle, event, cx)
                     } else {
                         false
@@ -555,16 +593,18 @@ impl AppContext {
     pub fn open_window<V: 'static + Render>(
         &mut self,
         options: crate::WindowOptions,
-        build_root_view: impl FnOnce(&mut WindowContext) -> View<V>,
+        build_root_view: impl FnOnce(&mut Window, &mut App) -> Entity<V>,
     ) -> anyhow::Result<WindowHandle<V>> {
         self.update(|cx| {
             let id = cx.windows.insert(None);
             let handle = WindowHandle::new(id);
             match Window::new(handle.into(), options, cx) {
                 Ok(mut window) => {
-                    let root_view = build_root_view(&mut WindowContext::new(cx, &mut window));
-                    window.root_view.replace(root_view.into());
-                    WindowContext::new(cx, &mut window).defer(|cx| cx.appearance_changed());
+                    cx.window_update_stack.push(id);
+                    let root_view = build_root_view(&mut window, cx);
+                    cx.window_update_stack.pop();
+                    window.root_model.replace(root_view.into());
+                    window.defer(cx, |window: &mut Window, cx| window.appearance_changed(cx));
                     cx.window_handles.insert(id, window.handle);
                     cx.windows.get_mut(id).unwrap().replace(window);
                     Ok(handle)
@@ -577,12 +617,6 @@ impl AppContext {
         })
     }
 
-    /// Obtain a new [`FocusHandle`], which allows you to track and manipulate the keyboard focus
-    /// for elements rendered within this window.
-    pub fn focus_handle(&self) -> FocusHandle {
-        FocusHandle::new(&self.focus_handles)
-    }
-
     /// Instructs the platform to activate the application by bringing it to the foreground.
     pub fn activate(&self, ignoring_other_apps: bool) {
         self.platform.activate(ignoring_other_apps);
@@ -809,7 +843,7 @@ impl AppContext {
                         event,
                     } => self.apply_emit_effect(emitter, event_type, event),
 
-                    Effect::Refresh => {
+                    Effect::RefreshWindows => {
                         self.apply_refresh_effect();
                     }
 
@@ -820,6 +854,13 @@ impl AppContext {
                     Effect::Defer { callback } => {
                         self.apply_defer_effect(callback);
                     }
+                    Effect::ModelCreated {
+                        entity,
+                        tid,
+                        window,
+                    } => {
+                        self.apply_model_created_effect(entity, tid, window);
+                    }
                 }
             } else {
                 #[cfg(any(test, feature = "test-support"))]
@@ -828,11 +869,12 @@ impl AppContext {
                     .values()
                     .filter_map(|window| {
                         let window = window.as_ref()?;
-                        window.dirty.get().then_some(window.handle)
+                        window.invalidator.is_dirty().then_some(window.handle)
                     })
                     .collect::<Vec<_>>()
                 {
-                    self.update_window(window, |_, cx| cx.draw()).unwrap();
+                    self.update_window(window, |_, window, cx| window.draw(cx))
+                        .unwrap();
                 }
 
                 if self.pending_effects.is_empty() {
@@ -871,9 +913,9 @@ impl AppContext {
                 if count.load(SeqCst) == 0 {
                     for window_handle in self.windows() {
                         window_handle
-                            .update(self, |_, cx| {
-                                if cx.window.focus == Some(handle_id) {
-                                    cx.blur();
+                            .update(self, |_, window, _| {
+                                if window.focus == Some(handle_id) {
+                                    window.blur();
                                 }
                             })
                             .unwrap();
@@ -908,7 +950,8 @@ impl AppContext {
     fn apply_refresh_effect(&mut self) {
         for window in self.windows.values_mut() {
             if let Some(window) = window.as_mut() {
-                window.dirty.set(true);
+                window.refreshing = true;
+                window.invalidator.set_dirty(true);
             }
         }
     }
@@ -924,6 +967,57 @@ impl AppContext {
         callback(self);
     }
 
+    fn apply_model_created_effect(
+        &mut self,
+        entity: AnyEntity,
+        tid: TypeId,
+        window: Option<WindowId>,
+    ) {
+        self.new_model_observers.clone().retain(&tid, |observer| {
+            if let Some(id) = window {
+                self.update_window_id(id, {
+                    let entity = entity.clone();
+                    |_, window, cx| (observer)(entity, &mut Some(window), cx)
+                })
+                .expect("All windows should be off the stack when flushing effects");
+            } else {
+                (observer)(entity.clone(), &mut None, self)
+            }
+            true
+        });
+    }
+
+    fn update_window_id<T, F>(&mut self, id: WindowId, update: F) -> Result<T>
+    where
+        F: FnOnce(AnyView, &mut Window, &mut App) -> T,
+    {
+        self.update(|cx| {
+            let mut window = cx
+                .windows
+                .get_mut(id)
+                .ok_or_else(|| anyhow!("window not found"))?
+                .take()
+                .ok_or_else(|| anyhow!("window not found"))?;
+
+            let root_view = window.root_model.clone().unwrap();
+
+            cx.window_update_stack.push(window.handle.id);
+            let result = update(root_view, &mut window, cx);
+            cx.window_update_stack.pop();
+
+            if window.removed {
+                cx.window_handles.remove(&id);
+                cx.windows.remove(id);
+            } else {
+                cx.windows
+                    .get_mut(id)
+                    .ok_or_else(|| anyhow!("window not found"))?
+                    .replace(window);
+            }
+
+            Ok(result)
+        })
+    }
     /// Creates an `AsyncAppContext`, which can be cloned and has a static lifetime
     /// so it can be held across `await` points.
     pub fn to_async(&self) -> AsyncAppContext {
@@ -956,7 +1050,7 @@ impl AppContext {
 
     /// Schedules the given function to be run at the end of the current effect cycle, allowing entities
     /// that are currently on the stack to be returned to the app.
-    pub fn defer(&mut self, f: impl FnOnce(&mut AppContext) + 'static) {
+    pub fn defer(&mut self, f: impl FnOnce(&mut App) + 'static) {
         self.push_effect(Effect::Defer {
             callback: Box::new(f),
         });
@@ -1073,35 +1167,11 @@ impl AppContext {
     /// Restore the global of the given type after it is moved to the stack.
     pub(crate) fn end_global_lease<G: Global>(&mut self, lease: GlobalLease<G>) {
         let global_type = TypeId::of::<G>();
+
         self.push_effect(Effect::NotifyGlobalObservers { global_type });
         self.globals_by_type.insert(global_type, lease.global);
     }
 
-    pub(crate) fn new_view_observer(&self, key: TypeId, value: NewViewListener) -> Subscription {
-        let (subscription, activate) = self.new_view_observers.insert(key, value);
-        activate();
-        subscription
-    }
-
-    /// Arrange for the given function to be invoked whenever a view of the specified type is created.
-    /// The function will be passed a mutable reference to the view along with an appropriate context.
-    pub fn observe_new_views<V: 'static>(
-        &self,
-        on_new: impl 'static + Fn(&mut V, &mut ViewContext<V>),
-    ) -> Subscription {
-        self.new_view_observer(
-            TypeId::of::<V>(),
-            Box::new(move |any_view: AnyView, cx: &mut WindowContext| {
-                any_view
-                    .downcast::<V>()
-                    .unwrap()
-                    .update(cx, |view_state, cx| {
-                        on_new(view_state, cx);
-                    })
-            }),
-        )
-    }
-
     pub(crate) fn new_model_observer(&self, key: TypeId, value: NewModelListener) -> Subscription {
         let (subscription, activate) = self.new_model_observers.insert(key, value);
         activate();
@@ -1110,32 +1180,37 @@ impl AppContext {
 
     /// Arrange for the given function to be invoked whenever a view of the specified type is created.
     /// The function will be passed a mutable reference to the view along with an appropriate context.
-    pub fn observe_new_models<T: 'static>(
+    pub fn observe_new<T: 'static>(
         &self,
-        on_new: impl 'static + Fn(&mut T, &mut ModelContext<T>),
+        on_new: impl 'static + Fn(&mut T, Option<&mut Window>, &mut Context<T>),
     ) -> Subscription {
         self.new_model_observer(
             TypeId::of::<T>(),
-            Box::new(move |any_model: AnyModel, cx: &mut AppContext| {
-                any_model
-                    .downcast::<T>()
-                    .unwrap()
-                    .update(cx, |model_state, cx| {
-                        on_new(model_state, cx);
-                    })
-            }),
+            Box::new(
+                move |any_model: AnyEntity, window: &mut Option<&mut Window>, cx: &mut App| {
+                    any_model
+                        .downcast::<T>()
+                        .unwrap()
+                        .update(cx, |model_state, cx| {
+                            if let Some(window) = window {
+                                on_new(model_state, Some(window), cx);
+                            } else {
+                                on_new(model_state, None, cx);
+                            }
+                        })
+                },
+            ),
         )
     }
 
     /// Observe the release of a model or view. The callback is invoked after the model or view
     /// has no more strong references but before it has been dropped.
-    pub fn observe_release<E, T>(
+    pub fn observe_release<T>(
         &self,
-        handle: &E,
-        on_release: impl FnOnce(&mut T, &mut AppContext) + 'static,
+        handle: &Entity<T>,
+        on_release: impl FnOnce(&mut T, &mut App) + 'static,
     ) -> Subscription
     where
-        E: Entity<T>,
         T: 'static,
     {
         let (subscription, activate) = self.release_listeners.insert(
@@ -1149,12 +1224,35 @@ impl AppContext {
         subscription
     }
 
+    /// Observe the release of a model or view. The callback is invoked after the model or view
+    /// has no more strong references but before it has been dropped.
+    pub fn observe_release_in<T>(
+        &self,
+        handle: &Entity<T>,
+        window: &Window,
+        on_release: impl FnOnce(&mut T, &mut Window, &mut App) + 'static,
+    ) -> Subscription
+    where
+        T: 'static,
+    {
+        let window_handle = window.handle;
+        let (subscription, activate) = self.release_listeners.insert(
+            handle.entity_id(),
+            Box::new(move |entity, cx| {
+                let entity = entity.downcast_mut().expect("invalid entity type");
+                let _ = window_handle.update(cx, |_, window, cx| on_release(entity, window, cx));
+            }),
+        );
+        activate();
+        subscription
+    }
+
     /// Register a callback to be invoked when a keystroke is received by the application
     /// in any window. Note that this fires after all other action and event mechanisms have resolved
     /// and that this API will not be invoked if the event's propagation is stopped.
     pub fn observe_keystrokes(
         &mut self,
-        mut f: impl FnMut(&KeystrokeEvent, &mut WindowContext) + 'static,
+        mut f: impl FnMut(&KeystrokeEvent, &mut Window, &mut App) + 'static,
     ) -> Subscription {
         fn inner(
             keystroke_observers: &SubscriberSet<(), KeystrokeObserver>,
@@ -1167,8 +1265,8 @@ impl AppContext {
 
         inner(
             &mut self.keystroke_observers,
-            Box::new(move |event, cx| {
-                f(event, cx);
+            Box::new(move |event, window, cx| {
+                f(event, window, cx);
                 true
             }),
         )
@@ -1177,13 +1275,13 @@ impl AppContext {
     /// Register key bindings.
     pub fn bind_keys(&mut self, bindings: impl IntoIterator<Item = KeyBinding>) {
         self.keymap.borrow_mut().add_bindings(bindings);
-        self.pending_effects.push_back(Effect::Refresh);
+        self.pending_effects.push_back(Effect::RefreshWindows);
     }
 
     /// Clear all key bindings in the app.
     pub fn clear_key_bindings(&mut self) {
         self.keymap.borrow_mut().clear();
-        self.pending_effects.push_back(Effect::Refresh);
+        self.pending_effects.push_back(Effect::RefreshWindows);
     }
 
     /// Register a global listener for actions invoked via the keyboard.
@@ -1230,6 +1328,13 @@ impl AppContext {
         self.actions.all_action_names()
     }
 
+    /// Returns key bindings that invoke the given action on the currently focused element, without
+    /// checking context. Bindings are returned in the order they were added. For display, the last
+    /// binding should take precedence.
+    pub fn all_bindings_for_input(&self, input: &[Keystroke]) -> Vec<KeyBinding> {
+        RefCell::borrow(&self.keymap).all_bindings_for_input(input)
+    }
+
     /// Get all non-internal actions that have been registered, along with their schemas.
     pub fn action_schemas(
         &self,
@@ -1247,7 +1352,7 @@ impl AppContext {
     /// It is not possible to cancel the quit event at this point.
     pub fn on_app_quit<Fut>(
         &self,
-        mut on_quit: impl FnMut(&mut AppContext) -> Fut + 'static,
+        mut on_quit: impl FnMut(&mut App) -> Fut + 'static,
     ) -> Subscription
     where
         Fut: 'static + Future<Output = ()>,
@@ -1266,8 +1371,8 @@ impl AppContext {
     pub(crate) fn clear_pending_keystrokes(&mut self) {
         for window in self.windows() {
             window
-                .update(self, |_, cx| {
-                    cx.clear_pending_keystrokes();
+                .update(self, |_, window, _| {
+                    window.clear_pending_keystrokes();
                 })
                 .ok();
         }
@@ -1279,7 +1384,7 @@ impl AppContext {
         let mut action_available = false;
         if let Some(window) = self.active_window() {
             if let Ok(window_action_available) =
-                window.update(self, |_, cx| cx.is_action_available(action))
+                window.update(self, |_, window, cx| window.is_action_available(action, cx))
             {
                 action_available = window_action_available;
             }
@@ -1319,7 +1424,9 @@ impl AppContext {
     pub fn dispatch_action(&mut self, action: &dyn Action) {
         if let Some(active_window) = self.active_window() {
             active_window
-                .update(self, |_, cx| cx.dispatch_action(action.boxed_clone()))
+                .update(self, |_, window, cx| {
+                    window.dispatch_action(action.boxed_clone(), cx)
+                })
                 .log_err();
         } else {
             self.dispatch_global_action(action);
@@ -1389,7 +1496,8 @@ impl AppContext {
                 Option<&str>,
                 &[&str],
                 PromptHandle,
-                &mut WindowContext,
+                &mut Window,
+                &mut App,
             ) -> RenderablePromptHandle
             + 'static,
     ) {
@@ -1425,6 +1533,36 @@ impl AppContext {
         (task, is_first)
     }
 
+    /// Obtain a new [`FocusHandle`], which allows you to track and manipulate the keyboard focus
+    /// for elements rendered within this window.
+    #[track_caller]
+    pub fn focus_handle(&self) -> FocusHandle {
+        FocusHandle::new(&self.focus_handles)
+    }
+
+    /// Tell GPUI that an entity has changed and observers of it should be notified.
+    pub fn notify(&mut self, entity_id: EntityId) {
+        let window_invalidators = mem::take(
+            self.window_invalidators_by_entity
+                .entry(entity_id)
+                .or_default(),
+        );
+
+        if window_invalidators.is_empty() {
+            if self.pending_notifications.insert(entity_id) {
+                self.pending_effects
+                    .push_back(Effect::Notify { emitter: entity_id });
+            }
+        } else {
+            for invalidator in window_invalidators.values() {
+                invalidator.invalidate_view(entity_id, self);
+            }
+        }
+
+        self.window_invalidators_by_entity
+            .insert(entity_id, window_invalidators);
+    }
+
     /// Get the name for this App.
     #[cfg(any(test, feature = "test-support", debug_assertions))]
     pub fn get_name(&self) -> &'static str {
@@ -1437,32 +1575,25 @@ impl AppContext {
     }
 }
 
-impl Context for AppContext {
+impl AppContext for App {
     type Result<T> = T;
 
     /// Build an entity that is owned by the application. The given function will be invoked with
     /// a `ModelContext` and must return an object representing the entity. A `Model` handle will be returned,
     /// which can be used to access the entity in a context.
-    fn new_model<T: 'static>(
-        &mut self,
-        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
-    ) -> Model<T> {
+    fn new<T: 'static>(&mut self, build_model: impl FnOnce(&mut Context<'_, T>) -> T) -> Entity<T> {
         self.update(|cx| {
             let slot = cx.entities.reserve();
             let model = slot.clone();
-            let entity = build_model(&mut ModelContext::new(cx, slot.downgrade()));
-            cx.entities.insert(slot, entity);
+            let entity = build_model(&mut Context::new_context(cx, slot.downgrade()));
 
-            // Non-generic part to avoid leaking SubscriberSet to invokers of `new_view`.
-            fn notify_observers(cx: &mut AppContext, tid: TypeId, model: AnyModel) {
-                cx.new_model_observers.clone().retain(&tid, |observer| {
-                    let any_model = model.clone();
-                    (observer)(any_model, cx);
-                    true
-                });
-            }
-            notify_observers(cx, TypeId::of::<T>(), AnyModel::from(model.clone()));
+            cx.push_effect(Effect::ModelCreated {
+                entity: model.clone().into_any(),
+                tid: TypeId::of::<T>(),
+                window: cx.window_update_stack.last().cloned(),
+            });
 
+            cx.entities.insert(slot, entity);
             model
         })
     }
@@ -1474,11 +1605,11 @@ impl Context for AppContext {
     fn insert_model<T: 'static>(
         &mut self,
         reservation: Reservation<T>,
-        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
-    ) -> Self::Result<Model<T>> {
+        build_model: impl FnOnce(&mut Context<'_, T>) -> T,
+    ) -> Self::Result<Entity<T>> {
         self.update(|cx| {
             let slot = reservation.0;
-            let entity = build_model(&mut ModelContext::new(cx, slot.downgrade()));
+            let entity = build_model(&mut Context::new_context(cx, slot.downgrade()));
             cx.entities.insert(slot, entity)
         })
     }
@@ -1487,12 +1618,15 @@ impl Context for AppContext {
     /// entity along with a `ModelContext` for the entity.
     fn update_model<T: 'static, R>(
         &mut self,
-        model: &Model<T>,
-        update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
+        model: &Entity<T>,
+        update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
     ) -> R {
         self.update(|cx| {
             let mut entity = cx.entities.lease(model);
-            let result = update(&mut entity, &mut ModelContext::new(cx, model.downgrade()));
+            let result = update(
+                &mut entity,
+                &mut Context::new_context(cx, model.downgrade()),
+            );
             cx.entities.end_lease(entity);
             result
         })
@@ -1500,8 +1634,8 @@ impl Context for AppContext {
 
     fn read_model<T, R>(
         &self,
-        handle: &Model<T>,
-        read: impl FnOnce(&T, &AppContext) -> R,
+        handle: &Entity<T>,
+        read: impl FnOnce(&T, &App) -> R,
     ) -> Self::Result<R>
     where
         T: 'static,
@@ -1512,37 +1646,15 @@ impl Context for AppContext {
 
     fn update_window<T, F>(&mut self, handle: AnyWindowHandle, update: F) -> Result<T>
     where
-        F: FnOnce(AnyView, &mut WindowContext) -> T,
+        F: FnOnce(AnyView, &mut Window, &mut App) -> T,
     {
-        self.update(|cx| {
-            let mut window = cx
-                .windows
-                .get_mut(handle.id)
-                .ok_or_else(|| anyhow!("window not found"))?
-                .take()
-                .ok_or_else(|| anyhow!("window not found"))?;
-
-            let root_view = window.root_view.clone().unwrap();
-            let result = update(root_view, &mut WindowContext::new(cx, &mut window));
-
-            if window.removed {
-                cx.window_handles.remove(&handle.id);
-                cx.windows.remove(handle.id);
-            } else {
-                cx.windows
-                    .get_mut(handle.id)
-                    .ok_or_else(|| anyhow!("window not found"))?
-                    .replace(window);
-            }
-
-            Ok(result)
-        })
+        self.update_window_id(handle.id, update)
     }
 
     fn read_window<T, R>(
         &self,
         window: &WindowHandle<T>,
-        read: impl FnOnce(View<T>, &AppContext) -> R,
+        read: impl FnOnce(Entity<T>, &App) -> R,
     ) -> Result<R>
     where
         T: 'static,
@@ -1552,9 +1664,9 @@ impl Context for AppContext {
             .get(window.id)
             .ok_or_else(|| anyhow!("window not found"))?
             .as_ref()
-            .unwrap();
+            .expect("attempted to read a window that is already on the stack");
 
-        let root_view = window.root_view.clone().unwrap();
+        let root_view = window.root_model.clone().unwrap();
         let view = root_view
             .downcast::<T>()
             .map_err(|_| anyhow!("root view's type has changed"))?;
@@ -1573,15 +1685,35 @@ pub(crate) enum Effect {
         event_type: TypeId,
         event: Box<dyn Any>,
     },
-    Refresh,
+    RefreshWindows,
     NotifyGlobalObservers {
         global_type: TypeId,
     },
     Defer {
-        callback: Box<dyn FnOnce(&mut AppContext) + 'static>,
+        callback: Box<dyn FnOnce(&mut App) + 'static>,
+    },
+    ModelCreated {
+        entity: AnyEntity,
+        tid: TypeId,
+        window: Option<WindowId>,
     },
 }
 
+impl std::fmt::Debug for Effect {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Effect::Notify { emitter } => write!(f, "Notify({})", emitter),
+            Effect::Emit { emitter, .. } => write!(f, "Emit({:?})", emitter),
+            Effect::RefreshWindows => write!(f, "RefreshWindows"),
+            Effect::NotifyGlobalObservers { global_type } => {
+                write!(f, "NotifyGlobalObservers({:?})", global_type)
+            }
+            Effect::Defer { .. } => write!(f, "Defer(..)"),
+            Effect::ModelCreated { entity, .. } => write!(f, "ModelCreated({:?})", entity),
+        }
+    }
+}
+
 /// Wraps a global variable value during `update_global` while the value has been moved to the stack.
 pub(crate) struct GlobalLease<G: Global> {
     global: Box<dyn Any>,
@@ -1638,7 +1770,7 @@ pub struct AnyTooltip {
     /// Given the bounds of the tooltip, checks whether the tooltip should still be visible and
     /// updates its state accordingly. This is needed atop the hovered element's mouse move handler
     /// to handle the case where the element is not painted (e.g. via use of `visible_on_hover`).
-    pub check_visible_and_update: Rc<dyn Fn(Bounds<Pixels>, &mut WindowContext) -> bool>,
+    pub check_visible_and_update: Rc<dyn Fn(Bounds<Pixels>, &mut Window, &mut App) -> bool>,
 }
 
 /// A keystroke event, and potentially the associated action

crates/gpui/src/app/async_context.rs 🔗

@@ -1,14 +1,15 @@
 use crate::{
-    AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, BorrowAppContext, Context,
-    DismissEvent, FocusableView, ForegroundExecutor, Global, Model, ModelContext, PromptLevel,
-    Render, Reservation, Result, Task, View, ViewContext, VisualContext, WindowContext,
-    WindowHandle,
+    AnyView, AnyWindowHandle, App, AppCell, AppContext, BackgroundExecutor, BorrowAppContext,
+    Entity, Focusable, ForegroundExecutor, Global, PromptLevel, Render, Reservation, Result, Task,
+    VisualContext, Window, WindowHandle,
 };
 use anyhow::{anyhow, Context as _};
 use derive_more::{Deref, DerefMut};
 use futures::channel::oneshot;
 use std::{future::Future, rc::Weak};
 
+use super::Context;
+
 /// An async-friendly version of [AppContext] with a static lifetime so it can be held across `await` points in async code.
 /// You're provided with an instance when calling [AppContext::spawn], and you can also create one with [AppContext::to_async].
 /// Internally, this holds a weak reference to an `AppContext`, so its methods are fallible to protect against cases where the [AppContext] is dropped.
@@ -19,19 +20,19 @@ pub struct AsyncAppContext {
     pub(crate) foreground_executor: ForegroundExecutor,
 }
 
-impl Context for AsyncAppContext {
+impl AppContext for AsyncAppContext {
     type Result<T> = Result<T>;
 
-    fn new_model<T: 'static>(
+    fn new<T: 'static>(
         &mut self,
-        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
-    ) -> Self::Result<Model<T>> {
+        build_model: impl FnOnce(&mut Context<'_, T>) -> T,
+    ) -> Self::Result<Entity<T>> {
         let app = self
             .app
             .upgrade()
             .ok_or_else(|| anyhow!("app was released"))?;
         let mut app = app.borrow_mut();
-        Ok(app.new_model(build_model))
+        Ok(app.new(build_model))
     }
 
     fn reserve_model<T: 'static>(&mut self) -> Result<Reservation<T>> {
@@ -46,8 +47,8 @@ impl Context for AsyncAppContext {
     fn insert_model<T: 'static>(
         &mut self,
         reservation: Reservation<T>,
-        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
-    ) -> Result<Model<T>> {
+        build_model: impl FnOnce(&mut Context<'_, T>) -> T,
+    ) -> Result<Entity<T>> {
         let app = self
             .app
             .upgrade()
@@ -58,8 +59,8 @@ impl Context for AsyncAppContext {
 
     fn update_model<T: 'static, R>(
         &mut self,
-        handle: &Model<T>,
-        update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
+        handle: &Entity<T>,
+        update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
     ) -> Self::Result<R> {
         let app = self
             .app
@@ -71,8 +72,8 @@ impl Context for AsyncAppContext {
 
     fn read_model<T, R>(
         &self,
-        handle: &Model<T>,
-        callback: impl FnOnce(&T, &AppContext) -> R,
+        handle: &Entity<T>,
+        callback: impl FnOnce(&T, &App) -> R,
     ) -> Self::Result<R>
     where
         T: 'static,
@@ -84,7 +85,7 @@ impl Context for AsyncAppContext {
 
     fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
     where
-        F: FnOnce(AnyView, &mut WindowContext) -> T,
+        F: FnOnce(AnyView, &mut Window, &mut App) -> T,
     {
         let app = self.app.upgrade().context("app was released")?;
         let mut lock = app.borrow_mut();
@@ -94,7 +95,7 @@ impl Context for AsyncAppContext {
     fn read_window<T, R>(
         &self,
         window: &WindowHandle<T>,
-        read: impl FnOnce(View<T>, &AppContext) -> R,
+        read: impl FnOnce(Entity<T>, &App) -> R,
     ) -> Result<R>
     where
         T: 'static,
@@ -113,7 +114,7 @@ impl AsyncAppContext {
             .upgrade()
             .ok_or_else(|| anyhow!("app was released"))?;
         let mut lock = app.borrow_mut();
-        lock.refresh();
+        lock.refresh_windows();
         Ok(())
     }
 
@@ -128,7 +129,7 @@ impl AsyncAppContext {
     }
 
     /// Invoke the given function in the context of the app, then flush any effects produced during its invocation.
-    pub fn update<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> Result<R> {
+    pub fn update<R>(&self, f: impl FnOnce(&mut App) -> R) -> Result<R> {
         let app = self
             .app
             .upgrade()
@@ -141,7 +142,7 @@ impl AsyncAppContext {
     pub fn open_window<V>(
         &self,
         options: crate::WindowOptions,
-        build_root_view: impl FnOnce(&mut WindowContext) -> View<V>,
+        build_root_view: impl FnOnce(&mut Window, &mut App) -> Entity<V>,
     ) -> Result<WindowHandle<V>>
     where
         V: 'static + Render,
@@ -178,7 +179,7 @@ impl AsyncAppContext {
     ///
     /// Panics if no global state of the specified type has been assigned.
     /// Returns an error if the `AppContext` has been dropped.
-    pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result<R> {
+    pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &App) -> R) -> Result<R> {
         let app = self
             .app
             .upgrade()
@@ -193,10 +194,7 @@ impl AsyncAppContext {
     /// if no state of the specified type has been assigned.
     ///
     /// Returns an error if no state of the specified type has been assigned the `AppContext` has been dropped.
-    pub fn try_read_global<G: Global, R>(
-        &self,
-        read: impl FnOnce(&G, &AppContext) -> R,
-    ) -> Option<R> {
+    pub fn try_read_global<G: Global, R>(&self, read: impl FnOnce(&G, &App) -> R) -> Option<R> {
         let app = self.app.upgrade()?;
         let app = app.borrow_mut();
         Some(read(app.try_global()?, &app))
@@ -206,7 +204,7 @@ impl AsyncAppContext {
     /// for updating the global state of the specified type.
     pub fn update_global<G: Global, R>(
         &self,
-        update: impl FnOnce(&mut G, &mut AppContext) -> R,
+        update: impl FnOnce(&mut G, &mut App) -> R,
     ) -> Result<R> {
         let app = self
             .app
@@ -228,7 +226,7 @@ pub struct AsyncWindowContext {
 }
 
 impl AsyncWindowContext {
-    pub(crate) fn new(app: AsyncAppContext, window: AnyWindowHandle) -> Self {
+    pub(crate) fn new_context(app: AsyncAppContext, window: AnyWindowHandle) -> Self {
         Self { app, window }
     }
 
@@ -238,41 +236,47 @@ impl AsyncWindowContext {
     }
 
     /// A convenience method for [`AppContext::update_window`].
-    pub fn update<R>(&mut self, update: impl FnOnce(&mut WindowContext) -> R) -> Result<R> {
-        self.app.update_window(self.window, |_, cx| update(cx))
+    pub fn update<R>(&mut self, update: impl FnOnce(&mut Window, &mut App) -> R) -> Result<R> {
+        self.app
+            .update_window(self.window, |_, window, cx| update(window, cx))
     }
 
     /// A convenience method for [`AppContext::update_window`].
     pub fn update_root<R>(
         &mut self,
-        update: impl FnOnce(AnyView, &mut WindowContext) -> R,
+        update: impl FnOnce(AnyView, &mut Window, &mut App) -> R,
     ) -> Result<R> {
         self.app.update_window(self.window, update)
     }
 
-    /// A convenience method for [`WindowContext::on_next_frame`].
-    pub fn on_next_frame(&mut self, f: impl FnOnce(&mut WindowContext) + 'static) {
-        self.window.update(self, |_, cx| cx.on_next_frame(f)).ok();
+    /// A convenience method for [`Window::on_next_frame`].
+    pub fn on_next_frame(&mut self, f: impl FnOnce(&mut Window, &mut App) + 'static) {
+        self.window
+            .update(self, |_, window, _| window.on_next_frame(f))
+            .ok();
     }
 
     /// A convenience method for [`AppContext::global`].
     pub fn read_global<G: Global, R>(
         &mut self,
-        read: impl FnOnce(&G, &WindowContext) -> R,
+        read: impl FnOnce(&G, &Window, &App) -> R,
     ) -> Result<R> {
-        self.window.update(self, |_, cx| read(cx.global(), cx))
+        self.window
+            .update(self, |_, window, cx| read(cx.global(), window, cx))
     }
 
     /// A convenience method for [`AppContext::update_global`].
     /// for updating the global state of the specified type.
     pub fn update_global<G, R>(
         &mut self,
-        update: impl FnOnce(&mut G, &mut WindowContext) -> R,
+        update: impl FnOnce(&mut G, &mut Window, &mut App) -> R,
     ) -> Result<R>
     where
         G: Global,
     {
-        self.window.update(self, |_, cx| cx.update_global(update))
+        self.window.update(self, |_, window, cx| {
+            cx.update_global(|global, cx| update(global, window, cx))
+        })
     }
 
     /// Schedule a future to be executed on the main thread. This is used for collecting
@@ -296,50 +300,49 @@ impl AsyncWindowContext {
         answers: &[&str],
     ) -> oneshot::Receiver<usize> {
         self.window
-            .update(self, |_, cx| cx.prompt(level, message, detail, answers))
+            .update(self, |_, window, cx| {
+                window.prompt(level, message, detail, answers, cx)
+            })
             .unwrap_or_else(|_| oneshot::channel().1)
     }
 }
 
-impl Context for AsyncWindowContext {
+impl AppContext for AsyncWindowContext {
     type Result<T> = Result<T>;
 
-    fn new_model<T>(
-        &mut self,
-        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
-    ) -> Result<Model<T>>
+    fn new<T>(&mut self, build_model: impl FnOnce(&mut Context<'_, T>) -> T) -> Result<Entity<T>>
     where
         T: 'static,
     {
-        self.window.update(self, |_, cx| cx.new_model(build_model))
+        self.window.update(self, |_, _, cx| cx.new(build_model))
     }
 
     fn reserve_model<T: 'static>(&mut self) -> Result<Reservation<T>> {
-        self.window.update(self, |_, cx| cx.reserve_model())
+        self.window.update(self, |_, _, cx| cx.reserve_model())
     }
 
     fn insert_model<T: 'static>(
         &mut self,
         reservation: Reservation<T>,
-        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
-    ) -> Self::Result<Model<T>> {
+        build_model: impl FnOnce(&mut Context<'_, T>) -> T,
+    ) -> Self::Result<Entity<T>> {
         self.window
-            .update(self, |_, cx| cx.insert_model(reservation, build_model))
+            .update(self, |_, _, cx| cx.insert_model(reservation, build_model))
     }
 
     fn update_model<T: 'static, R>(
         &mut self,
-        handle: &Model<T>,
-        update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
+        handle: &Entity<T>,
+        update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
     ) -> Result<R> {
         self.window
-            .update(self, |_, cx| cx.update_model(handle, update))
+            .update(self, |_, _, cx| cx.update_model(handle, update))
     }
 
     fn read_model<T, R>(
         &self,
-        handle: &Model<T>,
-        read: impl FnOnce(&T, &AppContext) -> R,
+        handle: &Entity<T>,
+        read: impl FnOnce(&T, &App) -> R,
     ) -> Self::Result<R>
     where
         T: 'static,
@@ -349,7 +352,7 @@ impl Context for AsyncWindowContext {
 
     fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
     where
-        F: FnOnce(AnyView, &mut WindowContext) -> T,
+        F: FnOnce(AnyView, &mut Window, &mut App) -> T,
     {
         self.app.update_window(window, update)
     }
@@ -357,7 +360,7 @@ impl Context for AsyncWindowContext {
     fn read_window<T, R>(
         &self,
         window: &WindowHandle<T>,
-        read: impl FnOnce(View<T>, &AppContext) -> R,
+        read: impl FnOnce(Entity<T>, &App) -> R,
     ) -> Result<R>
     where
         T: 'static,
@@ -367,51 +370,46 @@ impl Context for AsyncWindowContext {
 }
 
 impl VisualContext for AsyncWindowContext {
-    fn new_view<V>(
+    fn window_handle(&self) -> AnyWindowHandle {
+        self.window
+    }
+
+    fn new_window_model<T: 'static>(
         &mut self,
-        build_view_state: impl FnOnce(&mut ViewContext<V>) -> V,
-    ) -> Self::Result<View<V>>
-    where
-        V: 'static + Render,
-    {
+        build_model: impl FnOnce(&mut Window, &mut Context<T>) -> T,
+    ) -> Self::Result<Entity<T>> {
         self.window
-            .update(self, |_, cx| cx.new_view(build_view_state))
+            .update(self, |_, window, cx| cx.new(|cx| build_model(window, cx)))
     }
 
-    fn update_view<V: 'static, R>(
+    fn update_window_model<T: 'static, R>(
         &mut self,
-        view: &View<V>,
-        update: impl FnOnce(&mut V, &mut ViewContext<V>) -> R,
+        view: &Entity<T>,
+        update: impl FnOnce(&mut T, &mut Window, &mut Context<T>) -> R,
     ) -> Self::Result<R> {
-        self.window
-            .update(self, |_, cx| cx.update_view(view, update))
+        self.window.update(self, |_, window, cx| {
+            view.update(cx, |model, cx| update(model, window, cx))
+        })
     }
 
     fn replace_root_view<V>(
         &mut self,
-        build_view: impl FnOnce(&mut ViewContext<V>) -> V,
-    ) -> Self::Result<View<V>>
+        build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V,
+    ) -> Self::Result<Entity<V>>
     where
         V: 'static + Render,
     {
-        self.window
-            .update(self, |_, cx| cx.replace_root_view(build_view))
-    }
-
-    fn focus_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
-    where
-        V: FocusableView,
-    {
-        self.window.update(self, |_, cx| {
-            view.read(cx).focus_handle(cx).clone().focus(cx);
+        self.window.update(self, |_, window, cx| {
+            window.replace_root_model(cx, build_view)
         })
     }
 
-    fn dismiss_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
+    fn focus<V>(&mut self, view: &Entity<V>) -> Self::Result<()>
     where
-        V: crate::ManagedView,
+        V: Focusable,
     {
-        self.window
-            .update(self, |_, cx| view.update(cx, |_, cx| cx.emit(DismissEvent)))
+        self.window.update(self, |_, window, cx| {
+            view.read(cx).focus_handle(cx).clone().focus(window);
+        })
     }
 }

crates/gpui/src/app/entity_map.rs 🔗

@@ -1,10 +1,12 @@
-use crate::{seal::Sealed, AppContext, Context, Entity, ModelContext};
+use crate::{seal::Sealed, App, AppContext, VisualContext, Window};
 use anyhow::{anyhow, Result};
+use collections::FxHashSet;
 use derive_more::{Deref, DerefMut};
 use parking_lot::{RwLock, RwLockUpgradableReadGuard};
 use slotmap::{KeyData, SecondaryMap, SlotMap};
 use std::{
     any::{type_name, Any, TypeId},
+    cell::RefCell,
     fmt::{self, Display},
     hash::{Hash, Hasher},
     marker::PhantomData,
@@ -20,6 +22,8 @@ use std::{
 #[cfg(any(test, feature = "test-support"))]
 use collections::HashMap;
 
+use super::Context;
+
 slotmap::new_key_type! {
     /// A unique identifier for a model or view across the application.
     pub struct EntityId;
@@ -51,6 +55,7 @@ impl Display for EntityId {
 
 pub(crate) struct EntityMap {
     entities: SecondaryMap<EntityId, Box<dyn Any>>,
+    pub accessed_entities: RefCell<FxHashSet<EntityId>>,
     ref_counts: Arc<RwLock<EntityRefCounts>>,
 }
 
@@ -65,6 +70,7 @@ impl EntityMap {
     pub fn new() -> Self {
         Self {
             entities: SecondaryMap::new(),
+            accessed_entities: RefCell::new(FxHashSet::default()),
             ref_counts: Arc::new(RwLock::new(EntityRefCounts {
                 counts: SlotMap::with_key(),
                 dropped_entity_ids: Vec::new(),
@@ -80,14 +86,17 @@ impl EntityMap {
     /// Reserve a slot for an entity, which you can subsequently use with `insert`.
     pub fn reserve<T: 'static>(&self) -> Slot<T> {
         let id = self.ref_counts.write().counts.insert(1.into());
-        Slot(Model::new(id, Arc::downgrade(&self.ref_counts)))
+        Slot(Entity::new(id, Arc::downgrade(&self.ref_counts)))
     }
 
     /// Insert an entity into a slot obtained by calling `reserve`.
-    pub fn insert<T>(&mut self, slot: Slot<T>, entity: T) -> Model<T>
+    pub fn insert<T>(&mut self, slot: Slot<T>, entity: T) -> Entity<T>
     where
         T: 'static,
     {
+        let mut accessed_entities = self.accessed_entities.borrow_mut();
+        accessed_entities.insert(slot.entity_id);
+
         let model = slot.0;
         self.entities.insert(model.entity_id, Box::new(entity));
         model
@@ -95,16 +104,19 @@ impl EntityMap {
 
     /// Move an entity to the stack.
     #[track_caller]
-    pub fn lease<'a, T>(&mut self, model: &'a Model<T>) -> Lease<'a, T> {
-        self.assert_valid_context(model);
+    pub fn lease<'a, T>(&mut self, pointer: &'a Entity<T>) -> Lease<'a, T> {
+        self.assert_valid_context(pointer);
+        let mut accessed_entities = self.accessed_entities.borrow_mut();
+        accessed_entities.insert(pointer.entity_id);
+
         let entity = Some(
             self.entities
-                .remove(model.entity_id)
+                .remove(pointer.entity_id)
                 .unwrap_or_else(|| double_lease_panic::<T>("update")),
         );
         Lease {
-            model,
             entity,
+            pointer,
             entity_type: PhantomData,
         }
     }
@@ -112,27 +124,41 @@ impl EntityMap {
     /// Returns an entity after moving it to the stack.
     pub fn end_lease<T>(&mut self, mut lease: Lease<T>) {
         self.entities
-            .insert(lease.model.entity_id, lease.entity.take().unwrap());
+            .insert(lease.pointer.entity_id, lease.entity.take().unwrap());
     }
 
-    pub fn read<T: 'static>(&self, model: &Model<T>) -> &T {
+    pub fn read<T: 'static>(&self, model: &Entity<T>) -> &T {
         self.assert_valid_context(model);
+        let mut accessed_entities = self.accessed_entities.borrow_mut();
+        accessed_entities.insert(model.entity_id);
+
         self.entities
             .get(model.entity_id)
             .and_then(|entity| entity.downcast_ref())
             .unwrap_or_else(|| double_lease_panic::<T>("read"))
     }
 
-    fn assert_valid_context(&self, model: &AnyModel) {
+    fn assert_valid_context(&self, model: &AnyEntity) {
         debug_assert!(
             Weak::ptr_eq(&model.entity_map, &Arc::downgrade(&self.ref_counts)),
             "used a model with the wrong context"
         );
     }
 
+    pub fn extend_accessed(&mut self, entities: &FxHashSet<EntityId>) {
+        self.accessed_entities
+            .borrow_mut()
+            .extend(entities.iter().copied());
+    }
+
+    pub fn clear_accessed(&mut self) {
+        self.accessed_entities.borrow_mut().clear();
+    }
+
     pub fn take_dropped(&mut self) -> Vec<(EntityId, Box<dyn Any>)> {
         let mut ref_counts = self.ref_counts.write();
         let dropped_entity_ids = mem::take(&mut ref_counts.dropped_entity_ids);
+        let mut accessed_entities = self.accessed_entities.borrow_mut();
 
         dropped_entity_ids
             .into_iter()
@@ -143,6 +169,7 @@ impl EntityMap {
                     0,
                     "dropped an entity that was referenced"
                 );
+                accessed_entities.remove(&entity_id);
                 // If the EntityId was allocated with `Context::reserve`,
                 // the entity may not have been inserted.
                 Some((entity_id, self.entities.remove(entity_id)?))
@@ -160,7 +187,7 @@ fn double_lease_panic<T>(operation: &str) -> ! {
 
 pub(crate) struct Lease<'a, T> {
     entity: Option<Box<dyn Any>>,
-    pub model: &'a Model<T>,
+    pub pointer: &'a Entity<T>,
     entity_type: PhantomData<T>,
 }
 
@@ -187,10 +214,10 @@ impl<'a, T> Drop for Lease<'a, T> {
 }
 
 #[derive(Deref, DerefMut)]
-pub(crate) struct Slot<T>(Model<T>);
+pub(crate) struct Slot<T>(Entity<T>);
 
 /// A dynamically typed reference to a model, which can be downcast into a `Model<T>`.
-pub struct AnyModel {
+pub struct AnyEntity {
     pub(crate) entity_id: EntityId,
     pub(crate) entity_type: TypeId,
     entity_map: Weak<RwLock<EntityRefCounts>>,
@@ -198,7 +225,7 @@ pub struct AnyModel {
     handle_id: HandleId,
 }
 
-impl AnyModel {
+impl AnyEntity {
     fn new(id: EntityId, entity_type: TypeId, entity_map: Weak<RwLock<EntityRefCounts>>) -> Self {
         Self {
             entity_id: id,
@@ -225,8 +252,8 @@ impl AnyModel {
     }
 
     /// Converts this model handle into a weak variant, which does not prevent it from being released.
-    pub fn downgrade(&self) -> AnyWeakModel {
-        AnyWeakModel {
+    pub fn downgrade(&self) -> AnyWeakEntity {
+        AnyWeakEntity {
             entity_id: self.entity_id,
             entity_type: self.entity_type,
             entity_ref_counts: self.entity_map.clone(),
@@ -235,10 +262,10 @@ impl AnyModel {
 
     /// Converts this model handle into a strongly-typed model handle of the given type.
     /// If this model handle is not of the specified type, returns itself as an error variant.
-    pub fn downcast<T: 'static>(self) -> Result<Model<T>, AnyModel> {
+    pub fn downcast<T: 'static>(self) -> Result<Entity<T>, AnyEntity> {
         if TypeId::of::<T>() == self.entity_type {
-            Ok(Model {
-                any_model: self,
+            Ok(Entity {
+                any_entity: self,
                 entity_type: PhantomData,
             })
         } else {
@@ -247,7 +274,7 @@ impl AnyModel {
     }
 }
 
-impl Clone for AnyModel {
+impl Clone for AnyEntity {
     fn clone(&self) -> Self {
         if let Some(entity_map) = self.entity_map.upgrade() {
             let entity_map = entity_map.read();
@@ -275,7 +302,7 @@ impl Clone for AnyModel {
     }
 }
 
-impl Drop for AnyModel {
+impl Drop for AnyEntity {
     fn drop(&mut self) {
         if let Some(entity_map) = self.entity_map.upgrade() {
             let entity_map = entity_map.upgradable_read();
@@ -302,27 +329,27 @@ impl Drop for AnyModel {
     }
 }
 
-impl<T> From<Model<T>> for AnyModel {
-    fn from(model: Model<T>) -> Self {
-        model.any_model
+impl<T> From<Entity<T>> for AnyEntity {
+    fn from(model: Entity<T>) -> Self {
+        model.any_entity
     }
 }
 
-impl Hash for AnyModel {
+impl Hash for AnyEntity {
     fn hash<H: Hasher>(&self, state: &mut H) {
         self.entity_id.hash(state);
     }
 }
 
-impl PartialEq for AnyModel {
+impl PartialEq for AnyEntity {
     fn eq(&self, other: &Self) -> bool {
         self.entity_id == other.entity_id
     }
 }
 
-impl Eq for AnyModel {}
+impl Eq for AnyEntity {}
 
-impl std::fmt::Debug for AnyModel {
+impl std::fmt::Debug for AnyEntity {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_struct("AnyModel")
             .field("entity_id", &self.entity_id.as_u64())
@@ -333,76 +360,67 @@ impl std::fmt::Debug for AnyModel {
 /// A strong, well typed reference to a struct which is managed
 /// by GPUI
 #[derive(Deref, DerefMut)]
-pub struct Model<T> {
+pub struct Entity<T> {
     #[deref]
     #[deref_mut]
-    pub(crate) any_model: AnyModel,
+    pub(crate) any_entity: AnyEntity,
     pub(crate) entity_type: PhantomData<T>,
 }
 
-unsafe impl<T> Send for Model<T> {}
-unsafe impl<T> Sync for Model<T> {}
-impl<T> Sealed for Model<T> {}
+unsafe impl<T> Send for Entity<T> {}
+unsafe impl<T> Sync for Entity<T> {}
+impl<T> Sealed for Entity<T> {}
 
-impl<T: 'static> Entity<T> for Model<T> {
-    type Weak = WeakModel<T>;
+impl<T: 'static> Entity<T> {
+    fn new(id: EntityId, entity_map: Weak<RwLock<EntityRefCounts>>) -> Self
+    where
+        T: 'static,
+    {
+        Self {
+            any_entity: AnyEntity::new(id, TypeId::of::<T>(), entity_map),
+            entity_type: PhantomData,
+        }
+    }
 
-    fn entity_id(&self) -> EntityId {
-        self.any_model.entity_id
+    /// Get the entity ID associated with this entity
+    pub fn entity_id(&self) -> EntityId {
+        self.any_entity.entity_id
     }
 
-    fn downgrade(&self) -> Self::Weak {
-        WeakModel {
-            any_model: self.any_model.downgrade(),
+    /// Downgrade this entity pointer to a non-retaining weak pointer
+    pub fn downgrade(&self) -> WeakEntity<T> {
+        WeakEntity {
+            any_entity: self.any_entity.downgrade(),
             entity_type: self.entity_type,
         }
     }
 
-    fn upgrade_from(weak: &Self::Weak) -> Option<Self>
+    /// Upgrade the given weak pointer to a retaining pointer, if it still exists
+    pub fn upgrade_from(weak: &WeakEntity<T>) -> Option<Self>
     where
         Self: Sized,
     {
-        Some(Model {
-            any_model: weak.any_model.upgrade()?,
+        Some(Entity {
+            any_entity: weak.any_entity.upgrade()?,
             entity_type: weak.entity_type,
         })
     }
-}
-
-impl<T: 'static> Model<T> {
-    fn new(id: EntityId, entity_map: Weak<RwLock<EntityRefCounts>>) -> Self
-    where
-        T: 'static,
-    {
-        Self {
-            any_model: AnyModel::new(id, TypeId::of::<T>(), entity_map),
-            entity_type: PhantomData,
-        }
-    }
-
-    /// Downgrade the this to a weak model reference
-    pub fn downgrade(&self) -> WeakModel<T> {
-        // Delegate to the trait implementation to keep behavior in one place.
-        // This method was included to improve method resolution in the presence of
-        // the Model's deref
-        Entity::downgrade(self)
-    }
 
     /// Convert this into a dynamically typed model.
-    pub fn into_any(self) -> AnyModel {
-        self.any_model
+    pub fn into_any(self) -> AnyEntity {
+        self.any_entity
     }
 
     /// Grab a reference to this entity from the context.
-    pub fn read<'a>(&self, cx: &'a AppContext) -> &'a T {
+    pub fn read<'a>(&self, cx: &'a App) -> &'a T {
         cx.entities.read(self)
     }
 
     /// Read the entity referenced by this model with the given function.
-    pub fn read_with<R, C: Context>(
+    pub fn read_with<R, C: AppContext>(
         &self,
         cx: &C,
-        f: impl FnOnce(&T, &AppContext) -> R,
+        f: impl FnOnce(&T, &App) -> R,
     ) -> C::Result<R> {
         cx.read_model(self, f)
     }
@@ -415,62 +433,76 @@ impl<T: 'static> Model<T> {
     pub fn update<C, R>(
         &self,
         cx: &mut C,
-        update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
+        update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
     ) -> C::Result<R>
     where
-        C: Context,
+        C: AppContext,
     {
         cx.update_model(self, update)
     }
+
+    /// Updates the entity referenced by this model with the given function if
+    /// the referenced entity still exists, within a visual context that has a window.
+    /// Returns an error if the entity has been released.
+    pub fn update_in<C, R>(
+        &self,
+        cx: &mut C,
+        update: impl FnOnce(&mut T, &mut Window, &mut Context<'_, T>) -> R,
+    ) -> C::Result<R>
+    where
+        C: VisualContext,
+    {
+        cx.update_window_model(self, update)
+    }
 }
 
-impl<T> Clone for Model<T> {
+impl<T> Clone for Entity<T> {
     fn clone(&self) -> Self {
         Self {
-            any_model: self.any_model.clone(),
+            any_entity: self.any_entity.clone(),
             entity_type: self.entity_type,
         }
     }
 }
 
-impl<T> std::fmt::Debug for Model<T> {
+impl<T> std::fmt::Debug for Entity<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_struct("Model")
-            .field("entity_id", &self.any_model.entity_id)
+            .field("entity_id", &self.any_entity.entity_id)
             .field("entity_type", &type_name::<T>())
             .finish()
     }
 }
 
-impl<T> Hash for Model<T> {
+impl<T> Hash for Entity<T> {
     fn hash<H: Hasher>(&self, state: &mut H) {
-        self.any_model.hash(state);
+        self.any_entity.hash(state);
     }
 }
 
-impl<T> PartialEq for Model<T> {
+impl<T> PartialEq for Entity<T> {
     fn eq(&self, other: &Self) -> bool {
-        self.any_model == other.any_model
+        self.any_entity == other.any_entity
     }
 }
 
-impl<T> Eq for Model<T> {}
+impl<T> Eq for Entity<T> {}
 
-impl<T> PartialEq<WeakModel<T>> for Model<T> {
-    fn eq(&self, other: &WeakModel<T>) -> bool {
-        self.any_model.entity_id() == other.entity_id()
+impl<T> PartialEq<WeakEntity<T>> for Entity<T> {
+    fn eq(&self, other: &WeakEntity<T>) -> bool {
+        self.any_entity.entity_id() == other.entity_id()
     }
 }
 
 /// A type erased, weak reference to a model.
 #[derive(Clone)]
-pub struct AnyWeakModel {
+pub struct AnyWeakEntity {
     pub(crate) entity_id: EntityId,
     entity_type: TypeId,
     entity_ref_counts: Weak<RwLock<EntityRefCounts>>,
 }
 
-impl AnyWeakModel {
+impl AnyWeakEntity {
     /// Get the entity ID associated with this weak reference.
     pub fn entity_id(&self) -> EntityId {
         self.entity_id
@@ -487,7 +519,7 @@ impl AnyWeakModel {
     }
 
     /// Upgrade this weak model reference to a strong reference.
-    pub fn upgrade(&self) -> Option<AnyModel> {
+    pub fn upgrade(&self) -> Option<AnyEntity> {
         let ref_counts = &self.entity_ref_counts.upgrade()?;
         let ref_counts = ref_counts.read();
         let ref_count = ref_counts.counts.get(self.entity_id)?;
@@ -499,7 +531,7 @@ impl AnyWeakModel {
         ref_count.fetch_add(1, SeqCst);
         drop(ref_counts);
 
-        Some(AnyModel {
+        Some(AnyEntity {
             entity_id: self.entity_id,
             entity_type: self.entity_type,
             entity_map: self.entity_ref_counts.clone(),
@@ -537,7 +569,7 @@ impl AnyWeakModel {
     }
 }
 
-impl std::fmt::Debug for AnyWeakModel {
+impl std::fmt::Debug for AnyWeakEntity {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_struct(type_name::<Self>())
             .field("entity_id", &self.entity_id)
@@ -546,61 +578,61 @@ impl std::fmt::Debug for AnyWeakModel {
     }
 }
 
-impl<T> From<WeakModel<T>> for AnyWeakModel {
-    fn from(model: WeakModel<T>) -> Self {
-        model.any_model
+impl<T> From<WeakEntity<T>> for AnyWeakEntity {
+    fn from(model: WeakEntity<T>) -> Self {
+        model.any_entity
     }
 }
 
-impl Hash for AnyWeakModel {
+impl Hash for AnyWeakEntity {
     fn hash<H: Hasher>(&self, state: &mut H) {
         self.entity_id.hash(state);
     }
 }
 
-impl PartialEq for AnyWeakModel {
+impl PartialEq for AnyWeakEntity {
     fn eq(&self, other: &Self) -> bool {
         self.entity_id == other.entity_id
     }
 }
 
-impl Eq for AnyWeakModel {}
+impl Eq for AnyWeakEntity {}
 
 /// A weak reference to a model of the given type.
 #[derive(Deref, DerefMut)]
-pub struct WeakModel<T> {
+pub struct WeakEntity<T> {
     #[deref]
     #[deref_mut]
-    any_model: AnyWeakModel,
+    any_entity: AnyWeakEntity,
     entity_type: PhantomData<T>,
 }
 
-impl<T> std::fmt::Debug for WeakModel<T> {
+impl<T> std::fmt::Debug for WeakEntity<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_struct(&type_name::<Self>())
-            .field("entity_id", &self.any_model.entity_id)
+            .field("entity_id", &self.any_entity.entity_id)
             .field("entity_type", &type_name::<T>())
             .finish()
     }
 }
 
-unsafe impl<T> Send for WeakModel<T> {}
-unsafe impl<T> Sync for WeakModel<T> {}
+unsafe impl<T> Send for WeakEntity<T> {}
+unsafe impl<T> Sync for WeakEntity<T> {}
 
-impl<T> Clone for WeakModel<T> {
+impl<T> Clone for WeakEntity<T> {
     fn clone(&self) -> Self {
         Self {
-            any_model: self.any_model.clone(),
+            any_entity: self.any_entity.clone(),
             entity_type: self.entity_type,
         }
     }
 }
 
-impl<T: 'static> WeakModel<T> {
+impl<T: 'static> WeakEntity<T> {
     /// Upgrade this weak model reference into a strong model reference
-    pub fn upgrade(&self) -> Option<Model<T>> {
+    pub fn upgrade(&self) -> Option<Entity<T>> {
         // Delegate to the trait implementation to keep behavior in one place.
-        Model::upgrade_from(self)
+        Entity::upgrade_from(self)
     }
 
     /// Updates the entity referenced by this model with the given function if
@@ -609,25 +641,45 @@ impl<T: 'static> WeakModel<T> {
     pub fn update<C, R>(
         &self,
         cx: &mut C,
-        update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
+        update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
     ) -> Result<R>
     where
-        C: Context,
+        C: AppContext,
         Result<C::Result<R>>: crate::Flatten<R>,
     {
         crate::Flatten::flatten(
             self.upgrade()
-                .ok_or_else(|| anyhow!("entity release"))
+                .ok_or_else(|| anyhow!("entity released"))
                 .map(|this| cx.update_model(&this, update)),
         )
     }
 
+    /// Updates the entity referenced by this model with the given function if
+    /// the referenced entity still exists, within a visual context that has a window.
+    /// Returns an error if the entity has been released.
+    pub fn update_in<C, R>(
+        &self,
+        cx: &mut C,
+        update: impl FnOnce(&mut T, &mut Window, &mut Context<'_, T>) -> R,
+    ) -> Result<R>
+    where
+        C: VisualContext,
+        Result<C::Result<R>>: crate::Flatten<R>,
+    {
+        let window = cx.window_handle();
+        let this = self.upgrade().ok_or_else(|| anyhow!("entity released"))?;
+
+        crate::Flatten::flatten(window.update(cx, |_, window, cx| {
+            this.update(cx, |model, cx| update(model, window, cx))
+        }))
+    }
+
     /// Reads the entity referenced by this model with the given function if
     /// the referenced entity still exists. Returns an error if the entity has
     /// been released.
-    pub fn read_with<C, R>(&self, cx: &C, read: impl FnOnce(&T, &AppContext) -> R) -> Result<R>
+    pub fn read_with<C, R>(&self, cx: &C, read: impl FnOnce(&T, &App) -> R) -> Result<R>
     where
-        C: Context,
+        C: AppContext,
         Result<C::Result<R>>: crate::Flatten<R>,
     {
         crate::Flatten::flatten(
@@ -638,23 +690,23 @@ impl<T: 'static> WeakModel<T> {
     }
 }
 
-impl<T> Hash for WeakModel<T> {
+impl<T> Hash for WeakEntity<T> {
     fn hash<H: Hasher>(&self, state: &mut H) {
-        self.any_model.hash(state);
+        self.any_entity.hash(state);
     }
 }
 
-impl<T> PartialEq for WeakModel<T> {
+impl<T> PartialEq for WeakEntity<T> {
     fn eq(&self, other: &Self) -> bool {
-        self.any_model == other.any_model
+        self.any_entity == other.any_entity
     }
 }
 
-impl<T> Eq for WeakModel<T> {}
+impl<T> Eq for WeakEntity<T> {}
 
-impl<T> PartialEq<Model<T>> for WeakModel<T> {
-    fn eq(&self, other: &Model<T>) -> bool {
-        self.entity_id() == other.any_model.entity_id()
+impl<T> PartialEq<Entity<T>> for WeakEntity<T> {
+    fn eq(&self, other: &Entity<T>) -> bool {
+        self.entity_id() == other.any_entity.entity_id()
     }
 }
 

crates/gpui/src/app/model_context.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
-    AnyView, AnyWindowHandle, AppContext, AsyncAppContext, Context, Effect, Entity, EntityId,
-    EventEmitter, Model, Reservation, Subscription, Task, View, WeakModel, WindowContext,
-    WindowHandle,
+    AnyView, AnyWindowHandle, App, AppContext, AsyncAppContext, DispatchPhase, Effect, EntityId,
+    EventEmitter, FocusHandle, FocusOutEvent, Focusable, Global, KeystrokeObserver, Reservation,
+    SubscriberSet, Subscription, Task, WeakEntity, WeakFocusHandle, Window, WindowHandle,
 };
 use anyhow::Result;
 use derive_more::{Deref, DerefMut};
@@ -10,19 +10,22 @@ use std::{
     any::{Any, TypeId},
     borrow::{Borrow, BorrowMut},
     future::Future,
+    sync::Arc,
 };
 
+use super::{AsyncWindowContext, Entity, KeystrokeEvent};
+
 /// The app context, with specialized behavior for the given model.
 #[derive(Deref, DerefMut)]
-pub struct ModelContext<'a, T> {
+pub struct Context<'a, T> {
     #[deref]
     #[deref_mut]
-    app: &'a mut AppContext,
-    model_state: WeakModel<T>,
+    app: &'a mut App,
+    model_state: WeakEntity<T>,
 }
 
-impl<'a, T: 'static> ModelContext<'a, T> {
-    pub(crate) fn new(app: &'a mut AppContext, model_state: WeakModel<T>) -> Self {
+impl<'a, T: 'static> Context<'a, T> {
+    pub(crate) fn new_context(app: &'a mut App, model_state: WeakEntity<T>) -> Self {
         Self { app, model_state }
     }
 
@@ -32,28 +35,27 @@ impl<'a, T: 'static> ModelContext<'a, T> {
     }
 
     /// Returns a handle to the model belonging to this context.
-    pub fn handle(&self) -> Model<T> {
+    pub fn model(&self) -> Entity<T> {
         self.weak_model()
             .upgrade()
             .expect("The entity must be alive if we have a model context")
     }
 
     /// Returns a weak handle to the model belonging to this context.
-    pub fn weak_model(&self) -> WeakModel<T> {
+    pub fn weak_model(&self) -> WeakEntity<T> {
         self.model_state.clone()
     }
 
     /// Arranges for the given function to be called whenever [`ModelContext::notify`] or
     /// [`ViewContext::notify`](crate::ViewContext::notify) is called with the given model or view.
-    pub fn observe<W, E>(
+    pub fn observe<W>(
         &mut self,
-        entity: &E,
-        mut on_notify: impl FnMut(&mut T, E, &mut ModelContext<'_, T>) + 'static,
+        entity: &Entity<W>,
+        mut on_notify: impl FnMut(&mut T, Entity<W>, &mut Context<'_, T>) + 'static,
     ) -> Subscription
     where
         T: 'static,
         W: 'static,
-        E: Entity<W>,
     {
         let this = self.weak_model();
         self.app.observe_internal(entity, move |e, cx| {
@@ -67,15 +69,14 @@ impl<'a, T: 'static> ModelContext<'a, T> {
     }
 
     /// Subscribe to an event type from another model or view
-    pub fn subscribe<T2, E, Evt>(
+    pub fn subscribe<T2, Evt>(
         &mut self,
-        entity: &E,
-        mut on_event: impl FnMut(&mut T, E, &Evt, &mut ModelContext<'_, T>) + 'static,
+        entity: &Entity<T2>,
+        mut on_event: impl FnMut(&mut T, Entity<T2>, &Evt, &mut Context<'_, T>) + 'static,
     ) -> Subscription
     where
         T: 'static,
         T2: 'static + EventEmitter<Evt>,
-        E: Entity<T2>,
         Evt: 'static,
     {
         let this = self.weak_model();
@@ -90,10 +91,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
     }
 
     /// Register a callback to be invoked when GPUI releases this model.
-    pub fn on_release(
-        &self,
-        on_release: impl FnOnce(&mut T, &mut AppContext) + 'static,
-    ) -> Subscription
+    pub fn on_release(&self, on_release: impl FnOnce(&mut T, &mut App) + 'static) -> Subscription
     where
         T: 'static,
     {
@@ -109,15 +107,14 @@ impl<'a, T: 'static> ModelContext<'a, T> {
     }
 
     /// Register a callback to be run on the release of another model or view
-    pub fn observe_release<T2, E>(
+    pub fn observe_release<T2>(
         &self,
-        entity: &E,
-        on_release: impl FnOnce(&mut T, &mut T2, &mut ModelContext<'_, T>) + 'static,
+        entity: &Entity<T2>,
+        on_release: impl FnOnce(&mut T, &mut T2, &mut Context<'_, T>) + 'static,
     ) -> Subscription
     where
         T: Any,
         T2: 'static,
-        E: Entity<T2>,
     {
         let entity_id = entity.entity_id();
         let this = self.weak_model();
@@ -137,7 +134,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
     /// Register a callback to for updates to the given global
     pub fn observe_global<G: 'static>(
         &mut self,
-        mut f: impl FnMut(&mut T, &mut ModelContext<'_, T>) + 'static,
+        mut f: impl FnMut(&mut T, &mut Context<'_, T>) + 'static,
     ) -> Subscription
     where
         T: 'static,
@@ -155,7 +152,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
     /// The future returned from this callback will be polled for up to [crate::SHUTDOWN_TIMEOUT] until the app fully quits.
     pub fn on_app_quit<Fut>(
         &self,
-        mut on_quit: impl FnMut(&mut T, &mut ModelContext<T>) -> Fut + 'static,
+        mut on_quit: impl FnMut(&mut T, &mut Context<T>) -> Fut + 'static,
     ) -> Subscription
     where
         Fut: 'static + Future<Output = ()>,
@@ -180,21 +177,13 @@ impl<'a, T: 'static> ModelContext<'a, T> {
 
     /// Tell GPUI that this model has changed and observers of it should be notified.
     pub fn notify(&mut self) {
-        if self
-            .app
-            .pending_notifications
-            .insert(self.model_state.entity_id)
-        {
-            self.app.pending_effects.push_back(Effect::Notify {
-                emitter: self.model_state.entity_id,
-            });
-        }
+        self.app.notify(self.model_state.entity_id);
     }
 
     /// Spawn the future returned by the given function.
     /// The function is provided a weak handle to the model owned by this context and a context that can be held across await points.
     /// The returned task must be held or detached.
-    pub fn spawn<Fut, R>(&self, f: impl FnOnce(WeakModel<T>, AsyncAppContext) -> Fut) -> Task<R>
+    pub fn spawn<Fut, R>(&self, f: impl FnOnce(WeakEntity<T>, AsyncAppContext) -> Fut) -> Task<R>
     where
         T: 'static,
         Fut: Future<Output = R> + 'static,
@@ -203,9 +192,476 @@ impl<'a, T: 'static> ModelContext<'a, T> {
         let this = self.weak_model();
         self.app.spawn(|cx| f(this, cx))
     }
+
+    /// Convenience method for accessing view state in an event callback.
+    ///
+    /// Many GPUI callbacks take the form of `Fn(&E, &mut Window, &mut AppContext)`,
+    /// but it's often useful to be able to access view state in these
+    /// callbacks. This method provides a convenient way to do so.
+    pub fn listener<E: ?Sized>(
+        &self,
+        f: impl Fn(&mut T, &E, &mut Window, &mut Context<T>) + 'static,
+    ) -> impl Fn(&E, &mut Window, &mut App) + 'static {
+        let view = self.model().downgrade();
+        move |e: &E, window: &mut Window, cx: &mut App| {
+            view.update(cx, |view, cx| f(view, e, window, cx)).ok();
+        }
+    }
+
+    /// Focus the given view in the given window. View type is required to implement Focusable.
+    pub fn focus_view<W: Focusable>(&mut self, view: &Entity<W>, window: &mut Window) {
+        window.focus(&view.focus_handle(self));
+    }
+
+    /// Sets a given callback to be run on the next frame.
+    pub fn on_next_frame(
+        &self,
+        window: &mut Window,
+        f: impl FnOnce(&mut T, &mut Window, &mut Context<T>) + 'static,
+    ) where
+        T: 'static,
+    {
+        let view = self.model();
+        window.on_next_frame(move |window, cx| view.update(cx, |view, cx| f(view, window, cx)));
+    }
+
+    /// Schedules the given function to be run at the end of the current effect cycle, allowing entities
+    /// that are currently on the stack to be returned to the app.
+    pub fn defer_in(
+        &mut self,
+        window: &Window,
+        f: impl FnOnce(&mut T, &mut Window, &mut Context<T>) + 'static,
+    ) {
+        let view = self.model();
+        window.defer(self, move |window, cx| {
+            view.update(cx, |view, cx| f(view, window, cx))
+        });
+    }
+
+    /// Observe another model or view for changes to its state, as tracked by [`ModelContext::notify`].
+    pub fn observe_in<V2>(
+        &mut self,
+        observed: &Entity<V2>,
+        window: &mut Window,
+        mut on_notify: impl FnMut(&mut T, Entity<V2>, &mut Window, &mut Context<'_, T>) + 'static,
+    ) -> Subscription
+    where
+        V2: 'static,
+        T: 'static,
+    {
+        let observed_id = observed.entity_id();
+        let observed = observed.downgrade();
+        let window_handle = window.handle;
+        let observer = self.weak_model();
+        self.new_observer(
+            observed_id,
+            Box::new(move |cx| {
+                window_handle
+                    .update(cx, |_, window, cx| {
+                        if let Some((observer, observed)) =
+                            observer.upgrade().zip(observed.upgrade())
+                        {
+                            observer.update(cx, |observer, cx| {
+                                on_notify(observer, observed, window, cx);
+                            });
+                            true
+                        } else {
+                            false
+                        }
+                    })
+                    .unwrap_or(false)
+            }),
+        )
+    }
+
+    /// Subscribe to events emitted by another model or view.
+    /// The entity to which you're subscribing must implement the [`EventEmitter`] trait.
+    /// The callback will be invoked with a reference to the current view, a handle to the emitting entity (either a [`View`] or [`Model`]), the event, and a view context for the current view.
+    pub fn subscribe_in<Emitter, Evt>(
+        &mut self,
+        emitter: &Entity<Emitter>,
+        window: &Window,
+        mut on_event: impl FnMut(&mut T, &Entity<Emitter>, &Evt, &mut Window, &mut Context<'_, T>)
+            + 'static,
+    ) -> Subscription
+    where
+        Emitter: EventEmitter<Evt>,
+        Evt: 'static,
+    {
+        let emitter = emitter.downgrade();
+        let window_handle = window.handle;
+        let subscriber = self.weak_model();
+        self.new_subscription(
+            emitter.entity_id(),
+            (
+                TypeId::of::<Evt>(),
+                Box::new(move |event, cx| {
+                    window_handle
+                        .update(cx, |_, window, cx| {
+                            if let Some((subscriber, emitter)) =
+                                subscriber.upgrade().zip(emitter.upgrade())
+                            {
+                                let event = event.downcast_ref().expect("invalid event type");
+                                subscriber.update(cx, |subscriber, cx| {
+                                    on_event(subscriber, &emitter, event, window, cx);
+                                });
+                                true
+                            } else {
+                                false
+                            }
+                        })
+                        .unwrap_or(false)
+                }),
+            ),
+        )
+    }
+
+    /// Register a callback to be invoked when the view is released.
+    ///
+    /// The callback receives a handle to the view's window. This handle may be
+    /// invalid, if the window was closed before the view was released.
+    pub fn on_release_in(
+        &mut self,
+        window: &Window,
+        on_release: impl FnOnce(&mut T, AnyWindowHandle, &mut App) + 'static,
+    ) -> Subscription {
+        let window_handle = window.handle;
+        let (subscription, activate) = self.release_listeners.insert(
+            self.entity_id(),
+            Box::new(move |this, cx| {
+                let this = this.downcast_mut().expect("invalid entity type");
+                on_release(this, window_handle, cx)
+            }),
+        );
+        activate();
+        subscription
+    }
+
+    /// Register a callback to be invoked when the given Model or View is released.
+    pub fn observe_release_in<V2>(
+        &self,
+        observed: &Entity<V2>,
+        window: &Window,
+        mut on_release: impl FnMut(&mut T, &mut V2, &mut Window, &mut Context<'_, T>) + 'static,
+    ) -> Subscription
+    where
+        T: 'static,
+        V2: 'static,
+    {
+        let observer = self.weak_model();
+        let window_handle = window.handle;
+        let (subscription, activate) = self.release_listeners.insert(
+            observed.entity_id(),
+            Box::new(move |observed, cx| {
+                let observed = observed
+                    .downcast_mut()
+                    .expect("invalid observed entity type");
+                let _ = window_handle.update(cx, |_, window, cx| {
+                    observer.update(cx, |this, cx| on_release(this, observed, window, cx))
+                });
+            }),
+        );
+        activate();
+        subscription
+    }
+
+    /// Register a callback to be invoked when the window is resized.
+    pub fn observe_window_bounds(
+        &self,
+        window: &mut Window,
+        mut callback: impl FnMut(&mut T, &mut Window, &mut Context<T>) + 'static,
+    ) -> Subscription {
+        let view = self.weak_model();
+        let (subscription, activate) = window.bounds_observers.insert(
+            (),
+            Box::new(move |window, cx| {
+                view.update(cx, |view, cx| callback(view, window, cx))
+                    .is_ok()
+            }),
+        );
+        activate();
+        subscription
+    }
+
+    /// Register a callback to be invoked when the window is activated or deactivated.
+    pub fn observe_window_activation(
+        &self,
+        window: &mut Window,
+        mut callback: impl FnMut(&mut T, &mut Window, &mut Context<T>) + 'static,
+    ) -> Subscription {
+        let view = self.weak_model();
+        let (subscription, activate) = window.activation_observers.insert(
+            (),
+            Box::new(move |window, cx| {
+                view.update(cx, |view, cx| callback(view, window, cx))
+                    .is_ok()
+            }),
+        );
+        activate();
+        subscription
+    }
+
+    /// Registers a callback to be invoked when the window appearance changes.
+    pub fn observe_window_appearance(
+        &self,
+        window: &mut Window,
+        mut callback: impl FnMut(&mut T, &mut Window, &mut Context<T>) + 'static,
+    ) -> Subscription {
+        let view = self.weak_model();
+        let (subscription, activate) = window.appearance_observers.insert(
+            (),
+            Box::new(move |window, cx| {
+                view.update(cx, |view, cx| callback(view, window, cx))
+                    .is_ok()
+            }),
+        );
+        activate();
+        subscription
+    }
+
+    /// Register a callback to be invoked when a keystroke is received by the application
+    /// in any window. Note that this fires after all other action and event mechanisms have resolved
+    /// and that this API will not be invoked if the event's propagation is stopped.
+    pub fn observe_keystrokes(
+        &mut self,
+        mut f: impl FnMut(&mut T, &KeystrokeEvent, &mut Window, &mut Context<T>) + 'static,
+    ) -> Subscription {
+        fn inner(
+            keystroke_observers: &SubscriberSet<(), KeystrokeObserver>,
+            handler: KeystrokeObserver,
+        ) -> Subscription {
+            let (subscription, activate) = keystroke_observers.insert((), handler);
+            activate();
+            subscription
+        }
+
+        let view = self.weak_model();
+        inner(
+            &mut self.keystroke_observers,
+            Box::new(move |event, window, cx| {
+                if let Some(view) = view.upgrade() {
+                    view.update(cx, |view, cx| f(view, event, window, cx));
+                    true
+                } else {
+                    false
+                }
+            }),
+        )
+    }
+
+    /// Register a callback to be invoked when the window's pending input changes.
+    pub fn observe_pending_input(
+        &self,
+        window: &mut Window,
+        mut callback: impl FnMut(&mut T, &mut Window, &mut Context<T>) + 'static,
+    ) -> Subscription {
+        let view = self.weak_model();
+        let (subscription, activate) = window.pending_input_observers.insert(
+            (),
+            Box::new(move |window, cx| {
+                view.update(cx, |view, cx| callback(view, window, cx))
+                    .is_ok()
+            }),
+        );
+        activate();
+        subscription
+    }
+
+    /// Register a listener to be called when the given focus handle receives focus.
+    /// Returns a subscription and persists until the subscription is dropped.
+    pub fn on_focus(
+        &mut self,
+        handle: &FocusHandle,
+        window: &mut Window,
+        mut listener: impl FnMut(&mut T, &mut Window, &mut Context<T>) + 'static,
+    ) -> Subscription {
+        let view = self.weak_model();
+        let focus_id = handle.id;
+        let (subscription, activate) =
+            window.new_focus_listener(Box::new(move |event, window, cx| {
+                view.update(cx, |view, cx| {
+                    if event.previous_focus_path.last() != Some(&focus_id)
+                        && event.current_focus_path.last() == Some(&focus_id)
+                    {
+                        listener(view, window, cx)
+                    }
+                })
+                .is_ok()
+            }));
+        self.defer(|_| activate());
+        subscription
+    }
+
+    /// Register a listener to be called when the given focus handle or one of its descendants receives focus.
+    /// This does not fire if the given focus handle - or one of its descendants - was previously focused.
+    /// Returns a subscription and persists until the subscription is dropped.
+    pub fn on_focus_in(
+        &mut self,
+        handle: &FocusHandle,
+        window: &mut Window,
+        mut listener: impl FnMut(&mut T, &mut Window, &mut Context<T>) + 'static,
+    ) -> Subscription {
+        let view = self.weak_model();
+        let focus_id = handle.id;
+        let (subscription, activate) =
+            window.new_focus_listener(Box::new(move |event, window, cx| {
+                view.update(cx, |view, cx| {
+                    if event.is_focus_in(focus_id) {
+                        listener(view, window, cx)
+                    }
+                })
+                .is_ok()
+            }));
+        self.defer(|_| activate());
+        subscription
+    }
+
+    /// Register a listener to be called when the given focus handle loses focus.
+    /// Returns a subscription and persists until the subscription is dropped.
+    pub fn on_blur(
+        &mut self,
+        handle: &FocusHandle,
+        window: &mut Window,
+        mut listener: impl FnMut(&mut T, &mut Window, &mut Context<T>) + 'static,
+    ) -> Subscription {
+        let view = self.weak_model();
+        let focus_id = handle.id;
+        let (subscription, activate) =
+            window.new_focus_listener(Box::new(move |event, window, cx| {
+                view.update(cx, |view, cx| {
+                    if event.previous_focus_path.last() == Some(&focus_id)
+                        && event.current_focus_path.last() != Some(&focus_id)
+                    {
+                        listener(view, window, cx)
+                    }
+                })
+                .is_ok()
+            }));
+        self.defer(|_| activate());
+        subscription
+    }
+
+    /// Register a listener to be called when nothing in the window has focus.
+    /// This typically happens when the node that was focused is removed from the tree,
+    /// and this callback lets you chose a default place to restore the users focus.
+    /// Returns a subscription and persists until the subscription is dropped.
+    pub fn on_focus_lost(
+        &mut self,
+        window: &mut Window,
+        mut listener: impl FnMut(&mut T, &mut Window, &mut Context<T>) + 'static,
+    ) -> Subscription {
+        let view = self.weak_model();
+        let (subscription, activate) = window.focus_lost_listeners.insert(
+            (),
+            Box::new(move |window, cx| {
+                view.update(cx, |view, cx| listener(view, window, cx))
+                    .is_ok()
+            }),
+        );
+        self.defer(|_| activate());
+        subscription
+    }
+
+    /// Register a listener to be called when the given focus handle or one of its descendants loses focus.
+    /// Returns a subscription and persists until the subscription is dropped.
+    pub fn on_focus_out(
+        &mut self,
+        handle: &FocusHandle,
+        window: &mut Window,
+        mut listener: impl FnMut(&mut T, FocusOutEvent, &mut Window, &mut Context<T>) + 'static,
+    ) -> Subscription {
+        let view = self.weak_model();
+        let focus_id = handle.id;
+        let (subscription, activate) =
+            window.new_focus_listener(Box::new(move |event, window, cx| {
+                view.update(cx, |view, cx| {
+                    if let Some(blurred_id) = event.previous_focus_path.last().copied() {
+                        if event.is_focus_out(focus_id) {
+                            let event = FocusOutEvent {
+                                blurred: WeakFocusHandle {
+                                    id: blurred_id,
+                                    handles: Arc::downgrade(&cx.focus_handles),
+                                },
+                            };
+                            listener(view, event, window, cx)
+                        }
+                    }
+                })
+                .is_ok()
+            }));
+        self.defer(|_| activate());
+        subscription
+    }
+
+    /// Schedule a future to be run asynchronously.
+    /// The given callback is invoked with a [`WeakModel<V>`] to avoid leaking the view for a long-running process.
+    /// It's also given an [`AsyncWindowContext`], which can be used to access the state of the view across await points.
+    /// The returned future will be polled on the main thread.
+    pub fn spawn_in<Fut, R>(
+        &self,
+        window: &Window,
+        f: impl FnOnce(WeakEntity<T>, AsyncWindowContext) -> Fut,
+    ) -> Task<R>
+    where
+        R: 'static,
+        Fut: Future<Output = R> + 'static,
+    {
+        let view = self.weak_model();
+        window.spawn(self, |mut cx| f(view, cx))
+    }
+
+    /// Register a callback to be invoked when the given global state changes.
+    pub fn observe_global_in<G: Global>(
+        &mut self,
+        window: &Window,
+        mut f: impl FnMut(&mut T, &mut Window, &mut Context<'_, T>) + 'static,
+    ) -> Subscription {
+        let window_handle = window.handle;
+        let view = self.weak_model();
+        let (subscription, activate) = self.global_observers.insert(
+            TypeId::of::<G>(),
+            Box::new(move |cx| {
+                window_handle
+                    .update(cx, |_, window, cx| {
+                        view.update(cx, |view, cx| f(view, window, cx)).is_ok()
+                    })
+                    .unwrap_or(false)
+            }),
+        );
+        self.defer(move |_| activate());
+        subscription
+    }
+
+    /// Register a callback to be invoked when the given Action type is dispatched to the window.
+    pub fn on_action(
+        &mut self,
+        action_type: TypeId,
+        window: &mut Window,
+        listener: impl Fn(&mut T, &dyn Any, DispatchPhase, &mut Window, &mut Context<T>) + 'static,
+    ) {
+        let handle = self.weak_model();
+        window.on_action(action_type, move |action, phase, window, cx| {
+            handle
+                .update(cx, |view, cx| {
+                    listener(view, action, phase, window, cx);
+                })
+                .ok();
+        });
+    }
+
+    /// Move focus to the current view, assuming it implements [`Focusable`].
+    pub fn focus_self(&mut self, window: &mut Window)
+    where
+        T: Focusable,
+    {
+        let view = self.model();
+        window.defer(self, move |window, cx| {
+            view.read(cx).focus_handle(cx).focus(window)
+        })
+    }
 }
 
-impl<'a, T> ModelContext<'a, T> {
+impl<'a, T> Context<'a, T> {
     /// Emit an event of the specified type, which can be handled by other entities that have subscribed via `subscribe` methods on their respective contexts.
     pub fn emit<Evt>(&mut self, event: Evt)
     where
@@ -220,14 +676,11 @@ impl<'a, T> ModelContext<'a, T> {
     }
 }
 
-impl<'a, T> Context for ModelContext<'a, T> {
+impl<'a, T> AppContext for Context<'a, T> {
     type Result<U> = U;
 
-    fn new_model<U: 'static>(
-        &mut self,
-        build_model: impl FnOnce(&mut ModelContext<'_, U>) -> U,
-    ) -> Model<U> {
-        self.app.new_model(build_model)
+    fn new<U: 'static>(&mut self, build_model: impl FnOnce(&mut Context<'_, U>) -> U) -> Entity<U> {
+        self.app.new(build_model)
     }
 
     fn reserve_model<U: 'static>(&mut self) -> Reservation<U> {
@@ -237,23 +690,23 @@ impl<'a, T> Context for ModelContext<'a, T> {
     fn insert_model<U: 'static>(
         &mut self,
         reservation: Reservation<U>,
-        build_model: impl FnOnce(&mut ModelContext<'_, U>) -> U,
-    ) -> Self::Result<Model<U>> {
+        build_model: impl FnOnce(&mut Context<'_, U>) -> U,
+    ) -> Self::Result<Entity<U>> {
         self.app.insert_model(reservation, build_model)
     }
 
     fn update_model<U: 'static, R>(
         &mut self,
-        handle: &Model<U>,
-        update: impl FnOnce(&mut U, &mut ModelContext<'_, U>) -> R,
+        handle: &Entity<U>,
+        update: impl FnOnce(&mut U, &mut Context<'_, U>) -> R,
     ) -> R {
         self.app.update_model(handle, update)
     }
 
     fn read_model<U, R>(
         &self,
-        handle: &Model<U>,
-        read: impl FnOnce(&U, &AppContext) -> R,
+        handle: &Entity<U>,
+        read: impl FnOnce(&U, &App) -> R,
     ) -> Self::Result<R>
     where
         U: 'static,
@@ -263,7 +716,7 @@ impl<'a, T> Context for ModelContext<'a, T> {
 
     fn update_window<R, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<R>
     where
-        F: FnOnce(AnyView, &mut WindowContext) -> R,
+        F: FnOnce(AnyView, &mut Window, &mut App) -> R,
     {
         self.app.update_window(window, update)
     }
@@ -271,7 +724,7 @@ impl<'a, T> Context for ModelContext<'a, T> {
     fn read_window<U, R>(
         &self,
         window: &WindowHandle<U>,
-        read: impl FnOnce(View<U>, &AppContext) -> R,
+        read: impl FnOnce(Entity<U>, &App) -> R,
     ) -> Result<R>
     where
         U: 'static,
@@ -280,14 +733,14 @@ impl<'a, T> Context for ModelContext<'a, T> {
     }
 }
 
-impl<T> Borrow<AppContext> for ModelContext<'_, T> {
-    fn borrow(&self) -> &AppContext {
+impl<T> Borrow<App> for Context<'_, T> {
+    fn borrow(&self) -> &App {
         self.app
     }
 }
 
-impl<T> BorrowMut<AppContext> for ModelContext<'_, T> {
-    fn borrow_mut(&mut self) -> &mut AppContext {
+impl<T> BorrowMut<App> for Context<'_, T> {
+    fn borrow_mut(&mut self) -> &mut App {
         self.app
     }
 }

crates/gpui/src/app/test_context.rs 🔗

@@ -1,11 +1,11 @@
 use crate::{
-    Action, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext, AvailableSpace,
-    BackgroundExecutor, BorrowAppContext, Bounds, ClipboardItem, Context, DrawPhase, Drawable,
-    Element, Empty, Entity, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Model,
-    ModelContext, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent,
-    MouseUpEvent, Pixels, Platform, Point, Render, Result, Size, Task, TestDispatcher,
-    TestPlatform, TestScreenCaptureSource, TestWindow, TextSystem, View, ViewContext,
-    VisualContext, WindowBounds, WindowContext, WindowHandle, WindowOptions,
+    Action, AnyView, AnyWindowHandle, App, AppCell, AppContext, AsyncAppContext, AvailableSpace,
+    BackgroundExecutor, BorrowAppContext, Bounds, ClipboardItem, DrawPhase, Drawable, Element,
+    Empty, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Modifiers,
+    ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
+    Platform, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform,
+    TestScreenCaptureSource, TestWindow, TextSystem, VisualContext, Window, WindowBounds,
+    WindowHandle, WindowOptions,
 };
 use anyhow::{anyhow, bail};
 use futures::{channel::oneshot, Stream, StreamExt};
@@ -29,15 +29,15 @@ pub struct TestAppContext {
     on_quit: Rc<RefCell<Vec<Box<dyn FnOnce() + 'static>>>>,
 }
 
-impl Context for TestAppContext {
+impl AppContext for TestAppContext {
     type Result<T> = T;
 
-    fn new_model<T: 'static>(
+    fn new<T: 'static>(
         &mut self,
-        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
-    ) -> Self::Result<Model<T>> {
+        build_model: impl FnOnce(&mut Context<'_, T>) -> T,
+    ) -> Self::Result<Entity<T>> {
         let mut app = self.app.borrow_mut();
-        app.new_model(build_model)
+        app.new(build_model)
     }
 
     fn reserve_model<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> {
@@ -48,16 +48,16 @@ impl Context for TestAppContext {
     fn insert_model<T: 'static>(
         &mut self,
         reservation: crate::Reservation<T>,
-        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
-    ) -> Self::Result<Model<T>> {
+        build_model: impl FnOnce(&mut Context<'_, T>) -> T,
+    ) -> Self::Result<Entity<T>> {
         let mut app = self.app.borrow_mut();
         app.insert_model(reservation, build_model)
     }
 
     fn update_model<T: 'static, R>(
         &mut self,
-        handle: &Model<T>,
-        update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
+        handle: &Entity<T>,
+        update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
     ) -> Self::Result<R> {
         let mut app = self.app.borrow_mut();
         app.update_model(handle, update)
@@ -65,8 +65,8 @@ impl Context for TestAppContext {
 
     fn read_model<T, R>(
         &self,
-        handle: &Model<T>,
-        read: impl FnOnce(&T, &AppContext) -> R,
+        handle: &Entity<T>,
+        read: impl FnOnce(&T, &App) -> R,
     ) -> Self::Result<R>
     where
         T: 'static,
@@ -77,7 +77,7 @@ impl Context for TestAppContext {
 
     fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
     where
-        F: FnOnce(AnyView, &mut WindowContext) -> T,
+        F: FnOnce(AnyView, &mut Window, &mut App) -> T,
     {
         let mut lock = self.app.borrow_mut();
         lock.update_window(window, f)
@@ -86,7 +86,7 @@ impl Context for TestAppContext {
     fn read_window<T, R>(
         &self,
         window: &WindowHandle<T>,
-        read: impl FnOnce(View<T>, &AppContext) -> R,
+        read: impl FnOnce(Entity<T>, &App) -> R,
     ) -> Result<R>
     where
         T: 'static,
@@ -108,7 +108,7 @@ impl TestAppContext {
         let text_system = Arc::new(TextSystem::new(platform.text_system()));
 
         Self {
-            app: AppContext::new(platform.clone(), asset_source, http_client),
+            app: App::new_app(platform.clone(), asset_source, http_client),
             background_executor,
             foreground_executor,
             dispatcher: dispatcher.clone(),
@@ -149,7 +149,7 @@ impl TestAppContext {
     /// Schedules all windows to be redrawn on the next effect cycle.
     pub fn refresh(&mut self) -> Result<()> {
         let mut app = self.app.borrow_mut();
-        app.refresh();
+        app.refresh_windows();
         Ok(())
     }
 
@@ -164,13 +164,13 @@ impl TestAppContext {
     }
 
     /// Gives you an `&mut AppContext` for the duration of the closure
-    pub fn update<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> R {
+    pub fn update<R>(&self, f: impl FnOnce(&mut App) -> R) -> R {
         let mut cx = self.app.borrow_mut();
         cx.update(f)
     }
 
     /// Gives you an `&AppContext` for the duration of the closure
-    pub fn read<R>(&self, f: impl FnOnce(&AppContext) -> R) -> R {
+    pub fn read<R>(&self, f: impl FnOnce(&App) -> R) -> R {
         let cx = self.app.borrow();
         f(&cx)
     }
@@ -179,7 +179,7 @@ impl TestAppContext {
     /// can be retrieved with `self.test_window(handle)`
     pub fn add_window<F, V>(&mut self, build_window: F) -> WindowHandle<V>
     where
-        F: FnOnce(&mut ViewContext<V>) -> V,
+        F: FnOnce(&mut Window, &mut Context<V>) -> V,
         V: 'static + Render,
     {
         let mut cx = self.app.borrow_mut();
@@ -191,7 +191,7 @@ impl TestAppContext {
                 window_bounds: Some(WindowBounds::Windowed(bounds)),
                 ..Default::default()
             },
-            |cx| cx.new_view(build_window),
+            |window, cx| cx.new(|cx| build_window(window, cx)),
         )
         .unwrap()
     }
@@ -206,7 +206,7 @@ impl TestAppContext {
                     window_bounds: Some(WindowBounds::Windowed(bounds)),
                     ..Default::default()
                 },
-                |cx| cx.new_view(|_| Empty),
+                |_, cx| cx.new(|_| Empty),
             )
             .unwrap();
         drop(cx);
@@ -218,9 +218,12 @@ impl TestAppContext {
     /// Adds a new window, and returns its root view and a `VisualTestContext` which can be used
     /// as a `WindowContext` for the rest of the test. Typically you would shadow this context with
     /// the returned one. `let (view, cx) = cx.add_window_view(...);`
-    pub fn add_window_view<F, V>(&mut self, build_root_view: F) -> (View<V>, &mut VisualTestContext)
+    pub fn add_window_view<F, V>(
+        &mut self,
+        build_root_view: F,
+    ) -> (Entity<V>, &mut VisualTestContext)
     where
-        F: FnOnce(&mut ViewContext<V>) -> V,
+        F: FnOnce(&mut Window, &mut Context<V>) -> V,
         V: 'static + Render,
     {
         let mut cx = self.app.borrow_mut();
@@ -231,11 +234,11 @@ impl TestAppContext {
                     window_bounds: Some(WindowBounds::Windowed(bounds)),
                     ..Default::default()
                 },
-                |cx| cx.new_view(build_root_view),
+                |window, cx| cx.new(|cx| build_root_view(window, cx)),
             )
             .unwrap();
         drop(cx);
-        let view = window.root_view(self).unwrap();
+        let view = window.root_model(self).unwrap();
         let cx = VisualTestContext::from_window(*window.deref(), self).as_mut();
         cx.run_until_parked();
 
@@ -315,16 +318,13 @@ impl TestAppContext {
 
     /// runs the given closure with a reference to the global
     /// panics if `has_global` would return false.
-    pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> R {
+    pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &App) -> R) -> R {
         let app = self.app.borrow();
         read(app.global(), &app)
     }
 
     /// runs the given closure with a reference to the global (if set)
-    pub fn try_read_global<G: Global, R>(
-        &self,
-        read: impl FnOnce(&G, &AppContext) -> R,
-    ) -> Option<R> {
+    pub fn try_read_global<G: Global, R>(&self, read: impl FnOnce(&G, &App) -> R) -> Option<R> {
         let lock = self.app.borrow();
         Some(read(lock.try_global()?, &lock))
     }
@@ -336,10 +336,7 @@ impl TestAppContext {
     }
 
     /// updates the global in this context. (panics if `has_global` would return false)
-    pub fn update_global<G: Global, R>(
-        &mut self,
-        update: impl FnOnce(&mut G, &mut AppContext) -> R,
-    ) -> R {
+    pub fn update_global<G: Global, R>(&mut self, update: impl FnOnce(&mut G, &mut App) -> R) -> R {
         let mut lock = self.app.borrow_mut();
         lock.update(|cx| cx.update_global(update))
     }
@@ -365,7 +362,9 @@ impl TestAppContext {
         A: Action,
     {
         window
-            .update(self, |_, cx| cx.dispatch_action(action.boxed_clone()))
+            .update(self, |_, window, cx| {
+                window.dispatch_action(action.boxed_clone(), cx)
+            })
             .unwrap();
 
         self.background_executor.run_until_parked()
@@ -401,8 +400,10 @@ impl TestAppContext {
 
     /// dispatches a single Keystroke (see also `simulate_keystrokes` and `simulate_input`)
     pub fn dispatch_keystroke(&mut self, window: AnyWindowHandle, keystroke: Keystroke) {
-        self.update_window(window, |_, cx| cx.dispatch_keystroke(keystroke))
-            .unwrap();
+        self.update_window(window, |_, window, cx| {
+            window.dispatch_keystroke(keystroke, cx)
+        })
+        .unwrap();
     }
 
     /// Returns the `TestWindow` backing the given handle.
@@ -421,7 +422,7 @@ impl TestAppContext {
     }
 
     /// Returns a stream of notifications whenever the View or Model is updated.
-    pub fn notifications<T: 'static>(&mut self, entity: &impl Entity<T>) -> impl Stream<Item = ()> {
+    pub fn notifications<T: 'static>(&mut self, entity: &Entity<T>) -> impl Stream<Item = ()> {
         let (tx, rx) = futures::channel::mpsc::unbounded();
         self.update(|cx| {
             cx.observe(entity, {
@@ -440,14 +441,14 @@ impl TestAppContext {
     /// Returns a stream of events emitted by the given Model.
     pub fn events<Evt, T: 'static + EventEmitter<Evt>>(
         &mut self,
-        entity: &Model<T>,
+        entity: &Entity<T>,
     ) -> futures::channel::mpsc::UnboundedReceiver<Evt>
     where
         Evt: 'static + Clone,
     {
         let (tx, rx) = futures::channel::mpsc::unbounded();
         entity
-            .update(self, |_, cx: &mut ModelContext<T>| {
+            .update(self, |_, cx: &mut Context<T>| {
                 cx.subscribe(entity, move |_model, _handle, event, _cx| {
                     let _ = tx.unbounded_send(event.clone());
                 })
@@ -460,8 +461,8 @@ impl TestAppContext {
     /// don't need to jump in at a specific time).
     pub async fn condition<T: 'static>(
         &mut self,
-        model: &Model<T>,
-        mut predicate: impl FnMut(&mut T, &mut ModelContext<T>) -> bool,
+        model: &Entity<T>,
+        mut predicate: impl FnMut(&mut T, &mut Context<T>) -> bool,
     ) {
         let timer = self.executor().timer(Duration::from_secs(3));
         let mut notifications = self.notifications(model);
@@ -492,7 +493,7 @@ impl TestAppContext {
     }
 }
 
-impl<T: 'static> Model<T> {
+impl<T: 'static> Entity<T> {
     /// Block until the next event is emitted by the model, then return it.
     pub fn next_event<Event>(&self, cx: &mut TestAppContext) -> impl Future<Output = Event>
     where
@@ -515,34 +516,9 @@ impl<T: 'static> Model<T> {
             event
         }
     }
-
-    /// Returns a future that resolves when the model notifies.
-    pub fn next_notification(&self, cx: &TestAppContext) -> impl Future<Output = ()> {
-        use postage::prelude::{Sink as _, Stream as _};
-
-        let (mut tx, mut rx) = postage::mpsc::channel(1);
-        let mut cx = cx.app.app.borrow_mut();
-        let subscription = cx.observe(self, move |_, _| {
-            tx.try_send(()).ok();
-        });
-
-        let duration = if std::env::var("CI").is_ok() {
-            Duration::from_secs(5)
-        } else {
-            Duration::from_secs(1)
-        };
-
-        async move {
-            let notification = crate::util::timeout(duration, rx.recv())
-                .await
-                .expect("next notification timed out");
-            drop(subscription);
-            notification.expect("model dropped while test was waiting for its next notification")
-        }
-    }
 }
 
-impl<V: 'static> View<V> {
+impl<V: 'static> Entity<V> {
     /// Returns a future that resolves when the view is next updated.
     pub fn next_notification(
         &self,
@@ -552,7 +528,7 @@ impl<V: 'static> View<V> {
         use postage::prelude::{Sink as _, Stream as _};
 
         let (mut tx, mut rx) = postage::mpsc::channel(1);
-        let subscription = cx.app.app.borrow_mut().observe(self, move |_, _| {
+        let subscription = cx.app.borrow_mut().observe(self, move |_, _| {
             tx.try_send(()).ok();
         });
 
@@ -574,12 +550,12 @@ impl<V: 'static> View<V> {
     }
 }
 
-impl<V> View<V> {
+impl<V> Entity<V> {
     /// Returns a future that resolves when the condition becomes true.
     pub fn condition<Evt>(
         &self,
         cx: &TestAppContext,
-        mut predicate: impl FnMut(&V, &AppContext) -> bool,
+        mut predicate: impl FnMut(&V, &App) -> bool,
     ) -> impl Future<Output = ()>
     where
         Evt: 'static,
@@ -645,6 +621,8 @@ impl<V> View<V> {
 }
 
 use derive_more::{Deref, DerefMut};
+
+use super::{Context, Entity};
 #[derive(Deref, DerefMut, Clone)]
 /// A VisualTestContext is the test-equivalent of a `WindowContext`. It allows you to
 /// run window-specific test code.
@@ -657,14 +635,11 @@ pub struct VisualTestContext {
 }
 
 impl VisualTestContext {
-    /// Get the underlying window handle underlying this context.
-    pub fn handle(&self) -> AnyWindowHandle {
-        self.window
-    }
-
     /// Provides the `WindowContext` for the duration of the closure.
-    pub fn update<R>(&mut self, f: impl FnOnce(&mut WindowContext) -> R) -> R {
-        self.cx.update_window(self.window, |_, cx| f(cx)).unwrap()
+    pub fn update<R>(&mut self, f: impl FnOnce(&mut Window, &mut App) -> R) -> R {
+        self.cx
+            .update_window(self.window, |_, window, cx| f(window, cx))
+            .unwrap()
     }
 
     /// Creates a new VisualTestContext. You would typically shadow the passed in
@@ -781,7 +756,7 @@ impl VisualTestContext {
 
     /// debug_bounds returns the bounds of the element with the given selector.
     pub fn debug_bounds(&mut self, selector: &'static str) -> Option<Bounds<Pixels>> {
-        self.update(|cx| cx.window.rendered_frame.debug_bounds.get(selector).copied())
+        self.update(|window, _| window.rendered_frame.debug_bounds.get(selector).copied())
     }
 
     /// Draw an element to the window. Useful for simulating events or actions
@@ -789,22 +764,22 @@ impl VisualTestContext {
         &mut self,
         origin: Point<Pixels>,
         space: impl Into<Size<AvailableSpace>>,
-        f: impl FnOnce(&mut WindowContext) -> E,
+        f: impl FnOnce(&mut Window, &mut App) -> E,
     ) -> (E::RequestLayoutState, E::PrepaintState)
     where
         E: Element,
     {
-        self.update(|cx| {
-            cx.window.draw_phase = DrawPhase::Prepaint;
-            let mut element = Drawable::new(f(cx));
-            element.layout_as_root(space.into(), cx);
-            cx.with_absolute_element_offset(origin, |cx| element.prepaint(cx));
+        self.update(|window, cx| {
+            window.invalidator.set_phase(DrawPhase::Prepaint);
+            let mut element = Drawable::new(f(window, cx));
+            element.layout_as_root(space.into(), window, cx);
+            window.with_absolute_element_offset(origin, |window| element.prepaint(window, cx));
 
-            cx.window.draw_phase = DrawPhase::Paint;
-            let (request_layout_state, prepaint_state) = element.paint(cx);
+            window.invalidator.set_phase(DrawPhase::Paint);
+            let (request_layout_state, prepaint_state) = element.paint(window, cx);
 
-            cx.window.draw_phase = DrawPhase::None;
-            cx.refresh();
+            window.invalidator.set_phase(DrawPhase::None);
+            window.refresh();
 
             (request_layout_state, prepaint_state)
         })
@@ -831,8 +806,8 @@ impl VisualTestContext {
     pub fn simulate_close(&mut self) -> bool {
         let handler = self
             .cx
-            .update_window(self.window, |_, cx| {
-                cx.window
+            .update_window(self.window, |_, window, _| {
+                window
                     .platform_window
                     .as_test()
                     .unwrap()
@@ -845,8 +820,8 @@ impl VisualTestContext {
         if let Some(mut handler) = handler {
             let should_close = handler();
             self.cx
-                .update_window(self.window, |_, cx| {
-                    cx.window.platform_window.on_should_close(handler);
+                .update_window(self.window, |_, window, _| {
+                    window.platform_window.on_should_close(handler);
                 })
                 .unwrap();
             should_close
@@ -870,14 +845,14 @@ impl VisualTestContext {
     }
 }
 
-impl Context for VisualTestContext {
-    type Result<T> = <TestAppContext as Context>::Result<T>;
+impl AppContext for VisualTestContext {
+    type Result<T> = <TestAppContext as AppContext>::Result<T>;
 
-    fn new_model<T: 'static>(
+    fn new<T: 'static>(
         &mut self,
-        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
-    ) -> Self::Result<Model<T>> {
-        self.cx.new_model(build_model)
+        build_model: impl FnOnce(&mut Context<'_, T>) -> T,
+    ) -> Self::Result<Entity<T>> {
+        self.cx.new(build_model)
     }
 
     fn reserve_model<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> {
@@ -887,15 +862,15 @@ impl Context for VisualTestContext {
     fn insert_model<T: 'static>(
         &mut self,
         reservation: crate::Reservation<T>,
-        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
-    ) -> Self::Result<Model<T>> {
+        build_model: impl FnOnce(&mut Context<'_, T>) -> T,
+    ) -> Self::Result<Entity<T>> {
         self.cx.insert_model(reservation, build_model)
     }
 
     fn update_model<T, R>(
         &mut self,
-        handle: &Model<T>,
-        update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
+        handle: &Entity<T>,
+        update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
     ) -> Self::Result<R>
     where
         T: 'static,
@@ -905,8 +880,8 @@ impl Context for VisualTestContext {
 
     fn read_model<T, R>(
         &self,
-        handle: &Model<T>,
-        read: impl FnOnce(&T, &AppContext) -> R,
+        handle: &Entity<T>,
+        read: impl FnOnce(&T, &App) -> R,
     ) -> Self::Result<R>
     where
         T: 'static,
@@ -916,7 +891,7 @@ impl Context for VisualTestContext {
 
     fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
     where
-        F: FnOnce(AnyView, &mut WindowContext) -> T,
+        F: FnOnce(AnyView, &mut Window, &mut App) -> T,
     {
         self.cx.update_window(window, f)
     }
@@ -924,7 +899,7 @@ impl Context for VisualTestContext {
     fn read_window<T, R>(
         &self,
         window: &WindowHandle<T>,
-        read: impl FnOnce(View<T>, &AppContext) -> R,
+        read: impl FnOnce(Entity<T>, &App) -> R,
     ) -> Result<R>
     where
         T: 'static,
@@ -934,55 +909,52 @@ impl Context for VisualTestContext {
 }
 
 impl VisualContext for VisualTestContext {
-    fn new_view<V>(
+    /// Get the underlying window handle underlying this context.
+    fn window_handle(&self) -> AnyWindowHandle {
+        self.window
+    }
+
+    fn new_window_model<T: 'static>(
         &mut self,
-        build_view: impl FnOnce(&mut ViewContext<V>) -> V,
-    ) -> Self::Result<View<V>>
-    where
-        V: 'static + Render,
-    {
+        build_model: impl FnOnce(&mut Window, &mut Context<'_, T>) -> T,
+    ) -> Self::Result<Entity<T>> {
         self.window
-            .update(&mut self.cx, |_, cx| cx.new_view(build_view))
+            .update(&mut self.cx, |_, window, cx| {
+                cx.new(|cx| build_model(window, cx))
+            })
             .unwrap()
     }
 
-    fn update_view<V: 'static, R>(
+    fn update_window_model<V: 'static, R>(
         &mut self,
-        view: &View<V>,
-        update: impl FnOnce(&mut V, &mut ViewContext<V>) -> R,
+        view: &Entity<V>,
+        update: impl FnOnce(&mut V, &mut Window, &mut Context<V>) -> R,
     ) -> Self::Result<R> {
         self.window
-            .update(&mut self.cx, |_, cx| cx.update_view(view, update))
+            .update(&mut self.cx, |_, window, cx| {
+                view.update(cx, |v, cx| update(v, window, cx))
+            })
             .unwrap()
     }
 
     fn replace_root_view<V>(
         &mut self,
-        build_view: impl FnOnce(&mut ViewContext<V>) -> V,
-    ) -> Self::Result<View<V>>
+        build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V,
+    ) -> Self::Result<Entity<V>>
     where
         V: 'static + Render,
     {
         self.window
-            .update(&mut self.cx, |_, cx| cx.replace_root_view(build_view))
-            .unwrap()
-    }
-
-    fn focus_view<V: crate::FocusableView>(&mut self, view: &View<V>) -> Self::Result<()> {
-        self.window
-            .update(&mut self.cx, |_, cx| {
-                view.read(cx).focus_handle(cx).clone().focus(cx)
+            .update(&mut self.cx, |_, window, cx| {
+                window.replace_root_model(cx, build_view)
             })
             .unwrap()
     }
 
-    fn dismiss_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
-    where
-        V: crate::ManagedView,
-    {
+    fn focus<V: crate::Focusable>(&mut self, view: &Entity<V>) -> Self::Result<()> {
         self.window
-            .update(&mut self.cx, |_, cx| {
-                view.update(cx, |_, cx| cx.emit(crate::DismissEvent))
+            .update(&mut self.cx, |_, window, cx| {
+                view.read(cx).focus_handle(cx).clone().focus(window)
             })
             .unwrap()
     }
@@ -990,11 +962,12 @@ impl VisualContext for VisualTestContext {
 
 impl AnyWindowHandle {
     /// Creates the given view in this window.
-    pub fn build_view<V: Render + 'static>(
+    pub fn build_model<V: Render + 'static>(
         &self,
         cx: &mut TestAppContext,
-        build_view: impl FnOnce(&mut ViewContext<V>) -> V,
-    ) -> View<V> {
-        self.update(cx, |_, cx| cx.new_view(build_view)).unwrap()
+        build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V,
+    ) -> Entity<V> {
+        self.update(cx, |_, window, cx| cx.new(|cx| build_view(window, cx)))
+            .unwrap()
     }
 }

crates/gpui/src/asset_cache.rs 🔗

@@ -1,4 +1,4 @@
-use crate::{AppContext, SharedString, SharedUri};
+use crate::{App, SharedString, SharedUri};
 use futures::Future;
 
 use std::fmt::Debug;
@@ -47,7 +47,7 @@ pub trait Asset: 'static {
     /// Load the asset asynchronously
     fn load(
         source: Self::Source,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> impl Future<Output = Self::Output> + Send + 'static;
 }
 
@@ -66,7 +66,7 @@ impl<R: Clone + Send, E: Clone + Send + std::error::Error, T: Asset<Output = Res
 
     fn load(
         source: Self::Source,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> impl Future<Output = Self::Output> + Send + 'static {
         let load = T::load(source, cx);
         async {

crates/gpui/src/element.rs 🔗

@@ -32,8 +32,8 @@
 //! your own custom layout algorithm or rendering a code editor.
 
 use crate::{
-    util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, DispatchNodeId, ElementId, FocusHandle,
-    LayoutId, Pixels, Point, Size, Style, ViewContext, WindowContext, ELEMENT_ARENA,
+    util::FluentBuilder, App, ArenaBox, AvailableSpace, Bounds, Context, DispatchNodeId, ElementId,
+    FocusHandle, LayoutId, Pixels, Point, Size, Style, Window, ELEMENT_ARENA,
 };
 use derive_more::{Deref, DerefMut};
 pub(crate) use smallvec::SmallVec;
@@ -64,7 +64,8 @@ pub trait Element: 'static + IntoElement {
     fn request_layout(
         &mut self,
         id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState);
 
     /// After laying out an element, we need to commit its bounds to the current frame for hitbox
@@ -74,7 +75,8 @@ pub trait Element: 'static + IntoElement {
         id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         request_layout: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Self::PrepaintState;
 
     /// Once layout has been completed, this method will be called to paint the element to the screen.
@@ -85,7 +87,8 @@ pub trait Element: 'static + IntoElement {
         bounds: Bounds<Pixels>,
         request_layout: &mut Self::RequestLayoutState,
         prepaint: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     );
 
     /// Convert this element into a dynamically-typed [`AnyElement`].
@@ -115,11 +118,11 @@ impl<T: IntoElement> FluentBuilder for T {}
 /// models. Views are drawn to the screen and care about the current window's state, models are not and do not.
 pub trait Render: 'static + Sized {
     /// Render this view into an element tree.
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement;
+    fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement;
 }
 
 impl Render for Empty {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         Empty
     }
 }
@@ -133,7 +136,7 @@ pub trait RenderOnce: 'static {
     /// Render this component into an element tree. Note that this method
     /// takes ownership of self, as compared to [`Render::render()`] method
     /// which takes a mutable reference.
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement;
+    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement;
 }
 
 /// This is a helper trait to provide a uniform interface for constructing elements that
@@ -184,10 +187,11 @@ impl<C: RenderOnce> Element for Component<C> {
     fn request_layout(
         &mut self,
         _id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
-        let mut element = self.0.take().unwrap().render(cx).into_any_element();
-        let layout_id = element.request_layout(cx);
+        let mut element = self.0.take().unwrap().render(window, cx).into_any_element();
+        let layout_id = element.request_layout(window, cx);
         (layout_id, element)
     }
 
@@ -196,9 +200,10 @@ impl<C: RenderOnce> Element for Component<C> {
         _id: Option<&GlobalElementId>,
         _: Bounds<Pixels>,
         element: &mut AnyElement,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        element.prepaint(cx);
+        element.prepaint(window, cx);
     }
 
     fn paint(
@@ -207,9 +212,10 @@ impl<C: RenderOnce> Element for Component<C> {
         _: Bounds<Pixels>,
         element: &mut Self::RequestLayoutState,
         _: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        element.paint(cx);
+        element.paint(window, cx);
     }
 }
 
@@ -228,16 +234,17 @@ pub struct GlobalElementId(pub(crate) SmallVec<[ElementId; 32]>);
 trait ElementObject {
     fn inner_element(&mut self) -> &mut dyn Any;
 
-    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId;
+    fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId;
 
-    fn prepaint(&mut self, cx: &mut WindowContext);
+    fn prepaint(&mut self, window: &mut Window, cx: &mut App);
 
-    fn paint(&mut self, cx: &mut WindowContext);
+    fn paint(&mut self, window: &mut Window, cx: &mut App);
 
     fn layout_as_root(
         &mut self,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Size<Pixels>;
 }
 
@@ -282,19 +289,19 @@ impl<E: Element> Drawable<E> {
         }
     }
 
-    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
+    fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId {
         match mem::take(&mut self.phase) {
             ElementDrawPhase::Start => {
                 let global_id = self.element.id().map(|element_id| {
-                    cx.window.element_id_stack.push(element_id);
-                    GlobalElementId(cx.window.element_id_stack.clone())
+                    window.element_id_stack.push(element_id);
+                    GlobalElementId(window.element_id_stack.clone())
                 });
 
                 let (layout_id, request_layout) =
-                    self.element.request_layout(global_id.as_ref(), cx);
+                    self.element.request_layout(global_id.as_ref(), window, cx);
 
                 if global_id.is_some() {
-                    cx.window.element_id_stack.pop();
+                    window.element_id_stack.pop();
                 }
 
                 self.phase = ElementDrawPhase::RequestLayout {
@@ -308,7 +315,7 @@ impl<E: Element> Drawable<E> {
         }
     }
 
-    pub(crate) fn prepaint(&mut self, cx: &mut WindowContext) {
+    pub(crate) fn prepaint(&mut self, window: &mut Window, cx: &mut App) {
         match mem::take(&mut self.phase) {
             ElementDrawPhase::RequestLayout {
                 layout_id,
@@ -322,19 +329,23 @@ impl<E: Element> Drawable<E> {
                 ..
             } => {
                 if let Some(element_id) = self.element.id() {
-                    cx.window.element_id_stack.push(element_id);
-                    debug_assert_eq!(global_id.as_ref().unwrap().0, cx.window.element_id_stack);
+                    window.element_id_stack.push(element_id);
+                    debug_assert_eq!(global_id.as_ref().unwrap().0, window.element_id_stack);
                 }
 
-                let bounds = cx.layout_bounds(layout_id);
-                let node_id = cx.window.next_frame.dispatch_tree.push_node();
-                let prepaint =
-                    self.element
-                        .prepaint(global_id.as_ref(), bounds, &mut request_layout, cx);
-                cx.window.next_frame.dispatch_tree.pop_node();
+                let bounds = window.layout_bounds(layout_id);
+                let node_id = window.next_frame.dispatch_tree.push_node();
+                let prepaint = self.element.prepaint(
+                    global_id.as_ref(),
+                    bounds,
+                    &mut request_layout,
+                    window,
+                    cx,
+                );
+                window.next_frame.dispatch_tree.pop_node();
 
                 if global_id.is_some() {
-                    cx.window.element_id_stack.pop();
+                    window.element_id_stack.pop();
                 }
 
                 self.phase = ElementDrawPhase::Prepaint {
@@ -351,7 +362,8 @@ impl<E: Element> Drawable<E> {
 
     pub(crate) fn paint(
         &mut self,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (E::RequestLayoutState, E::PrepaintState) {
         match mem::take(&mut self.phase) {
             ElementDrawPhase::Prepaint {
@@ -363,21 +375,22 @@ impl<E: Element> Drawable<E> {
                 ..
             } => {
                 if let Some(element_id) = self.element.id() {
-                    cx.window.element_id_stack.push(element_id);
-                    debug_assert_eq!(global_id.as_ref().unwrap().0, cx.window.element_id_stack);
+                    window.element_id_stack.push(element_id);
+                    debug_assert_eq!(global_id.as_ref().unwrap().0, window.element_id_stack);
                 }
 
-                cx.window.next_frame.dispatch_tree.set_active_node(node_id);
+                window.next_frame.dispatch_tree.set_active_node(node_id);
                 self.element.paint(
                     global_id.as_ref(),
                     bounds,
                     &mut request_layout,
                     &mut prepaint,
+                    window,
                     cx,
                 );
 
                 if global_id.is_some() {
-                    cx.window.element_id_stack.pop();
+                    window.element_id_stack.pop();
                 }
 
                 self.phase = ElementDrawPhase::Painted;
@@ -390,10 +403,11 @@ impl<E: Element> Drawable<E> {
     pub(crate) fn layout_as_root(
         &mut self,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Size<Pixels> {
         if matches!(&self.phase, ElementDrawPhase::Start) {
-            self.request_layout(cx);
+            self.request_layout(window, cx);
         }
 
         let layout_id = match mem::take(&mut self.phase) {
@@ -402,7 +416,7 @@ impl<E: Element> Drawable<E> {
                 global_id,
                 request_layout,
             } => {
-                cx.compute_layout(layout_id, available_space);
+                window.compute_layout(layout_id, available_space, cx);
                 self.phase = ElementDrawPhase::LayoutComputed {
                     layout_id,
                     global_id,
@@ -418,7 +432,7 @@ impl<E: Element> Drawable<E> {
                 request_layout,
             } => {
                 if available_space != prev_available_space {
-                    cx.compute_layout(layout_id, available_space);
+                    window.compute_layout(layout_id, available_space, cx);
                 }
                 self.phase = ElementDrawPhase::LayoutComputed {
                     layout_id,
@@ -431,7 +445,7 @@ impl<E: Element> Drawable<E> {
             _ => panic!("cannot measure after painting"),
         };
 
-        cx.layout_bounds(layout_id).size
+        window.layout_bounds(layout_id).size
     }
 }
 
@@ -444,24 +458,25 @@ where
         &mut self.element
     }
 
-    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
-        Drawable::request_layout(self, cx)
+    fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId {
+        Drawable::request_layout(self, window, cx)
     }
 
-    fn prepaint(&mut self, cx: &mut WindowContext) {
-        Drawable::prepaint(self, cx);
+    fn prepaint(&mut self, window: &mut Window, cx: &mut App) {
+        Drawable::prepaint(self, window, cx);
     }
 
-    fn paint(&mut self, cx: &mut WindowContext) {
-        Drawable::paint(self, cx);
+    fn paint(&mut self, window: &mut Window, cx: &mut App) {
+        Drawable::paint(self, window, cx);
     }
 
     fn layout_as_root(
         &mut self,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Size<Pixels> {
-        Drawable::layout_as_root(self, available_space, cx)
+        Drawable::layout_as_root(self, available_space, window, cx)
     }
 }
 
@@ -487,19 +502,19 @@ impl AnyElement {
 
     /// Request the layout ID of the element stored in this `AnyElement`.
     /// Used for laying out child elements in a parent element.
-    pub fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
-        self.0.request_layout(cx)
+    pub fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId {
+        self.0.request_layout(window, cx)
     }
 
     /// Prepares the element to be painted by storing its bounds, giving it a chance to draw hitboxes and
     /// request autoscroll before the final paint pass is confirmed.
-    pub fn prepaint(&mut self, cx: &mut WindowContext) -> Option<FocusHandle> {
-        let focus_assigned = cx.window.next_frame.focus.is_some();
+    pub fn prepaint(&mut self, window: &mut Window, cx: &mut App) -> Option<FocusHandle> {
+        let focus_assigned = window.next_frame.focus.is_some();
 
-        self.0.prepaint(cx);
+        self.0.prepaint(window, cx);
 
         if !focus_assigned {
-            if let Some(focus_id) = cx.window.next_frame.focus {
+            if let Some(focus_id) = window.next_frame.focus {
                 return FocusHandle::for_id(focus_id, &cx.focus_handles);
             }
         }
@@ -508,17 +523,18 @@ impl AnyElement {
     }
 
     /// Paints the element stored in this `AnyElement`.
-    pub fn paint(&mut self, cx: &mut WindowContext) {
-        self.0.paint(cx);
+    pub fn paint(&mut self, window: &mut Window, cx: &mut App) {
+        self.0.paint(window, cx);
     }
 
     /// Performs layout for this element within the given available space and returns its size.
     pub fn layout_as_root(
         &mut self,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Size<Pixels> {
-        self.0.layout_as_root(available_space, cx)
+        self.0.layout_as_root(available_space, window, cx)
     }
 
     /// Prepaints this element at the given absolute origin.
@@ -526,9 +542,10 @@ impl AnyElement {
     pub fn prepaint_at(
         &mut self,
         origin: Point<Pixels>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<FocusHandle> {
-        cx.with_absolute_element_offset(origin, |cx| self.prepaint(cx))
+        window.with_absolute_element_offset(origin, |window| self.prepaint(window, cx))
     }
 
     /// Performs layout on this element in the available space, then prepaints it at the given absolute origin.
@@ -537,10 +554,11 @@ impl AnyElement {
         &mut self,
         origin: Point<Pixels>,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<FocusHandle> {
-        self.layout_as_root(available_space, cx);
-        cx.with_absolute_element_offset(origin, |cx| self.prepaint(cx))
+        self.layout_as_root(available_space, window, cx);
+        window.with_absolute_element_offset(origin, |window| self.prepaint(window, cx))
     }
 }
 
@@ -555,9 +573,10 @@ impl Element for AnyElement {
     fn request_layout(
         &mut self,
         _: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
-        let layout_id = self.request_layout(cx);
+        let layout_id = self.request_layout(window, cx);
         (layout_id, ())
     }
 
@@ -566,9 +585,10 @@ impl Element for AnyElement {
         _: Option<&GlobalElementId>,
         _: Bounds<Pixels>,
         _: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        self.prepaint(cx);
+        self.prepaint(window, cx);
     }
 
     fn paint(
@@ -577,9 +597,10 @@ impl Element for AnyElement {
         _: Bounds<Pixels>,
         _: &mut Self::RequestLayoutState,
         _: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        self.paint(cx);
+        self.paint(window, cx);
     }
 }
 
@@ -617,9 +638,10 @@ impl Element for Empty {
     fn request_layout(
         &mut self,
         _id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
-        (cx.request_layout(Style::default(), None), ())
+        (window.request_layout(Style::default(), None, cx), ())
     }
 
     fn prepaint(
@@ -627,7 +649,8 @@ impl Element for Empty {
         _id: Option<&GlobalElementId>,
         _bounds: Bounds<Pixels>,
         _state: &mut Self::RequestLayoutState,
-        _cx: &mut WindowContext,
+        _window: &mut Window,
+        _cx: &mut App,
     ) {
     }
 
@@ -637,7 +660,8 @@ impl Element for Empty {
         _bounds: Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
         _prepaint: &mut Self::PrepaintState,
-        _cx: &mut WindowContext,
+        _window: &mut Window,
+        _cx: &mut App,
     ) {
     }
 }

crates/gpui/src/elements/anchored.rs 🔗

@@ -2,8 +2,8 @@ use smallvec::SmallVec;
 use taffy::style::{Display, Position};
 
 use crate::{
-    point, AnyElement, Axis, Bounds, Corner, Edges, Element, GlobalElementId, IntoElement,
-    LayoutId, ParentElement, Pixels, Point, Size, Style, WindowContext,
+    point, AnyElement, App, Axis, Bounds, Corner, Edges, Element, GlobalElementId, IntoElement,
+    LayoutId, ParentElement, Pixels, Point, Size, Style, Window,
 };
 
 /// The state that the anchored element element uses to track its children.
@@ -94,12 +94,13 @@ impl Element for Anchored {
     fn request_layout(
         &mut self,
         _id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (crate::LayoutId, Self::RequestLayoutState) {
         let child_layout_ids = self
             .children
             .iter_mut()
-            .map(|child| child.request_layout(cx))
+            .map(|child| child.request_layout(window, cx))
             .collect::<SmallVec<_>>();
 
         let anchored_style = Style {
@@ -108,7 +109,7 @@ impl Element for Anchored {
             ..Style::default()
         };
 
-        let layout_id = cx.request_layout(anchored_style, child_layout_ids.iter().copied());
+        let layout_id = window.request_layout(anchored_style, child_layout_ids.iter().copied(), cx);
 
         (layout_id, AnchoredState { child_layout_ids })
     }
@@ -118,7 +119,8 @@ impl Element for Anchored {
         _id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         request_layout: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         if request_layout.child_layout_ids.is_empty() {
             return;
@@ -127,7 +129,7 @@ impl Element for Anchored {
         let mut child_min = point(Pixels::MAX, Pixels::MAX);
         let mut child_max = Point::default();
         for child_layout_id in &request_layout.child_layout_ids {
-            let child_bounds = cx.layout_bounds(*child_layout_id);
+            let child_bounds = window.layout_bounds(*child_layout_id);
             child_min = child_min.min(&child_bounds.origin);
             child_max = child_max.max(&child_bounds.bottom_right());
         }
@@ -143,7 +145,7 @@ impl Element for Anchored {
 
         let limits = Bounds {
             origin: Point::default(),
-            size: cx.viewport_size(),
+            size: window.viewport_size(),
         };
 
         if self.fit_mode == AnchoredFitMode::SwitchAnchor {
@@ -199,9 +201,9 @@ impl Element for Anchored {
         let offset = desired.origin - bounds.origin;
         let offset = point(offset.x.round(), offset.y.round());
 
-        cx.with_element_offset(offset, |cx| {
+        window.with_element_offset(offset, |window| {
             for child in &mut self.children {
-                child.prepaint(cx);
+                child.prepaint(window, cx);
             }
         })
     }
@@ -212,10 +214,11 @@ impl Element for Anchored {
         _bounds: crate::Bounds<crate::Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
         _prepaint: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         for child in &mut self.children {
-            child.paint(cx);
+            child.paint(window, cx);
         }
     }
 }

crates/gpui/src/elements/animation.rs 🔗

@@ -1,6 +1,6 @@
 use std::time::{Duration, Instant};
 
-use crate::{AnyElement, Element, ElementId, GlobalElementId, IntoElement, WindowContext};
+use crate::{AnyElement, App, Element, ElementId, GlobalElementId, IntoElement, Window};
 
 pub use easing::*;
 
@@ -104,9 +104,10 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
     fn request_layout(
         &mut self,
         global_id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (crate::LayoutId, Self::RequestLayoutState) {
-        cx.with_element_state(global_id.unwrap(), |state, cx| {
+        window.with_element_state(global_id.unwrap(), |state, window| {
             let state = state.unwrap_or_else(|| AnimationState {
                 start: Instant::now(),
             });
@@ -133,10 +134,10 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
             let mut element = (self.animator)(element, delta).into_any_element();
 
             if !done {
-                cx.request_animation_frame();
+                window.request_animation_frame();
             }
 
-            ((element.request_layout(cx), element), state)
+            ((element.request_layout(window, cx), element), state)
         })
     }
 
@@ -145,9 +146,10 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
         _id: Option<&GlobalElementId>,
         _bounds: crate::Bounds<crate::Pixels>,
         element: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Self::PrepaintState {
-        element.prepaint(cx);
+        element.prepaint(window, cx);
     }
 
     fn paint(
@@ -156,9 +158,10 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
         _bounds: crate::Bounds<crate::Pixels>,
         element: &mut Self::RequestLayoutState,
         _: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        element.paint(cx);
+        element.paint(window, cx);
     }
 }
 

crates/gpui/src/elements/canvas.rs 🔗

@@ -1,15 +1,15 @@
 use refineable::Refineable as _;
 
 use crate::{
-    Bounds, Element, ElementId, GlobalElementId, IntoElement, Pixels, Style, StyleRefinement,
-    Styled, WindowContext,
+    App, Bounds, Element, ElementId, GlobalElementId, IntoElement, Pixels, Style, StyleRefinement,
+    Styled, Window,
 };
 
 /// Construct a canvas element with the given paint callback.
 /// Useful for adding short term custom drawing to a view.
 pub fn canvas<T>(
-    prepaint: impl 'static + FnOnce(Bounds<Pixels>, &mut WindowContext) -> T,
-    paint: impl 'static + FnOnce(Bounds<Pixels>, T, &mut WindowContext),
+    prepaint: impl 'static + FnOnce(Bounds<Pixels>, &mut Window, &mut App) -> T,
+    paint: impl 'static + FnOnce(Bounds<Pixels>, T, &mut Window, &mut App),
 ) -> Canvas<T> {
     Canvas {
         prepaint: Some(Box::new(prepaint)),
@@ -21,8 +21,8 @@ pub fn canvas<T>(
 /// A canvas element, meant for accessing the low level paint API without defining a whole
 /// custom element
 pub struct Canvas<T> {
-    prepaint: Option<Box<dyn FnOnce(Bounds<Pixels>, &mut WindowContext) -> T>>,
-    paint: Option<Box<dyn FnOnce(Bounds<Pixels>, T, &mut WindowContext)>>,
+    prepaint: Option<Box<dyn FnOnce(Bounds<Pixels>, &mut Window, &mut App) -> T>>,
+    paint: Option<Box<dyn FnOnce(Bounds<Pixels>, T, &mut Window, &mut App)>>,
     style: StyleRefinement,
 }
 
@@ -45,11 +45,12 @@ impl<T: 'static> Element for Canvas<T> {
     fn request_layout(
         &mut self,
         _id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (crate::LayoutId, Self::RequestLayoutState) {
         let mut style = Style::default();
         style.refine(&self.style);
-        let layout_id = cx.request_layout(style.clone(), []);
+        let layout_id = window.request_layout(style.clone(), [], cx);
         (layout_id, style)
     }
 
@@ -58,9 +59,10 @@ impl<T: 'static> Element for Canvas<T> {
         _id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         _request_layout: &mut Style,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<T> {
-        Some(self.prepaint.take().unwrap()(bounds, cx))
+        Some(self.prepaint.take().unwrap()(bounds, window, cx))
     }
 
     fn paint(
@@ -69,11 +71,12 @@ impl<T: 'static> Element for Canvas<T> {
         bounds: Bounds<Pixels>,
         style: &mut Style,
         prepaint: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let prepaint = prepaint.take().unwrap();
-        style.paint(bounds, cx, |cx| {
-            (self.paint.take().unwrap())(bounds, prepaint, cx)
+        style.paint(bounds, window, cx, |window, cx| {
+            (self.paint.take().unwrap())(bounds, prepaint, window, cx)
         });
     }
 }

crates/gpui/src/elements/deferred.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{
-    AnyElement, Bounds, Element, GlobalElementId, IntoElement, LayoutId, Pixels, WindowContext,
+    AnyElement, App, Bounds, Element, GlobalElementId, IntoElement, LayoutId, Pixels, Window,
 };
 
 /// Builds a `Deferred` element, which delays the layout and paint of its child.
@@ -38,9 +38,10 @@ impl Element for Deferred {
     fn request_layout(
         &mut self,
         _id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, ()) {
-        let layout_id = self.child.as_mut().unwrap().request_layout(cx);
+        let layout_id = self.child.as_mut().unwrap().request_layout(window, cx);
         (layout_id, ())
     }
 
@@ -49,11 +50,12 @@ impl Element for Deferred {
         _id: Option<&GlobalElementId>,
         _bounds: Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        _cx: &mut App,
     ) {
         let child = self.child.take().unwrap();
-        let element_offset = cx.element_offset();
-        cx.defer_draw(child, element_offset, self.priority)
+        let element_offset = window.element_offset();
+        window.defer_draw(child, element_offset, self.priority)
     }
 
     fn paint(
@@ -62,7 +64,8 @@ impl Element for Deferred {
         _bounds: Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
         _prepaint: &mut Self::PrepaintState,
-        _cx: &mut WindowContext,
+        _window: &mut Window,
+        _cx: &mut App,
     ) {
     }
 }

crates/gpui/src/elements/div.rs 🔗

@@ -16,12 +16,12 @@
 //! constructed by combining these two systems into an all-in-one element.
 
 use crate::{
-    point, px, size, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, Bounds,
-    ClickEvent, DispatchPhase, Element, ElementId, FocusHandle, Global, GlobalElementId, Hitbox,
+    point, px, size, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, App, Bounds, ClickEvent,
+    DispatchPhase, Element, ElementId, Entity, FocusHandle, Global, GlobalElementId, Hitbox,
     HitboxId, IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId,
     ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
     ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, Style,
-    StyleRefinement, Styled, Task, TooltipId, View, Visibility, WindowContext,
+    StyleRefinement, Styled, Task, TooltipId, Visibility, Window,
 };
 use collections::HashMap;
 use refineable::Refineable;
@@ -33,7 +33,6 @@ use std::{
     fmt::Debug,
     marker::PhantomData,
     mem,
-    ops::DerefMut,
     rc::Rc,
     sync::Arc,
     time::Duration,
@@ -68,7 +67,7 @@ pub struct DragMoveEvent<T> {
 
 impl<T: 'static> DragMoveEvent<T> {
     /// Returns the drag state for this event.
-    pub fn drag<'b>(&self, cx: &'b AppContext) -> &'b T {
+    pub fn drag<'b>(&self, cx: &'b App) -> &'b T {
         cx.active_drag
             .as_ref()
             .and_then(|drag| drag.value.downcast_ref::<T>())
@@ -89,13 +88,15 @@ impl Interactivity {
     pub fn on_mouse_down(
         &mut self,
         button: MouseButton,
-        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
     ) {
         self.mouse_down_listeners
-            .push(Box::new(move |event, phase, hitbox, cx| {
-                if phase == DispatchPhase::Bubble && event.button == button && hitbox.is_hovered(cx)
+            .push(Box::new(move |event, phase, hitbox, window, cx| {
+                if phase == DispatchPhase::Bubble
+                    && event.button == button
+                    && hitbox.is_hovered(window)
                 {
-                    (listener)(event, cx)
+                    (listener)(event, window, cx)
                 }
             }));
     }
@@ -106,12 +107,12 @@ impl Interactivity {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     pub fn capture_any_mouse_down(
         &mut self,
-        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
     ) {
         self.mouse_down_listeners
-            .push(Box::new(move |event, phase, hitbox, cx| {
-                if phase == DispatchPhase::Capture && hitbox.is_hovered(cx) {
-                    (listener)(event, cx)
+            .push(Box::new(move |event, phase, hitbox, window, cx| {
+                if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {
+                    (listener)(event, window, cx)
                 }
             }));
     }
@@ -122,12 +123,12 @@ impl Interactivity {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     pub fn on_any_mouse_down(
         &mut self,
-        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
     ) {
         self.mouse_down_listeners
-            .push(Box::new(move |event, phase, hitbox, cx| {
-                if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
-                    (listener)(event, cx)
+            .push(Box::new(move |event, phase, hitbox, window, cx| {
+                if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
+                    (listener)(event, window, cx)
                 }
             }));
     }
@@ -139,13 +140,15 @@ impl Interactivity {
     pub fn on_mouse_up(
         &mut self,
         button: MouseButton,
-        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
     ) {
         self.mouse_up_listeners
-            .push(Box::new(move |event, phase, hitbox, cx| {
-                if phase == DispatchPhase::Bubble && event.button == button && hitbox.is_hovered(cx)
+            .push(Box::new(move |event, phase, hitbox, window, cx| {
+                if phase == DispatchPhase::Bubble
+                    && event.button == button
+                    && hitbox.is_hovered(window)
                 {
-                    (listener)(event, cx)
+                    (listener)(event, window, cx)
                 }
             }));
     }
@@ -156,12 +159,12 @@ impl Interactivity {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     pub fn capture_any_mouse_up(
         &mut self,
-        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
     ) {
         self.mouse_up_listeners
-            .push(Box::new(move |event, phase, hitbox, cx| {
-                if phase == DispatchPhase::Capture && hitbox.is_hovered(cx) {
-                    (listener)(event, cx)
+            .push(Box::new(move |event, phase, hitbox, window, cx| {
+                if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {
+                    (listener)(event, window, cx)
                 }
             }));
     }
@@ -172,12 +175,12 @@ impl Interactivity {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     pub fn on_any_mouse_up(
         &mut self,
-        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
     ) {
         self.mouse_up_listeners
-            .push(Box::new(move |event, phase, hitbox, cx| {
-                if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
-                    (listener)(event, cx)
+            .push(Box::new(move |event, phase, hitbox, window, cx| {
+                if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
+                    (listener)(event, window, cx)
                 }
             }));
     }
@@ -189,12 +192,12 @@ impl Interactivity {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     pub fn on_mouse_down_out(
         &mut self,
-        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
     ) {
         self.mouse_down_listeners
-            .push(Box::new(move |event, phase, hitbox, cx| {
-                if phase == DispatchPhase::Capture && !hitbox.contains(&cx.mouse_position()) {
-                    (listener)(event, cx)
+            .push(Box::new(move |event, phase, hitbox, window, cx| {
+                if phase == DispatchPhase::Capture && !hitbox.contains(&window.mouse_position()) {
+                    (listener)(event, window, cx)
                 }
             }));
     }
@@ -207,15 +210,15 @@ impl Interactivity {
     pub fn on_mouse_up_out(
         &mut self,
         button: MouseButton,
-        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
     ) {
         self.mouse_up_listeners
-            .push(Box::new(move |event, phase, hitbox, cx| {
+            .push(Box::new(move |event, phase, hitbox, window, cx| {
                 if phase == DispatchPhase::Capture
                     && event.button == button
-                    && !hitbox.is_hovered(cx)
+                    && !hitbox.is_hovered(window)
                 {
-                    (listener)(event, cx);
+                    (listener)(event, window, cx);
                 }
             }));
     }
@@ -226,12 +229,12 @@ impl Interactivity {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     pub fn on_mouse_move(
         &mut self,
-        listener: impl Fn(&MouseMoveEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static,
     ) {
         self.mouse_move_listeners
-            .push(Box::new(move |event, phase, hitbox, cx| {
-                if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
-                    (listener)(event, cx);
+            .push(Box::new(move |event, phase, hitbox, window, cx| {
+                if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
+                    (listener)(event, window, cx);
                 }
             }));
     }
@@ -245,12 +248,12 @@ impl Interactivity {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     pub fn on_drag_move<T>(
         &mut self,
-        listener: impl Fn(&DragMoveEvent<T>, &mut WindowContext) + 'static,
+        listener: impl Fn(&DragMoveEvent<T>, &mut Window, &mut App) + 'static,
     ) where
         T: 'static,
     {
         self.mouse_move_listeners
-            .push(Box::new(move |event, phase, hitbox, cx| {
+            .push(Box::new(move |event, phase, hitbox, window, cx| {
                 if phase == DispatchPhase::Capture {
                     if let Some(drag) = &cx.active_drag {
                         if drag.value.as_ref().type_id() == TypeId::of::<T>() {
@@ -261,6 +264,7 @@ impl Interactivity {
                                     drag: PhantomData,
                                     dragged_item: Arc::clone(&drag.value),
                                 },
+                                window,
                                 cx,
                             );
                         }
@@ -275,12 +279,12 @@ impl Interactivity {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     pub fn on_scroll_wheel(
         &mut self,
-        listener: impl Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static,
     ) {
         self.scroll_wheel_listeners
-            .push(Box::new(move |event, phase, hitbox, cx| {
-                if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
-                    (listener)(event, cx);
+            .push(Box::new(move |event, phase, hitbox, window, cx| {
+                if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
+                    (listener)(event, window, cx);
                 }
             }));
     }
@@ -291,14 +295,14 @@ impl Interactivity {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     pub fn capture_action<A: Action>(
         &mut self,
-        listener: impl Fn(&A, &mut WindowContext) + 'static,
+        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
     ) {
         self.action_listeners.push((
             TypeId::of::<A>(),
-            Box::new(move |action, phase, cx| {
+            Box::new(move |action, phase, window, cx| {
                 let action = action.downcast_ref().unwrap();
                 if phase == DispatchPhase::Capture {
-                    (listener)(action, cx)
+                    (listener)(action, window, cx)
                 } else {
                     cx.propagate();
                 }
@@ -310,13 +314,13 @@ impl Interactivity {
     /// The imperative API equivalent to [`InteractiveElement::on_action`]
     ///
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
-    pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut WindowContext) + 'static) {
+    pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut Window, &mut App) + 'static) {
         self.action_listeners.push((
             TypeId::of::<A>(),
-            Box::new(move |action, phase, cx| {
+            Box::new(move |action, phase, window, cx| {
                 let action = action.downcast_ref().unwrap();
                 if phase == DispatchPhase::Bubble {
-                    (listener)(action, cx)
+                    (listener)(action, window, cx)
                 }
             }),
         ));
@@ -331,14 +335,14 @@ impl Interactivity {
     pub fn on_boxed_action(
         &mut self,
         action: &dyn Action,
-        listener: impl Fn(&dyn Action, &mut WindowContext) + 'static,
+        listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static,
     ) {
         let action = action.boxed_clone();
         self.action_listeners.push((
             (*action).type_id(),
-            Box::new(move |_, phase, cx| {
+            Box::new(move |_, phase, window, cx| {
                 if phase == DispatchPhase::Bubble {
-                    (listener)(&*action, cx)
+                    (listener)(&*action, window, cx)
                 }
             }),
         ));
@@ -348,11 +352,14 @@ impl Interactivity {
     /// The imperative API equivalent to [`InteractiveElement::on_key_down`]
     ///
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
-    pub fn on_key_down(&mut self, listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static) {
+    pub fn on_key_down(
+        &mut self,
+        listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
+    ) {
         self.key_down_listeners
-            .push(Box::new(move |event, phase, cx| {
+            .push(Box::new(move |event, phase, window, cx| {
                 if phase == DispatchPhase::Bubble {
-                    (listener)(event, cx)
+                    (listener)(event, window, cx)
                 }
             }));
     }
@@ -363,12 +370,12 @@ impl Interactivity {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     pub fn capture_key_down(
         &mut self,
-        listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
     ) {
         self.key_down_listeners
-            .push(Box::new(move |event, phase, cx| {
+            .push(Box::new(move |event, phase, window, cx| {
                 if phase == DispatchPhase::Capture {
-                    listener(event, cx)
+                    listener(event, window, cx)
                 }
             }));
     }
@@ -377,11 +384,11 @@ impl Interactivity {
     /// The imperative API equivalent to [`InteractiveElement::on_key_up`]
     ///
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
-    pub fn on_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) {
+    pub fn on_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static) {
         self.key_up_listeners
-            .push(Box::new(move |event, phase, cx| {
+            .push(Box::new(move |event, phase, window, cx| {
                 if phase == DispatchPhase::Bubble {
-                    listener(event, cx)
+                    listener(event, window, cx)
                 }
             }));
     }
@@ -390,11 +397,14 @@ impl Interactivity {
     /// The imperative API equivalent to [`InteractiveElement::on_key_up`]
     ///
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
-    pub fn capture_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) {
+    pub fn capture_key_up(
+        &mut self,
+        listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
+    ) {
         self.key_up_listeners
-            .push(Box::new(move |event, phase, cx| {
+            .push(Box::new(move |event, phase, window, cx| {
                 if phase == DispatchPhase::Capture {
-                    listener(event, cx)
+                    listener(event, window, cx)
                 }
             }));
     }
@@ -405,28 +415,33 @@ impl Interactivity {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     pub fn on_modifiers_changed(
         &mut self,
-        listener: impl Fn(&ModifiersChangedEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
     ) {
         self.modifiers_changed_listeners
-            .push(Box::new(move |event, cx| listener(event, cx)));
+            .push(Box::new(move |event, window, cx| {
+                listener(event, window, cx)
+            }));
     }
 
     /// Bind the given callback to drop events of the given type, whether or not the drag started on this element
     /// The imperative API equivalent to [`InteractiveElement::on_drop`]
     ///
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
-    pub fn on_drop<T: 'static>(&mut self, listener: impl Fn(&T, &mut WindowContext) + 'static) {
+    pub fn on_drop<T: 'static>(&mut self, listener: impl Fn(&T, &mut Window, &mut App) + 'static) {
         self.drop_listeners.push((
             TypeId::of::<T>(),
-            Box::new(move |dragged_value, cx| {
-                listener(dragged_value.downcast_ref().unwrap(), cx);
+            Box::new(move |dragged_value, window, cx| {
+                listener(dragged_value.downcast_ref().unwrap(), window, cx);
             }),
         ));
     }
 
     /// Use the given predicate to determine whether or not a drop event should be dispatched to this element
     /// The imperative API equivalent to [`InteractiveElement::can_drop`]
-    pub fn can_drop(&mut self, predicate: impl Fn(&dyn Any, &mut WindowContext) -> bool + 'static) {
+    pub fn can_drop(
+        &mut self,
+        predicate: impl Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static,
+    ) {
         self.can_drop_predicate = Some(Box::new(predicate));
     }
 
@@ -434,12 +449,14 @@ impl Interactivity {
     /// The imperative API equivalent to [`StatefulInteractiveElement::on_click`]
     ///
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
-    pub fn on_click(&mut self, listener: impl Fn(&ClickEvent, &mut WindowContext) + 'static)
+    pub fn on_click(&mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static)
     where
         Self: Sized,
     {
         self.click_listeners
-            .push(Box::new(move |event, cx| listener(event, cx)));
+            .push(Box::new(move |event, window, cx| {
+                listener(event, window, cx)
+            }));
     }
 
     /// On drag initiation, this callback will be used to create a new view to render the dragged value for a
@@ -451,7 +468,7 @@ impl Interactivity {
     pub fn on_drag<T, W>(
         &mut self,
         value: T,
-        constructor: impl Fn(&T, Point<Pixels>, &mut WindowContext) -> View<W> + 'static,
+        constructor: impl Fn(&T, Point<Pixels>, &mut Window, &mut App) -> Entity<W> + 'static,
     ) where
         Self: Sized,
         T: 'static,
@@ -463,8 +480,8 @@ impl Interactivity {
         );
         self.drag_listener = Some((
             Arc::new(value),
-            Box::new(move |value, offset, cx| {
-                constructor(value.downcast_ref().unwrap(), offset, cx).into()
+            Box::new(move |value, offset, window, cx| {
+                constructor(value.downcast_ref().unwrap(), offset, window, cx).into()
             }),
         ));
     }
@@ -474,7 +491,7 @@ impl Interactivity {
     /// The imperative API equivalent to [`StatefulInteractiveElement::on_drag`]
     ///
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
-    pub fn on_hover(&mut self, listener: impl Fn(&bool, &mut WindowContext) + 'static)
+    pub fn on_hover(&mut self, listener: impl Fn(&bool, &mut Window, &mut App) + 'static)
     where
         Self: Sized,
     {
@@ -487,7 +504,7 @@ impl Interactivity {
 
     /// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
     /// The imperative API equivalent to [`InteractiveElement::tooltip`]
-    pub fn tooltip(&mut self, build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static)
+    pub fn tooltip(&mut self, build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static)
     where
         Self: Sized,
     {
@@ -506,7 +523,7 @@ impl Interactivity {
     /// the tooltip. The imperative API equivalent to [`InteractiveElement::hoverable_tooltip`]
     pub fn hoverable_tooltip(
         &mut self,
-        build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static,
+        build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
     ) where
         Self: Sized,
     {
@@ -549,10 +566,10 @@ pub trait InteractiveElement: Sized {
     /// Track the focus state of the given focus handle on this element.
     /// If the focus handle is focused by the application, this element will
     /// apply its focused styles.
-    fn track_focus(mut self, focus_handle: &FocusHandle) -> Focusable<Self> {
+    fn track_focus(mut self, focus_handle: &FocusHandle) -> FocusableWrapper<Self> {
         self.interactivity().focusable = true;
         self.interactivity().tracked_focus_handle = Some(focus_handle.clone());
-        Focusable { element: self }
+        FocusableWrapper { element: self }
     }
 
     /// Set the keymap context for this element. This will be used to determine
@@ -598,7 +615,7 @@ pub trait InteractiveElement: Sized {
     fn on_mouse_down(
         mut self,
         button: MouseButton,
-        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.interactivity().on_mouse_down(button, listener);
         self
@@ -628,7 +645,7 @@ pub trait InteractiveElement: Sized {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     fn capture_any_mouse_down(
         mut self,
-        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.interactivity().capture_any_mouse_down(listener);
         self
@@ -640,7 +657,7 @@ pub trait InteractiveElement: Sized {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     fn on_any_mouse_down(
         mut self,
-        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.interactivity().on_any_mouse_down(listener);
         self
@@ -653,7 +670,7 @@ pub trait InteractiveElement: Sized {
     fn on_mouse_up(
         mut self,
         button: MouseButton,
-        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.interactivity().on_mouse_up(button, listener);
         self
@@ -665,7 +682,7 @@ pub trait InteractiveElement: Sized {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     fn capture_any_mouse_up(
         mut self,
-        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.interactivity().capture_any_mouse_up(listener);
         self
@@ -678,7 +695,7 @@ pub trait InteractiveElement: Sized {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     fn on_mouse_down_out(
         mut self,
-        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.interactivity().on_mouse_down_out(listener);
         self
@@ -692,7 +709,7 @@ pub trait InteractiveElement: Sized {
     fn on_mouse_up_out(
         mut self,
         button: MouseButton,
-        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.interactivity().on_mouse_up_out(button, listener);
         self
@@ -704,7 +721,7 @@ pub trait InteractiveElement: Sized {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     fn on_mouse_move(
         mut self,
-        listener: impl Fn(&MouseMoveEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.interactivity().on_mouse_move(listener);
         self
@@ -719,7 +736,7 @@ pub trait InteractiveElement: Sized {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     fn on_drag_move<T: 'static>(
         mut self,
-        listener: impl Fn(&DragMoveEvent<T>, &mut WindowContext) + 'static,
+        listener: impl Fn(&DragMoveEvent<T>, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.interactivity().on_drag_move(listener);
         self
@@ -731,7 +748,7 @@ pub trait InteractiveElement: Sized {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     fn on_scroll_wheel(
         mut self,
-        listener: impl Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.interactivity().on_scroll_wheel(listener);
         self
@@ -743,7 +760,7 @@ pub trait InteractiveElement: Sized {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     fn capture_action<A: Action>(
         mut self,
-        listener: impl Fn(&A, &mut WindowContext) + 'static,
+        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.interactivity().capture_action(listener);
         self
@@ -753,7 +770,10 @@ pub trait InteractiveElement: Sized {
     /// The fluent API equivalent to [`Interactivity::on_action`]
     ///
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
-    fn on_action<A: Action>(mut self, listener: impl Fn(&A, &mut WindowContext) + 'static) -> Self {
+    fn on_action<A: Action>(
+        mut self,
+        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
+    ) -> Self {
         self.interactivity().on_action(listener);
         self
     }
@@ -767,7 +787,7 @@ pub trait InteractiveElement: Sized {
     fn on_boxed_action(
         mut self,
         action: &dyn Action,
-        listener: impl Fn(&dyn Action, &mut WindowContext) + 'static,
+        listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.interactivity().on_boxed_action(action, listener);
         self
@@ -779,7 +799,7 @@ pub trait InteractiveElement: Sized {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     fn on_key_down(
         mut self,
-        listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.interactivity().on_key_down(listener);
         self
@@ -791,7 +811,7 @@ pub trait InteractiveElement: Sized {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     fn capture_key_down(
         mut self,
-        listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.interactivity().capture_key_down(listener);
         self
@@ -801,7 +821,10 @@ pub trait InteractiveElement: Sized {
     /// The fluent API equivalent to [`Interactivity::on_key_up`]
     ///
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
-    fn on_key_up(mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) -> Self {
+    fn on_key_up(
+        mut self,
+        listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
+    ) -> Self {
         self.interactivity().on_key_up(listener);
         self
     }
@@ -812,7 +835,7 @@ pub trait InteractiveElement: Sized {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     fn capture_key_up(
         mut self,
-        listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.interactivity().capture_key_up(listener);
         self
@@ -824,7 +847,7 @@ pub trait InteractiveElement: Sized {
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
     fn on_modifiers_changed(
         mut self,
-        listener: impl Fn(&ModifiersChangedEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.interactivity().on_modifiers_changed(listener);
         self
@@ -833,14 +856,15 @@ pub trait InteractiveElement: Sized {
     /// Apply the given style when the given data type is dragged over this element
     fn drag_over<S: 'static>(
         mut self,
-        f: impl 'static + Fn(StyleRefinement, &S, &WindowContext) -> StyleRefinement,
+        f: impl 'static + Fn(StyleRefinement, &S, &Window, &App) -> StyleRefinement,
     ) -> Self {
         self.interactivity().drag_over_styles.push((
             TypeId::of::<S>(),
-            Box::new(move |currently_dragged: &dyn Any, cx| {
+            Box::new(move |currently_dragged: &dyn Any, window, cx| {
                 f(
                     StyleRefinement::default(),
                     currently_dragged.downcast_ref::<S>().unwrap(),
+                    window,
                     cx,
                 )
             }),
@@ -868,7 +892,10 @@ pub trait InteractiveElement: Sized {
     /// The fluent API equivalent to [`Interactivity::on_drop`]
     ///
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
-    fn on_drop<T: 'static>(mut self, listener: impl Fn(&T, &mut WindowContext) + 'static) -> Self {
+    fn on_drop<T: 'static>(
+        mut self,
+        listener: impl Fn(&T, &mut Window, &mut App) + 'static,
+    ) -> Self {
         self.interactivity().on_drop(listener);
         self
     }
@@ -877,7 +904,7 @@ pub trait InteractiveElement: Sized {
     /// The fluent API equivalent to [`Interactivity::can_drop`]
     fn can_drop(
         mut self,
-        predicate: impl Fn(&dyn Any, &mut WindowContext) -> bool + 'static,
+        predicate: impl Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static,
     ) -> Self {
         self.interactivity().can_drop(predicate);
         self
@@ -893,7 +920,7 @@ pub trait InteractiveElement: Sized {
     /// Block the mouse from interacting with this element or any of its children
     /// The fluent API equivalent to [`Interactivity::occlude_mouse`]
     fn block_mouse_down(mut self) -> Self {
-        self.on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
+        self.on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
     }
 }
 
@@ -901,9 +928,9 @@ pub trait InteractiveElement: Sized {
 /// that require state.
 pub trait StatefulInteractiveElement: InteractiveElement {
     /// Set this element to focusable.
-    fn focusable(mut self) -> Focusable<Self> {
+    fn focusable(mut self) -> FocusableWrapper<Self> {
         self.interactivity().focusable = true;
-        Focusable { element: self }
+        FocusableWrapper { element: self }
     }
 
     /// Set the overflow x and y to scroll.
@@ -966,7 +993,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
     /// The fluent API equivalent to [`Interactivity::on_click`]
     ///
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
-    fn on_click(mut self, listener: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self
+    fn on_click(mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self
     where
         Self: Sized,
     {
@@ -984,7 +1011,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
     fn on_drag<T, W>(
         mut self,
         value: T,
-        constructor: impl Fn(&T, Point<Pixels>, &mut WindowContext) -> View<W> + 'static,
+        constructor: impl Fn(&T, Point<Pixels>, &mut Window, &mut App) -> Entity<W> + 'static,
     ) -> Self
     where
         Self: Sized,
@@ -1000,7 +1027,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
     /// The fluent API equivalent to [`Interactivity::on_hover`]
     ///
     /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
-    fn on_hover(mut self, listener: impl Fn(&bool, &mut WindowContext) + 'static) -> Self
+    fn on_hover(mut self, listener: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self
     where
         Self: Sized,
     {
@@ -1010,7 +1037,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
 
     /// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
     /// The fluent API equivalent to [`Interactivity::tooltip`]
-    fn tooltip(mut self, build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self
+    fn tooltip(mut self, build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self
     where
         Self: Sized,
     {
@@ -1023,7 +1050,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
     /// the tooltip. The fluent API equivalent to [`Interactivity::hoverable_tooltip`]
     fn hoverable_tooltip(
         mut self,
-        build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static,
+        build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
     ) -> Self
     where
         Self: Sized,
@@ -1055,40 +1082,41 @@ pub trait FocusableElement: InteractiveElement {
 }
 
 pub(crate) type MouseDownListener =
-    Box<dyn Fn(&MouseDownEvent, DispatchPhase, &Hitbox, &mut WindowContext) + 'static>;
+    Box<dyn Fn(&MouseDownEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
 pub(crate) type MouseUpListener =
-    Box<dyn Fn(&MouseUpEvent, DispatchPhase, &Hitbox, &mut WindowContext) + 'static>;
+    Box<dyn Fn(&MouseUpEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
 
 pub(crate) type MouseMoveListener =
-    Box<dyn Fn(&MouseMoveEvent, DispatchPhase, &Hitbox, &mut WindowContext) + 'static>;
+    Box<dyn Fn(&MouseMoveEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
 
 pub(crate) type ScrollWheelListener =
-    Box<dyn Fn(&ScrollWheelEvent, DispatchPhase, &Hitbox, &mut WindowContext) + 'static>;
+    Box<dyn Fn(&ScrollWheelEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
 
-pub(crate) type ClickListener = Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>;
+pub(crate) type ClickListener = Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>;
 
 pub(crate) type DragListener =
-    Box<dyn Fn(&dyn Any, Point<Pixels>, &mut WindowContext) -> AnyView + 'static>;
+    Box<dyn Fn(&dyn Any, Point<Pixels>, &mut Window, &mut App) -> AnyView + 'static>;
 
-type DropListener = Box<dyn Fn(&dyn Any, &mut WindowContext) + 'static>;
+type DropListener = Box<dyn Fn(&dyn Any, &mut Window, &mut App) + 'static>;
 
-type CanDropPredicate = Box<dyn Fn(&dyn Any, &mut WindowContext) -> bool + 'static>;
+type CanDropPredicate = Box<dyn Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static>;
 
 pub(crate) struct TooltipBuilder {
-    build: Rc<dyn Fn(&mut WindowContext) -> AnyView + 'static>,
+    build: Rc<dyn Fn(&mut Window, &mut App) -> AnyView + 'static>,
     hoverable: bool,
 }
 
 pub(crate) type KeyDownListener =
-    Box<dyn Fn(&KeyDownEvent, DispatchPhase, &mut WindowContext) + 'static>;
+    Box<dyn Fn(&KeyDownEvent, DispatchPhase, &mut Window, &mut App) + 'static>;
 
 pub(crate) type KeyUpListener =
-    Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut WindowContext) + 'static>;
+    Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut Window, &mut App) + 'static>;
 
 pub(crate) type ModifiersChangedListener =
-    Box<dyn Fn(&ModifiersChangedEvent, &mut WindowContext) + 'static>;
+    Box<dyn Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static>;
 
-pub(crate) type ActionListener = Box<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
+pub(crate) type ActionListener =
+    Box<dyn Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static>;
 
 /// Construct a new [`Div`] element
 #[track_caller]
@@ -1113,7 +1141,7 @@ pub fn div() -> Div {
 pub struct Div {
     interactivity: Interactivity,
     children: SmallVec<[AnyElement; 2]>,
-    prepaint_listener: Option<Box<dyn Fn(Vec<Bounds<Pixels>>, &mut WindowContext) + 'static>>,
+    prepaint_listener: Option<Box<dyn Fn(Vec<Bounds<Pixels>>, &mut Window, &mut App) + 'static>>,
 }
 
 impl Div {
@@ -1121,7 +1149,7 @@ impl Div {
     /// This allows you to store the [`Bounds`] of the children for later use.
     pub fn on_children_prepainted(
         mut self,
-        listener: impl Fn(Vec<Bounds<Pixels>>, &mut WindowContext) + 'static,
+        listener: impl Fn(Vec<Bounds<Pixels>>, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.prepaint_listener = Some(Box::new(listener));
         self
@@ -1167,21 +1195,22 @@ impl Element for Div {
     fn request_layout(
         &mut self,
         global_id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
         let mut child_layout_ids = SmallVec::new();
-        let layout_id = self
-            .interactivity
-            .request_layout(global_id, cx, |style, cx| {
-                cx.with_text_style(style.text_style().cloned(), |cx| {
-                    child_layout_ids = self
-                        .children
-                        .iter_mut()
-                        .map(|child| child.request_layout(cx))
-                        .collect::<SmallVec<_>>();
-                    cx.request_layout(style, child_layout_ids.iter().copied())
-                })
-            });
+        let layout_id =
+            self.interactivity
+                .request_layout(global_id, window, cx, |style, window, cx| {
+                    window.with_text_style(style.text_style().cloned(), |window| {
+                        child_layout_ids = self
+                            .children
+                            .iter_mut()
+                            .map(|child| child.request_layout(window, cx))
+                            .collect::<SmallVec<_>>();
+                        window.request_layout(style, child_layout_ids.iter().copied(), cx)
+                    })
+                });
         (layout_id, DivFrameState { child_layout_ids })
     }
 
@@ -1190,7 +1219,8 @@ impl Element for Div {
         global_id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         request_layout: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<Hitbox> {
         let has_prepaint_listener = self.prepaint_listener.is_some();
         let mut children_bounds = Vec::with_capacity(if has_prepaint_listener {
@@ -1202,7 +1232,7 @@ impl Element for Div {
         let mut child_min = point(Pixels::MAX, Pixels::MAX);
         let mut child_max = Point::default();
         if let Some(handle) = self.interactivity.scroll_anchor.as_ref() {
-            *handle.last_origin.borrow_mut() = bounds.origin - cx.element_offset();
+            *handle.last_origin.borrow_mut() = bounds.origin - window.element_offset();
         }
         let content_size = if request_layout.child_layout_ids.is_empty() {
             bounds.size
@@ -1213,7 +1243,7 @@ impl Element for Div {
             let requested = state.requested_scroll_top.take();
 
             for (ix, child_layout_id) in request_layout.child_layout_ids.iter().enumerate() {
-                let child_bounds = cx.layout_bounds(*child_layout_id);
+                let child_bounds = window.layout_bounds(*child_layout_id);
                 child_min = child_min.min(&child_bounds.origin);
                 child_max = child_max.max(&child_bounds.bottom_right());
                 state.child_bounds.push(child_bounds);
@@ -1228,7 +1258,7 @@ impl Element for Div {
             (child_max - child_min).into()
         } else {
             for child_layout_id in &request_layout.child_layout_ids {
-                let child_bounds = cx.layout_bounds(*child_layout_id);
+                let child_bounds = window.layout_bounds(*child_layout_id);
                 child_min = child_min.min(&child_bounds.origin);
                 child_max = child_max.max(&child_bounds.bottom_right());
 
@@ -1243,16 +1273,17 @@ impl Element for Div {
             global_id,
             bounds,
             content_size,
+            window,
             cx,
-            |_style, scroll_offset, hitbox, cx| {
-                cx.with_element_offset(scroll_offset, |cx| {
+            |_style, scroll_offset, hitbox, window, cx| {
+                window.with_element_offset(scroll_offset, |window| {
                     for child in &mut self.children {
-                        child.prepaint(cx);
+                        child.prepaint(window, cx);
                     }
                 });
 
                 if let Some(listener) = self.prepaint_listener.as_ref() {
-                    listener(children_bounds, cx);
+                    listener(children_bounds, window, cx);
                 }
 
                 hitbox
@@ -1266,14 +1297,21 @@ impl Element for Div {
         bounds: Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
         hitbox: &mut Option<Hitbox>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        self.interactivity
-            .paint(global_id, bounds, hitbox.as_ref(), cx, |_style, cx| {
+        self.interactivity.paint(
+            global_id,
+            bounds,
+            hitbox.as_ref(),
+            window,
+            cx,
+            |_style, window, cx| {
                 for child in &mut self.children {
-                    child.paint(cx);
+                    child.paint(window, cx);
                 }
-            });
+            },
+        );
     }
 }
 
@@ -1316,7 +1354,7 @@ pub struct Interactivity {
     pub(crate) group_active_style: Option<GroupStyle>,
     pub(crate) drag_over_styles: Vec<(
         TypeId,
-        Box<dyn Fn(&dyn Any, &mut WindowContext) -> StyleRefinement>,
+        Box<dyn Fn(&dyn Any, &mut Window, &mut App) -> StyleRefinement>,
     )>,
     pub(crate) group_drag_over_styles: Vec<(TypeId, GroupStyle)>,
     pub(crate) mouse_down_listeners: Vec<MouseDownListener>,
@@ -1331,7 +1369,7 @@ pub struct Interactivity {
     pub(crate) can_drop_predicate: Option<CanDropPredicate>,
     pub(crate) click_listeners: Vec<ClickListener>,
     pub(crate) drag_listener: Option<(Arc<dyn Any>, DragListener)>,
-    pub(crate) hover_listener: Option<Box<dyn Fn(&bool, &mut WindowContext)>>,
+    pub(crate) hover_listener: Option<Box<dyn Fn(&bool, &mut Window, &mut App)>>,
     pub(crate) tooltip_builder: Option<TooltipBuilder>,
     pub(crate) occlude_mouse: bool,
 
@@ -1347,12 +1385,13 @@ impl Interactivity {
     pub fn request_layout(
         &mut self,
         global_id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
-        f: impl FnOnce(Style, &mut WindowContext) -> LayoutId,
+        window: &mut Window,
+        cx: &mut App,
+        f: impl FnOnce(Style, &mut Window, &mut App) -> LayoutId,
     ) -> LayoutId {
-        cx.with_optional_element_state::<InteractiveElementState, _>(
+        window.with_optional_element_state::<InteractiveElementState, _>(
             global_id,
-            |element_state, cx| {
+            |element_state, window| {
                 let mut element_state =
                     element_state.map(|element_state| element_state.unwrap_or_default());
 
@@ -1398,8 +1437,8 @@ impl Interactivity {
                     }
                 }
 
-                let style = self.compute_style_internal(None, element_state.as_mut(), cx);
-                let layout_id = f(style, cx);
+                let style = self.compute_style_internal(None, element_state.as_mut(), window, cx);
+                let layout_id = f(style, window, cx);
                 (layout_id, element_state)
             },
         )
@@ -1411,19 +1450,20 @@ impl Interactivity {
         global_id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         content_size: Size<Pixels>,
-        cx: &mut WindowContext,
-        f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut WindowContext) -> R,
+        window: &mut Window,
+        cx: &mut App,
+        f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut Window, &mut App) -> R,
     ) -> R {
         self.content_size = content_size;
         if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
-            cx.set_focus_handle(focus_handle);
+            window.set_focus_handle(focus_handle, cx);
         }
-        cx.with_optional_element_state::<InteractiveElementState, _>(
+        window.with_optional_element_state::<InteractiveElementState, _>(
             global_id,
-            |element_state, cx| {
+            |element_state, window| {
                 let mut element_state =
                     element_state.map(|element_state| element_state.unwrap_or_default());
-                let style = self.compute_style_internal(None, element_state.as_mut(), cx);
+                let style = self.compute_style_internal(None, element_state.as_mut(), window, cx);
 
                 if let Some(element_state) = element_state.as_mut() {
                     if let Some(clicked_state) = element_state.clicked_state.as_ref() {

crates/gpui/src/elements/img.rs 🔗

@@ -1,8 +1,8 @@
 use crate::{
-    px, swap_rgba_pa_to_bgra, AbsoluteLength, AnyElement, AppContext, Asset, AssetLogger, Bounds,
+    px, swap_rgba_pa_to_bgra, AbsoluteLength, AnyElement, App, Asset, AssetLogger, Bounds,
     DefiniteLength, Element, ElementId, GlobalElementId, Hitbox, Image, InteractiveElement,
     Interactivity, IntoElement, LayoutId, Length, ObjectFit, Pixels, RenderImage, Resource,
-    SharedString, SharedUri, StyleRefinement, Styled, SvgSize, Task, WindowContext,
+    SharedString, SharedUri, StyleRefinement, Styled, SvgSize, Task, Window,
 };
 use anyhow::{anyhow, Result};
 
@@ -45,7 +45,7 @@ pub enum ImageSource {
     /// Cached image data
     Image(Arc<Image>),
     /// A custom loading function to use
-    Custom(Arc<dyn Fn(&mut WindowContext) -> Option<Result<Arc<RenderImage>, ImageCacheError>>>),
+    Custom(Arc<dyn Fn(&mut Window, &mut App) -> Option<Result<Arc<RenderImage>, ImageCacheError>>>),
 }
 
 fn is_uri(uri: &str) -> bool {
@@ -114,8 +114,9 @@ impl From<Arc<Image>> for ImageSource {
     }
 }
 
-impl<F: Fn(&mut WindowContext) -> Option<Result<Arc<RenderImage>, ImageCacheError>> + 'static>
-    From<F> for ImageSource
+impl<
+        F: Fn(&mut Window, &mut App) -> Option<Result<Arc<RenderImage>, ImageCacheError>> + 'static,
+    > From<F> for ImageSource
 {
     fn from(value: F) -> Self {
         Self::Custom(Arc::new(value))
@@ -248,14 +249,15 @@ impl Element for Img {
     fn request_layout(
         &mut self,
         global_id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
         let mut layout_state = ImgLayoutState {
             frame_index: 0,
             replacement: None,
         };
 
-        cx.with_optional_element_state(global_id, |state, cx| {
+        window.with_optional_element_state(global_id, |state, window| {
             let mut state = state.map(|state| {
                 state.unwrap_or(ImgState {
                     frame_index: 0,
@@ -266,12 +268,14 @@ impl Element for Img {
 
             let frame_index = state.as_ref().map(|state| state.frame_index).unwrap_or(0);
 
-            let layout_id = self
-                .interactivity
-                .request_layout(global_id, cx, |mut style, cx| {
+            let layout_id = self.interactivity.request_layout(
+                global_id,
+                window,
+                cx,
+                |mut style, window, cx| {
                     let mut replacement_id = None;
 
-                    match self.source.use_data(cx) {
+                    match self.source.use_data(window, cx) {
                         Some(Ok(data)) => {
                             if let Some(state) = &mut state {
                                 let frame_count = data.frame_count();
@@ -324,13 +328,13 @@ impl Element for Img {
                             }
 
                             if global_id.is_some() && data.frame_count() > 1 {
-                                cx.request_animation_frame();
+                                window.request_animation_frame();
                             }
                         }
                         Some(_err) => {
                             if let Some(fallback) = self.style.fallback.as_ref() {
                                 let mut element = fallback();
-                                replacement_id = Some(element.request_layout(cx));
+                                replacement_id = Some(element.request_layout(window, cx));
                                 layout_state.replacement = Some(element);
                             }
                             if let Some(state) = &mut state {
@@ -343,15 +347,16 @@ impl Element for Img {
                                     if started_loading.elapsed() > LOADING_DELAY {
                                         if let Some(loading) = self.style.loading.as_ref() {
                                             let mut element = loading();
-                                            replacement_id = Some(element.request_layout(cx));
+                                            replacement_id =
+                                                Some(element.request_layout(window, cx));
                                             layout_state.replacement = Some(element);
                                         }
                                     }
                                 } else {
-                                    let parent_view_id = cx.parent_view_id();
-                                    let task = cx.spawn(|mut cx| async move {
+                                    let parent_view_id = window.parent_view_id().unwrap();
+                                    let task = window.spawn(cx, |mut cx| async move {
                                         cx.background_executor().timer(LOADING_DELAY).await;
-                                        cx.update(|cx| {
+                                        cx.update(move |_, cx| {
                                             cx.notify(parent_view_id);
                                         })
                                         .ok();
@@ -362,8 +367,9 @@ impl Element for Img {
                         }
                     }
 
-                    cx.request_layout(style, replacement_id)
-                });
+                    window.request_layout(style, replacement_id, cx)
+                },
+            );
 
             layout_state.frame_index = frame_index;
 
@@ -376,16 +382,23 @@ impl Element for Img {
         global_id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         request_layout: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Self::PrepaintState {
-        self.interactivity
-            .prepaint(global_id, bounds, bounds.size, cx, |_, _, hitbox, cx| {
+        self.interactivity.prepaint(
+            global_id,
+            bounds,
+            bounds.size,
+            window,
+            cx,
+            |_, _, hitbox, window, cx| {
                 if let Some(replacement) = &mut request_layout.replacement {
-                    replacement.prepaint(cx);
+                    replacement.prepaint(window, cx);
                 }
 
                 hitbox
-            })
+            },
+        )
     }
 
     fn paint(
@@ -394,30 +407,38 @@ impl Element for Img {
         bounds: Bounds<Pixels>,
         layout_state: &mut Self::RequestLayoutState,
         hitbox: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let source = self.source.clone();
-        self.interactivity
-            .paint(global_id, bounds, hitbox.as_ref(), cx, |style, cx| {
-                let corner_radii = style.corner_radii.to_pixels(bounds.size, cx.rem_size());
-
-                if let Some(Ok(data)) = source.use_data(cx) {
+        self.interactivity.paint(
+            global_id,
+            bounds,
+            hitbox.as_ref(),
+            window,
+            cx,
+            |style, window, cx| {
+                let corner_radii = style.corner_radii.to_pixels(bounds.size, window.rem_size());
+
+                if let Some(Ok(data)) = source.use_data(window, cx) {
                     let new_bounds = self
                         .style
                         .object_fit
                         .get_bounds(bounds, data.size(layout_state.frame_index));
-                    cx.paint_image(
-                        new_bounds,
-                        corner_radii,
-                        data.clone(),
-                        layout_state.frame_index,
-                        self.style.grayscale,
-                    )
-                    .log_err();
+                    window
+                        .paint_image(
+                            new_bounds,
+                            corner_radii,
+                            data.clone(),
+                            layout_state.frame_index,
+                            self.style.grayscale,
+                        )
+                        .log_err();
                 } else if let Some(replacement) = &mut layout_state.replacement {
-                    replacement.paint(cx);
+                    replacement.paint(window, cx);
                 }
-            })
+            },
+        )
     }
 }
 
@@ -448,13 +469,14 @@ impl StatefulInteractiveElement for Img {}
 impl ImageSource {
     pub(crate) fn use_data(
         &self,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
         match self {
-            ImageSource::Resource(resource) => cx.use_asset::<ImgResourceLoader>(&resource),
-            ImageSource::Custom(loading_fn) => loading_fn(cx),
+            ImageSource::Resource(resource) => window.use_asset::<ImgResourceLoader>(&resource, cx),
+            ImageSource::Custom(loading_fn) => loading_fn(window, cx),
             ImageSource::Render(data) => Some(Ok(data.to_owned())),
-            ImageSource::Image(data) => cx.use_asset::<AssetLogger<ImageDecoder>>(data),
+            ImageSource::Image(data) => window.use_asset::<AssetLogger<ImageDecoder>>(data, cx),
         }
     }
 }
@@ -468,7 +490,7 @@ impl Asset for ImageDecoder {
 
     fn load(
         source: Self::Source,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> impl Future<Output = Self::Output> + Send + 'static {
         let renderer = cx.svg_renderer();
         async move { source.to_image_data(renderer).map_err(Into::into) }
@@ -485,7 +507,7 @@ impl Asset for ImageAssetLoader {
 
     fn load(
         source: Self::Source,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> impl Future<Output = Self::Output> + Send + 'static {
         let client = cx.http_client();
         // TODO: Can we make SVGs always rescale?

crates/gpui/src/elements/list.rs 🔗

@@ -8,9 +8,9 @@
 //! If all of your elements are the same height, see [`UniformList`] for a simpler API
 
 use crate::{
-    point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, DispatchPhase, Edges,
+    point, px, size, AnyElement, App, AvailableSpace, Bounds, ContentMask, DispatchPhase, Edges,
     Element, FocusHandle, GlobalElementId, Hitbox, IntoElement, Pixels, Point, ScrollWheelEvent,
-    Size, Style, StyleRefinement, Styled, WindowContext,
+    Size, Style, StyleRefinement, Styled, Window,
 };
 use collections::VecDeque;
 use refineable::Refineable as _;
@@ -49,14 +49,14 @@ pub struct ListState(Rc<RefCell<StateInner>>);
 struct StateInner {
     last_layout_bounds: Option<Bounds<Pixels>>,
     last_padding: Option<Edges<Pixels>>,
-    render_item: Box<dyn FnMut(usize, &mut WindowContext) -> AnyElement>,
+    render_item: Box<dyn FnMut(usize, &mut Window, &mut App) -> AnyElement>,
     items: SumTree<ListItem>,
     logical_scroll_top: Option<ListOffset>,
     alignment: ListAlignment,
     overdraw: Pixels,
     reset: bool,
     #[allow(clippy::type_complexity)]
-    scroll_handler: Option<Box<dyn FnMut(&ListScrollEvent, &mut WindowContext)>>,
+    scroll_handler: Option<Box<dyn FnMut(&ListScrollEvent, &mut Window, &mut App)>>,
 }
 
 /// Whether the list is scrolling from top to bottom or bottom to top.
@@ -146,12 +146,12 @@ impl ListItem {
         }
     }
 
-    fn contains_focused(&self, cx: &WindowContext) -> bool {
+    fn contains_focused(&self, window: &Window, cx: &App) -> bool {
         match self {
             ListItem::Unmeasured { focus_handle } | ListItem::Measured { focus_handle, .. } => {
                 focus_handle
                     .as_ref()
-                    .is_some_and(|handle| handle.contains_focused(cx))
+                    .is_some_and(|handle| handle.contains_focused(window, cx))
             }
         }
     }
@@ -186,7 +186,7 @@ impl ListState {
         render_item: R,
     ) -> Self
     where
-        R: 'static + FnMut(usize, &mut WindowContext) -> AnyElement,
+        R: 'static + FnMut(usize, &mut Window, &mut App) -> AnyElement,
     {
         let this = Self(Rc::new(RefCell::new(StateInner {
             last_layout_bounds: None,
@@ -272,7 +272,7 @@ impl ListState {
     /// Set a handler that will be called when the list is scrolled.
     pub fn set_scroll_handler(
         &self,
-        handler: impl FnMut(&ListScrollEvent, &mut WindowContext) + 'static,
+        handler: impl FnMut(&ListScrollEvent, &mut Window, &mut App) + 'static,
     ) {
         self.0.borrow_mut().scroll_handler = Some(Box::new(handler))
     }
@@ -371,7 +371,8 @@ impl StateInner {
         scroll_top: &ListOffset,
         height: Pixels,
         delta: Point<Pixels>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         // Drop scroll events after a reset, since we can't calculate
         // the new logical scroll top without the item heights
@@ -407,11 +408,12 @@ impl StateInner {
                     count: self.items.summary().count,
                     is_scrolled: self.logical_scroll_top.is_some(),
                 },
+                window,
                 cx,
             );
         }
 
-        cx.refresh();
+        window.refresh();
     }
 
     fn logical_scroll_top(&self) -> ListOffset {
@@ -439,7 +441,8 @@ impl StateInner {
         available_width: Option<Pixels>,
         available_height: Pixels,
         padding: &Edges<Pixels>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> LayoutItemsResponse {
         let old_items = self.items.clone();
         let mut measured_items = VecDeque::new();
@@ -472,8 +475,8 @@ impl StateInner {
             // If we're within the visible area or the height wasn't cached, render and measure the item's element
             if visible_height < available_height || size.is_none() {
                 let item_index = scroll_top.item_ix + ix;
-                let mut element = (self.render_item)(item_index, cx);
-                let element_size = element.layout_as_root(available_item_space, cx);
+                let mut element = (self.render_item)(item_index, window, cx);
+                let element_size = element.layout_as_root(available_item_space, window, cx);
                 size = Some(element_size);
                 if visible_height < available_height {
                     item_layouts.push_back(ItemLayout {
@@ -481,7 +484,7 @@ impl StateInner {
                         element,
                         size: element_size,
                     });
-                    if item.contains_focused(cx) {
+                    if item.contains_focused(window, cx) {
                         rendered_focused_item = true;
                     }
                 }
@@ -507,8 +510,8 @@ impl StateInner {
                 cursor.prev(&());
                 if let Some(item) = cursor.item() {
                     let item_index = cursor.start().0;
-                    let mut element = (self.render_item)(item_index, cx);
-                    let element_size = element.layout_as_root(available_item_space, cx);
+                    let mut element = (self.render_item)(item_index, window, cx);
+                    let element_size = element.layout_as_root(available_item_space, window, cx);
                     let focus_handle = item.focus_handle();
                     rendered_height += element_size.height;
                     measured_items.push_front(ListItem::Measured {
@@ -520,7 +523,7 @@ impl StateInner {
                         element,
                         size: element_size,
                     });
-                    if item.contains_focused(cx) {
+                    if item.contains_focused(window, cx) {
                         rendered_focused_item = true;
                     }
                 } else {
@@ -556,8 +559,8 @@ impl StateInner {
                 let size = if let ListItem::Measured { size, .. } = item {
                     *size
                 } else {
-                    let mut element = (self.render_item)(cursor.start().0, cx);
-                    element.layout_as_root(available_item_space, cx)
+                    let mut element = (self.render_item)(cursor.start().0, window, cx);
+                    element.layout_as_root(available_item_space, window, cx)
                 };
 
                 leading_overdraw += size.height;
@@ -587,10 +590,10 @@ impl StateInner {
                 .filter::<_, Count>(&(), |summary| summary.has_focus_handles);
             cursor.next(&());
             while let Some(item) = cursor.item() {
-                if item.contains_focused(cx) {
+                if item.contains_focused(window, cx) {
                     let item_index = cursor.start().0;
-                    let mut element = (self.render_item)(cursor.start().0, cx);
-                    let size = element.layout_as_root(available_item_space, cx);
+                    let mut element = (self.render_item)(cursor.start().0, window, cx);
+                    let size = element.layout_as_root(available_item_space, window, cx);
                     item_layouts.push_back(ItemLayout {
                         index: item_index,
                         element,
@@ -614,25 +617,31 @@ impl StateInner {
         bounds: Bounds<Pixels>,
         padding: Edges<Pixels>,
         autoscroll: bool,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Result<LayoutItemsResponse, ListOffset> {
-        cx.transact(|cx| {
-            let mut layout_response =
-                self.layout_items(Some(bounds.size.width), bounds.size.height, &padding, cx);
+        window.transact(|window| {
+            let mut layout_response = self.layout_items(
+                Some(bounds.size.width),
+                bounds.size.height,
+                &padding,
+                window,
+                cx,
+            );
 
             // Avoid honoring autoscroll requests from elements other than our children.
-            cx.take_autoscroll();
+            window.take_autoscroll();
 
             // Only paint the visible items, if there is actually any space for them (taking padding into account)
             if bounds.size.height > padding.top + padding.bottom {
                 let mut item_origin = bounds.origin + Point::new(px(0.), padding.top);
                 item_origin.y -= layout_response.scroll_top.offset_in_item;
                 for item in &mut layout_response.item_layouts {
-                    cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
-                        item.element.prepaint_at(item_origin, cx);
+                    window.with_content_mask(Some(ContentMask { bounds }), |window| {
+                        item.element.prepaint_at(item_origin, window, cx);
                     });
 
-                    if let Some(autoscroll_bounds) = cx.take_autoscroll() {
+                    if let Some(autoscroll_bounds) = window.take_autoscroll() {
                         if autoscroll {
                             if autoscroll_bounds.top() < bounds.top() {
                                 return Err(ListOffset {
@@ -653,12 +662,13 @@ impl StateInner {
                                     let Some(item) = cursor.item() else { break };
 
                                     let size = item.size().unwrap_or_else(|| {
-                                        let mut item = (self.render_item)(cursor.start().0, cx);
+                                        let mut item =
+                                            (self.render_item)(cursor.start().0, window, cx);
                                         let item_available_size = size(
                                             bounds.size.width.into(),
                                             AvailableSpace::MinContent,
                                         );
-                                        item.layout_as_root(item_available_size, cx)
+                                        item.layout_as_root(item_available_size, window, cx)
                                     });
                                     height -= size.height;
                                 }
@@ -716,14 +726,15 @@ impl Element for List {
     fn request_layout(
         &mut self,
         _id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (crate::LayoutId, Self::RequestLayoutState) {
         let layout_id = match self.sizing_behavior {
             ListSizingBehavior::Infer => {
                 let mut style = Style::default();
                 style.overflow.y = Overflow::Scroll;
                 style.refine(&self.style);
-                cx.with_text_style(style.text_style().cloned(), |cx| {
+                window.with_text_style(style.text_style().cloned(), |window| {
                     let state = &mut *self.state.0.borrow_mut();
 
                     let available_height = if let Some(last_bounds) = state.last_layout_bounds {
@@ -735,18 +746,19 @@ impl Element for List {
                     };
                     let padding = style.padding.to_pixels(
                         state.last_layout_bounds.unwrap_or_default().size.into(),
-                        cx.rem_size(),
+                        window.rem_size(),
                     );
 
-                    let layout_response = state.layout_items(None, available_height, &padding, cx);
+                    let layout_response =
+                        state.layout_items(None, available_height, &padding, window, cx);
                     let max_element_width = layout_response.max_item_width;
 
                     let summary = state.items.summary();
                     let total_height = summary.height;
 
-                    cx.request_measured_layout(
+                    window.request_measured_layout(
                         style,
-                        move |known_dimensions, available_space, _cx| {
+                        move |known_dimensions, available_space, _window, _cx| {
                             let width =
                                 known_dimensions
                                     .width
@@ -770,8 +782,8 @@ impl Element for List {
             ListSizingBehavior::Auto => {
                 let mut style = Style::default();
                 style.refine(&self.style);
-                cx.with_text_style(style.text_style().cloned(), |cx| {
-                    cx.request_layout(style, None)
+                window.with_text_style(style.text_style().cloned(), |window| {
+                    window.request_layout(style, None, cx)
                 })
             }
         };
@@ -783,7 +795,8 @@ impl Element for List {
         _id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         _: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> ListPrepaintState {
         let state = &mut *self.state.0.borrow_mut();
         state.reset = false;
@@ -791,7 +804,7 @@ impl Element for List {
         let mut style = Style::default();
         style.refine(&self.style);
 
-        let hitbox = cx.insert_hitbox(bounds, false);
+        let hitbox = window.insert_hitbox(bounds, false);
 
         // If the width of the list has changed, invalidate all cached item heights
         if state.last_layout_bounds.map_or(true, |last_bounds| {
@@ -807,12 +820,16 @@ impl Element for List {
             state.items = new_items;
         }
 
-        let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
-        let layout = match state.prepaint_items(bounds, padding, true, cx) {
+        let padding = style
+            .padding
+            .to_pixels(bounds.size.into(), window.rem_size());
+        let layout = match state.prepaint_items(bounds, padding, true, window, cx) {
             Ok(layout) => layout,
             Err(autoscroll_request) => {
                 state.logical_scroll_top = Some(autoscroll_request);
-                state.prepaint_items(bounds, padding, false, cx).unwrap()
+                state
+                    .prepaint_items(bounds, padding, false, window, cx)
+                    .unwrap()
             }
         };
 
@@ -827,11 +844,12 @@ impl Element for List {
         bounds: Bounds<crate::Pixels>,
         _: &mut Self::RequestLayoutState,
         prepaint: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
+        window.with_content_mask(Some(ContentMask { bounds }), |window| {
             for item in &mut prepaint.layout.item_layouts {
-                item.element.paint(cx);
+                item.element.paint(window, cx);
             }
         });
 
@@ -839,12 +857,13 @@ impl Element for List {
         let height = bounds.size.height;
         let scroll_top = prepaint.layout.scroll_top;
         let hitbox_id = prepaint.hitbox.id;
-        cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
-            if phase == DispatchPhase::Bubble && hitbox_id.is_hovered(cx) {
+        window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
+            if phase == DispatchPhase::Bubble && hitbox_id.is_hovered(window) {
                 list_state.0.borrow_mut().scroll(
                     &scroll_top,
                     height,
                     event.delta.pixel_delta(px(20.)),
+                    window,
                     cx,
                 )
             }
@@ -952,7 +971,7 @@ mod test {
 
         let cx = cx.add_empty_window();
 
-        let state = ListState::new(5, crate::ListAlignment::Top, px(10.), |_, _| {
+        let state = ListState::new(5, crate::ListAlignment::Top, px(10.), |_, _, _| {
             div().h(px(10.)).w_full().into_any()
         });
 
@@ -963,7 +982,7 @@ mod test {
         });
 
         // Paint
-        cx.draw(point(px(0.), px(0.)), size(px(100.), px(20.)), |_| {
+        cx.draw(point(px(0.), px(0.)), size(px(100.), px(20.)), |_, _| {
             list(state.clone()).w_full().h_full()
         });
 

crates/gpui/src/elements/surface.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
-    Bounds, Element, ElementId, GlobalElementId, IntoElement, LayoutId, ObjectFit, Pixels, Style,
-    StyleRefinement, Styled, WindowContext,
+    App, Bounds, Element, ElementId, GlobalElementId, IntoElement, LayoutId, ObjectFit, Pixels,
+    Style, StyleRefinement, Styled, Window,
 };
 #[cfg(target_os = "macos")]
 use media::core_video::CVImageBuffer;
@@ -56,11 +56,12 @@ impl Element for Surface {
     fn request_layout(
         &mut self,
         _global_id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
         let mut style = Style::default();
         style.refine(&self.style);
-        let layout_id = cx.request_layout(style, []);
+        let layout_id = window.request_layout(style, [], cx);
         (layout_id, ())
     }
 
@@ -69,7 +70,8 @@ impl Element for Surface {
         _global_id: Option<&GlobalElementId>,
         _bounds: Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
-        _cx: &mut WindowContext,
+        _window: &mut Window,
+        _cx: &mut App,
     ) -> Self::PrepaintState {
     }
 
@@ -79,7 +81,8 @@ impl Element for Surface {
         #[cfg_attr(not(target_os = "macos"), allow(unused_variables))] bounds: Bounds<Pixels>,
         _: &mut Self::RequestLayoutState,
         _: &mut Self::PrepaintState,
-        #[cfg_attr(not(target_os = "macos"), allow(unused_variables))] cx: &mut WindowContext,
+        #[cfg_attr(not(target_os = "macos"), allow(unused_variables))] window: &mut Window,
+        _: &mut App,
     ) {
         match &self.source {
             #[cfg(target_os = "macos")]
@@ -87,7 +90,7 @@ impl Element for Surface {
                 let size = crate::size(surface.width().into(), surface.height().into());
                 let new_bounds = self.object_fit.get_bounds(bounds, size);
                 // TODO: Add support for corner_radii
-                cx.paint_surface(new_bounds, surface.clone());
+                window.paint_surface(new_bounds, surface.clone());
             }
             #[allow(unreachable_patterns)]
             _ => {}

crates/gpui/src/elements/svg.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
-    geometry::Negate as _, point, px, radians, size, Bounds, Element, GlobalElementId, Hitbox,
+    geometry::Negate as _, point, px, radians, size, App, Bounds, Element, GlobalElementId, Hitbox,
     InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, Point, Radians, SharedString,
-    Size, StyleRefinement, Styled, TransformationMatrix, WindowContext,
+    Size, StyleRefinement, Styled, TransformationMatrix, Window,
 };
 use util::ResultExt;
 
@@ -47,11 +47,14 @@ impl Element for Svg {
     fn request_layout(
         &mut self,
         global_id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
-        let layout_id = self
-            .interactivity
-            .request_layout(global_id, cx, |style, cx| cx.request_layout(style, None));
+        let layout_id =
+            self.interactivity
+                .request_layout(global_id, window, cx, |style, window, cx| {
+                    window.request_layout(style, None, cx)
+                });
         (layout_id, ())
     }
 
@@ -60,10 +63,17 @@ impl Element for Svg {
         global_id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<Hitbox> {
-        self.interactivity
-            .prepaint(global_id, bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
+        self.interactivity.prepaint(
+            global_id,
+            bounds,
+            bounds.size,
+            window,
+            cx,
+            |_, _, hitbox, _, _| hitbox,
+        )
     }
 
     fn paint(
@@ -72,25 +82,33 @@ impl Element for Svg {
         bounds: Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
         hitbox: &mut Option<Hitbox>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) where
         Self: Sized,
     {
-        self.interactivity
-            .paint(global_id, bounds, hitbox.as_ref(), cx, |style, cx| {
+        self.interactivity.paint(
+            global_id,
+            bounds,
+            hitbox.as_ref(),
+            window,
+            cx,
+            |style, window, cx| {
                 if let Some((path, color)) = self.path.as_ref().zip(style.text.color) {
                     let transformation = self
                         .transformation
                         .as_ref()
                         .map(|transformation| {
-                            transformation.into_matrix(bounds.center(), cx.scale_factor())
+                            transformation.into_matrix(bounds.center(), window.scale_factor())
                         })
                         .unwrap_or_default();
 
-                    cx.paint_svg(bounds, path.clone(), transformation, color)
+                    window
+                        .paint_svg(bounds, path.clone(), transformation, color, cx)
                         .log_err();
                 }
-            })
+            },
+        )
     }
 }
 

crates/gpui/src/elements/text.rs 🔗

@@ -1,9 +1,8 @@
 use crate::{
-    register_tooltip_mouse_handlers, set_tooltip_on_window, ActiveTooltip, AnyView, Bounds,
+    register_tooltip_mouse_handlers, set_tooltip_on_window, ActiveTooltip, AnyView, App, Bounds,
     DispatchPhase, Element, ElementId, GlobalElementId, HighlightStyle, Hitbox, IntoElement,
     LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point, SharedString, Size,
-    TextRun, TextStyle, TooltipId, Truncate, WhiteSpace, WindowContext, WrappedLine,
-    WrappedLineLayout,
+    TextRun, TextStyle, TooltipId, Truncate, WhiteSpace, Window, WrappedLine, WrappedLineLayout,
 };
 use anyhow::anyhow;
 use parking_lot::{Mutex, MutexGuard};
@@ -28,10 +27,11 @@ impl Element for &'static str {
     fn request_layout(
         &mut self,
         _id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
         let mut state = TextLayout::default();
-        let layout_id = state.layout(SharedString::from(*self), None, cx);
+        let layout_id = state.layout(SharedString::from(*self), None, window, cx);
         (layout_id, state)
     }
 
@@ -40,7 +40,8 @@ impl Element for &'static str {
         _id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         text_layout: &mut Self::RequestLayoutState,
-        _cx: &mut WindowContext,
+        _window: &mut Window,
+        _cx: &mut App,
     ) {
         text_layout.prepaint(bounds, self)
     }
@@ -51,9 +52,10 @@ impl Element for &'static str {
         _bounds: Bounds<Pixels>,
         text_layout: &mut TextLayout,
         _: &mut (),
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        text_layout.paint(self, cx)
+        text_layout.paint(self, window, cx)
     }
 }
 
@@ -86,10 +88,11 @@ impl Element for SharedString {
 
         _id: Option<&GlobalElementId>,
 
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
         let mut state = TextLayout::default();
-        let layout_id = state.layout(self.clone(), None, cx);
+        let layout_id = state.layout(self.clone(), None, window, cx);
         (layout_id, state)
     }
 
@@ -98,7 +101,8 @@ impl Element for SharedString {
         _id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         text_layout: &mut Self::RequestLayoutState,
-        _cx: &mut WindowContext,
+        _window: &mut Window,
+        _cx: &mut App,
     ) {
         text_layout.prepaint(bounds, self.as_ref())
     }
@@ -109,9 +113,10 @@ impl Element for SharedString {
         _bounds: Bounds<Pixels>,
         text_layout: &mut Self::RequestLayoutState,
         _: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        text_layout.paint(self.as_ref(), cx)
+        text_layout.paint(self.as_ref(), window, cx)
     }
 }
 
@@ -197,9 +202,12 @@ impl Element for StyledText {
 
         _id: Option<&GlobalElementId>,
 
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
-        let layout_id = self.layout.layout(self.text.clone(), self.runs.take(), cx);
+        let layout_id = self
+            .layout
+            .layout(self.text.clone(), self.runs.take(), window, cx);
         (layout_id, ())
     }
 
@@ -208,7 +216,8 @@ impl Element for StyledText {
         _id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         _: &mut Self::RequestLayoutState,
-        _cx: &mut WindowContext,
+        _window: &mut Window,
+        _cx: &mut App,
     ) {
         self.layout.prepaint(bounds, &self.text)
     }
@@ -219,9 +228,10 @@ impl Element for StyledText {
         _bounds: Bounds<Pixels>,
         _: &mut Self::RequestLayoutState,
         _: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        self.layout.paint(&self.text, cx)
+        self.layout.paint(&self.text, window, cx)
     }
 }
 
@@ -256,13 +266,14 @@ impl TextLayout {
         &self,
         text: SharedString,
         runs: Option<Vec<TextRun>>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        _: &mut App,
     ) -> LayoutId {
-        let text_style = cx.text_style();
-        let font_size = text_style.font_size.to_pixels(cx.rem_size());
+        let text_style = window.text_style();
+        let font_size = text_style.font_size.to_pixels(window.rem_size());
         let line_height = text_style
             .line_height
-            .to_pixels(font_size.into(), cx.rem_size());
+            .to_pixels(font_size.into(), window.rem_size());
 
         let mut runs = if let Some(runs) = runs {
             runs
@@ -270,10 +281,10 @@ impl TextLayout {
             vec![text_style.to_run(text.len())]
         };
 
-        let layout_id = cx.request_measured_layout(Default::default(), {
+        let layout_id = window.request_measured_layout(Default::default(), {
             let element_state = self.clone();
 
-            move |known_dimensions, available_space, cx| {
+            move |known_dimensions, available_space, window, cx| {
                 let wrap_width = if text_style.white_space == WhiteSpace::Normal {
                     known_dimensions.width.or(match available_space.width {
                         crate::AvailableSpace::Definite(x) => Some(x),
@@ -312,7 +323,7 @@ impl TextLayout {
                     text.clone()
                 };
 
-                let Some(lines) = cx
+                let Some(lines) = window
                     .text_system()
                     .shape_text(
                         text, font_size, &runs, wrap_width, // Wrap if we know the width.
@@ -360,7 +371,7 @@ impl TextLayout {
         element_state.bounds = Some(bounds);
     }
 
-    fn paint(&self, text: &str, cx: &mut WindowContext) {
+    fn paint(&self, text: &str, window: &mut Window, cx: &mut App) {
         let element_state = self.lock();
         let element_state = element_state
             .as_ref()
@@ -374,7 +385,7 @@ impl TextLayout {
         let line_height = element_state.line_height;
         let mut line_origin = bounds.origin;
         for line in &element_state.lines {
-            line.paint(line_origin, line_height, cx).log_err();
+            line.paint(line_origin, line_height, window, cx).log_err();
             line_origin.y += line.size(line_height).height;
         }
     }
@@ -503,9 +514,9 @@ pub struct InteractiveText {
     element_id: ElementId,
     text: StyledText,
     click_listener:
-        Option<Box<dyn Fn(&[Range<usize>], InteractiveTextClickEvent, &mut WindowContext)>>,
-    hover_listener: Option<Box<dyn Fn(Option<usize>, MouseMoveEvent, &mut WindowContext)>>,
-    tooltip_builder: Option<Rc<dyn Fn(usize, &mut WindowContext) -> Option<AnyView>>>,
+        Option<Box<dyn Fn(&[Range<usize>], InteractiveTextClickEvent, &mut Window, &mut App)>>,
+    hover_listener: Option<Box<dyn Fn(Option<usize>, MouseMoveEvent, &mut Window, &mut App)>>,
+    tooltip_builder: Option<Rc<dyn Fn(usize, &mut Window, &mut App) -> Option<AnyView>>>,
     tooltip_id: Option<TooltipId>,
     clickable_ranges: Vec<Range<usize>>,
 }
@@ -543,13 +554,13 @@ impl InteractiveText {
     pub fn on_click(
         mut self,
         ranges: Vec<Range<usize>>,
-        listener: impl Fn(usize, &mut WindowContext) + 'static,
+        listener: impl Fn(usize, &mut Window, &mut App) + 'static,
     ) -> Self {
-        self.click_listener = Some(Box::new(move |ranges, event, cx| {
+        self.click_listener = Some(Box::new(move |ranges, event, window, cx| {
             for (range_ix, range) in ranges.iter().enumerate() {
                 if range.contains(&event.mouse_down_index) && range.contains(&event.mouse_up_index)
                 {
-                    listener(range_ix, cx);
+                    listener(range_ix, window, cx);
                 }
             }
         }));
@@ -561,7 +572,7 @@ impl InteractiveText {
     /// index of the hovered character, or None if the mouse leaves the text.
     pub fn on_hover(
         mut self,
-        listener: impl Fn(Option<usize>, MouseMoveEvent, &mut WindowContext) + 'static,
+        listener: impl Fn(Option<usize>, MouseMoveEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.hover_listener = Some(Box::new(listener));
         self
@@ -570,7 +581,7 @@ impl InteractiveText {
     /// tooltip lets you specify a tooltip for a given character index in the string.
     pub fn tooltip(
         mut self,
-        builder: impl Fn(usize, &mut WindowContext) -> Option<AnyView> + 'static,
+        builder: impl Fn(usize, &mut Window, &mut App) -> Option<AnyView> + 'static,
     ) -> Self {
         self.tooltip_builder = Some(Rc::new(builder));
         self
@@ -588,9 +599,10 @@ impl Element for InteractiveText {
     fn request_layout(
         &mut self,
         _id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
-        self.text.request_layout(None, cx)
+        self.text.request_layout(None, window, cx)
     }
 
     fn prepaint(
@@ -598,26 +610,27 @@ impl Element for InteractiveText {
         global_id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         state: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Hitbox {
-        cx.with_optional_element_state::<InteractiveTextState, _>(
+        window.with_optional_element_state::<InteractiveTextState, _>(
             global_id,
-            |interactive_state, cx| {
+            |interactive_state, window| {
                 let mut interactive_state = interactive_state
                     .map(|interactive_state| interactive_state.unwrap_or_default());
 
                 if let Some(interactive_state) = interactive_state.as_mut() {
                     if self.tooltip_builder.is_some() {
                         self.tooltip_id =
-                            set_tooltip_on_window(&interactive_state.active_tooltip, cx);
+                            set_tooltip_on_window(&interactive_state.active_tooltip, window);
                     } else {
                         // If there is no longer a tooltip builder, remove the active tooltip.
                         interactive_state.active_tooltip.take();
                     }
                 }
 
-                self.text.prepaint(None, bounds, state, cx);
-                let hitbox = cx.insert_hitbox(bounds, false);
+                self.text.prepaint(None, bounds, state, window, cx);
+                let hitbox = window.insert_hitbox(bounds, false);
                 (hitbox, interactive_state)
             },
         )
@@ -629,22 +642,23 @@ impl Element for InteractiveText {
         bounds: Bounds<Pixels>,
         _: &mut Self::RequestLayoutState,
         hitbox: &mut Hitbox,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let text_layout = self.text.layout().clone();
-        cx.with_element_state::<InteractiveTextState, _>(
+        window.with_element_state::<InteractiveTextState, _>(
             global_id.unwrap(),
-            |interactive_state, cx| {
+            |interactive_state, window| {
                 let mut interactive_state = interactive_state.unwrap_or_default();
                 if let Some(click_listener) = self.click_listener.take() {
-                    let mouse_position = cx.mouse_position();
+                    let mouse_position = window.mouse_position();
                     if let Ok(ix) = text_layout.index_for_position(mouse_position) {
                         if self
                             .clickable_ranges
                             .iter()
                             .any(|range| range.contains(&ix))
                         {
-                            cx.set_cursor_style(crate::CursorStyle::PointingHand, hitbox)
+                            window.set_cursor_style(crate::CursorStyle::PointingHand, hitbox)
                         }
                     }
 
@@ -653,55 +667,58 @@ impl Element for InteractiveText {
                     if let Some(mouse_down_index) = mouse_down.get() {
                         let hitbox = hitbox.clone();
                         let clickable_ranges = mem::take(&mut self.clickable_ranges);
-                        cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
-                            if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
-                                if let Ok(mouse_up_index) =
-                                    text_layout.index_for_position(event.position)
-                                {
-                                    click_listener(
-                                        &clickable_ranges,
-                                        InteractiveTextClickEvent {
-                                            mouse_down_index,
-                                            mouse_up_index,
-                                        },
-                                        cx,
-                                    )
+                        window.on_mouse_event(
+                            move |event: &MouseUpEvent, phase, window: &mut Window, cx| {
+                                if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
+                                    if let Ok(mouse_up_index) =
+                                        text_layout.index_for_position(event.position)
+                                    {
+                                        click_listener(
+                                            &clickable_ranges,
+                                            InteractiveTextClickEvent {
+                                                mouse_down_index,
+                                                mouse_up_index,
+                                            },
+                                            window,
+                                            cx,
+                                        )
+                                    }
+
+                                    mouse_down.take();
+                                    window.refresh();
                                 }
-
-                                mouse_down.take();
-                                cx.refresh();
-                            }
-                        });
+                            },
+                        );
                     } else {
                         let hitbox = hitbox.clone();
-                        cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
-                            if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
+                        window.on_mouse_event(move |event: &MouseDownEvent, phase, window, _| {
+                            if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
                                 if let Ok(mouse_down_index) =
                                     text_layout.index_for_position(event.position)
                                 {
                                     mouse_down.set(Some(mouse_down_index));
-                                    cx.refresh();
+                                    window.refresh();
                                 }
                             }
                         });
                     }
                 }
 
-                cx.on_mouse_event({
+                window.on_mouse_event({
                     let mut hover_listener = self.hover_listener.take();
                     let hitbox = hitbox.clone();
                     let text_layout = text_layout.clone();
                     let hovered_index = interactive_state.hovered_index.clone();
-                    move |event: &MouseMoveEvent, phase, cx| {
-                        if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
+                    move |event: &MouseMoveEvent, phase, window, cx| {
+                        if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
                             let current = hovered_index.get();
                             let updated = text_layout.index_for_position(event.position).ok();
                             if current != updated {
                                 hovered_index.set(updated);
                                 if let Some(hover_listener) = hover_listener.as_ref() {
-                                    hover_listener(updated, event.clone(), cx);
+                                    hover_listener(updated, event.clone(), window, cx);
                                 }
-                                cx.refresh();
+                                window.refresh();
                             }
                         }
                     }
@@ -713,11 +730,11 @@ impl Element for InteractiveText {
                     let build_tooltip = Rc::new({
                         let tooltip_is_hoverable = false;
                         let text_layout = text_layout.clone();
-                        move |cx: &mut WindowContext| {
+                        move |window: &mut Window, cx: &mut App| {
                             text_layout
-                                .index_for_position(cx.mouse_position())
+                                .index_for_position(window.mouse_position())
                                 .ok()
-                                .and_then(|position| tooltip_builder(position, cx))
+                                .and_then(|position| tooltip_builder(position, window, cx))
                                 .map(|view| (view, tooltip_is_hoverable))
                         }
                     });
@@ -726,9 +743,11 @@ impl Element for InteractiveText {
                     let source_bounds = hitbox.bounds;
                     let check_is_hovered = Rc::new({
                         let text_layout = text_layout.clone();
-                        move |cx: &WindowContext| {
-                            text_layout.index_for_position(cx.mouse_position()).is_ok()
-                                && source_bounds.contains(&cx.mouse_position())
+                        move |window: &Window| {
+                            text_layout
+                                .index_for_position(window.mouse_position())
+                                .is_ok()
+                                && source_bounds.contains(&window.mouse_position())
                                 && pending_mouse_down.get().is_none()
                         }
                     });
@@ -737,11 +756,11 @@ impl Element for InteractiveText {
                         self.tooltip_id,
                         build_tooltip,
                         check_is_hovered,
-                        cx,
+                        window,
                     );
                 }
 
-                self.text.paint(None, bounds, &mut (), &mut (), cx);
+                self.text.paint(None, bounds, &mut (), &mut (), window, cx);
 
                 ((), interactive_state)
             },

crates/gpui/src/elements/uniform_list.rs 🔗

@@ -5,10 +5,10 @@
 //! elements with uniform height.
 
 use crate::{
-    point, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementId,
-    GlobalElementId, Hitbox, InteractiveElement, Interactivity, IntoElement, IsZero, LayoutId,
-    ListSizingBehavior, Pixels, Render, ScrollHandle, Size, StyleRefinement, Styled, View,
-    ViewContext, WindowContext,
+    point, size, AnyElement, App, AvailableSpace, Bounds, ContentMask, Context, Element, ElementId,
+    Entity, GlobalElementId, Hitbox, InteractiveElement, Interactivity, IntoElement, IsZero,
+    LayoutId, ListSizingBehavior, Pixels, Render, ScrollHandle, Size, StyleRefinement, Styled,
+    Window,
 };
 use smallvec::SmallVec;
 use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
@@ -21,10 +21,10 @@ use super::ListHorizontalSizingBehavior;
 /// uniform_list will only render the visible subset of items.
 #[track_caller]
 pub fn uniform_list<I, R, V>(
-    view: View<V>,
+    view: Entity<V>,
     id: I,
     item_count: usize,
-    f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> Vec<R>,
+    f: impl 'static + Fn(&mut V, Range<usize>, &mut Window, &mut Context<V>) -> Vec<R>,
 ) -> UniformList
 where
     I: Into<ElementId>,
@@ -35,9 +35,9 @@ where
     let mut base_style = StyleRefinement::default();
     base_style.overflow.y = Some(Overflow::Scroll);
 
-    let render_range = move |range, cx: &mut WindowContext| {
+    let render_range = move |range, window: &mut Window, cx: &mut App| {
         view.update(cx, |this, cx| {
-            f(this, range, cx)
+            f(this, range, window, cx)
                 .into_iter()
                 .map(|component| component.into_any_element())
                 .collect()
@@ -68,8 +68,9 @@ where
 pub struct UniformList {
     item_count: usize,
     item_to_measure_index: usize,
-    render_items:
-        Box<dyn for<'a> Fn(Range<usize>, &'a mut WindowContext) -> SmallVec<[AnyElement; 64]>>,
+    render_items: Box<
+        dyn for<'a> Fn(Range<usize>, &'a mut Window, &'a mut App) -> SmallVec<[AnyElement; 64]>,
+    >,
     decorations: Vec<Box<dyn UniformListDecoration>>,
     interactivity: Interactivity,
     scroll_handle: Option<UniformListScrollHandle>,
@@ -168,18 +169,21 @@ impl Element for UniformList {
     fn request_layout(
         &mut self,
         global_id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
         let max_items = self.item_count;
-        let item_size = self.measure_item(None, cx);
-        let layout_id = self
-            .interactivity
-            .request_layout(global_id, cx, |style, cx| match self.sizing_behavior {
+        let item_size = self.measure_item(None, window, cx);
+        let layout_id = self.interactivity.request_layout(
+            global_id,
+            window,
+            cx,
+            |style, window, cx| match self.sizing_behavior {
                 ListSizingBehavior::Infer => {
-                    cx.with_text_style(style.text_style().cloned(), |cx| {
-                        cx.request_measured_layout(
+                    window.with_text_style(style.text_style().cloned(), |window| {
+                        window.request_measured_layout(
                             style,
-                            move |known_dimensions, available_space, _cx| {
+                            move |known_dimensions, available_space, _window, _cx| {
                                 let desired_height = item_size.height * max_items;
                                 let width = known_dimensions.width.unwrap_or(match available_space
                                     .width
@@ -200,10 +204,12 @@ impl Element for UniformList {
                         )
                     })
                 }
-                ListSizingBehavior::Auto => cx.with_text_style(style.text_style().cloned(), |cx| {
-                    cx.request_layout(style, None)
-                }),
-            });
+                ListSizingBehavior::Auto => window
+                    .with_text_style(style.text_style().cloned(), |window| {
+                        window.request_layout(style, None, cx)
+                    }),
+            },
+        );
 
         (
             layout_id,
@@ -219,11 +225,16 @@ impl Element for UniformList {
         global_id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         frame_state: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<Hitbox> {
-        let style = self.interactivity.compute_style(global_id, None, cx);
-        let border = style.border_widths.to_pixels(cx.rem_size());
-        let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
+        let style = self
+            .interactivity
+            .compute_style(global_id, None, window, cx);
+        let border = style.border_widths.to_pixels(window.rem_size());
+        let padding = style
+            .padding
+            .to_pixels(bounds.size.into(), window.rem_size());
 
         let padded_bounds = Bounds::from_corners(
             bounds.origin + point(border.left + padding.left, border.top + padding.top),
@@ -236,7 +247,7 @@ impl Element for UniformList {
             ListHorizontalSizingBehavior::Unconstrained
         );
 
-        let longest_item_size = self.measure_item(None, cx);
+        let longest_item_size = self.measure_item(None, window, cx);
         let content_width = if can_scroll_horizontally {
             padded_bounds.size.width.max(longest_item_size.width)
         } else {
@@ -262,10 +273,13 @@ impl Element for UniformList {
             global_id,
             bounds,
             content_size,
+            window,
             cx,
-            |style, mut scroll_offset, hitbox, cx| {
-                let border = style.border_widths.to_pixels(cx.rem_size());
-                let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
+            |style, mut scroll_offset, hitbox, window, cx| {
+                let border = style.border_widths.to_pixels(window.rem_size());
+                let padding = style
+                    .padding
+                    .to_pixels(bounds.size.into(), window.rem_size());
 
                 let padded_bounds = Bounds::from_corners(
                     bounds.origin + point(border.left + padding.left, border.top),
@@ -348,15 +362,15 @@ impl Element for UniformList {
                     let items = if y_flipped {
                         let flipped_range = self.item_count.saturating_sub(visible_range.end)
                             ..self.item_count.saturating_sub(visible_range.start);
-                        let mut items = (self.render_items)(flipped_range, cx);
+                        let mut items = (self.render_items)(flipped_range, window, cx);
                         items.reverse();
                         items
                     } else {
-                        (self.render_items)(visible_range.clone(), cx)
+                        (self.render_items)(visible_range.clone(), window, cx)
                     };
 
                     let content_mask = ContentMask { bounds };
-                    cx.with_content_mask(Some(content_mask), |cx| {
+                    window.with_content_mask(Some(content_mask), |window| {
                         for (mut item, ix) in items.into_iter().zip(visible_range.clone()) {
                             let item_origin = padded_bounds.origin
                                 + point(
@@ -376,8 +390,8 @@ impl Element for UniformList {
                                 AvailableSpace::Definite(available_width),
                                 AvailableSpace::Definite(item_height),
                             );
-                            item.layout_as_root(available_space, cx);
-                            item.prepaint_at(item_origin, cx);
+                            item.layout_as_root(available_space, window, cx);
+                            item.prepaint_at(item_origin, window, cx);
                             frame_state.items.push(item);
                         }
 
@@ -399,14 +413,15 @@ impl Element for UniformList {
                                 bounds,
                                 item_height,
                                 self.item_count,
+                                window,
                                 cx,
                             );
                             let available_space = size(
                                 AvailableSpace::Definite(bounds.size.width),
                                 AvailableSpace::Definite(bounds.size.height),
                             );
-                            decoration.layout_as_root(available_space, cx);
-                            decoration.prepaint_at(bounds.origin, cx);
+                            decoration.layout_as_root(available_space, window, cx);
+                            decoration.prepaint_at(bounds.origin, window, cx);
                             frame_state.decorations.push(decoration);
                         }
                     });
@@ -423,17 +438,24 @@ impl Element for UniformList {
         bounds: Bounds<crate::Pixels>,
         request_layout: &mut Self::RequestLayoutState,
         hitbox: &mut Option<Hitbox>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        self.interactivity
-            .paint(global_id, bounds, hitbox.as_ref(), cx, |_, cx| {
+        self.interactivity.paint(
+            global_id,
+            bounds,
+            hitbox.as_ref(),
+            window,
+            cx,
+            |_, window, cx| {
                 for item in &mut request_layout.items {
-                    item.paint(cx);
+                    item.paint(window, cx);
                 }
                 for decoration in &mut request_layout.decorations {
-                    decoration.paint(cx);
+                    decoration.paint(window, cx);
                 }
-            })
+            },
+        )
     }
 }
 
@@ -456,7 +478,8 @@ pub trait UniformListDecoration {
         bounds: Bounds<Pixels>,
         item_height: Pixels,
         item_count: usize,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> AnyElement;
 }
 
@@ -498,13 +521,18 @@ impl UniformList {
         self
     }
 
-    fn measure_item(&self, list_width: Option<Pixels>, cx: &mut WindowContext) -> Size<Pixels> {
+    fn measure_item(
+        &self,
+        list_width: Option<Pixels>,
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Size<Pixels> {
         if self.item_count == 0 {
             return Size::default();
         }
 
         let item_ix = cmp::min(self.item_to_measure_index, self.item_count - 1);
-        let mut items = (self.render_items)(item_ix..item_ix + 1, cx);
+        let mut items = (self.render_items)(item_ix..item_ix + 1, window, cx);
         let Some(mut item_to_measure) = items.pop() else {
             return Size::default();
         };
@@ -514,7 +542,7 @@ impl UniformList {
             }),
             AvailableSpace::MinContent,
         );
-        item_to_measure.layout_as_root(available_space, cx)
+        item_to_measure.layout_as_root(available_space, window, cx)
     }
 
     /// Track and render scroll state of this list with reference to the given scroll handle.

crates/gpui/src/executor.rs 🔗

@@ -1,4 +1,4 @@
-use crate::{AppContext, PlatformDispatcher};
+use crate::{App, PlatformDispatcher};
 use async_task::Runnable;
 use futures::channel::mpsc;
 use smol::prelude::*;
@@ -84,7 +84,7 @@ where
     /// Run the task to completion in the background and log any
     /// errors that occur.
     #[track_caller]
-    pub fn detach_and_log_err(self, cx: &AppContext) {
+    pub fn detach_and_log_err(self, cx: &App) {
         let location = core::panic::Location::caller();
         cx.foreground_executor()
             .spawn(self.log_tracked_err(*location))

crates/gpui/src/geometry.rs 🔗

@@ -13,7 +13,7 @@ use std::{
     ops::{Add, Div, Mul, MulAssign, Neg, Sub},
 };
 
-use crate::{AppContext, DisplayId};
+use crate::{App, DisplayId};
 
 /// Axis in a 2D cartesian space.
 #[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
@@ -725,7 +725,7 @@ pub fn bounds<T: Clone + Default + Debug>(origin: Point<T>, size: Size<T>) -> Bo
 
 impl Bounds<Pixels> {
     /// Generate a centered bounds for the given display or primary display if none is provided
-    pub fn centered(display_id: Option<DisplayId>, size: Size<Pixels>, cx: &AppContext) -> Self {
+    pub fn centered(display_id: Option<DisplayId>, size: Size<Pixels>, cx: &App) -> Self {
         let display = display_id
             .and_then(|id| cx.find_display(id))
             .or_else(|| cx.primary_display());
@@ -739,7 +739,7 @@ impl Bounds<Pixels> {
     }
 
     /// Generate maximized bounds for the given display or primary display if none is provided
-    pub fn maximized(display_id: Option<DisplayId>, cx: &AppContext) -> Self {
+    pub fn maximized(display_id: Option<DisplayId>, cx: &App) -> Self {
         let display = display_id
             .and_then(|id| cx.find_display(id))
             .or_else(|| cx.primary_display());

crates/gpui/src/global.rs 🔗

@@ -1,4 +1,4 @@
-use crate::{AppContext, BorrowAppContext};
+use crate::{App, BorrowAppContext};
 
 /// A marker trait for types that can be stored in GPUI's global state.
 ///
@@ -31,11 +31,11 @@ pub trait ReadGlobal {
     /// Returns the global instance of the implementing type.
     ///
     /// Panics if a global for that type has not been assigned.
-    fn global(cx: &AppContext) -> &Self;
+    fn global(cx: &App) -> &Self;
 }
 
 impl<T: Global> ReadGlobal for T {
-    fn global(cx: &AppContext) -> &Self {
+    fn global(cx: &App) -> &Self {
         cx.global::<T>()
     }
 }

crates/gpui/src/gpui.rs 🔗

@@ -138,7 +138,6 @@ pub use keymap::*;
 pub use platform::*;
 pub use refineable::*;
 pub use scene::*;
-use seal::Sealed;
 pub use shared_string::*;
 pub use shared_uri::*;
 pub use smol::Timer;
@@ -159,16 +158,16 @@ use taffy::TaffyLayoutEngine;
 
 /// The context trait, allows the different contexts in GPUI to be used
 /// interchangeably for certain operations.
-pub trait Context {
+pub trait AppContext {
     /// The result type for this context, used for async contexts that
     /// can't hold a direct reference to the application context.
     type Result<T>;
 
     /// Create a new model in the app context.
-    fn new_model<T: 'static>(
+    fn new<T: 'static>(
         &mut self,
-        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
-    ) -> Self::Result<Model<T>>;
+        build_model: impl FnOnce(&mut Context<'_, T>) -> T,
+    ) -> Self::Result<Entity<T>>;
 
     /// Reserve a slot for a model to be inserted later.
     /// The returned [Reservation] allows you to obtain the [EntityId] for the future model.
@@ -180,14 +179,14 @@ pub trait Context {
     fn insert_model<T: 'static>(
         &mut self,
         reservation: Reservation<T>,
-        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
-    ) -> Self::Result<Model<T>>;
+        build_model: impl FnOnce(&mut Context<'_, T>) -> T,
+    ) -> Self::Result<Entity<T>>;
 
     /// Update a model in the app context.
     fn update_model<T, R>(
         &mut self,
-        handle: &Model<T>,
-        update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
+        handle: &Entity<T>,
+        update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
     ) -> Self::Result<R>
     where
         T: 'static;
@@ -195,8 +194,8 @@ pub trait Context {
     /// Read a model from the app context.
     fn read_model<T, R>(
         &self,
-        handle: &Model<T>,
-        read: impl FnOnce(&T, &AppContext) -> R,
+        handle: &Entity<T>,
+        read: impl FnOnce(&T, &App) -> R,
     ) -> Self::Result<R>
     where
         T: 'static;
@@ -204,13 +203,13 @@ pub trait Context {
     /// Update a window for the given handle.
     fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
     where
-        F: FnOnce(AnyView, &mut WindowContext) -> T;
+        F: FnOnce(AnyView, &mut Window, &mut App) -> T;
 
     /// Read a window off of the application context.
     fn read_window<T, R>(
         &self,
         window: &WindowHandle<T>,
-        read: impl FnOnce(View<T>, &AppContext) -> R,
+        read: impl FnOnce(Entity<T>, &App) -> R,
     ) -> Result<R>
     where
         T: 'static;
@@ -229,56 +228,35 @@ impl<T: 'static> Reservation<T> {
 
 /// This trait is used for the different visual contexts in GPUI that
 /// require a window to be present.
-pub trait VisualContext: Context {
-    /// Construct a new view in the window referenced by this context.
-    fn new_view<V>(
-        &mut self,
-        build_view: impl FnOnce(&mut ViewContext<V>) -> V,
-    ) -> Self::Result<View<V>>
-    where
-        V: 'static + Render;
+pub trait VisualContext: AppContext {
+    /// Returns the handle of the window associated with this context.
+    fn window_handle(&self) -> AnyWindowHandle;
 
     /// Update a view with the given callback
-    fn update_view<V: 'static, R>(
+    fn update_window_model<T: 'static, R>(
         &mut self,
-        view: &View<V>,
-        update: impl FnOnce(&mut V, &mut ViewContext<V>) -> R,
+        model: &Entity<T>,
+        update: impl FnOnce(&mut T, &mut Window, &mut Context<T>) -> R,
     ) -> Self::Result<R>;
 
+    /// Update a view with the given callback
+    fn new_window_model<T: 'static>(
+        &mut self,
+        build_model: impl FnOnce(&mut Window, &mut Context<'_, T>) -> T,
+    ) -> Self::Result<Entity<T>>;
+
     /// Replace the root view of a window with a new view.
     fn replace_root_view<V>(
         &mut self,
-        build_view: impl FnOnce(&mut ViewContext<V>) -> V,
-    ) -> Self::Result<View<V>>
+        build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V,
+    ) -> Self::Result<Entity<V>>
     where
         V: 'static + Render;
 
-    /// Focus a view in the window, if it implements the [`FocusableView`] trait.
-    fn focus_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
-    where
-        V: FocusableView;
-
-    /// Dismiss a view in the window, if it implements the [`ManagedView`] trait.
-    fn dismiss_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
-    where
-        V: ManagedView;
-}
-
-/// A trait that allows models and views to be interchangeable in certain operations
-pub trait Entity<T>: Sealed {
-    /// The weak reference type for this entity.
-    type Weak: 'static;
-
-    /// The ID for this entity
-    fn entity_id(&self) -> EntityId;
-
-    /// Downgrade this entity to a weak reference.
-    fn downgrade(&self) -> Self::Weak;
-
-    /// Upgrade this entity from a weak reference.
-    fn upgrade_from(weak: &Self::Weak) -> Option<Self>
+    /// Focus a model in the window, if it implements the [`Focusable`] trait.
+    fn focus<V>(&mut self, model: &Entity<V>) -> Self::Result<()>
     where
-        Self: Sized;
+        V: Focusable;
 }
 
 /// A trait for tying together the types of a GPUI entity and the events it can
@@ -302,7 +280,7 @@ pub trait BorrowAppContext {
 
 impl<C> BorrowAppContext for C
 where
-    C: BorrowMut<AppContext>,
+    C: BorrowMut<App>,
 {
     fn set_global<G: Global>(&mut self, global: G) {
         self.borrow_mut().set_global(global)

crates/gpui/src/input.rs 🔗

@@ -1,4 +1,4 @@
-use crate::{Bounds, InputHandler, Pixels, UTF16Selection, View, ViewContext, WindowContext};
+use crate::{App, Bounds, Context, Entity, InputHandler, Pixels, UTF16Selection, Window};
 use std::ops::Range;
 
 /// Implement this trait to allow views to handle textual input when implementing an editor, field, etc.
@@ -13,28 +13,35 @@ pub trait ViewInputHandler: 'static + Sized {
         &mut self,
         range: Range<usize>,
         adjusted_range: &mut Option<Range<usize>>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<String>;
 
     /// See [`InputHandler::selected_text_range`] for details
     fn selected_text_range(
         &mut self,
         ignore_disabled_input: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<UTF16Selection>;
 
     /// See [`InputHandler::marked_text_range`] for details
-    fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
+    fn marked_text_range(
+        &self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Range<usize>>;
 
     /// See [`InputHandler::unmark_text`] for details
-    fn unmark_text(&mut self, cx: &mut ViewContext<Self>);
+    fn unmark_text(&mut self, window: &mut Window, cx: &mut Context<Self>);
 
     /// See [`InputHandler::replace_text_in_range`] for details
     fn replace_text_in_range(
         &mut self,
         range: Option<Range<usize>>,
         text: &str,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     );
 
     /// See [`InputHandler::replace_and_mark_text_in_range`] for details
@@ -43,7 +50,8 @@ pub trait ViewInputHandler: 'static + Sized {
         range: Option<Range<usize>>,
         new_text: &str,
         new_selected_range: Option<Range<usize>>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     );
 
     /// See [`InputHandler::bounds_for_range`] for details
@@ -51,14 +59,15 @@ pub trait ViewInputHandler: 'static + Sized {
         &mut self,
         range_utf16: Range<usize>,
         element_bounds: Bounds<Pixels>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<Bounds<Pixels>>;
 }
 
 /// The canonical implementation of [`PlatformInputHandler`]. Call [`WindowContext::handle_input`]
 /// with an instance during your element's paint.
 pub struct ElementInputHandler<V> {
-    view: View<V>,
+    view: Entity<V>,
     element_bounds: Bounds<Pixels>,
 }
 
@@ -67,7 +76,7 @@ impl<V: 'static> ElementInputHandler<V> {
     /// containing view.
     ///
     /// [element_paint]: crate::Element::paint
-    pub fn new(element_bounds: Bounds<Pixels>, view: View<V>) -> Self {
+    pub fn new(element_bounds: Bounds<Pixels>, view: Entity<V>) -> Self {
         ElementInputHandler {
             view,
             element_bounds,
@@ -79,25 +88,28 @@ impl<V: ViewInputHandler> InputHandler for ElementInputHandler<V> {
     fn selected_text_range(
         &mut self,
         ignore_disabled_input: bool,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<UTF16Selection> {
         self.view.update(cx, |view, cx| {
-            view.selected_text_range(ignore_disabled_input, cx)
+            view.selected_text_range(ignore_disabled_input, window, cx)
         })
     }
 
-    fn marked_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>> {
-        self.view.update(cx, |view, cx| view.marked_text_range(cx))
+    fn marked_text_range(&mut self, window: &mut Window, cx: &mut App) -> Option<Range<usize>> {
+        self.view
+            .update(cx, |view, cx| view.marked_text_range(window, cx))
     }
 
     fn text_for_range(
         &mut self,
         range_utf16: Range<usize>,
         adjusted_range: &mut Option<Range<usize>>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<String> {
         self.view.update(cx, |view, cx| {
-            view.text_for_range(range_utf16, adjusted_range, cx)
+            view.text_for_range(range_utf16, adjusted_range, window, cx)
         })
     }
 
@@ -105,10 +117,11 @@ impl<V: ViewInputHandler> InputHandler for ElementInputHandler<V> {
         &mut self,
         replacement_range: Option<Range<usize>>,
         text: &str,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         self.view.update(cx, |view, cx| {
-            view.replace_text_in_range(replacement_range, text, cx)
+            view.replace_text_in_range(replacement_range, text, window, cx)
         });
     }
 
@@ -117,24 +130,33 @@ impl<V: ViewInputHandler> InputHandler for ElementInputHandler<V> {
         range_utf16: Option<Range<usize>>,
         new_text: &str,
         new_selected_range: Option<Range<usize>>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         self.view.update(cx, |view, cx| {
-            view.replace_and_mark_text_in_range(range_utf16, new_text, new_selected_range, cx)
+            view.replace_and_mark_text_in_range(
+                range_utf16,
+                new_text,
+                new_selected_range,
+                window,
+                cx,
+            )
         });
     }
 
-    fn unmark_text(&mut self, cx: &mut WindowContext) {
-        self.view.update(cx, |view, cx| view.unmark_text(cx));
+    fn unmark_text(&mut self, window: &mut Window, cx: &mut App) {
+        self.view
+            .update(cx, |view, cx| view.unmark_text(window, cx));
     }
 
     fn bounds_for_range(
         &mut self,
         range_utf16: Range<usize>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<Bounds<Pixels>> {
         self.view.update(cx, |view, cx| {
-            view.bounds_for_range(range_utf16, self.element_bounds, cx)
+            view.bounds_for_range(range_utf16, self.element_bounds, window, cx)
         })
     }
 }

crates/gpui/src/interactive.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
-    point, seal::Sealed, Empty, IntoElement, Keystroke, Modifiers, Pixels, Point, Render,
-    ViewContext,
+    point, seal::Sealed, Context, Empty, IntoElement, Keystroke, Modifiers, Pixels, Point, Render,
+    Window,
 };
 use smallvec::SmallVec;
 use std::{any::Any, fmt::Debug, ops::Deref, path::PathBuf};
@@ -372,7 +372,7 @@ impl ExternalPaths {
 }
 
 impl Render for ExternalPaths {
-    fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
         // the platform will render icons for the dragged files
         Empty
     }
@@ -467,8 +467,8 @@ impl PlatformInput {
 mod test {
 
     use crate::{
-        self as gpui, div, FocusHandle, InteractiveElement, IntoElement, KeyBinding, Keystroke,
-        ParentElement, Render, TestAppContext, ViewContext, VisualContext,
+        self as gpui, div, AppContext as _, Context, FocusHandle, InteractiveElement, IntoElement,
+        KeyBinding, Keystroke, ParentElement, Render, TestAppContext, Window,
     };
 
     struct TestView {
@@ -480,19 +480,17 @@ mod test {
     actions!(test, [TestAction]);
 
     impl Render for TestView {
-        fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+        fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
             div().id("testview").child(
                 div()
                     .key_context("parent")
-                    .on_key_down(cx.listener(|this, _, cx| {
+                    .on_key_down(cx.listener(|this, _, _, cx| {
                         cx.stop_propagation();
                         this.saw_key_down = true
                     }))
-                    .on_action(
-                        cx.listener(|this: &mut TestView, _: &TestAction, _| {
-                            this.saw_action = true
-                        }),
-                    )
+                    .on_action(cx.listener(|this: &mut TestView, _: &TestAction, _, _| {
+                        this.saw_action = true
+                    }))
                     .child(
                         div()
                             .key_context("nested")
@@ -506,8 +504,8 @@ mod test {
     #[gpui::test]
     fn test_on_events(cx: &mut TestAppContext) {
         let window = cx.update(|cx| {
-            cx.open_window(Default::default(), |cx| {
-                cx.new_view(|cx| TestView {
+            cx.open_window(Default::default(), |_, cx| {
+                cx.new(|cx| TestView {
                     saw_key_down: false,
                     saw_action: false,
                     focus_handle: cx.focus_handle(),
@@ -521,14 +519,16 @@ mod test {
         });
 
         window
-            .update(cx, |test_view, cx| cx.focus(&test_view.focus_handle))
+            .update(cx, |test_view, window, _cx| {
+                window.focus(&test_view.focus_handle)
+            })
             .unwrap();
 
         cx.dispatch_keystroke(*window, Keystroke::parse("a").unwrap());
         cx.dispatch_keystroke(*window, Keystroke::parse("ctrl-g").unwrap());
 
         window
-            .update(cx, |test_view, _| {
+            .update(cx, |test_view, _, _| {
                 assert!(test_view.saw_key_down || test_view.saw_action);
                 assert!(test_view.saw_key_down);
                 assert!(test_view.saw_action);

crates/gpui/src/key_dispatch.rs 🔗

@@ -9,12 +9,12 @@
 /// actions!(editor,[Undo, Redo]);;
 ///
 /// impl Editor {
-///   fn undo(&mut self, _: &Undo, _cx: &mut ViewContext<Self>) { ... }
-///   fn redo(&mut self, _: &Redo, _cx: &mut ViewContext<Self>) { ... }
+///   fn undo(&mut self, _: &Undo, _window: &mut Window, _cx: &mut ModelContext<Self>) { ... }
+///   fn redo(&mut self, _: &Redo, _window: &mut Window, _cx: &mut ModelContext<Self>) { ... }
 /// }
 ///
 /// impl Render for Editor {
-///   fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+///   fn render(&mut self, window: &mut Window, cx: &mut ModelContext<Self>) -> impl IntoElement {
 ///     div()
 ///       .track_focus(&self.focus_handle(cx))
 ///       .keymap_context("Editor")
@@ -50,8 +50,8 @@
 ///  KeyBinding::new("cmd-k left", pane::SplitLeft, Some("Pane"))
 ///
 use crate::{
-    Action, ActionRegistry, DispatchPhase, EntityId, FocusId, KeyBinding, KeyContext, Keymap,
-    Keystroke, ModifiersChangedEvent, WindowContext,
+    Action, ActionRegistry, App, DispatchPhase, EntityId, FocusId, KeyBinding, KeyContext, Keymap,
+    Keystroke, ModifiersChangedEvent, Window,
 };
 use collections::FxHashMap;
 use smallvec::SmallVec;
@@ -123,13 +123,13 @@ pub(crate) struct DispatchResult {
     pub(crate) to_replay: SmallVec<[Replay; 1]>,
 }
 
-type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>;
-type ModifiersChangedListener = Rc<dyn Fn(&ModifiersChangedEvent, &mut WindowContext)>;
+type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Window, &mut App)>;
+type ModifiersChangedListener = Rc<dyn Fn(&ModifiersChangedEvent, &mut Window, &mut App)>;
 
 #[derive(Clone)]
 pub(crate) struct DispatchActionListener {
     pub(crate) action_type: TypeId,
-    pub(crate) listener: Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>,
+    pub(crate) listener: Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Window, &mut App)>,
 }
 
 impl DispatchTree {
@@ -333,7 +333,7 @@ impl DispatchTree {
     pub fn on_action(
         &mut self,
         action_type: TypeId,
-        listener: Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>,
+        listener: Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Window, &mut App)>,
     ) {
         self.active_node()
             .action_listeners

crates/gpui/src/platform.rs 🔗

@@ -27,11 +27,11 @@ mod test;
 mod windows;
 
 use crate::{
-    point, Action, AnyWindowHandle, AsyncWindowContext, BackgroundExecutor, Bounds, DevicePixels,
-    DispatchEventResult, Font, FontId, FontMetrics, FontRun, ForegroundExecutor, GlyphId, GpuSpecs,
-    ImageSource, Keymap, LineLayout, Pixels, PlatformInput, Point, RenderGlyphParams, RenderImage,
-    RenderImageParams, RenderSvgParams, ScaledPixels, Scene, SharedString, Size, SvgRenderer,
-    SvgSize, Task, TaskLabel, WindowContext, DEFAULT_WINDOW_SIZE,
+    point, Action, AnyWindowHandle, App, AsyncWindowContext, BackgroundExecutor, Bounds,
+    DevicePixels, DispatchEventResult, Font, FontId, FontMetrics, FontRun, ForegroundExecutor,
+    GlyphId, GpuSpecs, ImageSource, Keymap, LineLayout, Pixels, PlatformInput, Point,
+    RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, ScaledPixels, Scene,
+    SharedString, Size, SvgRenderer, SvgSize, Task, TaskLabel, Window, DEFAULT_WINDOW_SIZE,
 };
 use anyhow::{anyhow, Result};
 use async_task::Runnable;
@@ -697,14 +697,17 @@ impl PlatformInputHandler {
 
     fn selected_text_range(&mut self, ignore_disabled_input: bool) -> Option<UTF16Selection> {
         self.cx
-            .update(|cx| self.handler.selected_text_range(ignore_disabled_input, cx))
+            .update(|window, cx| {
+                self.handler
+                    .selected_text_range(ignore_disabled_input, window, cx)
+            })
             .ok()
             .flatten()
     }
 
     fn marked_text_range(&mut self) -> Option<Range<usize>> {
         self.cx
-            .update(|cx| self.handler.marked_text_range(cx))
+            .update(|window, cx| self.handler.marked_text_range(window, cx))
             .ok()
             .flatten()
     }
@@ -716,16 +719,19 @@ impl PlatformInputHandler {
         adjusted: &mut Option<Range<usize>>,
     ) -> Option<String> {
         self.cx
-            .update(|cx| self.handler.text_for_range(range_utf16, adjusted, cx))
+            .update(|window, cx| {
+                self.handler
+                    .text_for_range(range_utf16, adjusted, window, cx)
+            })
             .ok()
             .flatten()
     }
 
     fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str) {
         self.cx
-            .update(|cx| {
+            .update(|window, cx| {
                 self.handler
-                    .replace_text_in_range(replacement_range, text, cx);
+                    .replace_text_in_range(replacement_range, text, window, cx);
             })
             .ok();
     }
@@ -737,11 +743,12 @@ impl PlatformInputHandler {
         new_selected_range: Option<Range<usize>>,
     ) {
         self.cx
-            .update(|cx| {
+            .update(|window, cx| {
                 self.handler.replace_and_mark_text_in_range(
                     range_utf16,
                     new_text,
                     new_selected_range,
+                    window,
                     cx,
                 )
             })
@@ -749,12 +756,14 @@ impl PlatformInputHandler {
     }
 
     fn unmark_text(&mut self) {
-        self.cx.update(|cx| self.handler.unmark_text(cx)).ok();
+        self.cx
+            .update(|window, cx| self.handler.unmark_text(window, cx))
+            .ok();
     }
 
     fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>> {
         self.cx
-            .update(|cx| self.handler.bounds_for_range(range_utf16, cx))
+            .update(|window, cx| self.handler.bounds_for_range(range_utf16, window, cx))
             .ok()
             .flatten()
     }
@@ -764,18 +773,19 @@ impl PlatformInputHandler {
         self.handler.apple_press_and_hold_enabled()
     }
 
-    pub(crate) fn dispatch_input(&mut self, input: &str, cx: &mut WindowContext) {
-        self.handler.replace_text_in_range(None, input, cx);
+    pub(crate) fn dispatch_input(&mut self, input: &str, window: &mut Window, cx: &mut App) {
+        self.handler.replace_text_in_range(None, input, window, cx);
     }
 
-    pub fn selected_bounds(&mut self, cx: &mut WindowContext) -> Option<Bounds<Pixels>> {
-        let selection = self.handler.selected_text_range(true, cx)?;
+    pub fn selected_bounds(&mut self, window: &mut Window, cx: &mut App) -> Option<Bounds<Pixels>> {
+        let selection = self.handler.selected_text_range(true, window, cx)?;
         self.handler.bounds_for_range(
             if selection.reversed {
                 selection.range.start..selection.range.start
             } else {
                 selection.range.end..selection.range.end
             },
+            window,
             cx,
         )
     }
@@ -805,14 +815,15 @@ pub trait InputHandler: 'static {
     fn selected_text_range(
         &mut self,
         ignore_disabled_input: bool,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<UTF16Selection>;
 
     /// Get the range of the currently marked text, if any
     /// Corresponds to [markedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438250-markedrange)
     ///
     /// Return value is in terms of UTF-16 characters, from 0 to the length of the document
-    fn marked_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>>;
+    fn marked_text_range(&mut self, window: &mut Window, cx: &mut App) -> Option<Range<usize>>;
 
     /// Get the text for the given document range in UTF-16 characters
     /// Corresponds to [attributedSubstring(forProposedRange: actualRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438238-attributedsubstring)
@@ -822,7 +833,8 @@ pub trait InputHandler: 'static {
         &mut self,
         range_utf16: Range<usize>,
         adjusted_range: &mut Option<Range<usize>>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<String>;
 
     /// Replace the text in the given document range with the given text
@@ -833,7 +845,8 @@ pub trait InputHandler: 'static {
         &mut self,
         replacement_range: Option<Range<usize>>,
         text: &str,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     );
 
     /// Replace the text in the given document range with the given text,
@@ -847,12 +860,13 @@ pub trait InputHandler: 'static {
         range_utf16: Option<Range<usize>>,
         new_text: &str,
         new_selected_range: Option<Range<usize>>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     );
 
     /// Remove the IME 'composing' state from the document
     /// Corresponds to [unmarkText()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438239-unmarktext)
-    fn unmark_text(&mut self, cx: &mut WindowContext);
+    fn unmark_text(&mut self, window: &mut Window, cx: &mut App);
 
     /// Get the bounds of the given document range in screen coordinates
     /// Corresponds to [firstRect(forCharacterRange:actualRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438240-firstrect)
@@ -861,7 +875,8 @@ pub trait InputHandler: 'static {
     fn bounds_for_range(
         &mut self,
         range_utf16: Range<usize>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<Bounds<Pixels>>;
 
     /// Allows a given input context to opt into getting raw key repeats instead of
@@ -1336,9 +1351,13 @@ impl Image {
     }
 
     /// Use the GPUI `use_asset` API to make this image renderable
-    pub fn use_render_image(self: Arc<Self>, cx: &mut WindowContext) -> Option<Arc<RenderImage>> {
+    pub fn use_render_image(
+        self: Arc<Self>,
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Option<Arc<RenderImage>> {
         ImageSource::Image(self)
-            .use_data(cx)
+            .use_data(window, cx)
             .and_then(|result| result.ok())
     }
 

crates/gpui/src/platform/app_menu.rs 🔗

@@ -1,4 +1,4 @@
-use crate::{Action, AppContext, Platform, SharedString};
+use crate::{Action, App, Platform, SharedString};
 use util::ResultExt;
 
 /// A menu of the application, either a main menu or a submenu
@@ -171,7 +171,7 @@ pub enum OsAction {
     Redo,
 }
 
-pub(crate) fn init_app_menus(platform: &dyn Platform, cx: &AppContext) {
+pub(crate) fn init_app_menus(platform: &dyn Platform, cx: &App) {
     platform.on_will_open_app_menu(Box::new({
         let cx = cx.to_async();
         move || {

crates/gpui/src/platform/linux/text_system.rs 🔗

@@ -3,7 +3,7 @@ use crate::{
     FontWeight, GlyphId, LineLayout, Pixels, PlatformTextSystem, Point, RenderGlyphParams,
     ShapedGlyph, SharedString, Size, SUBPIXEL_VARIANTS,
 };
-use anyhow::{anyhow, Context, Ok, Result};
+use anyhow::{anyhow, Context as _, Ok, Result};
 use collections::HashMap;
 use cosmic_text::{
     Attrs, AttrsList, CacheKey, Family, Font as CosmicTextFont, FontSystem, ShapeBuffer, ShapeLine,

crates/gpui/src/platform/windows/platform.rs 🔗

@@ -7,7 +7,7 @@ use std::{
 };
 
 use ::util::{paths::SanitizedPath, ResultExt};
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use async_task::Runnable;
 use futures::channel::oneshot::{self, Receiver};
 use itertools::Itertools;

crates/gpui/src/platform/windows/window.rs 🔗

@@ -11,7 +11,7 @@ use std::{
 };
 
 use ::util::ResultExt;
-use anyhow::{Context, Result};
+use anyhow::{Context as _, Result};
 use async_task::Runnable;
 use futures::channel::oneshot::{self, Receiver};
 use itertools::Itertools;

crates/gpui/src/prelude.rs 🔗

@@ -3,7 +3,7 @@
 //! application to avoid having to import each trait individually.
 
 pub use crate::{
-    util::FluentBuilder, BorrowAppContext, BorrowWindow, Context as _, Element, FocusableElement,
+    util::FluentBuilder, AppContext as _, BorrowAppContext, Context, Element, FocusableElement,
     InteractiveElement, IntoElement, ParentElement, Refineable, Render, RenderOnce,
     StatefulInteractiveElement, Styled, StyledImage, VisualContext,
 };

crates/gpui/src/style.rs 🔗

@@ -5,11 +5,11 @@ use std::{
 };
 
 use crate::{
-    black, phi, point, quad, rems, size, AbsoluteLength, Background, BackgroundTag, Bounds,
+    black, phi, point, quad, rems, size, AbsoluteLength, App, Background, BackgroundTag, Bounds,
     ContentMask, Corners, CornersRefinement, CursorStyle, DefiniteLength, DevicePixels, Edges,
     EdgesRefinement, Font, FontFallbacks, FontFeatures, FontStyle, FontWeight, Hsla, Length,
     Pixels, Point, PointRefinement, Rgba, SharedString, Size, SizeRefinement, Styled, TextRun,
-    WindowContext,
+    Window,
 };
 use collections::HashSet;
 use refineable::Refineable;
@@ -550,8 +550,9 @@ impl Style {
     pub fn paint(
         &self,
         bounds: Bounds<Pixels>,
-        cx: &mut WindowContext,
-        continuation: impl FnOnce(&mut WindowContext),
+        window: &mut Window,
+        cx: &mut App,
+        continuation: impl FnOnce(&mut Window, &mut App),
     ) {
         #[cfg(debug_assertions)]
         if self.debug_below {
@@ -560,12 +561,12 @@ impl Style {
 
         #[cfg(debug_assertions)]
         if self.debug || cx.has_global::<DebugBelow>() {
-            cx.paint_quad(crate::outline(bounds, crate::red()));
+            window.paint_quad(crate::outline(bounds, crate::red()));
         }
 
-        let rem_size = cx.rem_size();
+        let rem_size = window.rem_size();
 
-        cx.paint_shadows(
+        window.paint_shadows(
             bounds,
             self.corner_radii.to_pixels(bounds.size, rem_size),
             &self.box_shadow,
@@ -585,7 +586,7 @@ impl Style {
                 None => Hsla::default(),
             };
             border_color.a = 0.;
-            cx.paint_quad(quad(
+            window.paint_quad(quad(
                 bounds,
                 self.corner_radii.to_pixels(bounds.size, rem_size),
                 background_color.unwrap_or_default(),
@@ -594,7 +595,7 @@ impl Style {
             ));
         }
 
-        continuation(cx);
+        continuation(window, cx);
 
         if self.is_border_visible() {
             let corner_radii = self.corner_radii.to_pixels(bounds.size, rem_size);
@@ -629,31 +630,31 @@ impl Style {
                 self.border_color.unwrap_or_default(),
             );
 
-            cx.with_content_mask(Some(ContentMask { bounds: top_bounds }), |cx| {
-                cx.paint_quad(quad.clone());
+            window.with_content_mask(Some(ContentMask { bounds: top_bounds }), |window| {
+                window.paint_quad(quad.clone());
             });
-            cx.with_content_mask(
+            window.with_content_mask(
                 Some(ContentMask {
                     bounds: right_bounds,
                 }),
-                |cx| {
-                    cx.paint_quad(quad.clone());
+                |window| {
+                    window.paint_quad(quad.clone());
                 },
             );
-            cx.with_content_mask(
+            window.with_content_mask(
                 Some(ContentMask {
                     bounds: bottom_bounds,
                 }),
-                |cx| {
-                    cx.paint_quad(quad.clone());
+                |window| {
+                    window.paint_quad(quad.clone());
                 },
             );
-            cx.with_content_mask(
+            window.with_content_mask(
                 Some(ContentMask {
                     bounds: left_bounds,
                 }),
-                |cx| {
-                    cx.paint_quad(quad);
+                |window| {
+                    window.paint_quad(quad);
                 },
             );
         }

crates/gpui/src/taffy.rs 🔗

@@ -1,6 +1,5 @@
 use crate::{
-    AbsoluteLength, Bounds, DefiniteLength, Edges, Length, Pixels, Point, Size, Style,
-    WindowContext,
+    AbsoluteLength, App, Bounds, DefiniteLength, Edges, Length, Pixels, Point, Size, Style, Window,
 };
 use collections::{FxHashMap, FxHashSet};
 use smallvec::SmallVec;
@@ -12,8 +11,9 @@ use taffy::{
     TaffyTree, TraversePartialTree as _,
 };
 
-type NodeMeasureFn =
-    Box<dyn FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut WindowContext) -> Size<Pixels>>;
+type NodeMeasureFn = Box<
+    dyn FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut Window, &mut App) -> Size<Pixels>,
+>;
 
 struct NodeContext {
     measure: NodeMeasureFn,
@@ -71,7 +71,7 @@ impl TaffyLayoutEngine {
         &mut self,
         style: Style,
         rem_size: Pixels,
-        measure: impl FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut WindowContext) -> Size<Pixels>
+        measure: impl FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut Window, &mut App) -> Size<Pixels>
             + 'static,
     ) -> LayoutId {
         let taffy_style = style.to_taffy(rem_size);
@@ -140,7 +140,8 @@ impl TaffyLayoutEngine {
         &mut self,
         id: LayoutId,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         // Leaving this here until we have a better instrumentation approach.
         // println!("Laying out {} children", self.count_all_children(id)?);
@@ -184,7 +185,8 @@ impl TaffyLayoutEngine {
                         height: known_dimensions.height.map(Pixels),
                     };
 
-                    (node_context.measure)(known_dimensions, available_space.into(), cx).into()
+                    (node_context.measure)(known_dimensions, available_space.into(), window, cx)
+                        .into()
                 },
             )
             .expect(EXPECT_MESSAGE);

crates/gpui/src/test.rs 🔗

@@ -102,7 +102,7 @@ impl<T: 'static> futures::Stream for Observation<T> {
 }
 
 /// observe returns a stream of the change events from the given `View` or `Model`
-pub fn observe<T: 'static>(entity: &impl Entity<T>, cx: &mut TestAppContext) -> Observation<()> {
+pub fn observe<T: 'static>(entity: &Entity<T>, cx: &mut TestAppContext) -> Observation<()> {
     let (tx, rx) = smol::channel::unbounded();
     let _subscription = cx.update(|cx| {
         cx.observe(entity, move |_, _| {

crates/gpui/src/text_system/line.rs 🔗

@@ -1,7 +1,6 @@
 use crate::{
-    black, fill, point, px, size, Bounds, Half, Hsla, LineLayout, Pixels, Point, Result,
-    SharedString, StrikethroughStyle, UnderlineStyle, WindowContext, WrapBoundary,
-    WrappedLineLayout,
+    black, fill, point, px, size, App, Bounds, Half, Hsla, LineLayout, Pixels, Point, Result,
+    SharedString, StrikethroughStyle, UnderlineStyle, Window, WrapBoundary, WrappedLineLayout,
 };
 use derive_more::{Deref, DerefMut};
 use smallvec::SmallVec;
@@ -64,7 +63,8 @@ impl ShapedLine {
         &self,
         origin: Point<Pixels>,
         line_height: Pixels,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Result<()> {
         paint_line(
             origin,
@@ -72,6 +72,7 @@ impl ShapedLine {
             line_height,
             &self.decoration_runs,
             &[],
+            window,
             cx,
         )?;
 
@@ -102,7 +103,8 @@ impl WrappedLine {
         &self,
         origin: Point<Pixels>,
         line_height: Pixels,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Result<()> {
         paint_line(
             origin,
@@ -110,6 +112,7 @@ impl WrappedLine {
             line_height,
             &self.decoration_runs,
             &self.wrap_boundaries,
+            window,
             cx,
         )?;
 
@@ -123,7 +126,8 @@ fn paint_line(
     line_height: Pixels,
     decoration_runs: &[DecorationRun],
     wrap_boundaries: &[WrapBoundary],
-    cx: &mut WindowContext,
+    window: &mut Window,
+    cx: &mut App,
 ) -> Result<()> {
     let line_bounds = Bounds::new(
         origin,
@@ -132,7 +136,7 @@ fn paint_line(
             line_height * (wrap_boundaries.len() as f32 + 1.),
         ),
     );
-    cx.paint_layer(line_bounds, |cx| {
+    window.paint_layer(line_bounds, |window| {
         let padding_top = (line_height - layout.ascent - layout.descent) / 2.;
         let baseline_offset = point(px(0.), padding_top + layout.ascent);
         let mut decoration_runs = decoration_runs.iter();
@@ -159,7 +163,7 @@ fn paint_line(
                         if glyph_origin.x == background_origin.x {
                             background_origin.x -= max_glyph_size.width.half()
                         }
-                        cx.paint_quad(fill(
+                        window.paint_quad(fill(
                             Bounds {
                                 origin: *background_origin,
                                 size: size(glyph_origin.x - background_origin.x, line_height),
@@ -173,7 +177,7 @@ fn paint_line(
                         if glyph_origin.x == underline_origin.x {
                             underline_origin.x -= max_glyph_size.width.half();
                         };
-                        cx.paint_underline(
+                        window.paint_underline(
                             *underline_origin,
                             glyph_origin.x - underline_origin.x,
                             underline_style,
@@ -187,7 +191,7 @@ fn paint_line(
                         if glyph_origin.x == strikethrough_origin.x {
                             strikethrough_origin.x -= max_glyph_size.width.half();
                         };
-                        cx.paint_strikethrough(
+                        window.paint_strikethrough(
                             *strikethrough_origin,
                             glyph_origin.x - strikethrough_origin.x,
                             strikethrough_style,
@@ -281,7 +285,7 @@ fn paint_line(
                     if background_origin.x == glyph_origin.x {
                         background_origin.x -= max_glyph_size.width.half();
                     };
-                    cx.paint_quad(fill(
+                    window.paint_quad(fill(
                         Bounds {
                             origin: background_origin,
                             size: size(width, line_height),
@@ -294,7 +298,7 @@ fn paint_line(
                     if underline_origin.x == glyph_origin.x {
                         underline_origin.x -= max_glyph_size.width.half();
                     };
-                    cx.paint_underline(
+                    window.paint_underline(
                         underline_origin,
                         glyph_origin.x - underline_origin.x,
                         &underline_style,
@@ -307,7 +311,7 @@ fn paint_line(
                     if strikethrough_origin.x == glyph_origin.x {
                         strikethrough_origin.x -= max_glyph_size.width.half();
                     };
-                    cx.paint_strikethrough(
+                    window.paint_strikethrough(
                         strikethrough_origin,
                         glyph_origin.x - strikethrough_origin.x,
                         &strikethrough_style,
@@ -319,17 +323,17 @@ fn paint_line(
                     size: max_glyph_size,
                 };
 
-                let content_mask = cx.content_mask();
+                let content_mask = window.content_mask();
                 if max_glyph_bounds.intersects(&content_mask.bounds) {
                     if glyph.is_emoji {
-                        cx.paint_emoji(
+                        window.paint_emoji(
                             glyph_origin + baseline_offset,
                             run.font_id,
                             glyph.id,
                             layout.font_size,
                         )?;
                     } else {
-                        cx.paint_glyph(
+                        window.paint_glyph(
                             glyph_origin + baseline_offset,
                             run.font_id,
                             glyph.id,
@@ -352,7 +356,7 @@ fn paint_line(
             if last_line_end_x == background_origin.x {
                 background_origin.x -= max_glyph_size.width.half()
             };
-            cx.paint_quad(fill(
+            window.paint_quad(fill(
                 Bounds {
                     origin: background_origin,
                     size: size(last_line_end_x - background_origin.x, line_height),
@@ -365,7 +369,7 @@ fn paint_line(
             if last_line_end_x == underline_start.x {
                 underline_start.x -= max_glyph_size.width.half()
             };
-            cx.paint_underline(
+            window.paint_underline(
                 underline_start,
                 last_line_end_x - underline_start.x,
                 &underline_style,
@@ -376,7 +380,7 @@ fn paint_line(
             if last_line_end_x == strikethrough_start.x {
                 strikethrough_start.x -= max_glyph_size.width.half()
             };
-            cx.paint_strikethrough(
+            window.paint_strikethrough(
                 strikethrough_start,
                 last_line_end_x - strikethrough_start.x,
                 &strikethrough_style,

crates/gpui/src/view.rs 🔗

@@ -1,33 +1,20 @@
-use crate::Empty;
 use crate::{
-    seal::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, Bounds, ContentMask, Element,
-    ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, GlobalElementId, IntoElement,
-    LayoutId, Model, PaintIndex, Pixels, PrepaintStateIndex, Render, Style, StyleRefinement,
-    TextStyle, ViewContext, VisualContext, WeakModel, WindowContext,
+    AnyElement, AnyEntity, AnyWeakEntity, App, Bounds, ContentMask, Context, Element, ElementId,
+    Entity, EntityId, GlobalElementId, IntoElement, LayoutId, PaintIndex, Pixels,
+    PrepaintStateIndex, Render, Style, StyleRefinement, TextStyle, WeakEntity,
 };
-use anyhow::{Context, Result};
+use crate::{Empty, Window};
+use anyhow::Result;
+use collections::FxHashSet;
 use refineable::Refineable;
 use std::mem;
-use std::{
-    any::{type_name, TypeId},
-    fmt,
-    hash::{Hash, Hasher},
-    ops::Range,
-};
-
-/// A view is a piece of state that can be presented on screen by implementing the [Render] trait.
-/// Views implement [Element] and can composed with other views, and every window is created with a root view.
-pub struct View<V> {
-    /// A view is just a [Model] whose type implements `Render`, and the model is accessible via this field.
-    pub model: Model<V>,
-}
-
-impl<V> Sealed for View<V> {}
+use std::{any::TypeId, fmt, ops::Range};
 
 struct AnyViewState {
     prepaint_range: Range<PrepaintStateIndex>,
     paint_range: Range<PaintIndex>,
     cache_key: ViewCacheKey,
+    accessed_entities: FxHashSet<EntityId>,
 }
 
 #[derive(Default)]
@@ -37,61 +24,7 @@ struct ViewCacheKey {
     text_style: TextStyle,
 }
 
-impl<V: 'static> Entity<V> for View<V> {
-    type Weak = WeakView<V>;
-
-    fn entity_id(&self) -> EntityId {
-        self.model.entity_id
-    }
-
-    fn downgrade(&self) -> Self::Weak {
-        WeakView {
-            model: self.model.downgrade(),
-        }
-    }
-
-    fn upgrade_from(weak: &Self::Weak) -> Option<Self>
-    where
-        Self: Sized,
-    {
-        let model = weak.model.upgrade()?;
-        Some(View { model })
-    }
-}
-
-impl<V: 'static> View<V> {
-    /// Convert this strong view reference into a weak view reference.
-    pub fn downgrade(&self) -> WeakView<V> {
-        Entity::downgrade(self)
-    }
-
-    /// Updates the view's state with the given function, which is passed a mutable reference and a context.
-    pub fn update<C, R>(
-        &self,
-        cx: &mut C,
-        f: impl FnOnce(&mut V, &mut ViewContext<V>) -> R,
-    ) -> C::Result<R>
-    where
-        C: VisualContext,
-    {
-        cx.update_view(self, f)
-    }
-
-    /// Obtain a read-only reference to this view's state.
-    pub fn read<'a>(&self, cx: &'a AppContext) -> &'a V {
-        self.model.read(cx)
-    }
-
-    /// Gets a [FocusHandle] for this view when its state implements [FocusableView].
-    pub fn focus_handle(&self, cx: &AppContext) -> FocusHandle
-    where
-        V: FocusableView,
-    {
-        self.read(cx).focus_handle(cx)
-    }
-}
-
-impl<V: Render> Element for View<V> {
+impl<V: Render> Element for Entity<V> {
     type RequestLayoutState = AnyElement;
     type PrepaintState = ();
 
@@ -102,10 +35,11 @@ impl<V: Render> Element for View<V> {
     fn request_layout(
         &mut self,
         _id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
-        let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element());
-        let layout_id = element.request_layout(cx);
+        let mut element = self.update(cx, |view, cx| view.render(window, cx).into_any_element());
+        let layout_id = element.request_layout(window, cx);
         (layout_id, element)
     }
 
@@ -114,10 +48,11 @@ impl<V: Render> Element for View<V> {
         _id: Option<&GlobalElementId>,
         _: Bounds<Pixels>,
         element: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        cx.set_view_id(self.entity_id());
-        element.prepaint(cx);
+        window.set_view_id(self.entity_id());
+        element.prepaint(window, cx);
     }
 
     fn paint(
@@ -126,110 +61,31 @@ impl<V: Render> Element for View<V> {
         _: Bounds<Pixels>,
         element: &mut Self::RequestLayoutState,
         _: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        element.paint(cx);
-    }
-}
-
-impl<V> Clone for View<V> {
-    fn clone(&self) -> Self {
-        Self {
-            model: self.model.clone(),
-        }
-    }
-}
-
-impl<T> std::fmt::Debug for View<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct(&format!("View<{}>", type_name::<T>()))
-            .field("entity_id", &self.model.entity_id)
-            .finish_non_exhaustive()
-    }
-}
-
-impl<V> Hash for View<V> {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        self.model.hash(state);
-    }
-}
-
-impl<V> PartialEq for View<V> {
-    fn eq(&self, other: &Self) -> bool {
-        self.model == other.model
-    }
-}
-
-impl<V> Eq for View<V> {}
-
-/// A weak variant of [View] which does not prevent the view from being released.
-pub struct WeakView<V> {
-    pub(crate) model: WeakModel<V>,
-}
-
-impl<V: 'static> WeakView<V> {
-    /// Gets the entity id associated with this handle.
-    pub fn entity_id(&self) -> EntityId {
-        self.model.entity_id
-    }
-
-    /// Obtain a strong handle for the view if it hasn't been released.
-    pub fn upgrade(&self) -> Option<View<V>> {
-        Entity::upgrade_from(self)
-    }
-
-    /// Updates this view's state if it hasn't been released.
-    /// Returns an error if this view has been released.
-    pub fn update<C, R>(
-        &self,
-        cx: &mut C,
-        f: impl FnOnce(&mut V, &mut ViewContext<V>) -> R,
-    ) -> Result<R>
-    where
-        C: VisualContext,
-        Result<C::Result<R>>: Flatten<R>,
-    {
-        let view = self.upgrade().context("error upgrading view")?;
-        Ok(view.update(cx, f)).flatten()
-    }
-
-    /// Assert that the view referenced by this handle has been released.
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn assert_released(&self) {
-        self.model.assert_released()
+        element.paint(window, cx);
     }
 }
 
-impl<V> Clone for WeakView<V> {
-    fn clone(&self) -> Self {
-        Self {
-            model: self.model.clone(),
-        }
-    }
-}
-
-impl<V> Hash for WeakView<V> {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        self.model.hash(state);
-    }
-}
-
-impl<V> PartialEq for WeakView<V> {
-    fn eq(&self, other: &Self) -> bool {
-        self.model == other.model
-    }
-}
-
-impl<V> Eq for WeakView<V> {}
-
 /// A dynamically-typed handle to a view, which can be downcast to a [View] for a specific type.
 #[derive(Clone, Debug)]
 pub struct AnyView {
-    model: AnyModel,
-    render: fn(&AnyView, &mut WindowContext) -> AnyElement,
+    model: AnyEntity,
+    render: fn(&AnyView, &mut Window, &mut App) -> AnyElement,
     cached_style: Option<StyleRefinement>,
 }
 
+impl<V: Render> From<Entity<V>> for AnyView {
+    fn from(value: Entity<V>) -> Self {
+        AnyView {
+            model: value.into_any(),
+            render: any_view::render::<V>,
+            cached_style: None,
+        }
+    }
+}
+
 impl AnyView {
     /// Indicate that this view should be cached when using it as an element.
     /// When using this method, the view's previous layout and paint will be recycled from the previous frame if [ViewContext::notify] has not been called since it was rendered.
@@ -249,9 +105,9 @@ impl AnyView {
 
     /// Convert this to a [View] of a specific type.
     /// If this handle does not contain a view of the specified type, returns itself in an `Err` variant.
-    pub fn downcast<T: 'static>(self) -> Result<View<T>, Self> {
+    pub fn downcast<T: 'static>(self) -> Result<Entity<T>, Self> {
         match self.model.downcast() {
-            Ok(model) => Ok(View { model }),
+            Ok(model) => Ok(model),
             Err(model) => Err(Self {
                 model,
                 render: self.render,
@@ -271,16 +127,6 @@ impl AnyView {
     }
 }
 
-impl<V: Render> From<View<V>> for AnyView {
-    fn from(value: View<V>) -> Self {
-        AnyView {
-            model: value.model.into_any(),
-            render: any_view::render::<V>,
-            cached_style: None,
-        }
-    }
-}
-
 impl PartialEq for AnyView {
     fn eq(&self, other: &Self) -> bool {
         self.model == other.model
@@ -300,16 +146,17 @@ impl Element for AnyView {
     fn request_layout(
         &mut self,
         _id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
         if let Some(style) = self.cached_style.as_ref() {
             let mut root_style = Style::default();
             root_style.refine(style);
-            let layout_id = cx.request_layout(root_style, None);
+            let layout_id = window.request_layout(root_style, None, cx);
             (layout_id, None)
         } else {
-            let mut element = (self.render)(self, cx);
-            let layout_id = element.request_layout(cx);
+            let mut element = (self.render)(self, window, cx);
+            let layout_id = element.request_layout(window, cx);
             (layout_id, Some(element))
         }
     }
@@ -319,53 +166,64 @@ impl Element for AnyView {
         global_id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         element: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<AnyElement> {
-        cx.set_view_id(self.entity_id());
+        window.set_view_id(self.entity_id());
         if self.cached_style.is_some() {
-            cx.with_element_state::<AnyViewState, _>(global_id.unwrap(), |element_state, cx| {
-                let content_mask = cx.content_mask();
-                let text_style = cx.text_style();
-
-                if let Some(mut element_state) = element_state {
-                    if element_state.cache_key.bounds == bounds
-                        && element_state.cache_key.content_mask == content_mask
-                        && element_state.cache_key.text_style == text_style
-                        && !cx.window.dirty_views.contains(&self.entity_id())
-                        && !cx.window.refreshing
-                    {
-                        let prepaint_start = cx.prepaint_index();
-                        cx.reuse_prepaint(element_state.prepaint_range.clone());
-                        let prepaint_end = cx.prepaint_index();
-                        element_state.prepaint_range = prepaint_start..prepaint_end;
-                        return (None, element_state);
+            window.with_element_state::<AnyViewState, _>(
+                global_id.unwrap(),
+                |element_state, window| {
+                    let content_mask = window.content_mask();
+                    let text_style = window.text_style();
+
+                    if let Some(mut element_state) = element_state {
+                        if element_state.cache_key.bounds == bounds
+                            && element_state.cache_key.content_mask == content_mask
+                            && element_state.cache_key.text_style == text_style
+                            && !window.dirty_views.contains(&self.entity_id())
+                            && !window.refreshing
+                        {
+                            let prepaint_start = window.prepaint_index();
+                            window.reuse_prepaint(element_state.prepaint_range.clone());
+                            cx.entities
+                                .extend_accessed(&element_state.accessed_entities);
+                            let prepaint_end = window.prepaint_index();
+                            element_state.prepaint_range = prepaint_start..prepaint_end;
+
+                            return (None, element_state);
+                        }
                     }
-                }
-
-                let refreshing = mem::replace(&mut cx.window.refreshing, true);
-                let prepaint_start = cx.prepaint_index();
-                let mut element = (self.render)(self, cx);
-                element.layout_as_root(bounds.size.into(), cx);
-                element.prepaint_at(bounds.origin, cx);
-                let prepaint_end = cx.prepaint_index();
-                cx.window.refreshing = refreshing;
-
-                (
-                    Some(element),
-                    AnyViewState {
-                        prepaint_range: prepaint_start..prepaint_end,
-                        paint_range: PaintIndex::default()..PaintIndex::default(),
-                        cache_key: ViewCacheKey {
-                            bounds,
-                            content_mask,
-                            text_style,
+
+                    let refreshing = mem::replace(&mut window.refreshing, true);
+                    let prepaint_start = window.prepaint_index();
+                    let (mut element, accessed_entities) = cx.detect_accessed_entities(|cx| {
+                        let mut element = (self.render)(self, window, cx);
+                        element.layout_as_root(bounds.size.into(), window, cx);
+                        element.prepaint_at(bounds.origin, window, cx);
+                        element
+                    });
+                    let prepaint_end = window.prepaint_index();
+                    window.refreshing = refreshing;
+
+                    (
+                        Some(element),
+                        AnyViewState {
+                            accessed_entities,
+                            prepaint_range: prepaint_start..prepaint_end,
+                            paint_range: PaintIndex::default()..PaintIndex::default(),
+                            cache_key: ViewCacheKey {
+                                bounds,
+                                content_mask,
+                                text_style,
+                            },
                         },
-                    },
-                )
-            })
+                    )
+                },
+            )
         } else {
             let mut element = element.take().unwrap();
-            element.prepaint(cx);
+            element.prepaint(window, cx);
             Some(element)
         }
     }
@@ -376,35 +234,39 @@ impl Element for AnyView {
         _bounds: Bounds<Pixels>,
         _: &mut Self::RequestLayoutState,
         element: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         if self.cached_style.is_some() {
-            cx.with_element_state::<AnyViewState, _>(global_id.unwrap(), |element_state, cx| {
-                let mut element_state = element_state.unwrap();
-
-                let paint_start = cx.paint_index();
-
-                if let Some(element) = element {
-                    let refreshing = mem::replace(&mut cx.window.refreshing, true);
-                    element.paint(cx);
-                    cx.window.refreshing = refreshing;
-                } else {
-                    cx.reuse_paint(element_state.paint_range.clone());
-                }
+            window.with_element_state::<AnyViewState, _>(
+                global_id.unwrap(),
+                |element_state, window| {
+                    let mut element_state = element_state.unwrap();
+
+                    let paint_start = window.paint_index();
+
+                    if let Some(element) = element {
+                        let refreshing = mem::replace(&mut window.refreshing, true);
+                        element.paint(window, cx);
+                        window.refreshing = refreshing;
+                    } else {
+                        window.reuse_paint(element_state.paint_range.clone());
+                    }
 
-                let paint_end = cx.paint_index();
-                element_state.paint_range = paint_start..paint_end;
+                    let paint_end = window.paint_index();
+                    element_state.paint_range = paint_start..paint_end;
 
-                ((), element_state)
-            })
+                    ((), element_state)
+                },
+            )
         } else {
-            element.as_mut().unwrap().paint(cx);
+            element.as_mut().unwrap().paint(window, cx);
         }
     }
 }
 
-impl<V: 'static + Render> IntoElement for View<V> {
-    type Element = View<V>;
+impl<V: 'static + Render> IntoElement for Entity<V> {
+    type Element = Entity<V>;
 
     fn into_element(self) -> Self::Element {
         self
@@ -421,8 +283,8 @@ impl IntoElement for AnyView {
 
 /// A weak, dynamically-typed view handle that does not prevent the view from being released.
 pub struct AnyWeakView {
-    model: AnyWeakModel,
-    render: fn(&AnyView, &mut WindowContext) -> AnyElement,
+    model: AnyWeakEntity,
+    render: fn(&AnyView, &mut Window, &mut App) -> AnyElement,
 }
 
 impl AnyWeakView {
@@ -437,10 +299,10 @@ impl AnyWeakView {
     }
 }
 
-impl<V: 'static + Render> From<WeakView<V>> for AnyWeakView {
-    fn from(view: WeakView<V>) -> Self {
-        Self {
-            model: view.model.into(),
+impl<V: 'static + Render> From<WeakEntity<V>> for AnyWeakView {
+    fn from(view: WeakEntity<V>) -> Self {
+        AnyWeakView {
+            model: view.into(),
             render: any_view::render::<V>,
         }
     }
@@ -461,14 +323,15 @@ impl std::fmt::Debug for AnyWeakView {
 }
 
 mod any_view {
-    use crate::{AnyElement, AnyView, IntoElement, Render, WindowContext};
+    use crate::{AnyElement, AnyView, App, IntoElement, Render, Window};
 
     pub(crate) fn render<V: 'static + Render>(
         view: &AnyView,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> AnyElement {
         let view = view.clone().downcast::<V>().unwrap();
-        view.update(cx, |view, cx| view.render(cx).into_any_element())
+        view.update(cx, |view, cx| view.render(window, cx).into_any_element())
     }
 }
 
@@ -476,7 +339,7 @@ mod any_view {
 pub struct EmptyView;
 
 impl Render for EmptyView {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         Empty
     }
 }

crates/gpui/src/window.rs 🔗

@@ -1,19 +1,18 @@
 use crate::{
     point, prelude::*, px, size, transparent_black, Action, AnyDrag, AnyElement, AnyTooltip,
-    AnyView, AppContext, Arena, Asset, AsyncWindowContext, AvailableSpace, Background, Bounds,
+    AnyView, App, AppContext, Arena, Asset, AsyncWindowContext, AvailableSpace, Background, Bounds,
     BoxShadow, Context, Corners, CursorStyle, Decorations, DevicePixels, DispatchActionListener,
     DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter,
     FileDropEvent, Flatten, FontId, Global, GlobalElementId, GlyphId, GpuSpecs, Hsla, InputHandler,
-    IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent,
-    KeystrokeObserver, LayoutId, LineLayoutIndex, Model, ModelContext, Modifiers,
-    ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent,
-    Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler,
-    PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams,
-    RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge, ScaledPixels, Scene,
-    Shadow, SharedString, Size, StrikethroughStyle, Style, SubscriberSet, Subscription,
-    TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement, TransformationMatrix, Underline,
-    UnderlineStyle, View, VisualContext, WeakView, WindowAppearance, WindowBackgroundAppearance,
-    WindowBounds, WindowControls, WindowDecorations, WindowOptions, WindowParams, WindowTextSystem,
+    IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent, LayoutId,
+    LineLayoutIndex, Modifiers, ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent,
+    MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
+    PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render,
+    RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge,
+    ScaledPixels, Scene, Shadow, SharedString, Size, StrikethroughStyle, Style, SubscriberSet,
+    Subscription, TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement, TransformationMatrix,
+    Underline, UnderlineStyle, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
+    WindowControls, WindowDecorations, WindowOptions, WindowParams, WindowTextSystem,
     SUBPIXEL_VARIANTS,
 };
 use anyhow::{anyhow, Context as _, Result};
@@ -29,7 +28,7 @@ use slotmap::SlotMap;
 use smallvec::SmallVec;
 use std::{
     any::{Any, TypeId},
-    borrow::{Borrow, BorrowMut, Cow},
+    borrow::Cow,
     cell::{Cell, RefCell},
     cmp,
     fmt::{Debug, Display},
@@ -37,7 +36,7 @@ use std::{
     hash::{Hash, Hasher},
     marker::PhantomData,
     mem,
-    ops::Range,
+    ops::{DerefMut, Range},
     rc::Rc,
     sync::{
         atomic::{AtomicUsize, Ordering::SeqCst},
@@ -84,14 +83,100 @@ impl DispatchPhase {
     }
 }
 
-type AnyObserver = Box<dyn FnMut(&mut WindowContext) -> bool + 'static>;
+struct WindowInvalidatorInner {
+    pub dirty: bool,
+    pub draw_phase: DrawPhase,
+    pub dirty_views: FxHashSet<EntityId>,
+}
+
+#[derive(Clone)]
+pub(crate) struct WindowInvalidator {
+    inner: Rc<RefCell<WindowInvalidatorInner>>,
+}
+
+impl WindowInvalidator {
+    pub fn new() -> Self {
+        WindowInvalidator {
+            inner: Rc::new(RefCell::new(WindowInvalidatorInner {
+                dirty: true,
+                draw_phase: DrawPhase::None,
+                dirty_views: FxHashSet::default(),
+            })),
+        }
+    }
+
+    pub fn invalidate_view(&self, entity: EntityId, cx: &mut App) -> bool {
+        let mut inner = self.inner.borrow_mut();
+        if inner.draw_phase == DrawPhase::None {
+            inner.dirty = true;
+            inner.dirty_views.insert(entity);
+            cx.push_effect(Effect::Notify { emitter: entity });
+            true
+        } else {
+            false
+        }
+    }
+
+    pub fn is_dirty(&self) -> bool {
+        self.inner.borrow().dirty
+    }
+
+    pub fn set_dirty(&self, dirty: bool) {
+        self.inner.borrow_mut().dirty = dirty
+    }
+
+    pub fn set_phase(&self, phase: DrawPhase) {
+        self.inner.borrow_mut().draw_phase = phase
+    }
+
+    pub fn take_views(&self) -> FxHashSet<EntityId> {
+        mem::take(&mut self.inner.borrow_mut().dirty_views)
+    }
+
+    pub fn replace_views(&self, views: FxHashSet<EntityId>) {
+        self.inner.borrow_mut().dirty_views = views;
+    }
+
+    pub fn not_painting(&self) -> bool {
+        self.inner.borrow().draw_phase == DrawPhase::None
+    }
+
+    #[track_caller]
+    pub fn debug_assert_paint(&self) {
+        debug_assert!(
+            matches!(self.inner.borrow().draw_phase, DrawPhase::Paint),
+            "this method can only be called during paint"
+        );
+    }
+
+    #[track_caller]
+    pub fn debug_assert_prepaint(&self) {
+        debug_assert!(
+            matches!(self.inner.borrow().draw_phase, DrawPhase::Prepaint),
+            "this method can only be called during request_layout, or prepaint"
+        );
+    }
+
+    #[track_caller]
+    pub fn debug_assert_paint_or_prepaint(&self) {
+        debug_assert!(
+            matches!(
+                self.inner.borrow().draw_phase,
+                DrawPhase::Paint | DrawPhase::Prepaint
+            ),
+            "this method can only be called during request_layout, prepaint, or paint"
+        );
+    }
+}
+
+type AnyObserver = Box<dyn FnMut(&mut Window, &mut App) -> bool + 'static>;
 
-type AnyWindowFocusListener =
-    Box<dyn FnMut(&WindowFocusEvent, &mut WindowContext) -> bool + 'static>;
+pub(crate) type AnyWindowFocusListener =
+    Box<dyn FnMut(&WindowFocusEvent, &mut Window, &mut App) -> bool + 'static>;
 
-struct WindowFocusEvent {
-    previous_focus_path: SmallVec<[FocusId; 8]>,
-    current_focus_path: SmallVec<[FocusId; 8]>,
+pub(crate) struct WindowFocusEvent {
+    pub(crate) previous_focus_path: SmallVec<[FocusId; 8]>,
+    pub(crate) current_focus_path: SmallVec<[FocusId; 8]>,
 }
 
 impl WindowFocusEvent {
@@ -120,29 +205,32 @@ thread_local! {
     pub(crate) static ELEMENT_ARENA: RefCell<Arena> = RefCell::new(Arena::new(32 * 1024 * 1024));
 }
 
+pub(crate) type FocusMap = RwLock<SlotMap<FocusId, AtomicUsize>>;
+
 impl FocusId {
     /// Obtains whether the element associated with this handle is currently focused.
-    pub fn is_focused(&self, cx: &WindowContext) -> bool {
-        cx.window.focus == Some(*self)
+    pub fn is_focused(&self, window: &Window) -> bool {
+        window.focus == Some(*self)
     }
 
     /// Obtains whether the element associated with this handle contains the focused
     /// element or is itself focused.
-    pub fn contains_focused(&self, cx: &WindowContext) -> bool {
-        cx.focused()
-            .map_or(false, |focused| self.contains(focused.id, cx))
+    pub fn contains_focused(&self, window: &Window, cx: &App) -> bool {
+        window
+            .focused(cx)
+            .map_or(false, |focused| self.contains(focused.id, window))
     }
 
     /// Obtains whether the element associated with this handle is contained within the
     /// focused element or is itself focused.
-    pub fn within_focused(&self, cx: &WindowContext) -> bool {
-        let focused = cx.focused();
-        focused.map_or(false, |focused| focused.id.contains(*self, cx))
+    pub fn within_focused(&self, window: &Window, cx: &App) -> bool {
+        let focused = window.focused(cx);
+        focused.map_or(false, |focused| focused.id.contains(*self, window))
     }
 
     /// Obtains whether this handle contains the given handle in the most recently rendered frame.
-    pub(crate) fn contains(&self, other: Self, cx: &WindowContext) -> bool {
-        cx.window
+    pub(crate) fn contains(&self, other: Self, window: &Window) -> bool {
+        window
             .rendered_frame
             .dispatch_tree
             .focus_contains(*self, other)
@@ -152,7 +240,7 @@ impl FocusId {
 /// A handle which can be used to track and manipulate the focused element in a window.
 pub struct FocusHandle {
     pub(crate) id: FocusId,
-    handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
+    handles: Arc<FocusMap>,
 }
 
 impl std::fmt::Debug for FocusHandle {
@@ -162,7 +250,7 @@ impl std::fmt::Debug for FocusHandle {
 }
 
 impl FocusHandle {
-    pub(crate) fn new(handles: &Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>) -> Self {
+    pub(crate) fn new(handles: &Arc<FocusMap>) -> Self {
         let id = handles.write().insert(AtomicUsize::new(1));
         Self {
             id,
@@ -170,10 +258,7 @@ impl FocusHandle {
         }
     }
 
-    pub(crate) fn for_id(
-        id: FocusId,
-        handles: &Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
-    ) -> Option<Self> {
+    pub(crate) fn for_id(id: FocusId, handles: &Arc<FocusMap>) -> Option<Self> {
         let lock = handles.read();
         let ref_count = lock.get(id)?;
         if ref_count.load(SeqCst) == 0 {
@@ -196,41 +281,40 @@ impl FocusHandle {
     }
 
     /// Moves the focus to the element associated with this handle.
-    pub fn focus(&self, cx: &mut WindowContext) {
-        cx.focus(self)
+    pub fn focus(&self, window: &mut Window) {
+        window.focus(self)
     }
 
     /// Obtains whether the element associated with this handle is currently focused.
-    pub fn is_focused(&self, cx: &WindowContext) -> bool {
-        self.id.is_focused(cx)
+    pub fn is_focused(&self, window: &Window) -> bool {
+        self.id.is_focused(window)
     }
 
     /// Obtains whether the element associated with this handle contains the focused
     /// element or is itself focused.
-    pub fn contains_focused(&self, cx: &WindowContext) -> bool {
-        self.id.contains_focused(cx)
+    pub fn contains_focused(&self, window: &Window, cx: &App) -> bool {
+        self.id.contains_focused(window, cx)
     }
 
     /// Obtains whether the element associated with this handle is contained within the
     /// focused element or is itself focused.
-    pub fn within_focused(&self, cx: &WindowContext) -> bool {
-        self.id.within_focused(cx)
+    pub fn within_focused(&self, window: &Window, cx: &mut App) -> bool {
+        self.id.within_focused(window, cx)
     }
 
     /// Obtains whether this handle contains the given handle in the most recently rendered frame.
-    pub fn contains(&self, other: &Self, cx: &WindowContext) -> bool {
-        self.id.contains(other.id, cx)
+    pub fn contains(&self, other: &Self, window: &Window) -> bool {
+        self.id.contains(other.id, window)
     }
 
     /// Dispatch an action on the element that rendered this focus handle
-    pub fn dispatch_action(&self, action: &dyn Action, cx: &mut WindowContext) {
-        if let Some(node_id) = cx
-            .window
+    pub fn dispatch_action(&self, action: &dyn Action, window: &mut Window, cx: &mut App) {
+        if let Some(node_id) = window
             .rendered_frame
             .dispatch_tree
             .focusable_node_id(self.id)
         {
-            cx.dispatch_action_on_node(node_id, action)
+            window.dispatch_action_on_node(node_id, action, cx)
         }
     }
 }
@@ -263,7 +347,7 @@ impl Drop for FocusHandle {
 #[derive(Clone, Debug)]
 pub struct WeakFocusHandle {
     pub(crate) id: FocusId,
-    handles: Weak<RwLock<SlotMap<FocusId, AtomicUsize>>>,
+    pub(crate) handles: Weak<FocusMap>,
 }
 
 impl WeakFocusHandle {
@@ -294,26 +378,32 @@ impl PartialEq<WeakFocusHandle> for FocusHandle {
     }
 }
 
-/// FocusableView allows users of your view to easily
-/// focus it (using cx.focus_view(view))
-pub trait FocusableView: 'static + Render {
+/// Focusable allows users of your view to easily
+/// focus it (using window.focus_view(cx, view))
+pub trait Focusable: 'static {
     /// Returns the focus handle associated with this view.
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle;
+    fn focus_handle(&self, cx: &App) -> FocusHandle;
+}
+
+impl<V: Focusable> Focusable for Entity<V> {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
+        self.read(cx).focus_handle(cx)
+    }
 }
 
 /// ManagedView is a view (like a Modal, Popover, Menu, etc.)
 /// where the lifecycle of the view is handled by another view.
-pub trait ManagedView: FocusableView + EventEmitter<DismissEvent> {}
+pub trait ManagedView: Focusable + EventEmitter<DismissEvent> + Render {}
 
-impl<M: FocusableView + EventEmitter<DismissEvent>> ManagedView for M {}
+impl<M: Focusable + EventEmitter<DismissEvent> + Render> ManagedView for M {}
 
 /// Emitted by implementers of [`ManagedView`] to indicate the view should be dismissed, such as when a view is presented as a modal.
 pub struct DismissEvent;
 
-type FrameCallback = Box<dyn FnOnce(&mut WindowContext)>;
+type FrameCallback = Box<dyn FnOnce(&mut Window, &mut App)>;
 
 pub(crate) type AnyMouseListener =
-    Box<dyn FnMut(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
+    Box<dyn FnMut(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static>;
 
 #[derive(Clone)]
 pub(crate) struct CursorStyleRequest {
@@ -327,8 +417,8 @@ pub struct HitboxId(usize);
 
 impl HitboxId {
     /// Checks if the hitbox with this id is currently hovered.
-    pub fn is_hovered(&self, cx: &WindowContext) -> bool {
-        cx.window.mouse_hit_test.0.contains(self)
+    pub fn is_hovered(&self, window: &Window) -> bool {
+        window.mouse_hit_test.0.contains(self)
     }
 }
 
@@ -349,8 +439,8 @@ pub struct Hitbox {
 
 impl Hitbox {
     /// Checks if the hitbox is currently hovered.
-    pub fn is_hovered(&self, cx: &WindowContext) -> bool {
-        self.id.is_hovered(cx)
+    pub fn is_hovered(&self, window: &Window) -> bool {
+        self.id.is_hovered(window)
     }
 }
 
@@ -363,12 +453,13 @@ pub struct TooltipId(usize);
 
 impl TooltipId {
     /// Checks if the tooltip is currently hovered.
-    pub fn is_hovered(&self, cx: &WindowContext) -> bool {
-        cx.window
+    pub fn is_hovered(&self, window: &Window) -> bool {
+        window
             .tooltip_bounds
             .as_ref()
             .map_or(false, |tooltip_bounds| {
-                tooltip_bounds.id == *self && tooltip_bounds.bounds.contains(&cx.mouse_position())
+                tooltip_bounds.id == *self
+                    && tooltip_bounds.bounds.contains(&window.mouse_position())
             })
     }
 }
@@ -504,6 +595,7 @@ impl Frame {
 #[doc(hidden)]
 pub struct Window {
     pub(crate) handle: AnyWindowHandle,
+    pub(crate) invalidator: WindowInvalidator,
     pub(crate) removed: bool,
     pub(crate) platform_window: Box<dyn PlatformWindow>,
     display_id: Option<DisplayId>,
@@ -517,7 +609,7 @@ pub struct Window {
     rem_size_override_stack: SmallVec<[Pixels; 8]>,
     pub(crate) viewport_size: Size<Pixels>,
     layout_engine: Option<TaffyLayoutEngine>,
-    pub(crate) root_view: Option<AnyView>,
+    pub(crate) root_model: Option<AnyView>,
     pub(crate) element_id_stack: SmallVec<[ElementId; 32]>,
     pub(crate) text_style_stack: Vec<TextStyleRefinement>,
     pub(crate) element_offset_stack: Vec<Point<Pixels>>,
@@ -532,28 +624,26 @@ pub struct Window {
     next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>>,
     pub(crate) dirty_views: FxHashSet<EntityId>,
     focus_listeners: SubscriberSet<(), AnyWindowFocusListener>,
-    focus_lost_listeners: SubscriberSet<(), AnyObserver>,
+    pub(crate) focus_lost_listeners: SubscriberSet<(), AnyObserver>,
     default_prevented: bool,
     mouse_position: Point<Pixels>,
     mouse_hit_test: HitTest,
     modifiers: Modifiers,
     scale_factor: f32,
-    bounds_observers: SubscriberSet<(), AnyObserver>,
+    pub(crate) bounds_observers: SubscriberSet<(), AnyObserver>,
     appearance: WindowAppearance,
-    appearance_observers: SubscriberSet<(), AnyObserver>,
+    pub(crate) appearance_observers: SubscriberSet<(), AnyObserver>,
     active: Rc<Cell<bool>>,
     hovered: Rc<Cell<bool>>,
-    pub(crate) dirty: Rc<Cell<bool>>,
     pub(crate) needs_present: Rc<Cell<bool>>,
     pub(crate) last_input_timestamp: Rc<Cell<Instant>>,
     pub(crate) refreshing: bool,
-    pub(crate) draw_phase: DrawPhase,
-    activation_observers: SubscriberSet<(), AnyObserver>,
+    pub(crate) activation_observers: SubscriberSet<(), AnyObserver>,
     pub(crate) focus: Option<FocusId>,
     focus_enabled: bool,
     pending_input: Option<PendingInput>,
     pending_modifier: ModifierState,
-    pending_input_observers: SubscriberSet<(), AnyObserver>,
+    pub(crate) pending_input_observers: SubscriberSet<(), AnyObserver>,
     prompt: Option<RenderablePromptHandle>,
 }
 
@@ -584,11 +674,11 @@ pub(crate) struct ElementStateBox {
     pub(crate) type_name: &'static str,
 }
 
-fn default_bounds(display_id: Option<DisplayId>, cx: &mut AppContext) -> Bounds<Pixels> {
+fn default_bounds(display_id: Option<DisplayId>, cx: &mut App) -> Bounds<Pixels> {
     const DEFAULT_WINDOW_OFFSET: Point<Pixels> = point(px(0.), px(35.));
 
     cx.active_window()
-        .and_then(|w| w.update(cx, |_, cx| cx.bounds()).ok())
+        .and_then(|w| w.update(cx, |_, window, _| window.bounds()).ok())
         .map(|mut bounds| {
             bounds.origin += DEFAULT_WINDOW_OFFSET;
             bounds
@@ -608,7 +698,7 @@ impl Window {
     pub(crate) fn new(
         handle: AnyWindowHandle,
         options: WindowOptions,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Result<Self> {
         let WindowOptions {
             window_bounds,
@@ -648,7 +738,7 @@ impl Window {
         let scale_factor = platform_window.scale_factor();
         let appearance = platform_window.appearance();
         let text_system = Arc::new(WindowTextSystem::new(cx.text_system().clone()));
-        let dirty = Rc::new(Cell::new(true));
+        let invalidator = WindowInvalidator::new();
         let active = Rc::new(Cell::new(platform_window.is_active()));
         let hovered = Rc::new(Cell::new(platform_window.is_hovered()));
         let needs_present = Rc::new(Cell::new(false));
@@ -670,12 +760,12 @@ impl Window {
         platform_window.on_close(Box::new({
             let mut cx = cx.to_async();
             move || {
-                let _ = handle.update(&mut cx, |_, cx| cx.remove_window());
+                let _ = handle.update(&mut cx, |_, window, _| window.remove_window());
             }
         }));
         platform_window.on_request_frame(Box::new({
             let mut cx = cx.to_async();
-            let dirty = dirty.clone();
+            let invalidator = invalidator.clone();
             let active = active.clone();
             let needs_present = needs_present.clone();
             let next_frame_callbacks = next_frame_callbacks.clone();
@@ -684,9 +774,9 @@ impl Window {
                 let next_frame_callbacks = next_frame_callbacks.take();
                 if !next_frame_callbacks.is_empty() {
                     handle
-                        .update(&mut cx, |_, cx| {
+                        .update(&mut cx, |_, window, cx| {
                             for callback in next_frame_callbacks {
-                                callback(cx);
+                                callback(window, cx);
                             }
                         })
                         .log_err();
@@ -699,22 +789,24 @@ impl Window {
                     || (active.get()
                         && last_input_timestamp.get().elapsed() < Duration::from_secs(1));
 
-                if dirty.get() {
+                if invalidator.is_dirty() {
                     measure("frame duration", || {
                         handle
-                            .update(&mut cx, |_, cx| {
-                                cx.draw();
-                                cx.present();
+                            .update(&mut cx, |_, window, cx| {
+                                window.draw(cx);
+                                window.present();
                             })
                             .log_err();
                     })
                 } else if needs_present {
-                    handle.update(&mut cx, |_, cx| cx.present()).log_err();
+                    handle
+                        .update(&mut cx, |_, window, _| window.present())
+                        .log_err();
                 }
 
                 handle
-                    .update(&mut cx, |_, cx| {
-                        cx.complete_frame();
+                    .update(&mut cx, |_, window, _| {
+                        window.complete_frame();
                     })
                     .log_err();
             }
@@ -723,7 +815,7 @@ impl Window {
             let mut cx = cx.to_async();
             move |_, _| {
                 handle
-                    .update(&mut cx, |_, cx| cx.bounds_changed())
+                    .update(&mut cx, |_, window, cx| window.bounds_changed(cx))
                     .log_err();
             }
         }));
@@ -731,7 +823,7 @@ impl Window {
             let mut cx = cx.to_async();
             move || {
                 handle
-                    .update(&mut cx, |_, cx| cx.bounds_changed())
+                    .update(&mut cx, |_, window, cx| window.bounds_changed(cx))
                     .log_err();
             }
         }));
@@ -739,7 +831,7 @@ impl Window {
             let mut cx = cx.to_async();
             move || {
                 handle
-                    .update(&mut cx, |_, cx| cx.appearance_changed())
+                    .update(&mut cx, |_, window, cx| window.appearance_changed(cx))
                     .log_err();
             }
         }));
@@ -747,13 +839,13 @@ impl Window {
             let mut cx = cx.to_async();
             move |active| {
                 handle
-                    .update(&mut cx, |_, cx| {
-                        cx.window.active.set(active);
-                        cx.window
+                    .update(&mut cx, |_, window, cx| {
+                        window.active.set(active);
+                        window
                             .activation_observers
                             .clone()
-                            .retain(&(), |callback| callback(cx));
-                        cx.refresh();
+                            .retain(&(), |callback| callback(window, cx));
+                        window.refresh();
                     })
                     .log_err();
             }
@@ -762,9 +854,9 @@ impl Window {
             let mut cx = cx.to_async();
             move |active| {
                 handle
-                    .update(&mut cx, |_, cx| {
-                        cx.window.hovered.set(active);
-                        cx.refresh();
+                    .update(&mut cx, |_, window, _| {
+                        window.hovered.set(active);
+                        window.refresh();
                     })
                     .log_err();
             }
@@ -773,7 +865,7 @@ impl Window {
             let mut cx = cx.to_async();
             Box::new(move |event| {
                 handle
-                    .update(&mut cx, |_, cx| cx.dispatch_event(event))
+                    .update(&mut cx, |_, window, cx| window.dispatch_event(event, cx))
                     .log_err()
                     .unwrap_or(DispatchEventResult::default())
             })
@@ -785,6 +877,7 @@ impl Window {
 
         Ok(Window {
             handle,
+            invalidator,
             removed: false,
             platform_window,
             display_id,
@@ -794,7 +887,7 @@ impl Window {
             rem_size_override_stack: SmallVec::new(),
             viewport_size: content_size,
             layout_engine: Some(TaffyLayoutEngine::new()),
-            root_view: None,
+            root_model: None,
             element_id_stack: SmallVec::default(),
             text_style_stack: Vec::new(),
             element_offset_stack: Vec::new(),
@@ -820,11 +913,9 @@ impl Window {
             appearance_observers: SubscriberSet::new(),
             active,
             hovered,
-            dirty,
             needs_present,
             last_input_timestamp,
             refreshing: false,
-            draw_phase: DrawPhase::None,
             activation_observers: SubscriberSet::new(),
             focus: None,
             focus_enabled: true,
@@ -834,7 +925,11 @@ impl Window {
             prompt: None,
         })
     }
-    fn new_focus_listener(&self, value: AnyWindowFocusListener) -> (Subscription, impl FnOnce()) {
+
+    pub(crate) fn new_focus_listener(
+        &self,
+        value: AnyWindowFocusListener,
+    ) -> (Subscription, impl FnOnce()) {
         self.focus_listeners.insert((), value)
     }
 }
@@ -870,108 +965,143 @@ impl ContentMask<Pixels> {
     }
 }
 
-/// Provides access to application state in the context of a single window. Derefs
-/// to an [`AppContext`], so you can also pass a [`WindowContext`] to any method that takes
-/// an [`AppContext`] and call any [`AppContext`] methods.
-pub struct WindowContext<'a> {
-    pub(crate) app: &'a mut AppContext,
-    pub(crate) window: &'a mut Window,
-}
-
-impl<'a> WindowContext<'a> {
-    pub(crate) fn new(app: &'a mut AppContext, window: &'a mut Window) -> Self {
-        Self { app, window }
-    }
-
-    /// Obtain a handle to the window that belongs to this context.
-    pub fn window_handle(&self) -> AnyWindowHandle {
-        self.window.handle
-    }
-
-    /// Mark the window as dirty, scheduling it to be redrawn on the next frame.
-    pub fn refresh(&mut self) {
-        if self.window.draw_phase == DrawPhase::None {
-            self.window.refreshing = true;
-            self.window.dirty.set(true);
-        }
-    }
-
-    /// Indicate that this view has changed, which will invoke any observers and also mark the window as dirty.
+impl Window {
+    /// Indicate that a view has changed, which will invoke any observers and also mark the window as dirty.
     /// If this view or any of its ancestors are *cached*, notifying it will cause it or its ancestors to be redrawn.
     /// Note that this method will always cause a redraw, the entire window is refreshed if view_id is None.
-    pub fn notify(&mut self, view_id: Option<EntityId>) {
-        let Some(view_id) = view_id else {
+    pub(crate) fn notify(
+        &mut self,
+        notify_effect: bool,
+        entity_id: Option<EntityId>,
+        cx: &mut App,
+    ) {
+        let Some(view_id) = entity_id else {
             self.refresh();
             return;
         };
 
+        self.mark_view_dirty(view_id);
+
+        if notify_effect {
+            self.invalidator.invalidate_view(view_id, cx);
+        }
+    }
+
+    fn mark_view_dirty(&mut self, view_id: EntityId) {
+        // Mark ancestor views as dirty. If already in the `dirty_views` set, then all its ancestors
+        // should already be dirty.
         for view_id in self
-            .window
             .rendered_frame
             .dispatch_tree
             .view_path(view_id)
             .into_iter()
             .rev()
         {
-            if !self.window.dirty_views.insert(view_id) {
+            if !self.dirty_views.insert(view_id) {
                 break;
             }
         }
+    }
+
+    /// Registers a callback to be invoked when the window appearance changes.
+    pub fn observe_window_appearance(
+        &self,
+        mut callback: impl FnMut(&mut Window, &mut App) + 'static,
+    ) -> Subscription {
+        let (subscription, activate) = self.appearance_observers.insert(
+            (),
+            Box::new(move |window, cx| {
+                callback(window, cx);
+                true
+            }),
+        );
+        activate();
+        subscription
+    }
+
+    pub fn replace_root_model<V>(
+        &mut self,
+        cx: &mut App,
+        build_view: impl FnOnce(&mut Window, &mut Context<'_, V>) -> V,
+    ) -> Entity<V>
+    where
+        V: 'static + Render,
+    {
+        let view = cx.new(|cx| build_view(self, cx));
+        self.root_model = Some(view.clone().into());
+        self.refresh();
+        view
+    }
+
+    pub fn root_model<V>(&mut self) -> Option<Option<Entity<V>>>
+    where
+        V: 'static + Render,
+    {
+        self.root_model
+            .as_ref()
+            .map(|view| view.clone().downcast::<V>().ok())
+    }
+
+    /// Obtain a handle to the window that belongs to this context.
+    pub fn window_handle(&self) -> AnyWindowHandle {
+        self.handle
+    }
 
-        if self.window.draw_phase == DrawPhase::None {
-            self.window.dirty.set(true);
-            self.app.push_effect(Effect::Notify { emitter: view_id });
+    /// Mark the window as dirty, scheduling it to be redrawn on the next frame.
+    pub fn refresh(&mut self) {
+        if self.invalidator.not_painting() {
+            self.refreshing = true;
+            self.invalidator.set_dirty(true);
         }
     }
 
     /// Close this window.
     pub fn remove_window(&mut self) {
-        self.window.removed = true;
+        self.removed = true;
     }
 
     /// Obtain the currently focused [`FocusHandle`]. If no elements are focused, returns `None`.
-    pub fn focused(&self) -> Option<FocusHandle> {
-        self.window
-            .focus
-            .and_then(|id| FocusHandle::for_id(id, &self.app.focus_handles))
+    pub fn focused(&self, cx: &App) -> Option<FocusHandle> {
+        self.focus
+            .and_then(|id| FocusHandle::for_id(id, &cx.focus_handles))
     }
 
     /// Move focus to the element associated with the given [`FocusHandle`].
     pub fn focus(&mut self, handle: &FocusHandle) {
-        if !self.window.focus_enabled || self.window.focus == Some(handle.id) {
+        if !self.focus_enabled || self.focus == Some(handle.id) {
             return;
         }
 
-        self.window.focus = Some(handle.id);
+        self.focus = Some(handle.id);
         self.clear_pending_keystrokes();
         self.refresh();
     }
 
     /// Remove focus from all elements within this context's window.
     pub fn blur(&mut self) {
-        if !self.window.focus_enabled {
+        if !self.focus_enabled {
             return;
         }
 
-        self.window.focus = None;
+        self.focus = None;
         self.refresh();
     }
 
     /// Blur the window and don't allow anything in it to be focused again.
     pub fn disable_focus(&mut self) {
         self.blur();
-        self.window.focus_enabled = false;
+        self.focus_enabled = false;
     }
 
     /// Accessor for the text system.
     pub fn text_system(&self) -> &Arc<WindowTextSystem> {
-        &self.window.text_system
+        &self.text_system
     }
 
     /// The current text style. Which is composed of all the style refinements provided to `with_text_style`.
     pub fn text_style(&self) -> TextStyle {
         let mut style = TextStyle::default();
-        for refinement in &self.window.text_style_stack {
+        for refinement in &self.text_style_stack {
             style.refine(refinement);
         }
         style
@@ -980,48 +1110,48 @@ impl<'a> WindowContext<'a> {
     /// Check if the platform window is maximized
     /// On some platforms (namely Windows) this is different than the bounds being the size of the display
     pub fn is_maximized(&self) -> bool {
-        self.window.platform_window.is_maximized()
+        self.platform_window.is_maximized()
     }
 
     /// request a certain window decoration (Wayland)
     pub fn request_decorations(&self, decorations: WindowDecorations) {
-        self.window.platform_window.request_decorations(decorations);
+        self.platform_window.request_decorations(decorations);
     }
 
     /// Start a window resize operation (Wayland)
     pub fn start_window_resize(&self, edge: ResizeEdge) {
-        self.window.platform_window.start_window_resize(edge);
+        self.platform_window.start_window_resize(edge);
     }
 
     /// Return the `WindowBounds` to indicate that how a window should be opened
     /// after it has been closed
     pub fn window_bounds(&self) -> WindowBounds {
-        self.window.platform_window.window_bounds()
+        self.platform_window.window_bounds()
     }
 
     /// Return the `WindowBounds` excluding insets (Wayland and X11)
     pub fn inner_window_bounds(&self) -> WindowBounds {
-        self.window.platform_window.inner_window_bounds()
+        self.platform_window.inner_window_bounds()
     }
 
     /// Dispatch the given action on the currently focused element.
-    pub fn dispatch_action(&mut self, action: Box<dyn Action>) {
-        let focus_handle = self.focused();
+    pub fn dispatch_action(&mut self, action: Box<dyn Action>, cx: &mut App) {
+        let focus_handle = self.focused(cx);
 
-        let window = self.window.handle;
-        self.app.defer(move |cx| {
+        let window = self.handle;
+        cx.defer(move |cx| {
             window
-                .update(cx, |_, cx| {
+                .update(cx, |_, window, cx| {
                     let node_id = focus_handle
                         .and_then(|handle| {
-                            cx.window
+                            window
                                 .rendered_frame
                                 .dispatch_tree
                                 .focusable_node_id(handle.id)
                         })
-                        .unwrap_or_else(|| cx.window.rendered_frame.dispatch_tree.root_node_id());
+                        .unwrap_or_else(|| window.rendered_frame.dispatch_tree.root_node_id());
 
-                    cx.dispatch_action_on_node(node_id, action.as_ref());
+                    window.dispatch_action_on_node(node_id, action.as_ref(), cx);
                 })
                 .log_err();
         })
@@ -1031,54 +1161,52 @@ impl<'a> WindowContext<'a> {
         &mut self,
         event: &dyn Any,
         action: Option<Box<dyn Action>>,
+        cx: &mut App,
     ) {
         let Some(key_down_event) = event.downcast_ref::<KeyDownEvent>() else {
             return;
         };
 
-        self.keystroke_observers
-            .clone()
-            .retain(&(), move |callback| {
-                (callback)(
-                    &KeystrokeEvent {
-                        keystroke: key_down_event.keystroke.clone(),
-                        action: action.as_ref().map(|action| action.boxed_clone()),
-                    },
-                    self,
-                )
-            });
+        cx.keystroke_observers.clone().retain(&(), move |callback| {
+            (callback)(
+                &KeystrokeEvent {
+                    keystroke: key_down_event.keystroke.clone(),
+                    action: action.as_ref().map(|action| action.boxed_clone()),
+                },
+                self,
+                cx,
+            )
+        });
     }
 
     /// Schedules the given function to be run at the end of the current effect cycle, allowing entities
     /// that are currently on the stack to be returned to the app.
-    pub fn defer(&mut self, f: impl FnOnce(&mut WindowContext) + 'static) {
-        let handle = self.window.handle;
-        self.app.defer(move |cx| {
-            handle.update(cx, |_, cx| f(cx)).ok();
+    pub fn defer(&self, cx: &mut App, f: impl FnOnce(&mut Window, &mut App) + 'static) {
+        let handle = self.handle;
+        cx.defer(move |cx| {
+            handle.update(cx, |_, window, cx| f(window, cx)).ok();
         });
     }
 
     /// Subscribe to events emitted by a model or view.
     /// The entity to which you're subscribing must implement the [`EventEmitter`] trait.
     /// The callback will be invoked a handle to the emitting entity (either a [`View`] or [`Model`]), the event, and a window context for the current window.
-    pub fn observe<E, T>(
+    pub fn observe<T: 'static>(
         &mut self,
-        entity: &E,
-        mut on_notify: impl FnMut(E, &mut WindowContext<'_>) + 'static,
-    ) -> Subscription
-    where
-        E: Entity<T>,
-    {
-        let entity_id = entity.entity_id();
-        let entity = entity.downgrade();
-        let window_handle = self.window.handle;
-        self.app.new_observer(
+        observed: &Entity<T>,
+        cx: &mut App,
+        mut on_notify: impl FnMut(Entity<T>, &mut Window, &mut App) + 'static,
+    ) -> Subscription {
+        let entity_id = observed.entity_id();
+        let observed = observed.downgrade();
+        let window_handle = self.handle;
+        cx.new_observer(
             entity_id,
             Box::new(move |cx| {
                 window_handle
-                    .update(cx, |_, cx| {
-                        if let Some(handle) = E::upgrade_from(&entity) {
-                            on_notify(handle, cx);
+                    .update(cx, |_, window, cx| {
+                        if let Some(handle) = observed.upgrade() {
+                            on_notify(handle, window, cx);
                             true
                         } else {
                             false
@@ -1092,29 +1220,29 @@ impl<'a> WindowContext<'a> {
     /// Subscribe to events emitted by a model or view.
     /// The entity to which you're subscribing must implement the [`EventEmitter`] trait.
     /// The callback will be invoked a handle to the emitting entity (either a [`View`] or [`Model`]), the event, and a window context for the current window.
-    pub fn subscribe<Emitter, E, Evt>(
+    pub fn subscribe<Emitter, Evt>(
         &mut self,
-        entity: &E,
-        mut on_event: impl FnMut(E, &Evt, &mut WindowContext<'_>) + 'static,
+        entity: &Entity<Emitter>,
+        cx: &mut App,
+        mut on_event: impl FnMut(Entity<Emitter>, &Evt, &mut Window, &mut App) + 'static,
     ) -> Subscription
     where
         Emitter: EventEmitter<Evt>,
-        E: Entity<Emitter>,
         Evt: 'static,
     {
         let entity_id = entity.entity_id();
         let entity = entity.downgrade();
-        let window_handle = self.window.handle;
-        self.app.new_subscription(
+        let window_handle = self.handle;
+        cx.new_subscription(
             entity_id,
             (
                 TypeId::of::<Evt>(),
                 Box::new(move |event, cx| {
                     window_handle
-                        .update(cx, |_, cx| {
-                            if let Some(handle) = E::upgrade_from(&entity) {
+                        .update(cx, |_, window, cx| {
+                            if let Some(handle) = Entity::<Emitter>::upgrade_from(&entity) {
                                 let event = event.downcast_ref().expect("invalid event type");
-                                on_event(handle, event, cx);
+                                on_event(handle, event, window, cx);
                                 true
                             } else {
                                 false
@@ -1127,22 +1255,22 @@ impl<'a> WindowContext<'a> {
     }
 
     /// Register a callback to be invoked when the given Model or View is released.
-    pub fn observe_release<E, T>(
+    pub fn observe_release<T>(
         &self,
-        entity: &E,
-        mut on_release: impl FnOnce(&mut T, &mut WindowContext) + 'static,
+        entity: &Entity<T>,
+        cx: &mut App,
+        mut on_release: impl FnOnce(&mut T, &mut Window, &mut App) + 'static,
     ) -> Subscription
     where
-        E: Entity<T>,
         T: 'static,
     {
         let entity_id = entity.entity_id();
-        let window_handle = self.window.handle;
-        let (subscription, activate) = self.app.release_listeners.insert(
+        let window_handle = self.handle;
+        let (subscription, activate) = cx.release_listeners.insert(
             entity_id,
             Box::new(move |entity, cx| {
                 let entity = entity.downcast_mut().expect("invalid entity type");
-                let _ = window_handle.update(cx, |_, cx| on_release(entity, cx));
+                let _ = window_handle.update(cx, |_, window, cx| on_release(entity, window, cx));
             }),
         );
         activate();

crates/gpui/src/window/prompts.rs 🔗

@@ -3,20 +3,22 @@ use std::ops::Deref;
 use futures::channel::oneshot;
 
 use crate::{
-    div, opaque_grey, white, AnyView, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
-    IntoElement, ParentElement, PromptLevel, Render, StatefulInteractiveElement, Styled, View,
-    ViewContext, VisualContext, WindowContext,
+    div, opaque_grey, white, AnyView, App, AppContext as _, Context, Entity, EventEmitter,
+    FocusHandle, Focusable, InteractiveElement, IntoElement, ParentElement, PromptLevel, Render,
+    StatefulInteractiveElement, Styled,
 };
 
+use super::Window;
+
 /// The event emitted when a prompt's option is selected.
 /// The usize is the index of the selected option, from the actions
 /// passed to the prompt.
 pub struct PromptResponse(pub usize);
 
 /// A prompt that can be rendered in the window.
-pub trait Prompt: EventEmitter<PromptResponse> + FocusableView {}
+pub trait Prompt: EventEmitter<PromptResponse> + Focusable {}
 
-impl<V: EventEmitter<PromptResponse> + FocusableView> Prompt for V {}
+impl<V: EventEmitter<PromptResponse> + Focusable> Prompt for V {}
 
 /// A handle to a prompt that can be used to interact with it.
 pub struct PromptHandle {
@@ -29,25 +31,31 @@ impl PromptHandle {
     }
 
     /// Construct a new prompt handle from a view of the appropriate types
-    pub fn with_view<V: Prompt>(
+    pub fn with_view<V: Prompt + Render>(
         self,
-        view: View<V>,
-        cx: &mut WindowContext,
+        view: Entity<V>,
+        window: &mut Window,
+        cx: &mut App,
     ) -> RenderablePromptHandle {
         let mut sender = Some(self.sender);
-        let previous_focus = cx.focused();
-        cx.subscribe(&view, move |_, e: &PromptResponse, cx| {
+        let previous_focus = window.focused(cx);
+        let window_handle = window.window_handle();
+        cx.subscribe(&view, move |_: Entity<V>, e: &PromptResponse, cx| {
             if let Some(sender) = sender.take() {
                 sender.send(e.0).ok();
-                cx.window.prompt.take();
-                if let Some(previous_focus) = &previous_focus {
-                    cx.focus(previous_focus);
-                }
+                window_handle
+                    .update(cx, |_, window, _cx| {
+                        window.prompt.take();
+                        if let Some(previous_focus) = &previous_focus {
+                            window.focus(previous_focus);
+                        }
+                    })
+                    .ok();
             }
         })
         .detach();
 
-        cx.focus_view(&view);
+        window.focus(&view.focus_handle(cx));
 
         RenderablePromptHandle {
             view: Box::new(view),
@@ -68,19 +76,18 @@ pub fn fallback_prompt_renderer(
     detail: Option<&str>,
     actions: &[&str],
     handle: PromptHandle,
-    cx: &mut WindowContext,
+    window: &mut Window,
+    cx: &mut App,
 ) -> RenderablePromptHandle {
-    let renderer = cx.new_view({
-        |cx| FallbackPromptRenderer {
-            _level: level,
-            message: message.to_string(),
-            detail: detail.map(ToString::to_string),
-            actions: actions.iter().map(ToString::to_string).collect(),
-            focus: cx.focus_handle(),
-        }
+    let renderer = cx.new(|cx| FallbackPromptRenderer {
+        _level: level,
+        message: message.to_string(),
+        detail: detail.map(ToString::to_string),
+        actions: actions.iter().map(ToString::to_string).collect(),
+        focus: cx.focus_handle(),
     });
 
-    handle.with_view(renderer, cx)
+    handle.with_view(renderer, window, cx)
 }
 
 /// The default GPUI fallback for rendering prompts, when the platform doesn't support it.
@@ -93,7 +100,7 @@ pub struct FallbackPromptRenderer {
 }
 
 impl Render for FallbackPromptRenderer {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let prompt = div()
             .cursor_default()
             .track_focus(&self.focus)
@@ -133,7 +140,7 @@ impl Render for FallbackPromptRenderer {
                     .text_sm()
                     .child(action.clone())
                     .id(ix)
-                    .on_click(cx.listener(move |_, _, cx| {
+                    .on_click(cx.listener(move |_, _, _, cx| {
                         cx.emit(PromptResponse(ix));
                     }))
             }));
@@ -171,8 +178,8 @@ impl Render for FallbackPromptRenderer {
 
 impl EventEmitter<PromptResponse> for FallbackPromptRenderer {}
 
-impl FocusableView for FallbackPromptRenderer {
-    fn focus_handle(&self, _: &crate::AppContext) -> FocusHandle {
+impl Focusable for FallbackPromptRenderer {
+    fn focus_handle(&self, _: &crate::App) -> FocusHandle {
         self.focus.clone()
     }
 }
@@ -181,7 +188,7 @@ pub(crate) trait PromptViewHandle {
     fn any_view(&self) -> AnyView;
 }
 
-impl<V: Prompt> PromptViewHandle for View<V> {
+impl<V: Prompt + Render> PromptViewHandle for Entity<V> {
     fn any_view(&self) -> AnyView {
         self.clone().into()
     }
@@ -197,7 +204,8 @@ pub(crate) enum PromptBuilder {
                 Option<&str>,
                 &[&str],
                 PromptHandle,
-                &mut WindowContext,
+                &mut Window,
+                &mut App,
             ) -> RenderablePromptHandle,
         >,
     ),
@@ -210,7 +218,8 @@ impl Deref for PromptBuilder {
         Option<&str>,
         &[&str],
         PromptHandle,
-        &mut WindowContext,
+        &mut Window,
+        &mut App,
     ) -> RenderablePromptHandle;
 
     fn deref(&self) -> &Self::Target {

crates/gpui_macros/src/derive_render.rs 🔗

@@ -11,7 +11,7 @@ pub fn derive_render(input: TokenStream) -> TokenStream {
         impl #impl_generics gpui::Render for #type_name #type_generics
         #where_clause
         {
-            fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl gpui::Element {
+            fn render(&mut self, _window: &mut Window, _cx: &mut ModelContext<Self>) -> impl gpui::Element {
                 gpui::Empty
             }
         }

crates/gpui_macros/src/test.rs 🔗

@@ -164,7 +164,7 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
                     if let Type::Path(ty) = &*ty.elem {
                         let last_segment = ty.path.segments.last();
                         match last_segment.map(|s| s.ident.to_string()).as_deref() {
-                            Some("AppContext") => {
+                            Some("App") => {
                                 let cx_varname = format_ident!("cx_{}", ix);
                                 let cx_varname_lock = format_ident!("cx_{}_lock", ix);
                                 cx_vars.extend(quote!(

crates/html_to_markdown/src/html_to_markdown.rs 🔗

@@ -7,7 +7,7 @@ pub mod structure;
 
 use std::io::Read;
 
-use anyhow::{Context, Result};
+use anyhow::{Context as _, Result};
 use html5ever::driver::ParseOpts;
 use html5ever::parse_document;
 use html5ever::tendril::TendrilSink;

crates/image_viewer/src/image_viewer.rs 🔗

@@ -4,9 +4,9 @@ use anyhow::Context as _;
 use editor::items::entry_git_aware_label_color;
 use file_icons::FileIcons;
 use gpui::{
-    canvas, div, fill, img, opaque_grey, point, size, AnyElement, AppContext, Bounds, EventEmitter,
-    FocusHandle, FocusableView, InteractiveElement, IntoElement, Model, ObjectFit, ParentElement,
-    Render, Styled, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
+    canvas, div, fill, img, opaque_grey, point, size, AnyElement, App, Bounds, Context, Entity,
+    EventEmitter, FocusHandle, Focusable, InteractiveElement, IntoElement, ObjectFit,
+    ParentElement, Render, Styled, Task, WeakEntity, Window,
 };
 use persistence::IMAGE_VIEWER;
 use project::{image_store::ImageItemEvent, ImageItem, Project, ProjectPath};
@@ -22,16 +22,17 @@ use workspace::{
 const IMAGE_VIEWER_KIND: &str = "ImageView";
 
 pub struct ImageView {
-    image_item: Model<ImageItem>,
-    project: Model<Project>,
+    image_item: Entity<ImageItem>,
+    project: Entity<Project>,
     focus_handle: FocusHandle,
 }
 
 impl ImageView {
     pub fn new(
-        image_item: Model<ImageItem>,
-        project: Model<Project>,
-        cx: &mut ViewContext<Self>,
+        image_item: Entity<ImageItem>,
+        project: Entity<Project>,
+
+        cx: &mut Context<Self>,
     ) -> Self {
         cx.subscribe(&image_item, Self::on_image_event).detach();
         Self {
@@ -43,9 +44,9 @@ impl ImageView {
 
     fn on_image_event(
         &mut self,
-        _: Model<ImageItem>,
+        _: Entity<ImageItem>,
         event: &ImageItemEvent,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match event {
             ImageItemEvent::FileHandleChanged | ImageItemEvent::Reloaded => {
@@ -77,23 +78,23 @@ impl Item for ImageView {
 
     fn for_each_project_item(
         &self,
-        cx: &AppContext,
+        cx: &App,
         f: &mut dyn FnMut(gpui::EntityId, &dyn project::ProjectItem),
     ) {
         f(self.image_item.entity_id(), self.image_item.read(cx))
     }
 
-    fn is_singleton(&self, _cx: &AppContext) -> bool {
+    fn is_singleton(&self, _cx: &App) -> bool {
         true
     }
 
-    fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString> {
+    fn tab_tooltip_text(&self, cx: &App) -> Option<SharedString> {
         let abs_path = self.image_item.read(cx).file.as_local()?.abs_path(cx);
         let file_path = abs_path.compact().to_string_lossy().to_string();
         Some(file_path.into())
     }
 
-    fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement {
+    fn tab_content(&self, params: TabContentParams, _: &Window, cx: &App) -> AnyElement {
         let project_path = self.image_item.read(cx).project_path(cx);
 
         let label_color = if ItemSettings::get_global(cx).git_status {
@@ -129,7 +130,7 @@ impl Item for ImageView {
             .into_any_element()
     }
 
-    fn tab_icon(&self, cx: &WindowContext) -> Option<Icon> {
+    fn tab_icon(&self, _: &Window, cx: &App) -> Option<Icon> {
         let path = self.image_item.read(cx).path();
         ItemSettings::get_global(cx)
             .file_icons
@@ -138,11 +139,11 @@ impl Item for ImageView {
             .map(Icon::from_path)
     }
 
-    fn breadcrumb_location(&self, _: &AppContext) -> ToolbarItemLocation {
+    fn breadcrumb_location(&self, _: &App) -> ToolbarItemLocation {
         ToolbarItemLocation::PrimaryLeft
     }
 
-    fn breadcrumbs(&self, _theme: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
+    fn breadcrumbs(&self, _theme: &Theme, cx: &App) -> Option<Vec<BreadcrumbText>> {
         let text = breadcrumbs_text_for_image(self.project.read(cx), self.image_item.read(cx), cx);
         Some(vec![BreadcrumbText {
             text,
@@ -154,12 +155,13 @@ impl Item for ImageView {
     fn clone_on_split(
         &self,
         _workspace_id: Option<WorkspaceId>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<View<Self>>
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Entity<Self>>
     where
         Self: Sized,
     {
-        Some(cx.new_view(|cx| Self {
+        Some(cx.new(|cx| Self {
             image_item: self.image_item.clone(),
             project: self.project.clone(),
             focus_handle: cx.focus_handle(),
@@ -167,7 +169,7 @@ impl Item for ImageView {
     }
 }
 
-fn breadcrumbs_text_for_image(project: &Project, image: &ImageItem, cx: &AppContext) -> String {
+fn breadcrumbs_text_for_image(project: &Project, image: &ImageItem, cx: &App) -> String {
     let path = image.file.file_name(cx);
     if project.visible_worktrees(cx).count() <= 1 {
         return path.to_string_lossy().to_string();
@@ -190,13 +192,14 @@ impl SerializableItem for ImageView {
     }
 
     fn deserialize(
-        project: Model<Project>,
-        _workspace: WeakView<Workspace>,
+        project: Entity<Project>,
+        _workspace: WeakEntity<Workspace>,
         workspace_id: WorkspaceId,
         item_id: ItemId,
-        cx: &mut WindowContext,
-    ) -> Task<gpui::Result<View<Self>>> {
-        cx.spawn(|mut cx| async move {
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Task<gpui::Result<Entity<Self>>> {
+        window.spawn(cx, |mut cx| async move {
             let image_path = IMAGE_VIEWER
                 .get_image_path(item_id, workspace_id)?
                 .ok_or_else(|| anyhow::anyhow!("No image path found"))?;
@@ -218,16 +221,19 @@ impl SerializableItem for ImageView {
                 .update(&mut cx, |project, cx| project.open_image(project_path, cx))?
                 .await?;
 
-            cx.update(|cx| Ok(cx.new_view(|cx| ImageView::new(image_item, project, cx))))?
+            cx.update(|_, cx| Ok(cx.new(|cx| ImageView::new(image_item, project, cx))))?
         })
     }
 
     fn cleanup(
         workspace_id: WorkspaceId,
         alive_items: Vec<ItemId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<gpui::Result<()>> {
-        cx.spawn(|_| IMAGE_VIEWER.delete_unloaded_items(workspace_id, alive_items))
+        window.spawn(cx, |_| {
+            IMAGE_VIEWER.delete_unloaded_items(workspace_id, alive_items)
+        })
     }
 
     fn serialize(
@@ -235,7 +241,8 @@ impl SerializableItem for ImageView {
         workspace: &mut Workspace,
         item_id: ItemId,
         _closing: bool,
-        cx: &mut ViewContext<Self>,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<Task<gpui::Result<()>>> {
         let workspace_id = workspace.database_id()?;
         let image_path = self.image_item.read(cx).file.as_local()?.abs_path(cx);
@@ -255,16 +262,19 @@ impl SerializableItem for ImageView {
 }
 
 impl EventEmitter<()> for ImageView {}
-impl FocusableView for ImageView {
-    fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
+impl Focusable for ImageView {
+    fn focus_handle(&self, _cx: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
 
 impl Render for ImageView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let image = self.image_item.read(cx).image.clone();
-        let checkered_background = |bounds: Bounds<Pixels>, _, cx: &mut WindowContext| {
+        let checkered_background = |bounds: Bounds<Pixels>,
+                                    _,
+                                    window: &mut Window,
+                                    _cx: &mut App| {
             let square_size = 32.0;
 
             let start_y = bounds.origin.y.0;
@@ -289,7 +299,7 @@ impl Render for ImageView {
                         opaque_grey(0.7, 0.4)
                     };
 
-                    cx.paint_quad(fill(rect, color));
+                    window.paint_quad(fill(rect, color));
                     color_swapper = !color_swapper;
                     x += square_size;
                 }
@@ -299,7 +309,7 @@ impl Render for ImageView {
             }
         };
 
-        let checkered_background = canvas(|_, _| (), checkered_background)
+        let checkered_background = canvas(|_, _, _| (), checkered_background)
             .border_2()
             .border_color(cx.theme().styles.colors.border)
             .size_full()
@@ -334,9 +344,10 @@ impl ProjectItem for ImageView {
     type Item = ImageItem;
 
     fn for_project_item(
-        project: Model<Project>,
-        item: Model<Self::Item>,
-        cx: &mut ViewContext<Self>,
+        project: Entity<Project>,
+        item: Entity<Self::Item>,
+        _: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self
     where
         Self: Sized,
@@ -345,7 +356,7 @@ impl ProjectItem for ImageView {
     }
 }
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     workspace::register_project_item::<ImageView>(cx);
     workspace::register_serializable_item::<ImageView>(cx)
 }

crates/indexed_docs/src/extension_indexed_docs_provider.rs 🔗

@@ -4,13 +4,13 @@ use std::sync::Arc;
 use anyhow::Result;
 use async_trait::async_trait;
 use extension::{Extension, ExtensionHostProxy, ExtensionIndexedDocsProviderProxy};
-use gpui::AppContext;
+use gpui::App;
 
 use crate::{
     IndexedDocsDatabase, IndexedDocsProvider, IndexedDocsRegistry, PackageName, ProviderId,
 };
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     let proxy = ExtensionHostProxy::default_global(cx);
     proxy.register_indexed_docs_provider_proxy(IndexedDocsRegistryProxy {
         indexed_docs_registry: IndexedDocsRegistry::global(cx),

crates/indexed_docs/src/indexed_docs.rs 🔗

@@ -3,14 +3,14 @@ mod providers;
 mod registry;
 mod store;
 
-use gpui::AppContext;
+use gpui::App;
 
 pub use crate::extension_indexed_docs_provider::ExtensionIndexedDocsProvider;
 pub use crate::providers::rustdoc::*;
 pub use crate::registry::*;
 pub use crate::store::*;
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     IndexedDocsRegistry::init_global(cx);
     extension_indexed_docs_provider::init(cx);
 }

crates/indexed_docs/src/registry.rs 🔗

@@ -1,7 +1,7 @@
 use std::sync::Arc;
 
 use collections::HashMap;
-use gpui::{AppContext, BackgroundExecutor, Global, ReadGlobal, UpdateGlobal};
+use gpui::{App, BackgroundExecutor, Global, ReadGlobal, UpdateGlobal};
 use parking_lot::RwLock;
 
 use crate::{IndexedDocsProvider, IndexedDocsStore, ProviderId};
@@ -16,11 +16,11 @@ pub struct IndexedDocsRegistry {
 }
 
 impl IndexedDocsRegistry {
-    pub fn global(cx: &AppContext) -> Arc<Self> {
+    pub fn global(cx: &App) -> Arc<Self> {
         GlobalIndexedDocsRegistry::global(cx).0.clone()
     }
 
-    pub(crate) fn init_global(cx: &mut AppContext) {
+    pub(crate) fn init_global(cx: &mut App) {
         GlobalIndexedDocsRegistry::set_global(
             cx,
             GlobalIndexedDocsRegistry(Arc::new(Self::new(cx.background_executor().clone()))),

crates/indexed_docs/src/store.rs 🔗

@@ -9,7 +9,7 @@ use derive_more::{Deref, Display};
 use futures::future::{self, BoxFuture, Shared};
 use futures::FutureExt;
 use fuzzy::StringMatchCandidate;
-use gpui::{AppContext, BackgroundExecutor, Task};
+use gpui::{App, BackgroundExecutor, Task};
 use heed::types::SerdeBincode;
 use heed::Database;
 use parking_lot::RwLock;
@@ -62,7 +62,7 @@ pub struct IndexedDocsStore {
 }
 
 impl IndexedDocsStore {
-    pub fn try_global(provider: ProviderId, cx: &AppContext) -> Result<Arc<Self>> {
+    pub fn try_global(provider: ProviderId, cx: &App) -> Result<Arc<Self>> {
         let registry = IndexedDocsRegistry::global(cx);
         registry
             .get_provider_store(provider.clone())

crates/inline_completion/src/inline_completion.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{AppContext, Model, ModelContext};
+use gpui::{App, Context, Entity};
 use language::Buffer;
 use std::ops::Range;
 
@@ -28,35 +28,35 @@ pub trait InlineCompletionProvider: 'static + Sized {
     }
     fn is_enabled(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         cursor_position: language::Anchor,
-        cx: &AppContext,
+        cx: &App,
     ) -> bool;
     fn is_refreshing(&self) -> bool;
     fn refresh(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         cursor_position: language::Anchor,
         debounce: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     );
-    fn needs_terms_acceptance(&self, _cx: &AppContext) -> bool {
+    fn needs_terms_acceptance(&self, _cx: &App) -> bool {
         false
     }
     fn cycle(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         cursor_position: language::Anchor,
         direction: Direction,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     );
-    fn accept(&mut self, cx: &mut ModelContext<Self>);
-    fn discard(&mut self, cx: &mut ModelContext<Self>);
+    fn accept(&mut self, cx: &mut Context<Self>);
+    fn discard(&mut self, cx: &mut Context<Self>);
     fn suggest(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         cursor_position: language::Anchor,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<InlineCompletion>;
 }
 
@@ -65,40 +65,40 @@ pub trait InlineCompletionProviderHandle {
     fn display_name(&self) -> &'static str;
     fn is_enabled(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         cursor_position: language::Anchor,
-        cx: &AppContext,
+        cx: &App,
     ) -> bool;
     fn show_completions_in_menu(&self) -> bool;
     fn show_completions_in_normal_mode(&self) -> bool;
     fn show_tab_accept_marker(&self) -> bool;
-    fn needs_terms_acceptance(&self, cx: &AppContext) -> bool;
-    fn is_refreshing(&self, cx: &AppContext) -> bool;
+    fn needs_terms_acceptance(&self, cx: &App) -> bool;
+    fn is_refreshing(&self, cx: &App) -> bool;
     fn refresh(
         &self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         cursor_position: language::Anchor,
         debounce: bool,
-        cx: &mut AppContext,
+        cx: &mut App,
     );
     fn cycle(
         &self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         cursor_position: language::Anchor,
         direction: Direction,
-        cx: &mut AppContext,
+        cx: &mut App,
     );
-    fn accept(&self, cx: &mut AppContext);
-    fn discard(&self, cx: &mut AppContext);
+    fn accept(&self, cx: &mut App);
+    fn discard(&self, cx: &mut App);
     fn suggest(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         cursor_position: language::Anchor,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Option<InlineCompletion>;
 }
 
-impl<T> InlineCompletionProviderHandle for Model<T>
+impl<T> InlineCompletionProviderHandle for Entity<T>
 where
     T: InlineCompletionProvider,
 {
@@ -124,27 +124,27 @@ where
 
     fn is_enabled(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         cursor_position: language::Anchor,
-        cx: &AppContext,
+        cx: &App,
     ) -> bool {
         self.read(cx).is_enabled(buffer, cursor_position, cx)
     }
 
-    fn needs_terms_acceptance(&self, cx: &AppContext) -> bool {
+    fn needs_terms_acceptance(&self, cx: &App) -> bool {
         self.read(cx).needs_terms_acceptance(cx)
     }
 
-    fn is_refreshing(&self, cx: &AppContext) -> bool {
+    fn is_refreshing(&self, cx: &App) -> bool {
         self.read(cx).is_refreshing()
     }
 
     fn refresh(
         &self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         cursor_position: language::Anchor,
         debounce: bool,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) {
         self.update(cx, |this, cx| {
             this.refresh(buffer, cursor_position, debounce, cx)
@@ -153,29 +153,29 @@ where
 
     fn cycle(
         &self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         cursor_position: language::Anchor,
         direction: Direction,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) {
         self.update(cx, |this, cx| {
             this.cycle(buffer, cursor_position, direction, cx)
         })
     }
 
-    fn accept(&self, cx: &mut AppContext) {
+    fn accept(&self, cx: &mut App) {
         self.update(cx, |this, cx| this.accept(cx))
     }
 
-    fn discard(&self, cx: &mut AppContext) {
+    fn discard(&self, cx: &mut App) {
         self.update(cx, |this, cx| this.discard(cx))
     }
 
     fn suggest(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         cursor_position: language::Anchor,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Option<InlineCompletion> {
         self.update(cx, |this, cx| this.suggest(buffer, cursor_position, cx))
     }

crates/inline_completion_button/src/inline_completion_button.rs 🔗

@@ -5,9 +5,8 @@ use editor::{scroll::Autoscroll, Editor};
 use feature_flags::{FeatureFlagAppExt, PredictEditsFeatureFlag};
 use fs::Fs;
 use gpui::{
-    actions, div, pulsating_between, Action, Animation, AnimationExt, AppContext,
-    AsyncWindowContext, Corner, Entity, IntoElement, Model, ParentElement, Render, Subscription,
-    View, ViewContext, WeakView, WindowContext,
+    actions, div, pulsating_between, Action, Animation, AnimationExt, App, AsyncWindowContext,
+    Corner, Entity, IntoElement, ParentElement, Render, Subscription, WeakEntity,
 };
 use language::{
     language_settings::{
@@ -46,8 +45,8 @@ pub struct InlineCompletionButton {
     file: Option<Arc<dyn File>>,
     inline_completion_provider: Option<Arc<dyn inline_completion::InlineCompletionProviderHandle>>,
     fs: Arc<dyn Fs>,
-    workspace: WeakView<Workspace>,
-    user_store: Model<UserStore>,
+    workspace: WeakEntity<Workspace>,
+    user_store: Entity<UserStore>,
     popover_menu_handle: PopoverMenuHandle<ContextMenu>,
 }
 
@@ -59,7 +58,7 @@ enum SupermavenButtonStatus {
 }
 
 impl Render for InlineCompletionButton {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let all_language_settings = all_language_settings(None, cx);
 
         match all_language_settings.inline_completions.provider {
@@ -91,17 +90,18 @@ impl Render for InlineCompletionButton {
                     return div().child(
                         IconButton::new("copilot-error", icon)
                             .icon_size(IconSize::Small)
-                            .on_click(cx.listener(move |_, _, cx| {
-                                if let Some(workspace) = cx.window_handle().downcast::<Workspace>()
+                            .on_click(cx.listener(move |_, _, window, cx| {
+                                if let Some(workspace) =
+                                    window.window_handle().downcast::<Workspace>()
                                 {
                                     workspace
-                                        .update(cx, |workspace, cx| {
+                                        .update(cx, |workspace, _, cx| {
                                             workspace.show_toast(
                                                 Toast::new(
                                                     NotificationId::unique::<CopilotErrorToast>(),
                                                     format!("Copilot can't be started: {}", e),
                                                 )
-                                                .on_click("Reinstall Copilot", |cx| {
+                                                .on_click("Reinstall Copilot", |_, cx| {
                                                     if let Some(copilot) = Copilot::global(cx) {
                                                         copilot
                                                             .update(cx, |copilot, cx| {
@@ -116,27 +116,29 @@ impl Render for InlineCompletionButton {
                                         .ok();
                                 }
                             }))
-                            .tooltip(|cx| Tooltip::for_action("GitHub Copilot", &ToggleMenu, cx)),
+                            .tooltip(|window, cx| {
+                                Tooltip::for_action("GitHub Copilot", &ToggleMenu, window, cx)
+                            }),
                     );
                 }
-                let this = cx.view().clone();
+                let this = cx.model().clone();
 
                 div().child(
                     PopoverMenu::new("copilot")
-                        .menu(move |cx| {
+                        .menu(move |window, cx| {
                             Some(match status {
-                                Status::Authorized => {
-                                    this.update(cx, |this, cx| this.build_copilot_context_menu(cx))
-                                }
-                                _ => this.update(cx, |this, cx| this.build_copilot_start_menu(cx)),
+                                Status::Authorized => this.update(cx, |this, cx| {
+                                    this.build_copilot_context_menu(window, cx)
+                                }),
+                                _ => this.update(cx, |this, cx| {
+                                    this.build_copilot_start_menu(window, cx)
+                                }),
                             })
                         })
                         .anchor(Corner::BottomRight)
-                        .trigger(
-                            IconButton::new("copilot-icon", icon).tooltip(|cx| {
-                                Tooltip::for_action("GitHub Copilot", &ToggleMenu, cx)
-                            }),
-                        )
+                        .trigger(IconButton::new("copilot-icon", icon).tooltip(|window, cx| {
+                            Tooltip::for_action("GitHub Copilot", &ToggleMenu, window, cx)
+                        }))
                         .with_handle(self.popover_menu_handle.clone()),
                 )
             }
@@ -171,23 +173,23 @@ impl Render for InlineCompletionButton {
                 let icon = status.to_icon();
                 let tooltip_text = status.to_tooltip();
                 let has_menu = status.has_menu();
-                let this = cx.view().clone();
+                let this = cx.model().clone();
                 let fs = self.fs.clone();
 
                 return div().child(
                     PopoverMenu::new("supermaven")
-                        .menu(move |cx| match &status {
+                        .menu(move |window, cx| match &status {
                             SupermavenButtonStatus::NeedsActivation(activate_url) => {
-                                Some(ContextMenu::build(cx, |menu, _| {
+                                Some(ContextMenu::build(window, cx, |menu, _, _| {
                                     let fs = fs.clone();
                                     let activate_url = activate_url.clone();
-                                    menu.entry("Sign In", None, move |cx| {
+                                    menu.entry("Sign In", None, move |_, cx| {
                                         cx.open_url(activate_url.as_str())
                                     })
                                     .entry(
                                         "Use Copilot",
                                         None,
-                                        move |cx| {
+                                        move |_, cx| {
                                             set_completion_provider(
                                                 fs.clone(),
                                                 cx,
@@ -197,19 +199,26 @@ impl Render for InlineCompletionButton {
                                     )
                                 }))
                             }
-                            SupermavenButtonStatus::Ready => Some(
-                                this.update(cx, |this, cx| this.build_supermaven_context_menu(cx)),
-                            ),
+                            SupermavenButtonStatus::Ready => Some(this.update(cx, |this, cx| {
+                                this.build_supermaven_context_menu(window, cx)
+                            })),
                             _ => None,
                         })
                         .anchor(Corner::BottomRight)
-                        .trigger(IconButton::new("supermaven-icon", icon).tooltip(move |cx| {
-                            if has_menu {
-                                Tooltip::for_action(tooltip_text.clone(), &ToggleMenu, cx)
-                            } else {
-                                Tooltip::text(tooltip_text.clone(), cx)
-                            }
-                        }))
+                        .trigger(IconButton::new("supermaven-icon", icon).tooltip(
+                            move |window, cx| {
+                                if has_menu {
+                                    Tooltip::for_action(
+                                        tooltip_text.clone(),
+                                        &ToggleMenu,
+                                        window,
+                                        cx,
+                                    )
+                                } else {
+                                    Tooltip::text(tooltip_text.clone())(window, cx)
+                                }
+                            },
+                        ))
                         .with_handle(self.popover_menu_handle.clone()),
                 );
             }
@@ -240,29 +249,32 @@ impl Render for InlineCompletionButton {
                                 ))
                                 .into_any_element(),
                             )
-                            .tooltip(|cx| {
+                            .tooltip(|window, cx| {
                                 Tooltip::with_meta(
                                     "Edit Predictions",
                                     None,
                                     "Read Terms of Service",
+                                    window,
                                     cx,
                                 )
                             })
-                            .on_click(cx.listener(move |_, _, cx| {
+                            .on_click(cx.listener(move |_, _, window, cx| {
                                 let user_store = user_store.clone();
 
                                 if let Some(workspace) = workspace.upgrade() {
-                                    ZedPredictTos::toggle(workspace, user_store, cx);
+                                    ZedPredictTos::toggle(workspace, user_store, window, cx);
                                 }
                             })),
                     );
                 }
 
-                let this = cx.view().clone();
+                let this = cx.model().clone();
                 let button = IconButton::new("zeta", IconName::ZedPredict).when(
                     !self.popover_menu_handle.is_deployed(),
                     |button| {
-                        button.tooltip(|cx| Tooltip::for_action("Edit Prediction", &ToggleMenu, cx))
+                        button.tooltip(|window, cx| {
+                            Tooltip::for_action("Edit Prediction", &ToggleMenu, window, cx)
+                        })
                     },
                 );
 
@@ -272,8 +284,8 @@ impl Render for InlineCompletionButton {
                     .map_or(false, |provider| provider.is_refreshing(cx));
 
                 let mut popover_menu = PopoverMenu::new("zeta")
-                    .menu(move |cx| {
-                        Some(this.update(cx, |this, cx| this.build_zeta_context_menu(cx)))
+                    .menu(move |window, cx| {
+                        Some(this.update(cx, |this, cx| this.build_zeta_context_menu(window, cx)))
                     })
                     .anchor(Corner::BottomRight)
                     .with_handle(self.popover_menu_handle.clone());
@@ -300,11 +312,11 @@ impl Render for InlineCompletionButton {
 
 impl InlineCompletionButton {
     pub fn new(
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         fs: Arc<dyn Fs>,
-        user_store: Model<UserStore>,
+        user_store: Entity<UserStore>,
         popover_menu_handle: PopoverMenuHandle<ContextMenu>,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Self {
         if let Some(copilot) = Copilot::global(cx) {
             cx.observe(&copilot, |_, _, cx| cx.notify()).detach()
@@ -326,17 +338,21 @@ impl InlineCompletionButton {
         }
     }
 
-    pub fn build_copilot_start_menu(&mut self, cx: &mut ViewContext<Self>) -> View<ContextMenu> {
+    pub fn build_copilot_start_menu(
+        &mut self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Entity<ContextMenu> {
         let fs = self.fs.clone();
-        ContextMenu::build(cx, |menu, _| {
+        ContextMenu::build(window, cx, |menu, _, _| {
             menu.entry("Sign In", None, copilot::initiate_sign_in)
                 .entry("Disable Copilot", None, {
                     let fs = fs.clone();
-                    move |cx| hide_copilot(fs.clone(), cx)
+                    move |_window, cx| hide_copilot(fs.clone(), cx)
                 })
                 .entry("Use Supermaven", None, {
                     let fs = fs.clone();
-                    move |cx| {
+                    move |_window, cx| {
                         set_completion_provider(
                             fs.clone(),
                             cx,
@@ -347,11 +363,7 @@ impl InlineCompletionButton {
         })
     }
 
-    pub fn build_language_settings_menu(
-        &self,
-        mut menu: ContextMenu,
-        cx: &mut WindowContext,
-    ) -> ContextMenu {
+    pub fn build_language_settings_menu(&self, mut menu: ContextMenu, cx: &mut App) -> ContextMenu {
         let fs = self.fs.clone();
 
         if let Some(language) = self.language.clone() {
@@ -367,7 +379,9 @@ impl InlineCompletionButton {
                     language.name()
                 ),
                 None,
-                move |cx| toggle_inline_completions_for_language(language.clone(), fs.clone(), cx),
+                move |_, cx| {
+                    toggle_inline_completions_for_language(language.clone(), fs.clone(), cx)
+                },
             );
         }
 
@@ -383,18 +397,19 @@ impl InlineCompletionButton {
                     if path_enabled { "Hide" } else { "Show" }
                 ),
                 None,
-                move |cx| {
-                    if let Some(workspace) = cx.window_handle().downcast::<Workspace>() {
-                        if let Ok(workspace) = workspace.root_view(cx) {
+                move |window, cx| {
+                    if let Some(workspace) = window.window_handle().downcast::<Workspace>() {
+                        if let Ok(workspace) = workspace.root_model(cx) {
                             let workspace = workspace.downgrade();
-                            cx.spawn(|cx| {
-                                configure_disabled_globs(
-                                    workspace,
-                                    path_enabled.then_some(path.clone()),
-                                    cx,
-                                )
-                            })
-                            .detach_and_log_err(cx);
+                            window
+                                .spawn(cx, |cx| {
+                                    configure_disabled_globs(
+                                        workspace,
+                                        path_enabled.then_some(path.clone()),
+                                        cx,
+                                    )
+                                })
+                                .detach_and_log_err(cx);
                         }
                     }
                 },
@@ -409,12 +424,16 @@ impl InlineCompletionButton {
                 "Show Inline Completions for All Files"
             },
             None,
-            move |cx| toggle_inline_completions_globally(fs.clone(), cx),
+            move |_, cx| toggle_inline_completions_globally(fs.clone(), cx),
         )
     }
 
-    fn build_copilot_context_menu(&self, cx: &mut ViewContext<Self>) -> View<ContextMenu> {
-        ContextMenu::build(cx, |menu, cx| {
+    fn build_copilot_context_menu(
+        &self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Entity<ContextMenu> {
+        ContextMenu::build(window, cx, |menu, _, cx| {
             self.build_language_settings_menu(menu, cx)
                 .separator()
                 .link(
@@ -428,26 +447,34 @@ impl InlineCompletionButton {
         })
     }
 
-    fn build_supermaven_context_menu(&self, cx: &mut ViewContext<Self>) -> View<ContextMenu> {
-        ContextMenu::build(cx, |menu, cx| {
+    fn build_supermaven_context_menu(
+        &self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Entity<ContextMenu> {
+        ContextMenu::build(window, cx, |menu, _, cx| {
             self.build_language_settings_menu(menu, cx)
                 .separator()
                 .action("Sign Out", supermaven::SignOut.boxed_clone())
         })
     }
 
-    fn build_zeta_context_menu(&self, cx: &mut ViewContext<Self>) -> View<ContextMenu> {
+    fn build_zeta_context_menu(
+        &self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Entity<ContextMenu> {
         let workspace = self.workspace.clone();
-        ContextMenu::build(cx, |menu, cx| {
+        ContextMenu::build(window, cx, |menu, _window, cx| {
             self.build_language_settings_menu(menu, cx)
                 .separator()
                 .entry(
                     "Rate Completions",
                     Some(RateCompletions.boxed_clone()),
-                    move |cx| {
+                    move |window, cx| {
                         workspace
                             .update(cx, |workspace, cx| {
-                                RateCompletionModal::toggle(workspace, cx)
+                                RateCompletionModal::toggle(workspace, window, cx)
                             })
                             .ok();
                     },
@@ -455,7 +482,7 @@ impl InlineCompletionButton {
         })
     }
 
-    pub fn update_enabled(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
+    pub fn update_enabled(&mut self, editor: Entity<Editor>, cx: &mut Context<Self>) {
         let editor = editor.read(cx);
         let snapshot = editor.buffer().read(cx).snapshot(cx);
         let suggestion_anchor = editor.selections.newest_anchor().start;
@@ -476,16 +503,21 @@ impl InlineCompletionButton {
         self.language = language.cloned();
         self.file = file;
 
-        cx.notify()
+        cx.notify();
     }
 
-    pub fn toggle_menu(&mut self, cx: &mut ViewContext<Self>) {
-        self.popover_menu_handle.toggle(cx);
+    pub fn toggle_menu(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        self.popover_menu_handle.toggle(window, cx);
     }
 }
 
 impl StatusItemView for InlineCompletionButton {
-    fn set_active_pane_item(&mut self, item: Option<&dyn ItemHandle>, cx: &mut ViewContext<Self>) {
+    fn set_active_pane_item(
+        &mut self,
+        item: Option<&dyn ItemHandle>,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(editor) = item.and_then(|item| item.act_as::<Editor>(cx)) {
             self.editor_subscription = Some((
                 cx.observe(&editor, Self::update_enabled),
@@ -529,13 +561,13 @@ impl SupermavenButtonStatus {
 }
 
 async fn configure_disabled_globs(
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     path_to_disable: Option<Arc<Path>>,
     mut cx: AsyncWindowContext,
 ) -> Result<()> {
     let settings_editor = workspace
-        .update(&mut cx, |_, cx| {
-            create_and_open_local_file(paths::settings_file(), cx, || {
+        .update_in(&mut cx, |_, window, cx| {
+            create_and_open_local_file(paths::settings_file(), window, cx, || {
                 settings::initial_user_settings_content().as_ref().into()
             })
         })?
@@ -543,45 +575,47 @@ async fn configure_disabled_globs(
         .downcast::<Editor>()
         .unwrap();
 
-    settings_editor.downgrade().update(&mut cx, |item, cx| {
-        let text = item.buffer().read(cx).snapshot(cx).text();
-
-        let settings = cx.global::<SettingsStore>();
-        let edits = settings.edits_for_update::<AllLanguageSettings>(&text, |file| {
-            let copilot = file.inline_completions.get_or_insert_with(Default::default);
-            let globs = copilot.disabled_globs.get_or_insert_with(|| {
-                settings
-                    .get::<AllLanguageSettings>(None)
-                    .inline_completions
-                    .disabled_globs
-                    .iter()
-                    .map(|glob| glob.glob().to_string())
-                    .collect()
-            });
-
-            if let Some(path_to_disable) = &path_to_disable {
-                globs.push(path_to_disable.to_string_lossy().into_owned());
-            } else {
-                globs.clear();
-            }
-        });
+    settings_editor
+        .downgrade()
+        .update_in(&mut cx, |item, window, cx| {
+            let text = item.buffer().read(cx).snapshot(cx).text();
+
+            let settings = cx.global::<SettingsStore>();
+            let edits = settings.edits_for_update::<AllLanguageSettings>(&text, |file| {
+                let copilot = file.inline_completions.get_or_insert_with(Default::default);
+                let globs = copilot.disabled_globs.get_or_insert_with(|| {
+                    settings
+                        .get::<AllLanguageSettings>(None)
+                        .inline_completions
+                        .disabled_globs
+                        .iter()
+                        .map(|glob| glob.glob().to_string())
+                        .collect()
+                });
 
-        if !edits.is_empty() {
-            item.change_selections(Some(Autoscroll::newest()), cx, |selections| {
-                selections.select_ranges(edits.iter().map(|e| e.0.clone()));
+                if let Some(path_to_disable) = &path_to_disable {
+                    globs.push(path_to_disable.to_string_lossy().into_owned());
+                } else {
+                    globs.clear();
+                }
             });
 
-            // When *enabling* a path, don't actually perform an edit, just select the range.
-            if path_to_disable.is_some() {
-                item.edit(edits.iter().cloned(), cx);
+            if !edits.is_empty() {
+                item.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
+                    selections.select_ranges(edits.iter().map(|e| e.0.clone()));
+                });
+
+                // When *enabling* a path, don't actually perform an edit, just select the range.
+                if path_to_disable.is_some() {
+                    item.edit(edits.iter().cloned(), cx);
+                }
             }
-        }
-    })?;
+        })?;
 
     anyhow::Ok(())
 }
 
-fn toggle_inline_completions_globally(fs: Arc<dyn Fs>, cx: &mut AppContext) {
+fn toggle_inline_completions_globally(fs: Arc<dyn Fs>, cx: &mut App) {
     let show_inline_completions =
         all_language_settings(None, cx).inline_completions_enabled(None, None, cx);
     update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
@@ -589,11 +623,7 @@ fn toggle_inline_completions_globally(fs: Arc<dyn Fs>, cx: &mut AppContext) {
     });
 }
 
-fn set_completion_provider(
-    fs: Arc<dyn Fs>,
-    cx: &mut AppContext,
-    provider: InlineCompletionProvider,
-) {
+fn set_completion_provider(fs: Arc<dyn Fs>, cx: &mut App, provider: InlineCompletionProvider) {
     update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
         file.features
             .get_or_insert(Default::default())
@@ -601,11 +631,7 @@ fn set_completion_provider(
     });
 }
 
-fn toggle_inline_completions_for_language(
-    language: Arc<Language>,
-    fs: Arc<dyn Fs>,
-    cx: &mut AppContext,
-) {
+fn toggle_inline_completions_for_language(language: Arc<Language>, fs: Arc<dyn Fs>, cx: &mut App) {
     let show_inline_completions =
         all_language_settings(None, cx).inline_completions_enabled(Some(&language), None, cx);
     update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
@@ -616,7 +642,7 @@ fn toggle_inline_completions_for_language(
     });
 }
 
-fn hide_copilot(fs: Arc<dyn Fs>, cx: &mut AppContext) {
+fn hide_copilot(fs: Arc<dyn Fs>, cx: &mut App) {
     update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
         file.features
             .get_or_insert(Default::default())

crates/journal/src/journal.rs 🔗

@@ -2,7 +2,7 @@ use anyhow::Result;
 use chrono::{Datelike, Local, NaiveTime, Timelike};
 use editor::scroll::Autoscroll;
 use editor::Editor;
-use gpui::{actions, AppContext, ViewContext, WindowContext};
+use gpui::{actions, App, Context, Window};
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsSources};
@@ -50,25 +50,25 @@ impl settings::Settings for JournalSettings {
 
     type FileContent = Self;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
         sources.json_merge()
     }
 }
 
-pub fn init(_: Arc<AppState>, cx: &mut AppContext) {
+pub fn init(_: Arc<AppState>, cx: &mut App) {
     JournalSettings::register(cx);
 
-    cx.observe_new_views(
-        |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
-            workspace.register_action(|workspace, _: &NewJournalEntry, cx| {
-                new_journal_entry(workspace, cx);
+    cx.observe_new(
+        |workspace: &mut Workspace, _window, _cx: &mut Context<Workspace>| {
+            workspace.register_action(|workspace, _: &NewJournalEntry, window, cx| {
+                new_journal_entry(workspace, window, cx);
             });
         },
     )
     .detach();
 }
 
-pub fn new_journal_entry(workspace: &Workspace, cx: &mut WindowContext) {
+pub fn new_journal_entry(workspace: &Workspace, window: &mut Window, cx: &mut App) {
     let settings = JournalSettings::get_global(cx);
     let journal_dir = match journal_dir(settings.path.as_ref().unwrap()) {
         Some(journal_dir) => journal_dir,
@@ -117,51 +117,52 @@ pub fn new_journal_entry(workspace: &Workspace, cx: &mut WindowContext) {
     let app_state = workspace.app_state().clone();
     let view_snapshot = workspace.weak_handle().clone();
 
-    cx.spawn(|mut cx| async move {
-        let (journal_dir, entry_path) = create_entry.await?;
-        let opened = if open_new_workspace {
-            let (new_workspace, _) = cx
-                .update(|cx| {
-                    workspace::open_paths(
-                        &[journal_dir],
-                        app_state,
-                        workspace::OpenOptions::default(),
-                        cx,
-                    )
-                })?
-                .await?;
-            new_workspace
-                .update(&mut cx, |workspace, cx| {
-                    workspace.open_paths(vec![entry_path], OpenVisible::All, None, cx)
-                })?
-                .await
-        } else {
-            view_snapshot
-                .update(&mut cx, |workspace, cx| {
-                    workspace.open_paths(vec![entry_path], OpenVisible::All, None, cx)
-                })?
-                .await
-        };
-
-        if let Some(Some(Ok(item))) = opened.first() {
-            if let Some(editor) = item.downcast::<Editor>().map(|editor| editor.downgrade()) {
-                editor.update(&mut cx, |editor, cx| {
-                    let len = editor.buffer().read(cx).len(cx);
-                    editor.change_selections(Some(Autoscroll::center()), cx, |s| {
-                        s.select_ranges([len..len])
-                    });
-                    if len > 0 {
-                        editor.insert("\n\n", cx);
-                    }
-                    editor.insert(&entry_heading, cx);
-                    editor.insert("\n\n", cx);
-                })?;
+    window
+        .spawn(cx, |mut cx| async move {
+            let (journal_dir, entry_path) = create_entry.await?;
+            let opened = if open_new_workspace {
+                let (new_workspace, _) = cx
+                    .update(|_window, cx| {
+                        workspace::open_paths(
+                            &[journal_dir],
+                            app_state,
+                            workspace::OpenOptions::default(),
+                            cx,
+                        )
+                    })?
+                    .await?;
+                new_workspace
+                    .update(&mut cx, |workspace, window, cx| {
+                        workspace.open_paths(vec![entry_path], OpenVisible::All, None, window, cx)
+                    })?
+                    .await
+            } else {
+                view_snapshot
+                    .update_in(&mut cx, |workspace, window, cx| {
+                        workspace.open_paths(vec![entry_path], OpenVisible::All, None, window, cx)
+                    })?
+                    .await
+            };
+
+            if let Some(Some(Ok(item))) = opened.first() {
+                if let Some(editor) = item.downcast::<Editor>().map(|editor| editor.downgrade()) {
+                    editor.update_in(&mut cx, |editor, window, cx| {
+                        let len = editor.buffer().read(cx).len(cx);
+                        editor.change_selections(Some(Autoscroll::center()), window, cx, |s| {
+                            s.select_ranges([len..len])
+                        });
+                        if len > 0 {
+                            editor.insert("\n\n", window, cx);
+                        }
+                        editor.insert(&entry_heading, window, cx);
+                        editor.insert("\n\n", window, cx);
+                    })?;
+                }
             }
-        }
 
-        anyhow::Ok(())
-    })
-    .detach_and_log_err(cx);
+            anyhow::Ok(())
+        })
+        .detach_and_log_err(cx);
 }
 
 fn journal_dir(path: &str) -> Option<PathBuf> {

crates/language/src/buffer.rs 🔗

@@ -17,7 +17,7 @@ use crate::{
     LanguageScope, Outline, OutlineConfig, RunnableCapture, RunnableTag, TextObject,
     TreeSitterOptions,
 };
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use async_watch as watch;
 use clock::Lamport;
 pub use clock::ReplicaId;
@@ -25,8 +25,8 @@ use collections::HashMap;
 use fs::MTime;
 use futures::channel::oneshot;
 use gpui::{
-    AnyElement, AppContext, Context as _, EventEmitter, HighlightStyle, Model, ModelContext,
-    Pixels, SharedString, Task, TaskLabel, WindowContext,
+    AnyElement, App, AppContext as _, Context, Entity, EventEmitter, HighlightStyle, Pixels,
+    SharedString, Task, TaskLabel, Window,
 };
 use lsp::LanguageServerId;
 use parking_lot::Mutex;
@@ -137,7 +137,7 @@ pub enum ParseStatus {
 }
 
 struct BufferBranchState {
-    base_buffer: Model<Buffer>,
+    base_buffer: Entity<Buffer>,
     merged_operations: Vec<Lamport>,
 }
 
@@ -371,22 +371,22 @@ pub trait File: Send + Sync {
 
     /// Returns the path of this file relative to the worktree's parent directory (this means it
     /// includes the name of the worktree's root folder).
-    fn full_path(&self, cx: &AppContext) -> PathBuf;
+    fn full_path(&self, cx: &App) -> PathBuf;
 
     /// Returns the last component of this handle's absolute path. If this handle refers to the root
     /// of its worktree, then this method will return the name of the worktree itself.
-    fn file_name<'a>(&'a self, cx: &'a AppContext) -> &'a OsStr;
+    fn file_name<'a>(&'a self, cx: &'a App) -> &'a OsStr;
 
     /// Returns the id of the worktree to which this file belongs.
     ///
     /// This is needed for looking up project-specific settings.
-    fn worktree_id(&self, cx: &AppContext) -> WorktreeId;
+    fn worktree_id(&self, cx: &App) -> WorktreeId;
 
     /// Converts this file into an [`Any`] trait object.
     fn as_any(&self) -> &dyn Any;
 
     /// Converts this file into a protobuf message.
-    fn to_proto(&self, cx: &AppContext) -> rpc::proto::File;
+    fn to_proto(&self, cx: &App) -> rpc::proto::File;
 
     /// Return whether Zed considers this to be a private file.
     fn is_private(&self) -> bool;
@@ -420,13 +420,13 @@ impl DiskState {
 /// The file associated with a buffer, in the case where the file is on the local disk.
 pub trait LocalFile: File {
     /// Returns the absolute path of this file
-    fn abs_path(&self, cx: &AppContext) -> PathBuf;
+    fn abs_path(&self, cx: &App) -> PathBuf;
 
     /// Loads the file contents from disk and returns them as a UTF-8 encoded string.
-    fn load(&self, cx: &AppContext) -> Task<Result<String>>;
+    fn load(&self, cx: &App) -> Task<Result<String>>;
 
     /// Loads the file's contents from disk.
-    fn load_bytes(&self, cx: &AppContext) -> Task<Result<Vec<u8>>>;
+    fn load_bytes(&self, cx: &App) -> Task<Result<Vec<u8>>>;
 }
 
 /// The auto-indent behavior associated with an editing operation.
@@ -527,7 +527,8 @@ pub struct ChunkRenderer {
 }
 
 pub struct ChunkRendererContext<'a, 'b> {
-    pub context: &'a mut WindowContext<'b>,
+    pub window: &'a mut Window,
+    pub context: &'b mut App,
     pub max_width: Pixels,
 }
 
@@ -540,7 +541,7 @@ impl fmt::Debug for ChunkRenderer {
 }
 
 impl<'a, 'b> Deref for ChunkRendererContext<'a, 'b> {
-    type Target = WindowContext<'b>;
+    type Target = App;
 
     fn deref(&self) -> &Self::Target {
         self.context
@@ -605,7 +606,7 @@ impl EditPreview {
         current_snapshot: &BufferSnapshot,
         edits: &[(Range<Anchor>, String)],
         include_deletions: bool,
-        cx: &AppContext,
+        cx: &App,
     ) -> HighlightedEdits {
         let mut text = String::new();
         let mut highlights = Vec::new();
@@ -682,7 +683,7 @@ impl EditPreview {
         text: &mut String,
         highlights: &mut Vec<(Range<usize>, HighlightStyle)>,
         override_style: Option<HighlightStyle>,
-        cx: &AppContext,
+        cx: &App,
     ) {
         for chunk in self.highlighted_chunks(range) {
             let start = text.len();
@@ -745,7 +746,7 @@ impl EditPreview {
 
 impl Buffer {
     /// Create a new buffer with the given base text.
-    pub fn local<T: Into<String>>(base_text: T, cx: &ModelContext<Self>) -> Self {
+    pub fn local<T: Into<String>>(base_text: T, cx: &Context<Self>) -> Self {
         Self::build(
             TextBuffer::new(0, cx.entity_id().as_non_zero_u64().into(), base_text.into()),
             None,
@@ -757,7 +758,7 @@ impl Buffer {
     pub fn local_normalized(
         base_text_normalized: Rope,
         line_ending: LineEnding,
-        cx: &ModelContext<Self>,
+        cx: &Context<Self>,
     ) -> Self {
         Self::build(
             TextBuffer::new_normalized(
@@ -807,7 +808,7 @@ impl Buffer {
     }
 
     /// Serialize the buffer's state to a protobuf message.
-    pub fn to_proto(&self, cx: &AppContext) -> proto::BufferState {
+    pub fn to_proto(&self, cx: &App) -> proto::BufferState {
         proto::BufferState {
             id: self.remote_id().into(),
             file: self.file.as_ref().map(|f| f.to_proto(cx)),
@@ -822,7 +823,7 @@ impl Buffer {
     pub fn serialize_ops(
         &self,
         since: Option<clock::Global>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Vec<proto::Operation>> {
         let mut operations = Vec::new();
         operations.extend(self.deferred_ops.iter().map(proto::serialize_operation));
@@ -869,7 +870,7 @@ impl Buffer {
     }
 
     /// Assign a language to the buffer, returning the buffer.
-    pub fn with_language(mut self, language: Arc<Language>, cx: &mut ModelContext<Self>) -> Self {
+    pub fn with_language(mut self, language: Arc<Language>, cx: &mut Context<Self>) -> Self {
         self.set_language(Some(language), cx);
         self
     }
@@ -925,7 +926,7 @@ impl Buffer {
         text: Rope,
         language: Option<Arc<Language>>,
         language_registry: Option<Arc<LanguageRegistry>>,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> impl Future<Output = BufferSnapshot> {
         let entity_id = cx.reserve_model::<Self>().entity_id();
         let buffer_id = entity_id.as_non_zero_u64().into();
@@ -970,9 +971,9 @@ impl Buffer {
         }
     }
 
-    pub fn branch(&mut self, cx: &mut ModelContext<Self>) -> Model<Self> {
-        let this = cx.handle();
-        cx.new_model(|cx| {
+    pub fn branch(&mut self, cx: &mut Context<Self>) -> Entity<Self> {
+        let this = cx.model();
+        cx.new(|cx| {
             let mut branch = Self {
                 branch_state: Some(BufferBranchState {
                     base_buffer: this.clone(),
@@ -998,7 +999,7 @@ impl Buffer {
     pub fn preview_edits(
         &self,
         edits: Arc<[(Range<Anchor>, String)]>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<EditPreview> {
         let registry = self.language_registry();
         let language = self.language().cloned();
@@ -1027,7 +1028,7 @@ impl Buffer {
     ///
     /// If `ranges` is empty, then all changes will be applied. This buffer must
     /// be a branch buffer to call this method.
-    pub fn merge_into_base(&mut self, ranges: Vec<Range<usize>>, cx: &mut ModelContext<Self>) {
+    pub fn merge_into_base(&mut self, ranges: Vec<Range<usize>>, cx: &mut Context<Self>) {
         let Some(base_buffer) = self.base_buffer() else {
             debug_panic!("not a branch buffer");
             return;
@@ -1080,9 +1081,9 @@ impl Buffer {
 
     fn on_base_buffer_event(
         &mut self,
-        _: Model<Buffer>,
+        _: Entity<Buffer>,
         event: &BufferEvent,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let BufferEvent::Operation { operation, .. } = event else {
             return;
@@ -1137,7 +1138,7 @@ impl Buffer {
     }
 
     /// Assign a language to the buffer.
-    pub fn set_language(&mut self, language: Option<Arc<Language>>, cx: &mut ModelContext<Self>) {
+    pub fn set_language(&mut self, language: Option<Arc<Language>>, cx: &mut Context<Self>) {
         self.non_text_state_update_count += 1;
         self.syntax_map.lock().clear(&self.text);
         self.language = language;
@@ -1158,7 +1159,7 @@ impl Buffer {
     }
 
     /// Assign the buffer a new [`Capability`].
-    pub fn set_capability(&mut self, capability: Capability, cx: &mut ModelContext<Self>) {
+    pub fn set_capability(&mut self, capability: Capability, cx: &mut Context<Self>) {
         self.capability = capability;
         cx.emit(BufferEvent::CapabilityChanged)
     }
@@ -1168,7 +1169,7 @@ impl Buffer {
         &mut self,
         version: clock::Global,
         mtime: Option<MTime>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.saved_version = version;
         self.has_unsaved_edits
@@ -1180,13 +1181,13 @@ impl Buffer {
     }
 
     /// This method is called to signal that the buffer has been discarded.
-    pub fn discarded(&self, cx: &mut ModelContext<Self>) {
+    pub fn discarded(&self, cx: &mut Context<Self>) {
         cx.emit(BufferEvent::Discarded);
         cx.notify();
     }
 
     /// Reloads the contents of the buffer from disk.
-    pub fn reload(&mut self, cx: &ModelContext<Self>) -> oneshot::Receiver<Option<Transaction>> {
+    pub fn reload(&mut self, cx: &Context<Self>) -> oneshot::Receiver<Option<Transaction>> {
         let (tx, rx) = futures::channel::oneshot::channel();
         let prev_version = self.text.version();
         self.reload_task = Some(cx.spawn(|this, mut cx| async move {
@@ -1234,7 +1235,7 @@ impl Buffer {
         version: clock::Global,
         line_ending: LineEnding,
         mtime: Option<MTime>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.saved_version = version;
         self.has_unsaved_edits
@@ -1247,7 +1248,7 @@ impl Buffer {
 
     /// Updates the [`File`] backing this buffer. This should be called when
     /// the file has changed or has been deleted.
-    pub fn file_updated(&mut self, new_file: Arc<dyn File>, cx: &mut ModelContext<Self>) {
+    pub fn file_updated(&mut self, new_file: Arc<dyn File>, cx: &mut Context<Self>) {
         let was_dirty = self.is_dirty();
         let mut file_changed = false;
 
@@ -1279,7 +1280,7 @@ impl Buffer {
         }
     }
 
-    pub fn base_buffer(&self) -> Option<Model<Self>> {
+    pub fn base_buffer(&self) -> Option<Entity<Self>> {
         Some(self.branch_state.as_ref()?.base_buffer.clone())
     }
 
@@ -1345,7 +1346,7 @@ impl Buffer {
     /// initiate an additional reparse recursively. To avoid concurrent parses
     /// for the same buffer, we only initiate a new parse if we are not already
     /// parsing in the background.
-    pub fn reparse(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn reparse(&mut self, cx: &mut Context<Self>) {
         if self.parsing_in_background {
             return;
         }
@@ -1411,7 +1412,7 @@ impl Buffer {
         }
     }
 
-    fn did_finish_parsing(&mut self, syntax_snapshot: SyntaxSnapshot, cx: &mut ModelContext<Self>) {
+    fn did_finish_parsing(&mut self, syntax_snapshot: SyntaxSnapshot, cx: &mut Context<Self>) {
         self.non_text_state_update_count += 1;
         self.syntax_map.lock().did_parse(syntax_snapshot);
         self.request_autoindent(cx);
@@ -1429,7 +1430,7 @@ impl Buffer {
         &mut self,
         server_id: LanguageServerId,
         diagnostics: DiagnosticSet,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let lamport_timestamp = self.text.lamport_clock.tick();
         let op = Operation::UpdateDiagnostics {
@@ -1441,7 +1442,7 @@ impl Buffer {
         self.send_operation(op, true, cx);
     }
 
-    fn request_autoindent(&mut self, cx: &mut ModelContext<Self>) {
+    fn request_autoindent(&mut self, cx: &mut Context<Self>) {
         if let Some(indent_sizes) = self.compute_autoindents() {
             let indent_sizes = cx.background_executor().spawn(indent_sizes);
             match cx
@@ -1637,7 +1638,7 @@ impl Buffer {
     fn apply_autoindents(
         &mut self,
         indent_sizes: BTreeMap<u32, IndentSize>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.autoindent_requests.clear();
 
@@ -1695,7 +1696,7 @@ impl Buffer {
 
     /// Spawns a background task that asynchronously computes a `Diff` between the buffer's text
     /// and the given new text.
-    pub fn diff(&self, mut new_text: String, cx: &AppContext) -> Task<Diff> {
+    pub fn diff(&self, mut new_text: String, cx: &App) -> Task<Diff> {
         let old_text = self.as_rope().clone();
         let base_version = self.version();
         cx.background_executor()
@@ -1768,7 +1769,7 @@ impl Buffer {
 
     /// Spawns a background task that searches the buffer for any whitespace
     /// at the ends of a lines, and returns a `Diff` that removes that whitespace.
-    pub fn remove_trailing_whitespace(&self, cx: &AppContext) -> Task<Diff> {
+    pub fn remove_trailing_whitespace(&self, cx: &App) -> Task<Diff> {
         let old_text = self.as_rope().clone();
         let line_ending = self.line_ending();
         let base_version = self.version();
@@ -1788,7 +1789,7 @@ impl Buffer {
 
     /// Ensures that the buffer ends with a single newline character, and
     /// no other whitespace.
-    pub fn ensure_final_newline(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn ensure_final_newline(&mut self, cx: &mut Context<Self>) {
         let len = self.len();
         let mut offset = len;
         for chunk in self.as_rope().reversed_chunks_in_range(0..len) {
@@ -1810,7 +1811,7 @@ impl Buffer {
     /// Applies a diff to the buffer. If the buffer has changed since the given diff was
     /// calculated, then adjust the diff to account for those changes, and discard any
     /// parts of the diff that conflict with those changes.
-    pub fn apply_diff(&mut self, diff: Diff, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
+    pub fn apply_diff(&mut self, diff: Diff, cx: &mut Context<Self>) -> Option<TransactionId> {
         // Check for any edits to the buffer that have occurred since this diff
         // was computed.
         let snapshot = self.snapshot();
@@ -1916,7 +1917,7 @@ impl Buffer {
     }
 
     /// Terminates the current transaction, if this is the outermost transaction.
-    pub fn end_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
+    pub fn end_transaction(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> {
         self.end_transaction_at(Instant::now(), cx)
     }
 
@@ -1926,7 +1927,7 @@ impl Buffer {
     pub fn end_transaction_at(
         &mut self,
         now: Instant,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<TransactionId> {
         assert!(self.transaction_depth > 0);
         self.transaction_depth -= 1;
@@ -2002,7 +2003,7 @@ impl Buffer {
         selections: Arc<[Selection<Anchor>]>,
         line_mode: bool,
         cursor_shape: CursorShape,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let lamport_timestamp = self.text.lamport_clock.tick();
         self.remote_selections.insert(
@@ -2030,7 +2031,7 @@ impl Buffer {
 
     /// Clears the selections, so that other replicas of the buffer do not see any selections for
     /// this replica.
-    pub fn remove_active_selections(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn remove_active_selections(&mut self, cx: &mut Context<Self>) {
         if self
             .remote_selections
             .get(&self.text.replica_id())
@@ -2041,7 +2042,7 @@ impl Buffer {
     }
 
     /// Replaces the buffer's entire text.
-    pub fn set_text<T>(&mut self, text: T, cx: &mut ModelContext<Self>) -> Option<clock::Lamport>
+    pub fn set_text<T>(&mut self, text: T, cx: &mut Context<Self>) -> Option<clock::Lamport>
     where
         T: Into<Arc<str>>,
     {
@@ -2062,7 +2063,7 @@ impl Buffer {
         &mut self,
         edits_iter: I,
         autoindent_mode: Option<AutoindentMode>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<clock::Lamport>
     where
         I: IntoIterator<Item = (Range<S>, T)>,
@@ -2184,12 +2185,7 @@ impl Buffer {
         Some(edit_id)
     }
 
-    fn did_edit(
-        &mut self,
-        old_version: &clock::Global,
-        was_dirty: bool,
-        cx: &mut ModelContext<Self>,
-    ) {
+    fn did_edit(&mut self, old_version: &clock::Global, was_dirty: bool, cx: &mut Context<Self>) {
         if self.edits_since::<usize>(old_version).next().is_none() {
             return;
         }
@@ -2203,7 +2199,7 @@ impl Buffer {
         cx.notify();
     }
 
-    pub fn autoindent_ranges<I, T>(&mut self, ranges: I, cx: &mut ModelContext<Self>)
+    pub fn autoindent_ranges<I, T>(&mut self, ranges: I, cx: &mut Context<Self>)
     where
         I: IntoIterator<Item = Range<T>>,
         T: ToOffset + Copy,
@@ -2234,7 +2230,7 @@ impl Buffer {
         position: impl ToPoint,
         space_above: bool,
         space_below: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Point {
         let mut position = position.to_point(self);
 
@@ -2283,11 +2279,7 @@ impl Buffer {
     }
 
     /// Applies the given remote operations to the buffer.
-    pub fn apply_ops<I: IntoIterator<Item = Operation>>(
-        &mut self,
-        ops: I,
-        cx: &mut ModelContext<Self>,
-    ) {
+    pub fn apply_ops<I: IntoIterator<Item = Operation>>(&mut self, ops: I, cx: &mut Context<Self>) {
         self.pending_autoindent.take();
         let was_dirty = self.is_dirty();
         let old_version = self.version.clone();
@@ -2318,7 +2310,7 @@ impl Buffer {
         cx.notify();
     }
 
-    fn flush_deferred_ops(&mut self, cx: &mut ModelContext<Self>) {
+    fn flush_deferred_ops(&mut self, cx: &mut Context<Self>) {
         let mut deferred_ops = Vec::new();
         for op in self.deferred_ops.drain().iter().cloned() {
             if self.can_apply_op(&op) {
@@ -2353,7 +2345,7 @@ impl Buffer {
         }
     }
 
-    fn apply_op(&mut self, operation: Operation, cx: &mut ModelContext<Self>) {
+    fn apply_op(&mut self, operation: Operation, cx: &mut Context<Self>) {
         match operation {
             Operation::Buffer(_) => {
                 unreachable!("buffer operations should never be applied at this layer")
@@ -2423,7 +2415,7 @@ impl Buffer {
         server_id: LanguageServerId,
         diagnostics: DiagnosticSet,
         lamport_timestamp: clock::Lamport,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if lamport_timestamp > self.diagnostics_timestamp {
             let ix = self.diagnostics.binary_search_by_key(&server_id, |e| e.0);
@@ -2445,7 +2437,7 @@ impl Buffer {
         }
     }
 
-    fn send_operation(&self, operation: Operation, is_local: bool, cx: &mut ModelContext<Self>) {
+    fn send_operation(&self, operation: Operation, is_local: bool, cx: &mut Context<Self>) {
         cx.emit(BufferEvent::Operation {
             operation,
             is_local,
@@ -2453,13 +2445,13 @@ impl Buffer {
     }
 
     /// Removes the selections for a given peer.
-    pub fn remove_peer(&mut self, replica_id: ReplicaId, cx: &mut ModelContext<Self>) {
+    pub fn remove_peer(&mut self, replica_id: ReplicaId, cx: &mut Context<Self>) {
         self.remote_selections.remove(&replica_id);
         cx.notify();
     }
 
     /// Undoes the most recent transaction.
-    pub fn undo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
+    pub fn undo(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> {
         let was_dirty = self.is_dirty();
         let old_version = self.version.clone();
 
@@ -2476,7 +2468,7 @@ impl Buffer {
     pub fn undo_transaction(
         &mut self,
         transaction_id: TransactionId,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> bool {
         let was_dirty = self.is_dirty();
         let old_version = self.version.clone();
@@ -2493,7 +2485,7 @@ impl Buffer {
     pub fn undo_to_transaction(
         &mut self,
         transaction_id: TransactionId,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> bool {
         let was_dirty = self.is_dirty();
         let old_version = self.version.clone();
@@ -2509,11 +2501,7 @@ impl Buffer {
         undone
     }
 
-    pub fn undo_operations(
-        &mut self,
-        counts: HashMap<Lamport, u32>,
-        cx: &mut ModelContext<Buffer>,
-    ) {
+    pub fn undo_operations(&mut self, counts: HashMap<Lamport, u32>, cx: &mut Context<Buffer>) {
         let was_dirty = self.is_dirty();
         let operation = self.text.undo_operations(counts);
         let old_version = self.version.clone();
@@ -2522,7 +2510,7 @@ impl Buffer {
     }
 
     /// Manually redoes a specific transaction in the buffer's redo history.
-    pub fn redo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
+    pub fn redo(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> {
         let was_dirty = self.is_dirty();
         let old_version = self.version.clone();
 
@@ -2539,7 +2527,7 @@ impl Buffer {
     pub fn redo_to_transaction(
         &mut self,
         transaction_id: TransactionId,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> bool {
         let was_dirty = self.is_dirty();
         let old_version = self.version.clone();
@@ -2560,7 +2548,7 @@ impl Buffer {
         &mut self,
         server_id: LanguageServerId,
         triggers: BTreeSet<String>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.completion_triggers_timestamp = self.text.lamport_clock.tick();
         if triggers.is_empty() {
@@ -2614,7 +2602,7 @@ impl Buffer {
         &mut self,
         marked_string: &str,
         autoindent_mode: Option<AutoindentMode>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let edits = self.edits_for_marked_text(marked_string);
         self.edit(edits, autoindent_mode, cx);
@@ -2624,12 +2612,8 @@ impl Buffer {
         self.text.set_group_interval(group_interval);
     }
 
-    pub fn randomly_edit<T>(
-        &mut self,
-        rng: &mut T,
-        old_range_count: usize,
-        cx: &mut ModelContext<Self>,
-    ) where
+    pub fn randomly_edit<T>(&mut self, rng: &mut T, old_range_count: usize, cx: &mut Context<Self>)
+    where
         T: rand::Rng,
     {
         let mut edits: Vec<(Range<usize>, String)> = Vec::new();
@@ -2656,7 +2640,7 @@ impl Buffer {
         self.edit(edits, None, cx);
     }
 
-    pub fn randomly_undo_redo(&mut self, rng: &mut impl rand::Rng, cx: &mut ModelContext<Self>) {
+    pub fn randomly_undo_redo(&mut self, rng: &mut impl rand::Rng, cx: &mut Context<Self>) {
         let was_dirty = self.is_dirty();
         let old_version = self.version.clone();
 
@@ -2687,7 +2671,7 @@ impl BufferSnapshot {
     }
     /// Returns [`IndentSize`] for a given position that respects user settings
     /// and language preferences.
-    pub fn language_indent_size_at<T: ToOffset>(&self, position: T, cx: &AppContext) -> IndentSize {
+    pub fn language_indent_size_at<T: ToOffset>(&self, position: T, cx: &App) -> IndentSize {
         let settings = language_settings(
             self.language_at(position).map(|l| l.name()),
             self.file(),
@@ -3027,7 +3011,7 @@ impl BufferSnapshot {
     pub fn settings_at<'a, D: ToOffset>(
         &'a self,
         position: D,
-        cx: &'a AppContext,
+        cx: &'a App,
     ) -> Cow<'a, LanguageSettings> {
         language_settings(
             self.language_at(position).map(|l| l.name()),
@@ -4000,7 +3984,7 @@ impl BufferSnapshot {
     }
 
     /// Resolves the file path (relative to the worktree root) associated with the underlying file.
-    pub fn resolve_file_path(&self, cx: &AppContext, include_root: bool) -> Option<PathBuf> {
+    pub fn resolve_file_path(&self, cx: &App, include_root: bool) -> Option<PathBuf> {
         if let Some(file) = self.file() {
             if file.path().file_name().is_none() || include_root {
                 Some(file.full_path(cx))
@@ -4403,7 +4387,7 @@ impl File for TestFile {
         &self.path
     }
 
-    fn full_path(&self, _: &gpui::AppContext) -> PathBuf {
+    fn full_path(&self, _: &gpui::App) -> PathBuf {
         PathBuf::from(&self.root_name).join(self.path.as_ref())
     }
 
@@ -4415,11 +4399,11 @@ impl File for TestFile {
         unimplemented!()
     }
 
-    fn file_name<'a>(&'a self, _: &'a gpui::AppContext) -> &'a std::ffi::OsStr {
+    fn file_name<'a>(&'a self, _: &'a gpui::App) -> &'a std::ffi::OsStr {
         self.path().file_name().unwrap_or(self.root_name.as_ref())
     }
 
-    fn worktree_id(&self, _: &AppContext) -> WorktreeId {
+    fn worktree_id(&self, _: &App) -> WorktreeId {
         WorktreeId::from_usize(0)
     }
 
@@ -4427,7 +4411,7 @@ impl File for TestFile {
         unimplemented!()
     }
 
-    fn to_proto(&self, _: &AppContext) -> rpc::proto::File {
+    fn to_proto(&self, _: &App) -> rpc::proto::File {
         unimplemented!()
     }
 

crates/language/src/buffer_tests.rs 🔗

@@ -6,8 +6,8 @@ use crate::Buffer;
 use clock::ReplicaId;
 use collections::BTreeMap;
 use futures::FutureExt as _;
-use gpui::{AppContext, BorrowAppContext, Model};
-use gpui::{Context, TestAppContext};
+use gpui::TestAppContext;
+use gpui::{App, AppContext as _, BorrowAppContext, Entity};
 use indoc::indoc;
 use proto::deserialize_operation;
 use rand::prelude::*;
@@ -42,10 +42,10 @@ fn init_logger() {
 }
 
 #[gpui::test]
-fn test_line_endings(cx: &mut gpui::AppContext) {
+fn test_line_endings(cx: &mut gpui::App) {
     init_settings(cx, |_| {});
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let mut buffer =
             Buffer::local("one\r\ntwo\rthree", cx).with_language(Arc::new(rust_lang()), cx);
         assert_eq!(buffer.text(), "one\ntwo\nthree");
@@ -67,7 +67,7 @@ fn test_line_endings(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_select_language(cx: &mut AppContext) {
+fn test_select_language(cx: &mut App) {
     init_settings(cx, |_| {});
 
     let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
@@ -256,13 +256,13 @@ fn file(path: &str) -> Arc<dyn File> {
 }
 
 #[gpui::test]
-fn test_edit_events(cx: &mut gpui::AppContext) {
+fn test_edit_events(cx: &mut gpui::App) {
     let mut now = Instant::now();
     let buffer_1_events = Arc::new(Mutex::new(Vec::new()));
     let buffer_2_events = Arc::new(Mutex::new(Vec::new()));
 
-    let buffer1 = cx.new_model(|cx| Buffer::local("abcdef", cx));
-    let buffer2 = cx.new_model(|cx| {
+    let buffer1 = cx.new(|cx| Buffer::local("abcdef", cx));
+    let buffer2 = cx.new(|cx| {
         Buffer::remote(
             BufferId::from(cx.entity_id().as_non_zero_u64()),
             1,
@@ -354,7 +354,7 @@ fn test_edit_events(cx: &mut gpui::AppContext) {
 #[gpui::test]
 async fn test_apply_diff(cx: &mut TestAppContext) {
     let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n";
-    let buffer = cx.new_model(|cx| Buffer::local(text, cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx));
     let anchor = buffer.update(cx, |buffer, _| buffer.anchor_before(Point::new(3, 3)));
 
     let text = "a\nccc\ndddd\nffffff\n";
@@ -386,7 +386,7 @@ async fn test_normalize_whitespace(cx: &mut gpui::TestAppContext) {
     ]
     .join("\n");
 
-    let buffer = cx.new_model(|cx| Buffer::local(text, cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx));
 
     // Spawn a task to format the buffer's whitespace.
     // Pause so that the formatting task starts running.
@@ -450,8 +450,7 @@ async fn test_normalize_whitespace(cx: &mut gpui::TestAppContext) {
 #[gpui::test]
 async fn test_reparse(cx: &mut gpui::TestAppContext) {
     let text = "fn a() {}";
-    let buffer =
-        cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
 
     // Wait for the initial text to parse
     cx.executor().run_until_parked();
@@ -577,7 +576,7 @@ async fn test_reparse(cx: &mut gpui::TestAppContext) {
 
 #[gpui::test]
 async fn test_resetting_language(cx: &mut gpui::TestAppContext) {
-    let buffer = cx.new_model(|cx| {
+    let buffer = cx.new(|cx| {
         let mut buffer = Buffer::local("{}", cx).with_language(Arc::new(rust_lang()), cx);
         buffer.set_sync_parse_timeout(Duration::ZERO);
         buffer
@@ -626,8 +625,7 @@ async fn test_outline(cx: &mut gpui::TestAppContext) {
     "#
     .unindent();
 
-    let buffer =
-        cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
     let outline = buffer
         .update(cx, |buffer, _| buffer.snapshot().outline(None))
         .unwrap();
@@ -711,8 +709,7 @@ async fn test_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) {
     "#
     .unindent();
 
-    let buffer =
-        cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
     let outline = buffer
         .update(cx, |buffer, _| buffer.snapshot().outline(None))
         .unwrap();
@@ -748,7 +745,7 @@ async fn test_outline_with_extra_context(cx: &mut gpui::TestAppContext) {
     "#
     .unindent();
 
-    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(language), cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(language), cx));
     let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
 
     // extra context nodes are included in the outline.
@@ -774,7 +771,7 @@ async fn test_outline_with_extra_context(cx: &mut gpui::TestAppContext) {
 }
 
 #[gpui::test]
-fn test_outline_annotations(cx: &mut AppContext) {
+fn test_outline_annotations(cx: &mut App) {
     // Add this new test case
     let text = r#"
         /// This is a doc comment
@@ -794,8 +791,7 @@ fn test_outline_annotations(cx: &mut AppContext) {
     "#
     .unindent();
 
-    let buffer =
-        cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
     let outline = buffer
         .update(cx, |buffer, _| buffer.snapshot().outline(None))
         .unwrap();
@@ -845,8 +841,7 @@ async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
     "#
     .unindent();
 
-    let buffer =
-        cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
     let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
 
     // point is at the start of an item
@@ -916,7 +911,7 @@ async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
 }
 
 #[gpui::test]
-fn test_text_objects(cx: &mut AppContext) {
+fn test_text_objects(cx: &mut App) {
     let (text, ranges) = marked_text_ranges(
         indoc! {r#"
             impl Hello {
@@ -927,7 +922,7 @@ fn test_text_objects(cx: &mut AppContext) {
     );
 
     let buffer =
-        cx.new_model(|cx| Buffer::local(text.clone(), cx).with_language(Arc::new(rust_lang()), cx));
+        cx.new(|cx| Buffer::local(text.clone(), cx).with_language(Arc::new(rust_lang()), cx));
     let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
 
     let matches = snapshot
@@ -949,7 +944,7 @@ fn test_text_objects(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_enclosing_bracket_ranges(cx: &mut AppContext) {
+fn test_enclosing_bracket_ranges(cx: &mut App) {
     let mut assert = |selection_text, range_markers| {
         assert_bracket_pairs(selection_text, range_markers, rust_lang(), cx)
     };
@@ -1065,7 +1060,7 @@ fn test_enclosing_bracket_ranges(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(cx: &mut AppContext) {
+fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(cx: &mut App) {
     let mut assert = |selection_text, bracket_pair_texts| {
         assert_bracket_pairs(selection_text, bracket_pair_texts, javascript_lang(), cx)
     };
@@ -1097,8 +1092,8 @@ fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(cx: &
 }
 
 #[gpui::test]
-fn test_range_for_syntax_ancestor(cx: &mut AppContext) {
-    cx.new_model(|cx| {
+fn test_range_for_syntax_ancestor(cx: &mut App) {
+    cx.new(|cx| {
         let text = "fn a() { b(|c| {}) }";
         let buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
         let snapshot = buffer.snapshot();
@@ -1147,10 +1142,10 @@ fn test_range_for_syntax_ancestor(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_autoindent_with_soft_tabs(cx: &mut AppContext) {
+fn test_autoindent_with_soft_tabs(cx: &mut App) {
     init_settings(cx, |_| {});
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let text = "fn a() {}";
         let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
 
@@ -1187,12 +1182,12 @@ fn test_autoindent_with_soft_tabs(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_autoindent_with_hard_tabs(cx: &mut AppContext) {
+fn test_autoindent_with_hard_tabs(cx: &mut App) {
     init_settings(cx, |settings| {
         settings.defaults.hard_tabs = Some(true);
     });
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let text = "fn a() {}";
         let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
 
@@ -1229,10 +1224,10 @@ fn test_autoindent_with_hard_tabs(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppContext) {
+fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut App) {
     init_settings(cx, |_| {});
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let mut buffer = Buffer::local(
             "
             fn a() {
@@ -1371,7 +1366,7 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppC
         buffer
     });
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         eprintln!("second buffer: {:?}", cx.entity_id());
 
         let mut buffer = Buffer::local(
@@ -1434,10 +1429,10 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppC
 }
 
 #[gpui::test]
-fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut AppContext) {
+fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut App) {
     init_settings(cx, |_| {});
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let mut buffer = Buffer::local(
             "
             fn a() {
@@ -1495,10 +1490,10 @@ fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut Ap
 }
 
 #[gpui::test]
-fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut AppContext) {
+fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut App) {
     init_settings(cx, |_| {});
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let mut buffer = Buffer::local(
             "
             fn a() {}
@@ -1551,10 +1546,10 @@ fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut AppContext) {
+fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut App) {
     init_settings(cx, |_| {});
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let text = "a\nb";
         let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
         buffer.edit(
@@ -1568,10 +1563,10 @@ fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_autoindent_multi_line_insertion(cx: &mut AppContext) {
+fn test_autoindent_multi_line_insertion(cx: &mut App) {
     init_settings(cx, |_| {});
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let text = "
             const a: usize = 1;
             fn b() {
@@ -1609,10 +1604,10 @@ fn test_autoindent_multi_line_insertion(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_autoindent_block_mode(cx: &mut AppContext) {
+fn test_autoindent_block_mode(cx: &mut App) {
     init_settings(cx, |_| {});
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let text = r#"
             fn a() {
                 b();
@@ -1692,10 +1687,10 @@ fn test_autoindent_block_mode(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContext) {
+fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut App) {
     init_settings(cx, |_| {});
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let text = r#"
             fn a() {
                 if b() {
@@ -1771,10 +1766,10 @@ fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContex
 }
 
 #[gpui::test]
-fn test_autoindent_block_mode_multiple_adjacent_ranges(cx: &mut AppContext) {
+fn test_autoindent_block_mode_multiple_adjacent_ranges(cx: &mut App) {
     init_settings(cx, |_| {});
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let (text, ranges_to_replace) = marked_text_ranges(
             &"
             mod numbers {
@@ -1834,10 +1829,10 @@ fn test_autoindent_block_mode_multiple_adjacent_ranges(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_autoindent_language_without_indents_query(cx: &mut AppContext) {
+fn test_autoindent_language_without_indents_query(cx: &mut App) {
     init_settings(cx, |_| {});
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let text = "
             * one
                 - a
@@ -1878,7 +1873,7 @@ fn test_autoindent_language_without_indents_query(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_autoindent_with_injected_languages(cx: &mut AppContext) {
+fn test_autoindent_with_injected_languages(cx: &mut App) {
     init_settings(cx, |settings| {
         settings.languages.extend([
             (
@@ -1906,7 +1901,7 @@ fn test_autoindent_with_injected_languages(cx: &mut AppContext) {
     language_registry.add(html_language.clone());
     language_registry.add(javascript_language.clone());
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let (text, ranges) = marked_text_ranges(
             &"
                 <div>ˇ
@@ -1952,12 +1947,12 @@ fn test_autoindent_with_injected_languages(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) {
+fn test_autoindent_query_with_outdent_captures(cx: &mut App) {
     init_settings(cx, |settings| {
         settings.defaults.tab_size = Some(2.try_into().unwrap());
     });
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let mut buffer = Buffer::local("", cx).with_language(Arc::new(ruby_lang()), cx);
 
         let text = r#"
@@ -2001,7 +1996,7 @@ async fn test_async_autoindents_preserve_preview(cx: &mut TestAppContext) {
 
     // First we insert some newlines to request an auto-indent (asynchronously).
     // Then we request that a preview tab be preserved for the new version, even though it's edited.
-    let buffer = cx.new_model(|cx| {
+    let buffer = cx.new(|cx| {
         let text = "fn a() {}";
         let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
 
@@ -2053,11 +2048,11 @@ async fn test_async_autoindents_preserve_preview(cx: &mut TestAppContext) {
 }
 
 #[gpui::test]
-fn test_insert_empty_line(cx: &mut AppContext) {
+fn test_insert_empty_line(cx: &mut App) {
     init_settings(cx, |_| {});
 
     // Insert empty line at the beginning, requesting an empty line above
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let mut buffer = Buffer::local("abc\ndef\nghi", cx);
         let point = buffer.insert_empty_line(Point::new(0, 0), true, false, cx);
         assert_eq!(buffer.text(), "\nabc\ndef\nghi");
@@ -2066,7 +2061,7 @@ fn test_insert_empty_line(cx: &mut AppContext) {
     });
 
     // Insert empty line at the beginning, requesting an empty line above and below
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let mut buffer = Buffer::local("abc\ndef\nghi", cx);
         let point = buffer.insert_empty_line(Point::new(0, 0), true, true, cx);
         assert_eq!(buffer.text(), "\n\nabc\ndef\nghi");
@@ -2075,7 +2070,7 @@ fn test_insert_empty_line(cx: &mut AppContext) {
     });
 
     // Insert empty line at the start of a line, requesting empty lines above and below
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let mut buffer = Buffer::local("abc\ndef\nghi", cx);
         let point = buffer.insert_empty_line(Point::new(2, 0), true, true, cx);
         assert_eq!(buffer.text(), "abc\ndef\n\n\n\nghi");
@@ -2084,7 +2079,7 @@ fn test_insert_empty_line(cx: &mut AppContext) {
     });
 
     // Insert empty line in the middle of a line, requesting empty lines above and below
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let mut buffer = Buffer::local("abc\ndefghi\njkl", cx);
         let point = buffer.insert_empty_line(Point::new(1, 3), true, true, cx);
         assert_eq!(buffer.text(), "abc\ndef\n\n\n\nghi\njkl");
@@ -2093,7 +2088,7 @@ fn test_insert_empty_line(cx: &mut AppContext) {
     });
 
     // Insert empty line in the middle of a line, requesting empty line above only
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let mut buffer = Buffer::local("abc\ndefghi\njkl", cx);
         let point = buffer.insert_empty_line(Point::new(1, 3), true, false, cx);
         assert_eq!(buffer.text(), "abc\ndef\n\n\nghi\njkl");
@@ -2102,7 +2097,7 @@ fn test_insert_empty_line(cx: &mut AppContext) {
     });
 
     // Insert empty line in the middle of a line, requesting empty line below only
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let mut buffer = Buffer::local("abc\ndefghi\njkl", cx);
         let point = buffer.insert_empty_line(Point::new(1, 3), false, true, cx);
         assert_eq!(buffer.text(), "abc\ndef\n\n\nghi\njkl");
@@ -2111,7 +2106,7 @@ fn test_insert_empty_line(cx: &mut AppContext) {
     });
 
     // Insert empty line at the end, requesting empty lines above and below
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let mut buffer = Buffer::local("abc\ndef\nghi", cx);
         let point = buffer.insert_empty_line(Point::new(2, 3), true, true, cx);
         assert_eq!(buffer.text(), "abc\ndef\nghi\n\n\n");
@@ -2120,7 +2115,7 @@ fn test_insert_empty_line(cx: &mut AppContext) {
     });
 
     // Insert empty line at the end, requesting empty line above only
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let mut buffer = Buffer::local("abc\ndef\nghi", cx);
         let point = buffer.insert_empty_line(Point::new(2, 3), true, false, cx);
         assert_eq!(buffer.text(), "abc\ndef\nghi\n\n");
@@ -2129,7 +2124,7 @@ fn test_insert_empty_line(cx: &mut AppContext) {
     });
 
     // Insert empty line at the end, requesting empty line below only
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let mut buffer = Buffer::local("abc\ndef\nghi", cx);
         let point = buffer.insert_empty_line(Point::new(2, 3), false, true, cx);
         assert_eq!(buffer.text(), "abc\ndef\nghi\n\n");
@@ -2139,10 +2134,10 @@ fn test_insert_empty_line(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_language_scope_at_with_javascript(cx: &mut AppContext) {
+fn test_language_scope_at_with_javascript(cx: &mut App) {
     init_settings(cx, |_| {});
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let language = Language::new(
             LanguageConfig {
                 name: "JavaScript".into(),
@@ -2281,10 +2276,10 @@ fn test_language_scope_at_with_javascript(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_language_scope_at_with_rust(cx: &mut AppContext) {
+fn test_language_scope_at_with_rust(cx: &mut App) {
     init_settings(cx, |_| {});
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let language = Language::new(
             LanguageConfig {
                 name: "Rust".into(),
@@ -2350,10 +2345,10 @@ fn test_language_scope_at_with_rust(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) {
+fn test_language_scope_at_with_combined_injections(cx: &mut App) {
     init_settings(cx, |_| {});
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let text = r#"
             <ol>
             <% people.each do |person| %>
@@ -2398,10 +2393,10 @@ fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_language_at_with_hidden_languages(cx: &mut AppContext) {
+fn test_language_at_with_hidden_languages(cx: &mut App) {
     init_settings(cx, |_| {});
 
-    cx.new_model(|cx| {
+    cx.new(|cx| {
         let text = r#"
             this is an *emphasized* word.
         "#
@@ -2437,10 +2432,10 @@ fn test_language_at_with_hidden_languages(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_serialization(cx: &mut gpui::AppContext) {
+fn test_serialization(cx: &mut gpui::App) {
     let mut now = Instant::now();
 
-    let buffer1 = cx.new_model(|cx| {
+    let buffer1 = cx.new(|cx| {
         let mut buffer = Buffer::local("abc", cx);
         buffer.edit([(3..3, "D")], None, cx);
 
@@ -2463,7 +2458,7 @@ fn test_serialization(cx: &mut gpui::AppContext) {
     let ops = cx
         .background_executor()
         .block(buffer1.read(cx).serialize_ops(None, cx));
-    let buffer2 = cx.new_model(|cx| {
+    let buffer2 = cx.new(|cx| {
         let mut buffer = Buffer::from_proto(1, Capability::ReadWrite, state, None).unwrap();
         buffer.apply_ops(
             ops.into_iter()
@@ -2479,10 +2474,10 @@ fn test_serialization(cx: &mut gpui::AppContext) {
 fn test_branch_and_merge(cx: &mut TestAppContext) {
     cx.update(|cx| init_settings(cx, |_| {}));
 
-    let base = cx.new_model(|cx| Buffer::local("one\ntwo\nthree\n", cx));
+    let base = cx.new(|cx| Buffer::local("one\ntwo\nthree\n", cx));
 
     // Create a remote replica of the base buffer.
-    let base_replica = cx.new_model(|cx| {
+    let base_replica = cx.new(|cx| {
         Buffer::from_proto(1, Capability::ReadWrite, base.read(cx).to_proto(cx), None).unwrap()
     });
     base.update(cx, |_buffer, cx| {
@@ -2566,7 +2561,7 @@ fn test_branch_and_merge(cx: &mut TestAppContext) {
 fn test_merge_into_base(cx: &mut TestAppContext) {
     cx.update(|cx| init_settings(cx, |_| {}));
 
-    let base = cx.new_model(|cx| Buffer::local("abcdefghijk", cx));
+    let base = cx.new(|cx| Buffer::local("abcdefghijk", cx));
     let branch = base.update(cx, |buffer, cx| buffer.branch(cx));
 
     // Make 3 edits, merge one into the base.
@@ -2606,7 +2601,7 @@ fn test_merge_into_base(cx: &mut TestAppContext) {
 fn test_undo_after_merge_into_base(cx: &mut TestAppContext) {
     cx.update(|cx| init_settings(cx, |_| {}));
 
-    let base = cx.new_model(|cx| Buffer::local("abcdefghijk", cx));
+    let base = cx.new(|cx| Buffer::local("abcdefghijk", cx));
     let branch = base.update(cx, |buffer, cx| buffer.branch(cx));
 
     // Make 2 edits, merge one into the base.
@@ -2655,7 +2650,7 @@ async fn test_preview_edits(cx: &mut TestAppContext) {
         LanguageConfig::default(),
         Some(tree_sitter_rust::LANGUAGE.into()),
     ));
-    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
     let highlighted_edits = preview_edits(
         &buffer,
         cx,
@@ -2672,7 +2667,7 @@ async fn test_preview_edits(cx: &mut TestAppContext) {
     );
 
     async fn preview_edits(
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         cx: &mut TestAppContext,
         edits: impl IntoIterator<Item = (Range<Point>, &'static str)>,
     ) -> HighlightedEdits {
@@ -2714,7 +2709,7 @@ async fn test_preview_edits_interpolate(cx: &mut TestAppContext) {
         LanguageConfig::default(),
         Some(tree_sitter_rust::LANGUAGE.into()),
     ));
-    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 
     let edits = construct_edits(&buffer, [(Point::new(1, 4)..Point::new(1, 4), "first")], cx);
     let edit_preview = buffer
@@ -2754,7 +2749,7 @@ async fn test_preview_edits_interpolate(cx: &mut TestAppContext) {
     );
 
     fn construct_edits(
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         edits: impl IntoIterator<Item = (Range<Point>, &'static str)>,
         cx: &mut TestAppContext,
     ) -> Arc<[(Range<Anchor>, String)]> {
@@ -2775,7 +2770,7 @@ async fn test_preview_edits_interpolate(cx: &mut TestAppContext) {
 }
 
 #[gpui::test(iterations = 100)]
-fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
+fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
     let min_peers = env::var("MIN_PEERS")
         .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
         .unwrap_or(1);
@@ -2793,10 +2788,10 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
     let mut replica_ids = Vec::new();
     let mut buffers = Vec::new();
     let network = Arc::new(Mutex::new(Network::new(rng.clone())));
-    let base_buffer = cx.new_model(|cx| Buffer::local(base_text.as_str(), cx));
+    let base_buffer = cx.new(|cx| Buffer::local(base_text.as_str(), cx));
 
     for i in 0..rng.gen_range(min_peers..=max_peers) {
-        let buffer = cx.new_model(|cx| {
+        let buffer = cx.new(|cx| {
             let state = base_buffer.read(cx).to_proto(cx);
             let ops = cx
                 .background_executor()
@@ -2810,7 +2805,7 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
             );
             buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
             let network = network.clone();
-            cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
+            cx.subscribe(&cx.model(), move |buffer, _, event, _| {
                 if let BufferEvent::Operation {
                     operation,
                     is_local: true,
@@ -2920,7 +2915,7 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
                     new_replica_id,
                     replica_id
                 );
-                new_buffer = Some(cx.new_model(|cx| {
+                new_buffer = Some(cx.new(|cx| {
                     let mut new_buffer = Buffer::from_proto(
                         new_replica_id,
                         Capability::ReadWrite,
@@ -2941,7 +2936,7 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
                     );
                     new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
                     let network = network.clone();
-                    cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
+                    cx.subscribe(&cx.model(), move |buffer, _, event, _| {
                         if let BufferEvent::Operation {
                             operation,
                             is_local: true,
@@ -3357,7 +3352,7 @@ pub fn markdown_inline_lang() -> Language {
     .unwrap()
 }
 
-fn get_tree_sexp(buffer: &Model<Buffer>, cx: &mut gpui::TestAppContext) -> String {
+fn get_tree_sexp(buffer: &Entity<Buffer>, cx: &mut gpui::TestAppContext) -> String {
     buffer.update(cx, |buffer, _| {
         let snapshot = buffer.snapshot();
         let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
@@ -3370,12 +3365,11 @@ fn assert_bracket_pairs(
     selection_text: &'static str,
     bracket_pair_texts: Vec<&'static str>,
     language: Language,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) {
     let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
-    let buffer = cx.new_model(|cx| {
-        Buffer::local(expected_text.clone(), cx).with_language(Arc::new(language), cx)
-    });
+    let buffer =
+        cx.new(|cx| Buffer::local(expected_text.clone(), cx).with_language(Arc::new(language), cx));
     let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot());
 
     let selection_range = selection_ranges[0].clone();
@@ -3395,7 +3389,7 @@ fn assert_bracket_pairs(
     );
 }
 
-fn init_settings(cx: &mut AppContext, f: fn(&mut AllLanguageSettingsContent)) {
+fn init_settings(cx: &mut App, f: fn(&mut AllLanguageSettingsContent)) {
     let settings_store = SettingsStore::test(cx);
     cx.set_global(settings_store);
     crate::init(cx);

crates/language/src/language.rs 🔗

@@ -22,12 +22,12 @@ pub mod buffer_tests;
 pub mod markdown;
 
 use crate::language_settings::SoftWrap;
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use async_trait::async_trait;
 use collections::{HashMap, HashSet};
 use fs::Fs;
 use futures::Future;
-use gpui::{AppContext, AsyncAppContext, Model, SharedString, Task};
+use gpui::{App, AsyncAppContext, Entity, SharedString, Task};
 pub use highlight_map::HighlightMap;
 use http_client::HttpClient;
 pub use language_registry::{LanguageName, LoadedLanguage};
@@ -86,7 +86,7 @@ pub use tree_sitter::{Node, Parser, Tree, TreeCursor};
 /// Initializes the `language` crate.
 ///
 /// This should be called before making use of items from the create.
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     language_settings::init(cx);
 }
 
@@ -149,7 +149,7 @@ pub trait ToLspPosition {
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Location {
-    pub buffer: Model<Buffer>,
+    pub buffer: Entity<Buffer>,
     pub range: Range<Anchor>,
 }
 
@@ -266,7 +266,7 @@ impl CachedLspAdapter {
 // e.g. to display a notification or fetch data from the web.
 #[async_trait]
 pub trait LspAdapterDelegate: Send + Sync {
-    fn show_notification(&self, message: &str, cx: &mut AppContext);
+    fn show_notification(&self, message: &str, cx: &mut App);
     fn http_client(&self) -> Arc<dyn HttpClient>;
     fn worktree_id(&self) -> WorktreeId;
     fn worktree_root_path(&self) -> &Path;

crates/language/src/language_registry.rs 🔗

@@ -6,7 +6,7 @@ use crate::{
     with_parser, CachedLspAdapter, File, Language, LanguageConfig, LanguageId, LanguageMatcher,
     LanguageServerName, LspAdapter, ToolchainLister, PLAIN_TEXT,
 };
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use collections::{hash_map, HashMap, HashSet};
 
 use futures::{
@@ -14,7 +14,7 @@ use futures::{
     Future,
 };
 use globset::GlobSet;
-use gpui::{AppContext, BackgroundExecutor};
+use gpui::{App, BackgroundExecutor};
 use lsp::LanguageServerId;
 use parking_lot::{Mutex, RwLock};
 use postage::watch;
@@ -613,7 +613,7 @@ impl LanguageRegistry {
         self: &Arc<Self>,
         file: &Arc<dyn File>,
         content: Option<&Rope>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<AvailableLanguage> {
         let user_file_types = all_language_settings(Some(file), cx);
 

crates/language/src/language_settings.rs 🔗

@@ -9,7 +9,7 @@ use ec4rs::{
     Properties as EditorconfigProperties,
 };
 use globset::{Glob, GlobMatcher, GlobSet, GlobSetBuilder};
-use gpui::AppContext;
+use gpui::App;
 use itertools::{Either, Itertools};
 use schemars::{
     schema::{InstanceType, ObjectValidation, Schema, SchemaObject, SingleOrVec},
@@ -27,7 +27,7 @@ use std::{borrow::Cow, num::NonZeroU32, path::Path, sync::Arc};
 use util::serde::default_true;
 
 /// Initializes the language settings.
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     AllLanguageSettings::register(cx);
 }
 
@@ -35,7 +35,7 @@ pub fn init(cx: &mut AppContext) {
 pub fn language_settings<'a>(
     language: Option<LanguageName>,
     file: Option<&'a Arc<dyn File>>,
-    cx: &'a AppContext,
+    cx: &'a App,
 ) -> Cow<'a, LanguageSettings> {
     let location = file.map(|f| SettingsLocation {
         worktree_id: f.worktree_id(cx),
@@ -47,7 +47,7 @@ pub fn language_settings<'a>(
 /// Returns the settings for all languages from the provided file.
 pub fn all_language_settings<'a>(
     file: Option<&'a Arc<dyn File>>,
-    cx: &'a AppContext,
+    cx: &'a App,
 ) -> &'a AllLanguageSettings {
     let location = file.map(|f| SettingsLocation {
         worktree_id: f.worktree_id(cx),
@@ -857,7 +857,7 @@ impl AllLanguageSettings {
         &'a self,
         location: Option<SettingsLocation<'a>>,
         language_name: Option<&LanguageName>,
-        cx: &'a AppContext,
+        cx: &'a App,
     ) -> Cow<'a, LanguageSettings> {
         let settings = language_name
             .and_then(|name| self.languages.get(name))
@@ -890,7 +890,7 @@ impl AllLanguageSettings {
         &self,
         language: Option<&Arc<Language>>,
         path: Option<&Path>,
-        cx: &AppContext,
+        cx: &App,
     ) -> bool {
         if let Some(path) = path {
             if !self.inline_completions_enabled_for_path(path) {
@@ -979,7 +979,7 @@ impl settings::Settings for AllLanguageSettings {
 
     type FileContent = AllLanguageSettingsContent;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
         let default_value = sources.default;
 
         // A default is provided for all settings.
@@ -1095,7 +1095,7 @@ impl settings::Settings for AllLanguageSettings {
     fn json_schema(
         generator: &mut schemars::gen::SchemaGenerator,
         params: &settings::SettingsJsonSchemaParams,
-        _: &AppContext,
+        _: &App,
     ) -> schemars::schema::RootSchema {
         let mut root_schema = generator.root_schema_for::<Self::FileContent>();
 

crates/language/src/syntax_map/syntax_map_tests.rs 🔗

@@ -3,7 +3,7 @@ use crate::{
     buffer_tests::{markdown_inline_lang, markdown_lang},
     LanguageConfig, LanguageMatcher,
 };
-use gpui::AppContext;
+use gpui::App;
 use rand::rngs::StdRng;
 use std::{env, ops::Range, sync::Arc};
 use text::{Buffer, BufferId};
@@ -83,7 +83,7 @@ fn test_splice_included_ranges() {
 }
 
 #[gpui::test]
-fn test_syntax_map_layers_for_range(cx: &mut AppContext) {
+fn test_syntax_map_layers_for_range(cx: &mut App) {
     let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
     let language = Arc::new(rust_lang());
     registry.add(language.clone());
@@ -180,7 +180,7 @@ fn test_syntax_map_layers_for_range(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_dynamic_language_injection(cx: &mut AppContext) {
+fn test_dynamic_language_injection(cx: &mut App) {
     let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
     let markdown = Arc::new(markdown_lang());
     let markdown_inline = Arc::new(markdown_inline_lang());
@@ -268,7 +268,7 @@ fn test_dynamic_language_injection(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_typing_multiple_new_injections(cx: &mut AppContext) {
+fn test_typing_multiple_new_injections(cx: &mut App) {
     let (buffer, syntax_map) = test_edit_sequence(
         "Rust",
         &[
@@ -298,7 +298,7 @@ fn test_typing_multiple_new_injections(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_pasting_new_injection_line_between_others(cx: &mut AppContext) {
+fn test_pasting_new_injection_line_between_others(cx: &mut App) {
     let (buffer, syntax_map) = test_edit_sequence(
         "Rust",
         &[
@@ -346,7 +346,7 @@ fn test_pasting_new_injection_line_between_others(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_joining_injections_with_child_injections(cx: &mut AppContext) {
+fn test_joining_injections_with_child_injections(cx: &mut App) {
     let (buffer, syntax_map) = test_edit_sequence(
         "Rust",
         &[
@@ -391,7 +391,7 @@ fn test_joining_injections_with_child_injections(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_editing_edges_of_injection(cx: &mut AppContext) {
+fn test_editing_edges_of_injection(cx: &mut App) {
     test_edit_sequence(
         "Rust",
         &[
@@ -421,7 +421,7 @@ fn test_editing_edges_of_injection(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_edits_preceding_and_intersecting_injection(cx: &mut AppContext) {
+fn test_edits_preceding_and_intersecting_injection(cx: &mut App) {
     test_edit_sequence(
         "Rust",
         &[
@@ -434,7 +434,7 @@ fn test_edits_preceding_and_intersecting_injection(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_non_local_changes_create_injections(cx: &mut AppContext) {
+fn test_non_local_changes_create_injections(cx: &mut App) {
     test_edit_sequence(
         "Rust",
         &[
@@ -454,7 +454,7 @@ fn test_non_local_changes_create_injections(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_creating_many_injections_in_one_edit(cx: &mut AppContext) {
+fn test_creating_many_injections_in_one_edit(cx: &mut App) {
     test_edit_sequence(
         "Rust",
         &[
@@ -485,7 +485,7 @@ fn test_creating_many_injections_in_one_edit(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_editing_across_injection_boundary(cx: &mut AppContext) {
+fn test_editing_across_injection_boundary(cx: &mut App) {
     test_edit_sequence(
         "Rust",
         &[
@@ -514,7 +514,7 @@ fn test_editing_across_injection_boundary(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_removing_injection_by_replacing_across_boundary(cx: &mut AppContext) {
+fn test_removing_injection_by_replacing_across_boundary(cx: &mut App) {
     test_edit_sequence(
         "Rust",
         &[
@@ -541,7 +541,7 @@ fn test_removing_injection_by_replacing_across_boundary(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_combined_injections_simple(cx: &mut AppContext) {
+fn test_combined_injections_simple(cx: &mut App) {
     let (buffer, syntax_map) = test_edit_sequence(
         "ERB",
         &[
@@ -589,7 +589,7 @@ fn test_combined_injections_simple(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_combined_injections_empty_ranges(cx: &mut AppContext) {
+fn test_combined_injections_empty_ranges(cx: &mut App) {
     test_edit_sequence(
         "ERB",
         &[
@@ -608,7 +608,7 @@ fn test_combined_injections_empty_ranges(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_combined_injections_edit_edges_of_ranges(cx: &mut AppContext) {
+fn test_combined_injections_edit_edges_of_ranges(cx: &mut App) {
     let (buffer, syntax_map) = test_edit_sequence(
         "ERB",
         &[
@@ -640,7 +640,7 @@ fn test_combined_injections_edit_edges_of_ranges(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_combined_injections_splitting_some_injections(cx: &mut AppContext) {
+fn test_combined_injections_splitting_some_injections(cx: &mut App) {
     let (_buffer, _syntax_map) = test_edit_sequence(
         "ERB",
         &[
@@ -666,7 +666,7 @@ fn test_combined_injections_splitting_some_injections(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_combined_injections_editing_after_last_injection(cx: &mut AppContext) {
+fn test_combined_injections_editing_after_last_injection(cx: &mut App) {
     test_edit_sequence(
         "ERB",
         &[
@@ -687,7 +687,7 @@ fn test_combined_injections_editing_after_last_injection(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_combined_injections_inside_injections(cx: &mut AppContext) {
+fn test_combined_injections_inside_injections(cx: &mut App) {
     let (buffer, syntax_map) = test_edit_sequence(
         "Markdown",
         &[
@@ -764,7 +764,7 @@ fn test_combined_injections_inside_injections(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_empty_combined_injections_inside_injections(cx: &mut AppContext) {
+fn test_empty_combined_injections_inside_injections(cx: &mut App) {
     let (buffer, syntax_map) = test_edit_sequence(
         "Markdown",
         &[r#"
@@ -798,7 +798,7 @@ fn test_empty_combined_injections_inside_injections(cx: &mut AppContext) {
 }
 
 #[gpui::test(iterations = 50)]
-fn test_random_syntax_map_edits_rust_macros(rng: StdRng, cx: &mut AppContext) {
+fn test_random_syntax_map_edits_rust_macros(rng: StdRng, cx: &mut App) {
     let text = r#"
         fn test_something() {
             let vec = vec![5, 1, 3, 8];
@@ -824,7 +824,7 @@ fn test_random_syntax_map_edits_rust_macros(rng: StdRng, cx: &mut AppContext) {
 }
 
 #[gpui::test(iterations = 50)]
-fn test_random_syntax_map_edits_with_erb(rng: StdRng, cx: &mut AppContext) {
+fn test_random_syntax_map_edits_with_erb(rng: StdRng, cx: &mut App) {
     let text = r#"
         <div id="main">
         <% if one?(:two) %>
@@ -853,7 +853,7 @@ fn test_random_syntax_map_edits_with_erb(rng: StdRng, cx: &mut AppContext) {
 }
 
 #[gpui::test(iterations = 50)]
-fn test_random_syntax_map_edits_with_heex(rng: StdRng, cx: &mut AppContext) {
+fn test_random_syntax_map_edits_with_heex(rng: StdRng, cx: &mut App) {
     let text = r#"
         defmodule TheModule do
             def the_method(assigns) do
@@ -1060,11 +1060,7 @@ fn check_interpolation(
     }
 }
 
-fn test_edit_sequence(
-    language_name: &str,
-    steps: &[&str],
-    cx: &mut AppContext,
-) -> (Buffer, SyntaxMap) {
+fn test_edit_sequence(language_name: &str, steps: &[&str], cx: &mut App) -> (Buffer, SyntaxMap) {
     let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
     registry.add(Arc::new(elixir_lang()));
     registry.add(Arc::new(heex_lang()));

crates/language/src/task_context.rs 🔗

@@ -4,7 +4,7 @@ use crate::{LanguageToolchainStore, Location, Runnable};
 
 use anyhow::Result;
 use collections::HashMap;
-use gpui::{AppContext, Task};
+use gpui::{App, Task};
 use task::{TaskTemplates, TaskVariables};
 use text::BufferId;
 
@@ -27,7 +27,7 @@ pub trait ContextProvider: Send + Sync {
         _location: &Location,
         _project_env: Option<HashMap<String, String>>,
         _toolchains: Arc<dyn LanguageToolchainStore>,
-        _cx: &mut AppContext,
+        _cx: &mut App,
     ) -> Task<Result<TaskVariables>> {
         Task::ready(Ok(TaskVariables::default()))
     }
@@ -36,7 +36,7 @@ pub trait ContextProvider: Send + Sync {
     fn associated_tasks(
         &self,
         _: Option<Arc<dyn crate::File>>,
-        _cx: &AppContext,
+        _cx: &App,
     ) -> Option<TaskTemplates> {
         None
     }

crates/language_extension/src/extension_lsp_adapter.rs 🔗

@@ -4,7 +4,7 @@ use std::path::PathBuf;
 use std::pin::Pin;
 use std::sync::Arc;
 
-use anyhow::{Context, Result};
+use anyhow::{Context as _, Result};
 use async_trait::async_trait;
 use collections::HashMap;
 use extension::{Extension, ExtensionLanguageServerProxy, WorktreeDelegate};

crates/language_model/src/fake_provider.rs 🔗

@@ -4,7 +4,7 @@ use crate::{
     LanguageModelProviderState, LanguageModelRequest,
 };
 use futures::{channel::mpsc, future::BoxFuture, stream::BoxStream, FutureExt, StreamExt};
-use gpui::{AnyView, AppContext, AsyncAppContext, Model, Task, WindowContext};
+use gpui::{AnyView, App, AsyncAppContext, Entity, Task, Window};
 use http_client::Result;
 use parking_lot::Mutex;
 use serde::Serialize;
@@ -32,7 +32,7 @@ pub struct FakeLanguageModelProvider;
 impl LanguageModelProviderState for FakeLanguageModelProvider {
     type ObservableEntity = ();
 
-    fn observable_entity(&self) -> Option<Model<Self::ObservableEntity>> {
+    fn observable_entity(&self) -> Option<Entity<Self::ObservableEntity>> {
         None
     }
 }
@@ -46,23 +46,23 @@ impl LanguageModelProvider for FakeLanguageModelProvider {
         provider_name()
     }
 
-    fn provided_models(&self, _: &AppContext) -> Vec<Arc<dyn LanguageModel>> {
+    fn provided_models(&self, _: &App) -> Vec<Arc<dyn LanguageModel>> {
         vec![Arc::new(FakeLanguageModel::default())]
     }
 
-    fn is_authenticated(&self, _: &AppContext) -> bool {
+    fn is_authenticated(&self, _: &App) -> bool {
         true
     }
 
-    fn authenticate(&self, _: &mut AppContext) -> Task<Result<()>> {
+    fn authenticate(&self, _: &mut App) -> Task<Result<()>> {
         Task::ready(Ok(()))
     }
 
-    fn configuration_view(&self, _: &mut WindowContext) -> AnyView {
+    fn configuration_view(&self, _window: &mut Window, _: &mut App) -> AnyView {
         unimplemented!()
     }
 
-    fn reset_credentials(&self, _: &mut AppContext) -> Task<Result<()>> {
+    fn reset_credentials(&self, _: &mut App) -> Task<Result<()>> {
         Task::ready(Ok(()))
     }
 }
@@ -157,11 +157,7 @@ impl LanguageModel for FakeLanguageModel {
         1000000
     }
 
-    fn count_tokens(
-        &self,
-        _: LanguageModelRequest,
-        _: &AppContext,
-    ) -> BoxFuture<'static, Result<usize>> {
+    fn count_tokens(&self, _: LanguageModelRequest, _: &App) -> BoxFuture<'static, Result<usize>> {
         futures::future::ready(Ok(0)).boxed()
     }
 

crates/language_model/src/language_model.rs 🔗

@@ -10,7 +10,7 @@ pub mod fake_provider;
 use anyhow::Result;
 use futures::FutureExt;
 use futures::{future::BoxFuture, stream::BoxStream, StreamExt, TryStreamExt as _};
-use gpui::{AnyElement, AnyView, AppContext, AsyncAppContext, SharedString, Task, WindowContext};
+use gpui::{AnyElement, AnyView, App, AsyncAppContext, SharedString, Task, Window};
 pub use model::*;
 use proto::Plan;
 pub use rate_limiter::*;
@@ -25,7 +25,7 @@ use ui::IconName;
 
 pub const ZED_CLOUD_PROVIDER_ID: &str = "zed.dev";
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     registry::init(cx);
 }
 
@@ -113,7 +113,7 @@ pub trait LanguageModel: Send + Sync {
     fn provider_name(&self) -> LanguageModelProviderName;
     fn telemetry_id(&self) -> String;
 
-    fn api_key(&self, _cx: &AppContext) -> Option<String> {
+    fn api_key(&self, _cx: &App) -> Option<String> {
         None
     }
 
@@ -130,7 +130,7 @@ pub trait LanguageModel: Send + Sync {
     fn count_tokens(
         &self,
         request: LanguageModelRequest,
-        cx: &AppContext,
+        cx: &App,
     ) -> BoxFuture<'static, Result<usize>>;
 
     fn stream_completion(
@@ -237,22 +237,22 @@ pub trait LanguageModelProvider: 'static {
     fn icon(&self) -> IconName {
         IconName::ZedAssistant
     }
-    fn provided_models(&self, cx: &AppContext) -> Vec<Arc<dyn LanguageModel>>;
-    fn load_model(&self, _model: Arc<dyn LanguageModel>, _cx: &AppContext) {}
-    fn is_authenticated(&self, cx: &AppContext) -> bool;
-    fn authenticate(&self, cx: &mut AppContext) -> Task<Result<()>>;
-    fn configuration_view(&self, cx: &mut WindowContext) -> AnyView;
-    fn must_accept_terms(&self, _cx: &AppContext) -> bool {
+    fn provided_models(&self, cx: &App) -> Vec<Arc<dyn LanguageModel>>;
+    fn load_model(&self, _model: Arc<dyn LanguageModel>, _cx: &App) {}
+    fn is_authenticated(&self, cx: &App) -> bool;
+    fn authenticate(&self, cx: &mut App) -> Task<Result<()>>;
+    fn configuration_view(&self, window: &mut Window, cx: &mut App) -> AnyView;
+    fn must_accept_terms(&self, _cx: &App) -> bool {
         false
     }
     fn render_accept_terms(
         &self,
         _view: LanguageModelProviderTosView,
-        _cx: &mut WindowContext,
+        _cx: &mut App,
     ) -> Option<AnyElement> {
         None
     }
-    fn reset_credentials(&self, cx: &mut AppContext) -> Task<Result<()>>;
+    fn reset_credentials(&self, cx: &mut App) -> Task<Result<()>>;
 }
 
 #[derive(PartialEq, Eq)]
@@ -265,12 +265,12 @@ pub enum LanguageModelProviderTosView {
 pub trait LanguageModelProviderState: 'static {
     type ObservableEntity;
 
-    fn observable_entity(&self) -> Option<gpui::Model<Self::ObservableEntity>>;
+    fn observable_entity(&self) -> Option<gpui::Entity<Self::ObservableEntity>>;
 
     fn subscribe<T: 'static>(
         &self,
-        cx: &mut gpui::ModelContext<T>,
-        callback: impl Fn(&mut T, &mut gpui::ModelContext<T>) + 'static,
+        cx: &mut gpui::Context<T>,
+        callback: impl Fn(&mut T, &mut gpui::Context<T>) + 'static,
     ) -> Option<gpui::Subscription> {
         let entity = self.observable_entity()?;
         Some(cx.observe(&entity, move |this, _, cx| {

crates/language_model/src/registry.rs 🔗

@@ -3,15 +3,15 @@ use crate::{
     LanguageModelProviderState,
 };
 use collections::BTreeMap;
-use gpui::{prelude::*, AppContext, EventEmitter, Global, Model, ModelContext};
+use gpui::{prelude::*, App, Context, Entity, EventEmitter, Global};
 use std::sync::Arc;
 
-pub fn init(cx: &mut AppContext) {
-    let registry = cx.new_model(|_cx| LanguageModelRegistry::default());
+pub fn init(cx: &mut App) {
+    let registry = cx.new(|_cx| LanguageModelRegistry::default());
     cx.set_global(GlobalLanguageModelRegistry(registry));
 }
 
-struct GlobalLanguageModelRegistry(Model<LanguageModelRegistry>);
+struct GlobalLanguageModelRegistry(Entity<LanguageModelRegistry>);
 
 impl Global for GlobalLanguageModelRegistry {}
 
@@ -37,18 +37,18 @@ pub enum Event {
 impl EventEmitter<Event> for LanguageModelRegistry {}
 
 impl LanguageModelRegistry {
-    pub fn global(cx: &AppContext) -> Model<Self> {
+    pub fn global(cx: &App) -> Entity<Self> {
         cx.global::<GlobalLanguageModelRegistry>().0.clone()
     }
 
-    pub fn read_global(cx: &AppContext) -> &Self {
+    pub fn read_global(cx: &App) -> &Self {
         cx.global::<GlobalLanguageModelRegistry>().0.read(cx)
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn test(cx: &mut AppContext) -> crate::fake_provider::FakeLanguageModelProvider {
+    pub fn test(cx: &mut App) -> crate::fake_provider::FakeLanguageModelProvider {
         let fake_provider = crate::fake_provider::FakeLanguageModelProvider;
-        let registry = cx.new_model(|cx| {
+        let registry = cx.new(|cx| {
             let mut registry = Self::default();
             registry.register_provider(fake_provider.clone(), cx);
             let model = fake_provider.provided_models(cx)[0].clone();
@@ -62,7 +62,7 @@ impl LanguageModelRegistry {
     pub fn register_provider<T: LanguageModelProvider + LanguageModelProviderState>(
         &mut self,
         provider: T,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let id = provider.id();
 
@@ -77,11 +77,7 @@ impl LanguageModelRegistry {
         cx.emit(Event::AddedProvider(id));
     }
 
-    pub fn unregister_provider(
-        &mut self,
-        id: LanguageModelProviderId,
-        cx: &mut ModelContext<Self>,
-    ) {
+    pub fn unregister_provider(&mut self, id: LanguageModelProviderId, cx: &mut Context<Self>) {
         if self.providers.remove(&id).is_some() {
             cx.emit(Event::RemovedProvider(id));
         }
@@ -105,7 +101,7 @@ impl LanguageModelRegistry {
 
     pub fn available_models<'a>(
         &'a self,
-        cx: &'a AppContext,
+        cx: &'a App,
     ) -> impl Iterator<Item = Arc<dyn LanguageModel>> + 'a {
         self.providers
             .values()
@@ -120,7 +116,7 @@ impl LanguageModelRegistry {
         &mut self,
         provider: &LanguageModelProviderId,
         model_id: &LanguageModelId,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let Some(provider) = self.provider(provider) else {
             return;
@@ -135,7 +131,7 @@ impl LanguageModelRegistry {
     pub fn set_active_provider(
         &mut self,
         provider: Option<Arc<dyn LanguageModelProvider>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.active_model = provider.map(|provider| ActiveModel {
             provider,
@@ -147,7 +143,7 @@ impl LanguageModelRegistry {
     pub fn set_active_model(
         &mut self,
         model: Option<Arc<dyn LanguageModel>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if let Some(model) = model {
             let provider_id = model.provider_id();
@@ -179,7 +175,7 @@ impl LanguageModelRegistry {
     pub fn select_inline_alternative_models(
         &mut self,
         alternatives: impl IntoIterator<Item = (LanguageModelProviderId, LanguageModelId)>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let mut selected_alternatives = Vec::new();
 
@@ -212,8 +208,8 @@ mod tests {
     use crate::fake_provider::FakeLanguageModelProvider;
 
     #[gpui::test]
-    fn test_register_providers(cx: &mut AppContext) {
-        let registry = cx.new_model(|_| LanguageModelRegistry::default());
+    fn test_register_providers(cx: &mut App) {
+        let registry = cx.new(|_| LanguageModelRegistry::default());
 
         registry.update(cx, |registry, cx| {
             registry.register_provider(FakeLanguageModelProvider, cx);

crates/language_model/src/request.rs 🔗

@@ -3,7 +3,7 @@ use std::io::{Cursor, Write};
 use crate::role::Role;
 use crate::LanguageModelToolUse;
 use base64::write::EncoderWriter;
-use gpui::{point, size, AppContext, DevicePixels, Image, ObjectFit, RenderImage, Size, Task};
+use gpui::{point, size, App, DevicePixels, Image, ObjectFit, RenderImage, Size, Task};
 use image::{codecs::png::PngEncoder, imageops::resize, DynamicImage, ImageDecoder};
 use serde::{Deserialize, Serialize};
 use ui::{px, SharedString};
@@ -29,7 +29,7 @@ impl std::fmt::Debug for LanguageModelImage {
 const ANTHROPIC_SIZE_LIMT: f32 = 1568.;
 
 impl LanguageModelImage {
-    pub fn from_image(data: Image, cx: &mut AppContext) -> Task<Option<Self>> {
+    pub fn from_image(data: Image, cx: &mut App) -> Task<Option<Self>> {
         cx.background_executor().spawn(async move {
             match data.format() {
                 gpui::ImageFormat::Png

crates/language_model_selector/src/language_model_selector.rs 🔗

@@ -2,8 +2,8 @@ use std::sync::Arc;
 
 use feature_flags::ZedPro;
 use gpui::{
-    Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Model,
-    Subscription, Task, View, WeakView,
+    Action, AnyElement, App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable,
+    Subscription, Task, WeakEntity,
 };
 use language_model::{LanguageModel, LanguageModelAvailability, LanguageModelRegistry};
 use picker::{Picker, PickerDelegate};
@@ -13,10 +13,10 @@ use workspace::ShowConfiguration;
 
 const TRY_ZED_PRO_URL: &str = "https://zed.dev/pro";
 
-type OnModelChanged = Arc<dyn Fn(Arc<dyn LanguageModel>, &AppContext) + 'static>;
+type OnModelChanged = Arc<dyn Fn(Arc<dyn LanguageModel>, &App) + 'static>;
 
 pub struct LanguageModelSelector {
-    picker: View<Picker<LanguageModelPickerDelegate>>,
+    picker: Entity<Picker<LanguageModelPickerDelegate>>,
     /// The task used to update the picker's matches when there is a change to
     /// the language model registry.
     update_matches_task: Option<Task<()>>,
@@ -25,28 +25,31 @@ pub struct LanguageModelSelector {
 
 impl LanguageModelSelector {
     pub fn new(
-        on_model_changed: impl Fn(Arc<dyn LanguageModel>, &AppContext) + 'static,
-        cx: &mut ViewContext<Self>,
+        on_model_changed: impl Fn(Arc<dyn LanguageModel>, &App) + 'static,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let on_model_changed = Arc::new(on_model_changed);
 
         let all_models = Self::all_models(cx);
         let delegate = LanguageModelPickerDelegate {
-            language_model_selector: cx.view().downgrade(),
+            language_model_selector: cx.model().downgrade(),
             on_model_changed: on_model_changed.clone(),
             all_models: all_models.clone(),
             filtered_models: all_models,
             selected_index: 0,
         };
 
-        let picker =
-            cx.new_view(|cx| Picker::uniform_list(delegate, cx).max_height(Some(rems(20.).into())));
+        let picker = cx.new(|cx| {
+            Picker::uniform_list(delegate, window, cx).max_height(Some(rems(20.).into()))
+        });
 
         LanguageModelSelector {
             picker,
             update_matches_task: None,
-            _subscriptions: vec![cx.subscribe(
+            _subscriptions: vec![cx.subscribe_in(
                 &LanguageModelRegistry::global(cx),
+                window,
                 Self::handle_language_model_registry_event,
             )],
         }
@@ -54,9 +57,10 @@ impl LanguageModelSelector {
 
     fn handle_language_model_registry_event(
         &mut self,
-        _registry: Model<LanguageModelRegistry>,
+        _registry: &Entity<LanguageModelRegistry>,
         event: &language_model::Event,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match event {
             language_model::Event::ProviderStateChanged
@@ -65,7 +69,7 @@ impl LanguageModelSelector {
                 let task = self.picker.update(cx, |this, cx| {
                     let query = this.query(cx);
                     this.delegate.all_models = Self::all_models(cx);
-                    this.delegate.update_matches(query, cx)
+                    this.delegate.update_matches(query, window, cx)
                 });
                 self.update_matches_task = Some(task);
             }
@@ -73,7 +77,7 @@ impl LanguageModelSelector {
         }
     }
 
-    fn all_models(cx: &AppContext) -> Vec<ModelInfo> {
+    fn all_models(cx: &App) -> Vec<ModelInfo> {
         LanguageModelRegistry::global(cx)
             .read(cx)
             .providers()
@@ -98,14 +102,14 @@ impl LanguageModelSelector {
 
 impl EventEmitter<DismissEvent> for LanguageModelSelector {}
 
-impl FocusableView for LanguageModelSelector {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for LanguageModelSelector {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
 
 impl Render for LanguageModelSelector {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         self.picker.clone()
     }
 }
@@ -115,13 +119,13 @@ pub struct LanguageModelSelectorPopoverMenu<T>
 where
     T: PopoverTrigger,
 {
-    language_model_selector: View<LanguageModelSelector>,
+    language_model_selector: Entity<LanguageModelSelector>,
     trigger: T,
     handle: Option<PopoverMenuHandle<LanguageModelSelector>>,
 }
 
 impl<T: PopoverTrigger> LanguageModelSelectorPopoverMenu<T> {
-    pub fn new(language_model_selector: View<LanguageModelSelector>, trigger: T) -> Self {
+    pub fn new(language_model_selector: Entity<LanguageModelSelector>, trigger: T) -> Self {
         Self {
             language_model_selector,
             trigger,
@@ -136,11 +140,11 @@ impl<T: PopoverTrigger> LanguageModelSelectorPopoverMenu<T> {
 }
 
 impl<T: PopoverTrigger> RenderOnce for LanguageModelSelectorPopoverMenu<T> {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         let language_model_selector = self.language_model_selector.clone();
 
         PopoverMenu::new("model-switcher")
-            .menu(move |_cx| Some(language_model_selector.clone()))
+            .menu(move |_window, _cx| Some(language_model_selector.clone()))
             .trigger(self.trigger)
             .attach(gpui::Corner::BottomLeft)
             .when_some(self.handle.clone(), |menu, handle| menu.with_handle(handle))
@@ -155,7 +159,7 @@ struct ModelInfo {
 }
 
 pub struct LanguageModelPickerDelegate {
-    language_model_selector: WeakView<LanguageModelSelector>,
+    language_model_selector: WeakEntity<LanguageModelSelector>,
     on_model_changed: OnModelChanged,
     all_models: Vec<ModelInfo>,
     filtered_models: Vec<ModelInfo>,
@@ -173,16 +177,21 @@ impl PickerDelegate for LanguageModelPickerDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(&mut self, ix: usize, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.selected_index = ix.min(self.filtered_models.len().saturating_sub(1));
         cx.notify();
     }
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Select a model...".into()
     }
 
-    fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
+    fn update_matches(
+        &mut self,
+        query: String,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Task<()> {
         let all_models = self.all_models.clone();
 
         let llm_registry = LanguageModelRegistry::global(cx);
@@ -195,7 +204,7 @@ impl PickerDelegate for LanguageModelPickerDelegate {
             .map(|provider| provider.id())
             .collect::<Vec<_>>();
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let filtered_models = cx
                 .background_executor()
                 .spawn(async move {
@@ -228,16 +237,16 @@ impl PickerDelegate for LanguageModelPickerDelegate {
                 })
                 .await;
 
-            this.update(&mut cx, |this, cx| {
+            this.update_in(&mut cx, |this, window, cx| {
                 this.delegate.filtered_models = filtered_models;
-                this.delegate.set_selected_index(0, cx);
+                this.delegate.set_selected_index(0, window, cx);
                 cx.notify();
             })
             .ok();
         })
     }
 
-    fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _secondary: bool, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         if let Some(model_info) = self.filtered_models.get(self.selected_index) {
             let model = model_info.model.clone();
             (self.on_model_changed)(model.clone(), cx);
@@ -246,13 +255,13 @@ impl PickerDelegate for LanguageModelPickerDelegate {
         }
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+    fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.language_model_selector
             .update(cx, |_this, cx| cx.emit(DismissEvent))
             .ok();
     }
 
-    fn render_header(&self, cx: &mut ViewContext<Picker<Self>>) -> Option<AnyElement> {
+    fn render_header(&self, _: &mut Window, cx: &mut Context<Picker<Self>>) -> Option<AnyElement> {
         let configured_models_count = LanguageModelRegistry::global(cx)
             .read(cx)
             .providers()
@@ -279,7 +288,8 @@ impl PickerDelegate for LanguageModelPickerDelegate {
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         use feature_flags::FeatureFlagAppExt;
         let show_badges = cx.has_flag::<ZedPro>();
@@ -348,7 +358,11 @@ impl PickerDelegate for LanguageModelPickerDelegate {
         )
     }
 
-    fn render_footer(&self, cx: &mut ViewContext<Picker<Self>>) -> Option<gpui::AnyElement> {
+    fn render_footer(
+        &self,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Option<gpui::AnyElement> {
         use feature_flags::FeatureFlagAppExt;
 
         let plan = proto::Plan::ZedPro;
@@ -370,8 +384,9 @@ impl PickerDelegate for LanguageModelPickerDelegate {
                             .icon_size(IconSize::Small)
                             .icon_color(Color::Muted)
                             .icon_position(IconPosition::Start)
-                            .on_click(|_, cx| {
-                                cx.dispatch_action(Box::new(zed_actions::OpenAccountSettings))
+                            .on_click(|_, window, cx| {
+                                window
+                                    .dispatch_action(Box::new(zed_actions::OpenAccountSettings), cx)
                             }),
                         // Free user
                         Plan::Free => Button::new(
@@ -382,7 +397,7 @@ impl PickerDelegate for LanguageModelPickerDelegate {
                                 "Try Pro"
                             },
                         )
-                        .on_click(|_, cx| cx.open_url(TRY_ZED_PRO_URL)),
+                        .on_click(|_, _, cx| cx.open_url(TRY_ZED_PRO_URL)),
                     })
                 })
                 .child(
@@ -391,8 +406,8 @@ impl PickerDelegate for LanguageModelPickerDelegate {
                         .icon_size(IconSize::Small)
                         .icon_color(Color::Muted)
                         .icon_position(IconPosition::Start)
-                        .on_click(|_, cx| {
-                            cx.dispatch_action(ShowConfiguration.boxed_clone());
+                        .on_click(|_, window, cx| {
+                            window.dispatch_action(ShowConfiguration.boxed_clone(), cx);
                         }),
                 )
                 .into_any(),

crates/language_models/src/language_models.rs 🔗

@@ -2,7 +2,7 @@ use std::sync::Arc;
 
 use client::{Client, UserStore};
 use fs::Fs;
-use gpui::{AppContext, Model, ModelContext};
+use gpui::{App, Context, Entity};
 use language_model::{LanguageModelProviderId, LanguageModelRegistry, ZED_CLOUD_PROVIDER_ID};
 
 mod logging;
@@ -21,12 +21,7 @@ use crate::provider::open_ai::OpenAiLanguageModelProvider;
 pub use crate::settings::*;
 pub use logging::report_assistant_event;
 
-pub fn init(
-    user_store: Model<UserStore>,
-    client: Arc<Client>,
-    fs: Arc<dyn Fs>,
-    cx: &mut AppContext,
-) {
+pub fn init(user_store: Entity<UserStore>, client: Arc<Client>, fs: Arc<dyn Fs>, cx: &mut App) {
     crate::settings::init(fs, cx);
     let registry = LanguageModelRegistry::global(cx);
     registry.update(cx, |registry, cx| {
@@ -36,9 +31,9 @@ pub fn init(
 
 fn register_language_model_providers(
     registry: &mut LanguageModelRegistry,
-    user_store: Model<UserStore>,
+    user_store: Entity<UserStore>,
     client: Arc<Client>,
-    cx: &mut ModelContext<LanguageModelRegistry>,
+    cx: &mut Context<LanguageModelRegistry>,
 ) {
     use feature_flags::FeatureFlagAppExt;
 

crates/language_models/src/logging.rs 🔗

@@ -1,5 +1,5 @@
 use anthropic::{AnthropicError, ANTHROPIC_API_URL};
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use client::telemetry::Telemetry;
 use gpui::BackgroundExecutor;
 use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest};

crates/language_models/src/provider/anthropic.rs 🔗

@@ -6,8 +6,8 @@ use editor::{Editor, EditorElement, EditorStyle};
 use futures::Stream;
 use futures::{future::BoxFuture, stream::BoxStream, FutureExt, StreamExt, TryStreamExt as _};
 use gpui::{
-    AnyView, AppContext, AsyncAppContext, FontStyle, ModelContext, Subscription, Task, TextStyle,
-    View, WhiteSpace,
+    AnyView, App, AsyncAppContext, Context, Entity, FontStyle, Subscription, Task, TextStyle,
+    WhiteSpace,
 };
 use http_client::HttpClient;
 use language_model::{
@@ -58,7 +58,7 @@ pub struct AvailableModel {
 
 pub struct AnthropicLanguageModelProvider {
     http_client: Arc<dyn HttpClient>,
-    state: gpui::Model<State>,
+    state: gpui::Entity<State>,
 }
 
 const ANTHROPIC_API_KEY_VAR: &str = "ANTHROPIC_API_KEY";
@@ -70,7 +70,7 @@ pub struct State {
 }
 
 impl State {
-    fn reset_api_key(&self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn reset_api_key(&self, cx: &mut Context<Self>) -> Task<Result<()>> {
         let delete_credentials =
             cx.delete_credentials(&AllLanguageModelSettings::get_global(cx).anthropic.api_url);
         cx.spawn(|this, mut cx| async move {
@@ -83,7 +83,7 @@ impl State {
         })
     }
 
-    fn set_api_key(&mut self, api_key: String, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn set_api_key(&mut self, api_key: String, cx: &mut Context<Self>) -> Task<Result<()>> {
         let write_credentials = cx.write_credentials(
             AllLanguageModelSettings::get_global(cx)
                 .anthropic
@@ -106,7 +106,7 @@ impl State {
         self.api_key.is_some()
     }
 
-    fn authenticate(&self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn authenticate(&self, cx: &mut Context<Self>) -> Task<Result<()>> {
         if self.is_authenticated() {
             Task::ready(Ok(()))
         } else {
@@ -138,8 +138,8 @@ impl State {
 }
 
 impl AnthropicLanguageModelProvider {
-    pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut AppContext) -> Self {
-        let state = cx.new_model(|cx| State {
+    pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut App) -> Self {
+        let state = cx.new(|cx| State {
             api_key: None,
             api_key_from_env: false,
             _subscription: cx.observe_global::<SettingsStore>(|_, cx| {
@@ -154,7 +154,7 @@ impl AnthropicLanguageModelProvider {
 impl LanguageModelProviderState for AnthropicLanguageModelProvider {
     type ObservableEntity = State;
 
-    fn observable_entity(&self) -> Option<gpui::Model<Self::ObservableEntity>> {
+    fn observable_entity(&self) -> Option<gpui::Entity<Self::ObservableEntity>> {
         Some(self.state.clone())
     }
 }
@@ -172,7 +172,7 @@ impl LanguageModelProvider for AnthropicLanguageModelProvider {
         IconName::AiAnthropic
     }
 
-    fn provided_models(&self, cx: &AppContext) -> Vec<Arc<dyn LanguageModel>> {
+    fn provided_models(&self, cx: &App) -> Vec<Arc<dyn LanguageModel>> {
         let mut models = BTreeMap::default();
 
         // Add base models from anthropic::Model::iter()
@@ -223,20 +223,20 @@ impl LanguageModelProvider for AnthropicLanguageModelProvider {
             .collect()
     }
 
-    fn is_authenticated(&self, cx: &AppContext) -> bool {
+    fn is_authenticated(&self, cx: &App) -> bool {
         self.state.read(cx).is_authenticated()
     }
 
-    fn authenticate(&self, cx: &mut AppContext) -> Task<Result<()>> {
+    fn authenticate(&self, cx: &mut App) -> Task<Result<()>> {
         self.state.update(cx, |state, cx| state.authenticate(cx))
     }
 
-    fn configuration_view(&self, cx: &mut WindowContext) -> AnyView {
-        cx.new_view(|cx| ConfigurationView::new(self.state.clone(), cx))
+    fn configuration_view(&self, window: &mut Window, cx: &mut App) -> AnyView {
+        cx.new(|cx| ConfigurationView::new(self.state.clone(), window, cx))
             .into()
     }
 
-    fn reset_credentials(&self, cx: &mut AppContext) -> Task<Result<()>> {
+    fn reset_credentials(&self, cx: &mut App) -> Task<Result<()>> {
         self.state.update(cx, |state, cx| state.reset_api_key(cx))
     }
 }
@@ -244,14 +244,14 @@ impl LanguageModelProvider for AnthropicLanguageModelProvider {
 pub struct AnthropicModel {
     id: LanguageModelId,
     model: anthropic::Model,
-    state: gpui::Model<State>,
+    state: gpui::Entity<State>,
     http_client: Arc<dyn HttpClient>,
     request_limiter: RateLimiter,
 }
 
 pub fn count_anthropic_tokens(
     request: LanguageModelRequest,
-    cx: &AppContext,
+    cx: &App,
 ) -> BoxFuture<'static, Result<usize>> {
     cx.background_executor()
         .spawn(async move {
@@ -350,7 +350,7 @@ impl LanguageModel for AnthropicModel {
         format!("anthropic/{}", self.model.id())
     }
 
-    fn api_key(&self, cx: &AppContext) -> Option<String> {
+    fn api_key(&self, cx: &App) -> Option<String> {
         self.state.read(cx).api_key.clone()
     }
 
@@ -365,7 +365,7 @@ impl LanguageModel for AnthropicModel {
     fn count_tokens(
         &self,
         request: LanguageModelRequest,
-        cx: &AppContext,
+        cx: &App,
     ) -> BoxFuture<'static, Result<usize>> {
         count_anthropic_tokens(request, cx)
     }
@@ -562,15 +562,15 @@ pub fn map_to_language_model_completion_events(
 }
 
 struct ConfigurationView {
-    api_key_editor: View<Editor>,
-    state: gpui::Model<State>,
+    api_key_editor: Entity<Editor>,
+    state: gpui::Entity<State>,
     load_credentials_task: Option<Task<()>>,
 }
 
 impl ConfigurationView {
     const PLACEHOLDER_TEXT: &'static str = "sk-ant-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
 
-    fn new(state: gpui::Model<State>, cx: &mut ViewContext<Self>) -> Self {
+    fn new(state: gpui::Entity<State>, window: &mut Window, cx: &mut Context<Self>) -> Self {
         cx.observe(&state, |_, _, cx| {
             cx.notify();
         })
@@ -595,8 +595,8 @@ impl ConfigurationView {
         }));
 
         Self {
-            api_key_editor: cx.new_view(|cx| {
-                let mut editor = Editor::single_line(cx);
+            api_key_editor: cx.new(|cx| {
+                let mut editor = Editor::single_line(window, cx);
                 editor.set_placeholder_text(Self::PLACEHOLDER_TEXT, cx);
                 editor
             }),
@@ -605,14 +605,14 @@ impl ConfigurationView {
         }
     }
 
-    fn save_api_key(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
+    fn save_api_key(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
         let api_key = self.api_key_editor.read(cx).text(cx);
         if api_key.is_empty() {
             return;
         }
 
         let state = self.state.clone();
-        cx.spawn(|_, mut cx| async move {
+        cx.spawn_in(window, |_, mut cx| async move {
             state
                 .update(&mut cx, |state, cx| state.set_api_key(api_key, cx))?
                 .await
@@ -622,12 +622,12 @@ impl ConfigurationView {
         cx.notify();
     }
 
-    fn reset_api_key(&mut self, cx: &mut ViewContext<Self>) {
+    fn reset_api_key(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.api_key_editor
-            .update(cx, |editor, cx| editor.set_text("", cx));
+            .update(cx, |editor, cx| editor.set_text("", window, cx));
 
         let state = self.state.clone();
-        cx.spawn(|_, mut cx| async move {
+        cx.spawn_in(window, |_, mut cx| async move {
             state
                 .update(&mut cx, |state, cx| state.reset_api_key(cx))?
                 .await
@@ -637,7 +637,7 @@ impl ConfigurationView {
         cx.notify();
     }
 
-    fn render_api_key_editor(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render_api_key_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
         let settings = ThemeSettings::get_global(cx);
         let text_style = TextStyle {
             color: cx.theme().colors().text,
@@ -665,13 +665,13 @@ impl ConfigurationView {
         )
     }
 
-    fn should_render_editor(&self, cx: &mut ViewContext<Self>) -> bool {
+    fn should_render_editor(&self, cx: &mut Context<Self>) -> bool {
         !self.state.read(cx).is_authenticated()
     }
 }
 
 impl Render for ConfigurationView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         const ANTHROPIC_CONSOLE_URL: &str = "https://console.anthropic.com/settings/keys";
         const INSTRUCTIONS: [&str; 3] = [
             "To use Zed's assistant with Anthropic, you need to add an API key. Follow these steps:",
@@ -693,7 +693,7 @@ impl Render for ConfigurationView {
                         .icon(IconName::ExternalLink)
                         .icon_size(IconSize::XSmall)
                         .icon_color(Color::Muted)
-                        .on_click(move |_, cx| cx.open_url(ANTHROPIC_CONSOLE_URL))
+                        .on_click(move |_, _, cx| cx.open_url(ANTHROPIC_CONSOLE_URL))
                     )
                 )
                 .child(Label::new(INSTRUCTIONS[2]))
@@ -735,9 +735,9 @@ impl Render for ConfigurationView {
                         .icon_position(IconPosition::Start)
                         .disabled(env_var_set)
                         .when(env_var_set, |this| {
-                            this.tooltip(|cx| Tooltip::text(format!("To reset your API key, unset the {ANTHROPIC_API_KEY_VAR} environment variable."), cx))
+                            this.tooltip(Tooltip::text(format!("To reset your API key, unset the {ANTHROPIC_API_KEY_VAR} environment variable.")))
                         })
-                        .on_click(cx.listener(|this, _, cx| this.reset_api_key(cx))),
+                        .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx))),
                 )
                 .into_any()
         }

crates/language_models/src/provider/cloud.rs 🔗

@@ -12,8 +12,8 @@ use futures::{
     TryStreamExt as _,
 };
 use gpui::{
-    AnyElement, AnyView, AppContext, AsyncAppContext, EventEmitter, Global, Model, ModelContext,
-    ReadGlobal, Subscription, Task,
+    AnyElement, AnyView, App, AsyncAppContext, Context, Entity, EventEmitter, Global, ReadGlobal,
+    Subscription, Task,
 };
 use http_client::{AsyncBody, HttpClient, Method, Response, StatusCode};
 use language_model::{
@@ -99,7 +99,7 @@ pub struct AvailableModel {
     pub extra_beta_headers: Vec<String>,
 }
 
-struct GlobalRefreshLlmTokenListener(Model<RefreshLlmTokenListener>);
+struct GlobalRefreshLlmTokenListener(Entity<RefreshLlmTokenListener>);
 
 impl Global for GlobalRefreshLlmTokenListener {}
 
@@ -112,16 +112,16 @@ pub struct RefreshLlmTokenListener {
 impl EventEmitter<RefreshLlmTokenEvent> for RefreshLlmTokenListener {}
 
 impl RefreshLlmTokenListener {
-    pub fn register(client: Arc<Client>, cx: &mut AppContext) {
-        let listener = cx.new_model(|cx| RefreshLlmTokenListener::new(client, cx));
+    pub fn register(client: Arc<Client>, cx: &mut App) {
+        let listener = cx.new(|cx| RefreshLlmTokenListener::new(client, cx));
         cx.set_global(GlobalRefreshLlmTokenListener(listener));
     }
 
-    pub fn global(cx: &AppContext) -> Model<Self> {
+    pub fn global(cx: &App) -> Entity<Self> {
         GlobalRefreshLlmTokenListener::global(cx).0.clone()
     }
 
-    fn new(client: Arc<Client>, cx: &mut ModelContext<Self>) -> Self {
+    fn new(client: Arc<Client>, cx: &mut Context<Self>) -> Self {
         Self {
             _llm_token_subscription: client
                 .add_message_handler(cx.weak_model(), Self::handle_refresh_llm_token),
@@ -129,7 +129,7 @@ impl RefreshLlmTokenListener {
     }
 
     async fn handle_refresh_llm_token(
-        this: Model<Self>,
+        this: Entity<Self>,
         _: TypedEnvelope<proto::RefreshLlmToken>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -139,14 +139,14 @@ impl RefreshLlmTokenListener {
 
 pub struct CloudLanguageModelProvider {
     client: Arc<Client>,
-    state: gpui::Model<State>,
+    state: gpui::Entity<State>,
     _maintain_client_status: Task<()>,
 }
 
 pub struct State {
     client: Arc<Client>,
     llm_api_token: LlmApiToken,
-    user_store: Model<UserStore>,
+    user_store: Entity<UserStore>,
     status: client::Status,
     accept_terms: Option<Task<Result<()>>>,
     _settings_subscription: Subscription,
@@ -156,9 +156,9 @@ pub struct State {
 impl State {
     fn new(
         client: Arc<Client>,
-        user_store: Model<UserStore>,
+        user_store: Entity<UserStore>,
         status: client::Status,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Self {
         let refresh_llm_token_listener = RefreshLlmTokenListener::global(cx);
 
@@ -190,7 +190,7 @@ impl State {
         self.status.is_signed_out()
     }
 
-    fn authenticate(&self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn authenticate(&self, cx: &mut Context<Self>) -> Task<Result<()>> {
         let client = self.client.clone();
         cx.spawn(move |this, mut cx| async move {
             client.authenticate_and_connect(true, &cx).await?;
@@ -198,14 +198,14 @@ impl State {
         })
     }
 
-    fn has_accepted_terms_of_service(&self, cx: &AppContext) -> bool {
+    fn has_accepted_terms_of_service(&self, cx: &App) -> bool {
         self.user_store
             .read(cx)
             .current_user_has_accepted_terms()
             .unwrap_or(false)
     }
 
-    fn accept_terms_of_service(&mut self, cx: &mut ModelContext<Self>) {
+    fn accept_terms_of_service(&mut self, cx: &mut Context<Self>) {
         let user_store = self.user_store.clone();
         self.accept_terms = Some(cx.spawn(move |this, mut cx| async move {
             let _ = user_store
@@ -220,11 +220,11 @@ impl State {
 }
 
 impl CloudLanguageModelProvider {
-    pub fn new(user_store: Model<UserStore>, client: Arc<Client>, cx: &mut AppContext) -> Self {
+    pub fn new(user_store: Entity<UserStore>, client: Arc<Client>, cx: &mut App) -> Self {
         let mut status_rx = client.status();
         let status = *status_rx.borrow();
 
-        let state = cx.new_model(|cx| State::new(client.clone(), user_store.clone(), status, cx));
+        let state = cx.new(|cx| State::new(client.clone(), user_store.clone(), status, cx));
 
         let state_ref = state.downgrade();
         let maintain_client_status = cx.spawn(|mut cx| async move {
@@ -253,7 +253,7 @@ impl CloudLanguageModelProvider {
 impl LanguageModelProviderState for CloudLanguageModelProvider {
     type ObservableEntity = State;
 
-    fn observable_entity(&self) -> Option<gpui::Model<Self::ObservableEntity>> {
+    fn observable_entity(&self) -> Option<gpui::Entity<Self::ObservableEntity>> {
         Some(self.state.clone())
     }
 }
@@ -271,7 +271,7 @@ impl LanguageModelProvider for CloudLanguageModelProvider {
         IconName::AiZed
     }
 
-    fn provided_models(&self, cx: &AppContext) -> Vec<Arc<dyn LanguageModel>> {
+    fn provided_models(&self, cx: &App) -> Vec<Arc<dyn LanguageModel>> {
         let mut models = BTreeMap::default();
 
         if cx.is_staff() {
@@ -359,42 +359,42 @@ impl LanguageModelProvider for CloudLanguageModelProvider {
             .collect()
     }
 
-    fn is_authenticated(&self, cx: &AppContext) -> bool {
+    fn is_authenticated(&self, cx: &App) -> bool {
         !self.state.read(cx).is_signed_out()
     }
 
-    fn authenticate(&self, _cx: &mut AppContext) -> Task<Result<()>> {
+    fn authenticate(&self, _cx: &mut App) -> Task<Result<()>> {
         Task::ready(Ok(()))
     }
 
-    fn configuration_view(&self, cx: &mut WindowContext) -> AnyView {
-        cx.new_view(|_cx| ConfigurationView {
+    fn configuration_view(&self, _: &mut Window, cx: &mut App) -> AnyView {
+        cx.new(|_| ConfigurationView {
             state: self.state.clone(),
         })
         .into()
     }
 
-    fn must_accept_terms(&self, cx: &AppContext) -> bool {
+    fn must_accept_terms(&self, cx: &App) -> bool {
         !self.state.read(cx).has_accepted_terms_of_service(cx)
     }
 
     fn render_accept_terms(
         &self,
         view: LanguageModelProviderTosView,
-        cx: &mut WindowContext,
+        cx: &mut App,
     ) -> Option<AnyElement> {
         render_accept_terms(self.state.clone(), view, cx)
     }
 
-    fn reset_credentials(&self, _cx: &mut AppContext) -> Task<Result<()>> {
+    fn reset_credentials(&self, _cx: &mut App) -> Task<Result<()>> {
         Task::ready(Ok(()))
     }
 }
 
 fn render_accept_terms(
-    state: Model<State>,
+    state: Entity<State>,
     view_kind: LanguageModelProviderTosView,
-    cx: &mut WindowContext,
+    cx: &mut App,
 ) -> Option<AnyElement> {
     if state.read(cx).has_accepted_terms_of_service(cx) {
         return None;
@@ -407,7 +407,7 @@ fn render_accept_terms(
         .icon(IconName::ArrowUpRight)
         .icon_color(Color::Muted)
         .icon_size(IconSize::XSmall)
-        .on_click(move |_, cx| cx.open_url("https://zed.dev/terms-of-service"));
+        .on_click(move |_, _window, cx| cx.open_url("https://zed.dev/terms-of-service"));
 
     let text = "To start using Zed AI, please read and accept the";
 
@@ -435,7 +435,7 @@ fn render_accept_terms(
                     .disabled(accept_terms_disabled)
                     .on_click({
                         let state = state.downgrade();
-                        move |_, cx| {
+                        move |_, _window, cx| {
                             state
                                 .update(cx, |state, cx| state.accept_terms_of_service(cx))
                                 .ok();
@@ -592,7 +592,7 @@ impl LanguageModel for CloudLanguageModel {
     fn count_tokens(
         &self,
         request: LanguageModelRequest,
-        cx: &AppContext,
+        cx: &App,
     ) -> BoxFuture<'static, Result<usize>> {
         match self.model.clone() {
             CloudModel::Anthropic(_) => count_anthropic_tokens(request, cx),
@@ -856,11 +856,11 @@ impl LlmApiToken {
 }
 
 struct ConfigurationView {
-    state: gpui::Model<State>,
+    state: gpui::Entity<State>,
 }
 
 impl ConfigurationView {
-    fn authenticate(&mut self, cx: &mut ViewContext<Self>) {
+    fn authenticate(&mut self, cx: &mut Context<Self>) {
         self.state.update(cx, |state, cx| {
             state.authenticate(cx).detach_and_log_err(cx);
         });
@@ -869,7 +869,7 @@ impl ConfigurationView {
 }
 
 impl Render for ConfigurationView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         const ZED_AI_URL: &str = "https://zed.dev/ai";
 
         let is_connected = !self.state.read(cx).is_signed_out();
@@ -887,7 +887,9 @@ impl Render for ConfigurationView {
                 h_flex().child(
                     Button::new("manage_settings", "Manage Subscription")
                         .style(ButtonStyle::Tinted(TintColor::Accent))
-                        .on_click(cx.listener(|_, _, cx| cx.open_url(&zed_urls::account_url(cx)))),
+                        .on_click(
+                            cx.listener(|_, _, _, cx| cx.open_url(&zed_urls::account_url(cx))),
+                        ),
                 ),
             )
         } else if cx.has_flag::<ZedPro>() {
@@ -897,14 +899,14 @@ impl Render for ConfigurationView {
                     .child(
                         Button::new("learn_more", "Learn more")
                             .style(ButtonStyle::Subtle)
-                            .on_click(cx.listener(|_, _, cx| cx.open_url(ZED_AI_URL))),
+                            .on_click(cx.listener(|_, _, _, cx| cx.open_url(ZED_AI_URL))),
                     )
                     .child(
                         Button::new("upgrade", "Upgrade")
                             .style(ButtonStyle::Subtle)
                             .color(Color::Accent)
                             .on_click(
-                                cx.listener(|_, _, cx| cx.open_url(&zed_urls::account_url(cx))),
+                                cx.listener(|_, _, _, cx| cx.open_url(&zed_urls::account_url(cx))),
                             ),
                     ),
             )
@@ -934,7 +936,7 @@ impl Render for ConfigurationView {
                         .icon_color(Color::Muted)
                         .icon(IconName::Github)
                         .icon_position(IconPosition::Start)
-                        .on_click(cx.listener(move |this, _, cx| this.authenticate(cx))),
+                        .on_click(cx.listener(move |this, _, _, cx| this.authenticate(cx))),
                 )
         }
     }

crates/language_models/src/provider/copilot_chat.rs 🔗

@@ -11,7 +11,7 @@ use futures::future::BoxFuture;
 use futures::stream::BoxStream;
 use futures::{FutureExt, StreamExt};
 use gpui::{
-    percentage, svg, Animation, AnimationExt, AnyView, AppContext, AsyncAppContext, Model, Render,
+    percentage, svg, Animation, AnimationExt, AnyView, App, AsyncAppContext, Entity, Render,
     Subscription, Task, Transformation,
 };
 use language_model::{
@@ -34,7 +34,7 @@ const PROVIDER_NAME: &str = "GitHub Copilot Chat";
 pub struct CopilotChatSettings {}
 
 pub struct CopilotChatLanguageModelProvider {
-    state: Model<State>,
+    state: Entity<State>,
 }
 
 pub struct State {
@@ -43,7 +43,7 @@ pub struct State {
 }
 
 impl State {
-    fn is_authenticated(&self, cx: &AppContext) -> bool {
+    fn is_authenticated(&self, cx: &App) -> bool {
         CopilotChat::global(cx)
             .map(|m| m.read(cx).is_authenticated())
             .unwrap_or(false)
@@ -51,8 +51,8 @@ impl State {
 }
 
 impl CopilotChatLanguageModelProvider {
-    pub fn new(cx: &mut AppContext) -> Self {
-        let state = cx.new_model(|cx| {
+    pub fn new(cx: &mut App) -> Self {
+        let state = cx.new(|cx| {
             let _copilot_chat_subscription = CopilotChat::global(cx)
                 .map(|copilot_chat| cx.observe(&copilot_chat, |_, _, cx| cx.notify()));
             State {
@@ -70,7 +70,7 @@ impl CopilotChatLanguageModelProvider {
 impl LanguageModelProviderState for CopilotChatLanguageModelProvider {
     type ObservableEntity = State;
 
-    fn observable_entity(&self) -> Option<gpui::Model<Self::ObservableEntity>> {
+    fn observable_entity(&self) -> Option<gpui::Entity<Self::ObservableEntity>> {
         Some(self.state.clone())
     }
 }
@@ -88,7 +88,7 @@ impl LanguageModelProvider for CopilotChatLanguageModelProvider {
         IconName::Copilot
     }
 
-    fn provided_models(&self, _cx: &AppContext) -> Vec<Arc<dyn LanguageModel>> {
+    fn provided_models(&self, _cx: &App) -> Vec<Arc<dyn LanguageModel>> {
         CopilotChatModel::iter()
             .map(|model| {
                 Arc::new(CopilotChatLanguageModel {
@@ -99,11 +99,11 @@ impl LanguageModelProvider for CopilotChatLanguageModelProvider {
             .collect()
     }
 
-    fn is_authenticated(&self, cx: &AppContext) -> bool {
+    fn is_authenticated(&self, cx: &App) -> bool {
         self.state.read(cx).is_authenticated(cx)
     }
 
-    fn authenticate(&self, cx: &mut AppContext) -> Task<Result<()>> {
+    fn authenticate(&self, cx: &mut App) -> Task<Result<()>> {
         let result = if self.is_authenticated(cx) {
             Ok(())
         } else if let Some(copilot) = Copilot::global(cx) {
@@ -125,12 +125,12 @@ impl LanguageModelProvider for CopilotChatLanguageModelProvider {
         Task::ready(result)
     }
 
-    fn configuration_view(&self, cx: &mut WindowContext) -> AnyView {
+    fn configuration_view(&self, _: &mut Window, cx: &mut App) -> AnyView {
         let state = self.state.clone();
-        cx.new_view(|cx| ConfigurationView::new(state, cx)).into()
+        cx.new(|cx| ConfigurationView::new(state, cx)).into()
     }
 
-    fn reset_credentials(&self, _cx: &mut AppContext) -> Task<Result<()>> {
+    fn reset_credentials(&self, _cx: &mut App) -> Task<Result<()>> {
         Task::ready(Err(anyhow!(
             "Signing out of GitHub Copilot Chat is currently not supported."
         )))
@@ -170,7 +170,7 @@ impl LanguageModel for CopilotChatLanguageModel {
     fn count_tokens(
         &self,
         request: LanguageModelRequest,
-        cx: &AppContext,
+        cx: &App,
     ) -> BoxFuture<'static, Result<usize>> {
         match self.model {
             CopilotChatModel::Claude3_5Sonnet => count_anthropic_tokens(request, cx),
@@ -294,12 +294,12 @@ impl CopilotChatLanguageModel {
 
 struct ConfigurationView {
     copilot_status: Option<copilot::Status>,
-    state: Model<State>,
+    state: Entity<State>,
     _subscription: Option<Subscription>,
 }
 
 impl ConfigurationView {
-    pub fn new(state: Model<State>, cx: &mut ViewContext<Self>) -> Self {
+    pub fn new(state: Entity<State>, cx: &mut Context<Self>) -> Self {
         let copilot = Copilot::global(cx);
 
         Self {
@@ -316,7 +316,7 @@ impl ConfigurationView {
 }
 
 impl Render for ConfigurationView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         if self.state.read(cx).is_authenticated(cx) {
             const LABEL: &str = "Authorized.";
             h_flex()
@@ -327,7 +327,7 @@ impl Render for ConfigurationView {
             let loading_icon = svg()
                 .size_8()
                 .path(IconName::ArrowCircle.path())
-                .text_color(cx.text_style().color)
+                .text_color(window.text_style().color)
                 .with_animation(
                     "icon_circle_arrow",
                     Animation::new(Duration::from_secs(2)).repeat(),
@@ -378,7 +378,9 @@ impl Render for ConfigurationView {
                                         .icon_size(IconSize::Medium)
                                         .style(ui::ButtonStyle::Filled)
                                         .full_width()
-                                        .on_click(|_, cx| copilot::initiate_sign_in(cx)),
+                                        .on_click(|_, window, cx| {
+                                            copilot::initiate_sign_in(window, cx)
+                                        }),
                                 )
                                 .child(
                                     div().flex().w_full().items_center().child(

crates/language_models/src/provider/google.rs 🔗

@@ -4,8 +4,8 @@ use editor::{Editor, EditorElement, EditorStyle};
 use futures::{future::BoxFuture, FutureExt, StreamExt};
 use google_ai::stream_generate_content;
 use gpui::{
-    AnyView, AppContext, AsyncAppContext, FontStyle, ModelContext, Subscription, Task, TextStyle,
-    View, WhiteSpace,
+    AnyView, App, AsyncAppContext, Context, Entity, FontStyle, Subscription, Task, TextStyle,
+    WhiteSpace,
 };
 use http_client::HttpClient;
 use language_model::LanguageModelCompletionEvent;
@@ -43,7 +43,7 @@ pub struct AvailableModel {
 
 pub struct GoogleLanguageModelProvider {
     http_client: Arc<dyn HttpClient>,
-    state: gpui::Model<State>,
+    state: gpui::Entity<State>,
 }
 
 pub struct State {
@@ -59,7 +59,7 @@ impl State {
         self.api_key.is_some()
     }
 
-    fn reset_api_key(&self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn reset_api_key(&self, cx: &mut Context<Self>) -> Task<Result<()>> {
         let delete_credentials =
             cx.delete_credentials(&AllLanguageModelSettings::get_global(cx).google.api_url);
         cx.spawn(|this, mut cx| async move {
@@ -72,7 +72,7 @@ impl State {
         })
     }
 
-    fn set_api_key(&mut self, api_key: String, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn set_api_key(&mut self, api_key: String, cx: &mut Context<Self>) -> Task<Result<()>> {
         let settings = &AllLanguageModelSettings::get_global(cx).google;
         let write_credentials =
             cx.write_credentials(&settings.api_url, "Bearer", api_key.as_bytes());
@@ -86,7 +86,7 @@ impl State {
         })
     }
 
-    fn authenticate(&self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn authenticate(&self, cx: &mut Context<Self>) -> Task<Result<()>> {
         if self.is_authenticated() {
             Task::ready(Ok(()))
         } else {
@@ -118,8 +118,8 @@ impl State {
 }
 
 impl GoogleLanguageModelProvider {
-    pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut AppContext) -> Self {
-        let state = cx.new_model(|cx| State {
+    pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut App) -> Self {
+        let state = cx.new(|cx| State {
             api_key: None,
             api_key_from_env: false,
             _subscription: cx.observe_global::<SettingsStore>(|_, cx| {
@@ -134,7 +134,7 @@ impl GoogleLanguageModelProvider {
 impl LanguageModelProviderState for GoogleLanguageModelProvider {
     type ObservableEntity = State;
 
-    fn observable_entity(&self) -> Option<gpui::Model<Self::ObservableEntity>> {
+    fn observable_entity(&self) -> Option<gpui::Entity<Self::ObservableEntity>> {
         Some(self.state.clone())
     }
 }
@@ -152,7 +152,7 @@ impl LanguageModelProvider for GoogleLanguageModelProvider {
         IconName::AiGoogle
     }
 
-    fn provided_models(&self, cx: &AppContext) -> Vec<Arc<dyn LanguageModel>> {
+    fn provided_models(&self, cx: &App) -> Vec<Arc<dyn LanguageModel>> {
         let mut models = BTreeMap::default();
 
         // Add base models from google_ai::Model::iter()
@@ -191,20 +191,20 @@ impl LanguageModelProvider for GoogleLanguageModelProvider {
             .collect()
     }
 
-    fn is_authenticated(&self, cx: &AppContext) -> bool {
+    fn is_authenticated(&self, cx: &App) -> bool {
         self.state.read(cx).is_authenticated()
     }
 
-    fn authenticate(&self, cx: &mut AppContext) -> Task<Result<()>> {
+    fn authenticate(&self, cx: &mut App) -> Task<Result<()>> {
         self.state.update(cx, |state, cx| state.authenticate(cx))
     }
 
-    fn configuration_view(&self, cx: &mut WindowContext) -> AnyView {
-        cx.new_view(|cx| ConfigurationView::new(self.state.clone(), cx))
+    fn configuration_view(&self, window: &mut Window, cx: &mut App) -> AnyView {
+        cx.new(|cx| ConfigurationView::new(self.state.clone(), window, cx))
             .into()
     }
 
-    fn reset_credentials(&self, cx: &mut AppContext) -> Task<Result<()>> {
+    fn reset_credentials(&self, cx: &mut App) -> Task<Result<()>> {
         let state = self.state.clone();
         let delete_credentials =
             cx.delete_credentials(&AllLanguageModelSettings::get_global(cx).google.api_url);
@@ -221,7 +221,7 @@ impl LanguageModelProvider for GoogleLanguageModelProvider {
 pub struct GoogleLanguageModel {
     id: LanguageModelId,
     model: google_ai::Model,
-    state: gpui::Model<State>,
+    state: gpui::Entity<State>,
     http_client: Arc<dyn HttpClient>,
     rate_limiter: RateLimiter,
 }
@@ -254,7 +254,7 @@ impl LanguageModel for GoogleLanguageModel {
     fn count_tokens(
         &self,
         request: LanguageModelRequest,
-        cx: &AppContext,
+        cx: &App,
     ) -> BoxFuture<'static, Result<usize>> {
         let request = request.into_google(self.model.id().to_string());
         let http_client = self.http_client.clone();
@@ -326,19 +326,19 @@ impl LanguageModel for GoogleLanguageModel {
 }
 
 struct ConfigurationView {
-    api_key_editor: View<Editor>,
-    state: gpui::Model<State>,
+    api_key_editor: Entity<Editor>,
+    state: gpui::Entity<State>,
     load_credentials_task: Option<Task<()>>,
 }
 
 impl ConfigurationView {
-    fn new(state: gpui::Model<State>, cx: &mut ViewContext<Self>) -> Self {
+    fn new(state: gpui::Entity<State>, window: &mut Window, cx: &mut Context<Self>) -> Self {
         cx.observe(&state, |_, _, cx| {
             cx.notify();
         })
         .detach();
 
-        let load_credentials_task = Some(cx.spawn({
+        let load_credentials_task = Some(cx.spawn_in(window, {
             let state = state.clone();
             |this, mut cx| async move {
                 if let Some(task) = state
@@ -357,8 +357,8 @@ impl ConfigurationView {
         }));
 
         Self {
-            api_key_editor: cx.new_view(|cx| {
-                let mut editor = Editor::single_line(cx);
+            api_key_editor: cx.new(|cx| {
+                let mut editor = Editor::single_line(window, cx);
                 editor.set_placeholder_text("AIzaSy...", cx);
                 editor
             }),
@@ -367,14 +367,14 @@ impl ConfigurationView {
         }
     }
 
-    fn save_api_key(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
+    fn save_api_key(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
         let api_key = self.api_key_editor.read(cx).text(cx);
         if api_key.is_empty() {
             return;
         }
 
         let state = self.state.clone();
-        cx.spawn(|_, mut cx| async move {
+        cx.spawn_in(window, |_, mut cx| async move {
             state
                 .update(&mut cx, |state, cx| state.set_api_key(api_key, cx))?
                 .await
@@ -384,12 +384,12 @@ impl ConfigurationView {
         cx.notify();
     }
 
-    fn reset_api_key(&mut self, cx: &mut ViewContext<Self>) {
+    fn reset_api_key(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.api_key_editor
-            .update(cx, |editor, cx| editor.set_text("", cx));
+            .update(cx, |editor, cx| editor.set_text("", window, cx));
 
         let state = self.state.clone();
-        cx.spawn(|_, mut cx| async move {
+        cx.spawn_in(window, |_, mut cx| async move {
             state
                 .update(&mut cx, |state, cx| state.reset_api_key(cx))?
                 .await
@@ -399,7 +399,7 @@ impl ConfigurationView {
         cx.notify();
     }
 
-    fn render_api_key_editor(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render_api_key_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
         let settings = ThemeSettings::get_global(cx);
         let text_style = TextStyle {
             color: cx.theme().colors().text,
@@ -427,13 +427,13 @@ impl ConfigurationView {
         )
     }
 
-    fn should_render_editor(&self, cx: &mut ViewContext<Self>) -> bool {
+    fn should_render_editor(&self, cx: &mut Context<Self>) -> bool {
         !self.state.read(cx).is_authenticated()
     }
 }
 
 impl Render for ConfigurationView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         const GOOGLE_CONSOLE_URL: &str = "https://aistudio.google.com/app/apikey";
         const INSTRUCTIONS: [&str; 3] = [
             "To use Zed's assistant with Google AI, you need to add an API key. Follow these steps:",
@@ -456,7 +456,7 @@ impl Render for ConfigurationView {
                         .icon(IconName::ExternalLink)
                         .icon_size(IconSize::XSmall)
                         .icon_color(Color::Muted)
-                        .on_click(move |_, cx| cx.open_url(GOOGLE_CONSOLE_URL))
+                        .on_click(move |_, _, cx| cx.open_url(GOOGLE_CONSOLE_URL))
                     )
                 )
                 .child(Label::new(INSTRUCTIONS[2]))
@@ -498,9 +498,9 @@ impl Render for ConfigurationView {
                         .icon_position(IconPosition::Start)
                         .disabled(env_var_set)
                         .when(env_var_set, |this| {
-                            this.tooltip(|cx| Tooltip::text(format!("To reset your API key, unset the {GOOGLE_AI_API_KEY_VAR} environment variable."), cx))
+                            this.tooltip(Tooltip::text(format!("To reset your API key, unset the {GOOGLE_AI_API_KEY_VAR} environment variable.")))
                         })
-                        .on_click(cx.listener(|this, _, cx| this.reset_api_key(cx))),
+                        .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx))),
                 )
                 .into_any()
         }

crates/language_models/src/provider/lmstudio.rs 🔗

@@ -1,6 +1,6 @@
 use anyhow::{anyhow, Result};
 use futures::{future::BoxFuture, stream::BoxStream, FutureExt, StreamExt};
-use gpui::{AnyView, AppContext, AsyncAppContext, ModelContext, Subscription, Task};
+use gpui::{AnyView, App, AsyncAppContext, Context, Subscription, Task};
 use http_client::HttpClient;
 use language_model::LanguageModelCompletionEvent;
 use language_model::{
@@ -46,7 +46,7 @@ pub struct AvailableModel {
 
 pub struct LmStudioLanguageModelProvider {
     http_client: Arc<dyn HttpClient>,
-    state: gpui::Model<State>,
+    state: gpui::Entity<State>,
 }
 
 pub struct State {
@@ -61,7 +61,7 @@ impl State {
         !self.available_models.is_empty()
     }
 
-    fn fetch_models(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn fetch_models(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         let settings = &AllLanguageModelSettings::get_global(cx).lmstudio;
         let http_client = self.http_client.clone();
         let api_url = settings.api_url.clone();
@@ -85,12 +85,12 @@ impl State {
         })
     }
 
-    fn restart_fetch_models_task(&mut self, cx: &mut ModelContext<Self>) {
+    fn restart_fetch_models_task(&mut self, cx: &mut Context<Self>) {
         let task = self.fetch_models(cx);
         self.fetch_model_task.replace(task);
     }
 
-    fn authenticate(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn authenticate(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         if self.is_authenticated() {
             Task::ready(Ok(()))
         } else {
@@ -100,10 +100,10 @@ impl State {
 }
 
 impl LmStudioLanguageModelProvider {
-    pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut AppContext) -> Self {
+    pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut App) -> Self {
         let this = Self {
             http_client: http_client.clone(),
-            state: cx.new_model(|cx| {
+            state: cx.new(|cx| {
                 let subscription = cx.observe_global::<SettingsStore>({
                     let mut settings = AllLanguageModelSettings::get_global(cx).lmstudio.clone();
                     move |this: &mut State, cx| {
@@ -133,7 +133,7 @@ impl LmStudioLanguageModelProvider {
 impl LanguageModelProviderState for LmStudioLanguageModelProvider {
     type ObservableEntity = State;
 
-    fn observable_entity(&self) -> Option<gpui::Model<Self::ObservableEntity>> {
+    fn observable_entity(&self) -> Option<gpui::Entity<Self::ObservableEntity>> {
         Some(self.state.clone())
     }
 }
@@ -151,7 +151,7 @@ impl LanguageModelProvider for LmStudioLanguageModelProvider {
         IconName::AiLmStudio
     }
 
-    fn provided_models(&self, cx: &AppContext) -> Vec<Arc<dyn LanguageModel>> {
+    fn provided_models(&self, cx: &App) -> Vec<Arc<dyn LanguageModel>> {
         let mut models: BTreeMap<String, lmstudio::Model> = BTreeMap::default();
 
         // Add models from the LM Studio API
@@ -188,7 +188,7 @@ impl LanguageModelProvider for LmStudioLanguageModelProvider {
             .collect()
     }
 
-    fn load_model(&self, model: Arc<dyn LanguageModel>, cx: &AppContext) {
+    fn load_model(&self, model: Arc<dyn LanguageModel>, cx: &App) {
         let settings = &AllLanguageModelSettings::get_global(cx).lmstudio;
         let http_client = self.http_client.clone();
         let api_url = settings.api_url.clone();
@@ -197,20 +197,20 @@ impl LanguageModelProvider for LmStudioLanguageModelProvider {
             .detach_and_log_err(cx);
     }
 
-    fn is_authenticated(&self, cx: &AppContext) -> bool {
+    fn is_authenticated(&self, cx: &App) -> bool {
         self.state.read(cx).is_authenticated()
     }
 
-    fn authenticate(&self, cx: &mut AppContext) -> Task<Result<()>> {
+    fn authenticate(&self, cx: &mut App) -> Task<Result<()>> {
         self.state.update(cx, |state, cx| state.authenticate(cx))
     }
 
-    fn configuration_view(&self, cx: &mut WindowContext) -> AnyView {
+    fn configuration_view(&self, _window: &mut Window, cx: &mut App) -> AnyView {
         let state = self.state.clone();
-        cx.new_view(|cx| ConfigurationView::new(state, cx)).into()
+        cx.new(|cx| ConfigurationView::new(state, cx)).into()
     }
 
-    fn reset_credentials(&self, cx: &mut AppContext) -> Task<Result<()>> {
+    fn reset_credentials(&self, cx: &mut App) -> Task<Result<()>> {
         self.state.update(cx, |state, cx| state.fetch_models(cx))
     }
 }
@@ -279,7 +279,7 @@ impl LanguageModel for LmStudioLanguageModel {
     fn count_tokens(
         &self,
         request: LanguageModelRequest,
-        _cx: &AppContext,
+        _cx: &App,
     ) -> BoxFuture<'static, Result<usize>> {
         // Endpoint for this is coming soon. In the meantime, hacky estimation
         let token_count = request
@@ -369,12 +369,12 @@ impl LanguageModel for LmStudioLanguageModel {
 }
 
 struct ConfigurationView {
-    state: gpui::Model<State>,
+    state: gpui::Entity<State>,
     loading_models_task: Option<Task<()>>,
 }
 
 impl ConfigurationView {
-    pub fn new(state: gpui::Model<State>, cx: &mut ViewContext<Self>) -> Self {
+    pub fn new(state: gpui::Entity<State>, cx: &mut Context<Self>) -> Self {
         let loading_models_task = Some(cx.spawn({
             let state = state.clone();
             |this, mut cx| async move {
@@ -398,7 +398,7 @@ impl ConfigurationView {
         }
     }
 
-    fn retry_connection(&self, cx: &mut WindowContext) {
+    fn retry_connection(&self, cx: &mut App) {
         self.state
             .update(cx, |state, cx| state.fetch_models(cx))
             .detach_and_log_err(cx);
@@ -406,7 +406,7 @@ impl ConfigurationView {
 }
 
 impl Render for ConfigurationView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let is_authenticated = self.state.read(cx).is_authenticated();
 
         let lmstudio_intro = "Run local LLMs like Llama, Phi, and Qwen.";
@@ -460,7 +460,9 @@ impl Render for ConfigurationView {
                                                 .icon(IconName::ExternalLink)
                                                 .icon_size(IconSize::XSmall)
                                                 .icon_color(Color::Muted)
-                                                .on_click(move |_, cx| cx.open_url(LMSTUDIO_SITE))
+                                                .on_click(move |_, _window, cx| {
+                                                    cx.open_url(LMSTUDIO_SITE)
+                                                })
                                                 .into_any_element(),
                                         )
                                     } else {
@@ -473,7 +475,7 @@ impl Render for ConfigurationView {
                                             .icon(IconName::ExternalLink)
                                             .icon_size(IconSize::XSmall)
                                             .icon_color(Color::Muted)
-                                            .on_click(move |_, cx| {
+                                            .on_click(move |_, _window, cx| {
                                                 cx.open_url(LMSTUDIO_DOWNLOAD_URL)
                                             })
                                             .into_any_element(),
@@ -486,7 +488,9 @@ impl Render for ConfigurationView {
                                         .icon(IconName::ExternalLink)
                                         .icon_size(IconSize::XSmall)
                                         .icon_color(Color::Muted)
-                                        .on_click(move |_, cx| cx.open_url(LMSTUDIO_CATALOG_URL)),
+                                        .on_click(move |_, _window, cx| {
+                                            cx.open_url(LMSTUDIO_CATALOG_URL)
+                                        }),
                                 ),
                         )
                         .child(if is_authenticated {
@@ -508,7 +512,9 @@ impl Render for ConfigurationView {
                             Button::new("retry_lmstudio_models", "Connect")
                                 .icon_position(IconPosition::Start)
                                 .icon(IconName::ArrowCircle)
-                                .on_click(cx.listener(move |this, _, cx| this.retry_connection(cx)))
+                                .on_click(cx.listener(move |this, _, _window, cx| {
+                                    this.retry_connection(cx)
+                                }))
                                 .into_any_element()
                         }),
                 )

crates/language_models/src/provider/ollama.rs 🔗

@@ -1,6 +1,6 @@
 use anyhow::{anyhow, bail, Result};
 use futures::{future::BoxFuture, stream::BoxStream, FutureExt, StreamExt};
-use gpui::{AnyView, AppContext, AsyncAppContext, ModelContext, Subscription, Task};
+use gpui::{AnyView, App, AsyncAppContext, Context, Subscription, Task};
 use http_client::HttpClient;
 use language_model::LanguageModelCompletionEvent;
 use language_model::{
@@ -48,7 +48,7 @@ pub struct AvailableModel {
 
 pub struct OllamaLanguageModelProvider {
     http_client: Arc<dyn HttpClient>,
-    state: gpui::Model<State>,
+    state: gpui::Entity<State>,
 }
 
 pub struct State {
@@ -63,7 +63,7 @@ impl State {
         !self.available_models.is_empty()
     }
 
-    fn fetch_models(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn fetch_models(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         let settings = &AllLanguageModelSettings::get_global(cx).ollama;
         let http_client = self.http_client.clone();
         let api_url = settings.api_url.clone();
@@ -90,12 +90,12 @@ impl State {
         })
     }
 
-    fn restart_fetch_models_task(&mut self, cx: &mut ModelContext<Self>) {
+    fn restart_fetch_models_task(&mut self, cx: &mut Context<Self>) {
         let task = self.fetch_models(cx);
         self.fetch_model_task.replace(task);
     }
 
-    fn authenticate(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn authenticate(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         if self.is_authenticated() {
             Task::ready(Ok(()))
         } else {
@@ -105,10 +105,10 @@ impl State {
 }
 
 impl OllamaLanguageModelProvider {
-    pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut AppContext) -> Self {
+    pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut App) -> Self {
         let this = Self {
             http_client: http_client.clone(),
-            state: cx.new_model(|cx| {
+            state: cx.new(|cx| {
                 let subscription = cx.observe_global::<SettingsStore>({
                     let mut settings = AllLanguageModelSettings::get_global(cx).ollama.clone();
                     move |this: &mut State, cx| {
@@ -138,7 +138,7 @@ impl OllamaLanguageModelProvider {
 impl LanguageModelProviderState for OllamaLanguageModelProvider {
     type ObservableEntity = State;
 
-    fn observable_entity(&self) -> Option<gpui::Model<Self::ObservableEntity>> {
+    fn observable_entity(&self) -> Option<gpui::Entity<Self::ObservableEntity>> {
         Some(self.state.clone())
     }
 }
@@ -156,7 +156,7 @@ impl LanguageModelProvider for OllamaLanguageModelProvider {
         IconName::AiOllama
     }
 
-    fn provided_models(&self, cx: &AppContext) -> Vec<Arc<dyn LanguageModel>> {
+    fn provided_models(&self, cx: &App) -> Vec<Arc<dyn LanguageModel>> {
         let mut models: BTreeMap<String, ollama::Model> = BTreeMap::default();
 
         // Add models from the Ollama API
@@ -194,7 +194,7 @@ impl LanguageModelProvider for OllamaLanguageModelProvider {
             .collect()
     }
 
-    fn load_model(&self, model: Arc<dyn LanguageModel>, cx: &AppContext) {
+    fn load_model(&self, model: Arc<dyn LanguageModel>, cx: &App) {
         let settings = &AllLanguageModelSettings::get_global(cx).ollama;
         let http_client = self.http_client.clone();
         let api_url = settings.api_url.clone();
@@ -203,20 +203,21 @@ impl LanguageModelProvider for OllamaLanguageModelProvider {
             .detach_and_log_err(cx);
     }
 
-    fn is_authenticated(&self, cx: &AppContext) -> bool {
+    fn is_authenticated(&self, cx: &App) -> bool {
         self.state.read(cx).is_authenticated()
     }
 
-    fn authenticate(&self, cx: &mut AppContext) -> Task<Result<()>> {
+    fn authenticate(&self, cx: &mut App) -> Task<Result<()>> {
         self.state.update(cx, |state, cx| state.authenticate(cx))
     }
 
-    fn configuration_view(&self, cx: &mut WindowContext) -> AnyView {
+    fn configuration_view(&self, window: &mut Window, cx: &mut App) -> AnyView {
         let state = self.state.clone();
-        cx.new_view(|cx| ConfigurationView::new(state, cx)).into()
+        cx.new(|cx| ConfigurationView::new(state, window, cx))
+            .into()
     }
 
-    fn reset_credentials(&self, cx: &mut AppContext) -> Task<Result<()>> {
+    fn reset_credentials(&self, cx: &mut App) -> Task<Result<()>> {
         self.state.update(cx, |state, cx| state.fetch_models(cx))
     }
 }
@@ -305,7 +306,7 @@ impl LanguageModel for OllamaLanguageModel {
     fn count_tokens(
         &self,
         request: LanguageModelRequest,
-        _cx: &AppContext,
+        _cx: &App,
     ) -> BoxFuture<'static, Result<usize>> {
         // There is no endpoint for this _yet_ in Ollama
         // see: https://github.com/ollama/ollama/issues/1716 and https://github.com/ollama/ollama/issues/3582
@@ -407,13 +408,13 @@ impl LanguageModel for OllamaLanguageModel {
 }
 
 struct ConfigurationView {
-    state: gpui::Model<State>,
+    state: gpui::Entity<State>,
     loading_models_task: Option<Task<()>>,
 }
 
 impl ConfigurationView {
-    pub fn new(state: gpui::Model<State>, cx: &mut ViewContext<Self>) -> Self {
-        let loading_models_task = Some(cx.spawn({
+    pub fn new(state: gpui::Entity<State>, window: &mut Window, cx: &mut Context<Self>) -> Self {
+        let loading_models_task = Some(cx.spawn_in(window, {
             let state = state.clone();
             |this, mut cx| async move {
                 if let Some(task) = state
@@ -436,7 +437,7 @@ impl ConfigurationView {
         }
     }
 
-    fn retry_connection(&self, cx: &mut WindowContext) {
+    fn retry_connection(&self, cx: &mut App) {
         self.state
             .update(cx, |state, cx| state.fetch_models(cx))
             .detach_and_log_err(cx);
@@ -444,7 +445,7 @@ impl ConfigurationView {
 }
 
 impl Render for ConfigurationView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let is_authenticated = self.state.read(cx).is_authenticated();
 
         let ollama_intro = "Get up and running with Llama 3.3, Mistral, Gemma 2, and other large language models with Ollama.";
@@ -498,7 +499,7 @@ impl Render for ConfigurationView {
                                                 .icon(IconName::ExternalLink)
                                                 .icon_size(IconSize::XSmall)
                                                 .icon_color(Color::Muted)
-                                                .on_click(move |_, cx| cx.open_url(OLLAMA_SITE))
+                                                .on_click(move |_, _, cx| cx.open_url(OLLAMA_SITE))
                                                 .into_any_element(),
                                         )
                                     } else {
@@ -511,7 +512,9 @@ impl Render for ConfigurationView {
                                             .icon(IconName::ExternalLink)
                                             .icon_size(IconSize::XSmall)
                                             .icon_color(Color::Muted)
-                                            .on_click(move |_, cx| cx.open_url(OLLAMA_DOWNLOAD_URL))
+                                            .on_click(move |_, _, cx| {
+                                                cx.open_url(OLLAMA_DOWNLOAD_URL)
+                                            })
                                             .into_any_element(),
                                         )
                                     }
@@ -522,7 +525,7 @@ impl Render for ConfigurationView {
                                         .icon(IconName::ExternalLink)
                                         .icon_size(IconSize::XSmall)
                                         .icon_color(Color::Muted)
-                                        .on_click(move |_, cx| cx.open_url(OLLAMA_LIBRARY_URL)),
+                                        .on_click(move |_, _, cx| cx.open_url(OLLAMA_LIBRARY_URL)),
                                 ),
                         )
                         .child(if is_authenticated {
@@ -544,7 +547,9 @@ impl Render for ConfigurationView {
                             Button::new("retry_ollama_models", "Connect")
                                 .icon_position(IconPosition::Start)
                                 .icon(IconName::ArrowCircle)
-                                .on_click(cx.listener(move |this, _, cx| this.retry_connection(cx)))
+                                .on_click(
+                                    cx.listener(move |this, _, _, cx| this.retry_connection(cx)),
+                                )
                                 .into_any_element()
                         }),
                 )

crates/language_models/src/provider/open_ai.rs 🔗

@@ -3,8 +3,8 @@ use collections::BTreeMap;
 use editor::{Editor, EditorElement, EditorStyle};
 use futures::{future::BoxFuture, FutureExt, StreamExt};
 use gpui::{
-    AnyView, AppContext, AsyncAppContext, FontStyle, ModelContext, Subscription, Task, TextStyle,
-    View, WhiteSpace,
+    AnyView, App, AsyncAppContext, Context, Entity, FontStyle, Subscription, Task, TextStyle,
+    WhiteSpace,
 };
 use http_client::HttpClient;
 use language_model::{
@@ -47,7 +47,7 @@ pub struct AvailableModel {
 
 pub struct OpenAiLanguageModelProvider {
     http_client: Arc<dyn HttpClient>,
-    state: gpui::Model<State>,
+    state: gpui::Entity<State>,
 }
 
 pub struct State {
@@ -63,7 +63,7 @@ impl State {
         self.api_key.is_some()
     }
 
-    fn reset_api_key(&self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn reset_api_key(&self, cx: &mut Context<Self>) -> Task<Result<()>> {
         let settings = &AllLanguageModelSettings::get_global(cx).openai;
         let delete_credentials = cx.delete_credentials(&settings.api_url);
         cx.spawn(|this, mut cx| async move {
@@ -76,7 +76,7 @@ impl State {
         })
     }
 
-    fn set_api_key(&mut self, api_key: String, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn set_api_key(&mut self, api_key: String, cx: &mut Context<Self>) -> Task<Result<()>> {
         let settings = &AllLanguageModelSettings::get_global(cx).openai;
         let write_credentials =
             cx.write_credentials(&settings.api_url, "Bearer", api_key.as_bytes());
@@ -90,7 +90,7 @@ impl State {
         })
     }
 
-    fn authenticate(&self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn authenticate(&self, cx: &mut Context<Self>) -> Task<Result<()>> {
         if self.is_authenticated() {
             Task::ready(Ok(()))
         } else {
@@ -119,8 +119,8 @@ impl State {
 }
 
 impl OpenAiLanguageModelProvider {
-    pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut AppContext) -> Self {
-        let state = cx.new_model(|cx| State {
+    pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut App) -> Self {
+        let state = cx.new(|cx| State {
             api_key: None,
             api_key_from_env: false,
             _subscription: cx.observe_global::<SettingsStore>(|_this: &mut State, cx| {
@@ -135,7 +135,7 @@ impl OpenAiLanguageModelProvider {
 impl LanguageModelProviderState for OpenAiLanguageModelProvider {
     type ObservableEntity = State;
 
-    fn observable_entity(&self) -> Option<gpui::Model<Self::ObservableEntity>> {
+    fn observable_entity(&self) -> Option<gpui::Entity<Self::ObservableEntity>> {
         Some(self.state.clone())
     }
 }
@@ -153,7 +153,7 @@ impl LanguageModelProvider for OpenAiLanguageModelProvider {
         IconName::AiOpenAi
     }
 
-    fn provided_models(&self, cx: &AppContext) -> Vec<Arc<dyn LanguageModel>> {
+    fn provided_models(&self, cx: &App) -> Vec<Arc<dyn LanguageModel>> {
         let mut models = BTreeMap::default();
 
         // Add base models from open_ai::Model::iter()
@@ -194,20 +194,20 @@ impl LanguageModelProvider for OpenAiLanguageModelProvider {
             .collect()
     }
 
-    fn is_authenticated(&self, cx: &AppContext) -> bool {
+    fn is_authenticated(&self, cx: &App) -> bool {
         self.state.read(cx).is_authenticated()
     }
 
-    fn authenticate(&self, cx: &mut AppContext) -> Task<Result<()>> {
+    fn authenticate(&self, cx: &mut App) -> Task<Result<()>> {
         self.state.update(cx, |state, cx| state.authenticate(cx))
     }
 
-    fn configuration_view(&self, cx: &mut WindowContext) -> AnyView {
-        cx.new_view(|cx| ConfigurationView::new(self.state.clone(), cx))
+    fn configuration_view(&self, window: &mut Window, cx: &mut App) -> AnyView {
+        cx.new(|cx| ConfigurationView::new(self.state.clone(), window, cx))
             .into()
     }
 
-    fn reset_credentials(&self, cx: &mut AppContext) -> Task<Result<()>> {
+    fn reset_credentials(&self, cx: &mut App) -> Task<Result<()>> {
         self.state.update(cx, |state, cx| state.reset_api_key(cx))
     }
 }
@@ -215,7 +215,7 @@ impl LanguageModelProvider for OpenAiLanguageModelProvider {
 pub struct OpenAiLanguageModel {
     id: LanguageModelId,
     model: open_ai::Model,
-    state: gpui::Model<State>,
+    state: gpui::Entity<State>,
     http_client: Arc<dyn HttpClient>,
     request_limiter: RateLimiter,
 }
@@ -278,7 +278,7 @@ impl LanguageModel for OpenAiLanguageModel {
     fn count_tokens(
         &self,
         request: LanguageModelRequest,
-        cx: &AppContext,
+        cx: &App,
     ) -> BoxFuture<'static, Result<usize>> {
         count_open_ai_tokens(request, self.model.clone(), cx)
     }
@@ -342,7 +342,7 @@ impl LanguageModel for OpenAiLanguageModel {
 pub fn count_open_ai_tokens(
     request: LanguageModelRequest,
     model: open_ai::Model,
-    cx: &AppContext,
+    cx: &App,
 ) -> BoxFuture<'static, Result<usize>> {
     cx.background_executor()
         .spawn(async move {
@@ -372,15 +372,15 @@ pub fn count_open_ai_tokens(
 }
 
 struct ConfigurationView {
-    api_key_editor: View<Editor>,
-    state: gpui::Model<State>,
+    api_key_editor: Entity<Editor>,
+    state: gpui::Entity<State>,
     load_credentials_task: Option<Task<()>>,
 }
 
 impl ConfigurationView {
-    fn new(state: gpui::Model<State>, cx: &mut ViewContext<Self>) -> Self {
-        let api_key_editor = cx.new_view(|cx| {
-            let mut editor = Editor::single_line(cx);
+    fn new(state: gpui::Entity<State>, window: &mut Window, cx: &mut Context<Self>) -> Self {
+        let api_key_editor = cx.new(|cx| {
+            let mut editor = Editor::single_line(window, cx);
             editor.set_placeholder_text("sk-000000000000000000000000000000000000000000000000", cx);
             editor
         });
@@ -390,7 +390,7 @@ impl ConfigurationView {
         })
         .detach();
 
-        let load_credentials_task = Some(cx.spawn({
+        let load_credentials_task = Some(cx.spawn_in(window, {
             let state = state.clone();
             |this, mut cx| async move {
                 if let Some(task) = state
@@ -416,14 +416,14 @@ impl ConfigurationView {
         }
     }
 
-    fn save_api_key(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
+    fn save_api_key(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
         let api_key = self.api_key_editor.read(cx).text(cx);
         if api_key.is_empty() {
             return;
         }
 
         let state = self.state.clone();
-        cx.spawn(|_, mut cx| async move {
+        cx.spawn_in(window, |_, mut cx| async move {
             state
                 .update(&mut cx, |state, cx| state.set_api_key(api_key, cx))?
                 .await
@@ -433,12 +433,12 @@ impl ConfigurationView {
         cx.notify();
     }
 
-    fn reset_api_key(&mut self, cx: &mut ViewContext<Self>) {
+    fn reset_api_key(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.api_key_editor
-            .update(cx, |editor, cx| editor.set_text("", cx));
+            .update(cx, |editor, cx| editor.set_text("", window, cx));
 
         let state = self.state.clone();
-        cx.spawn(|_, mut cx| async move {
+        cx.spawn_in(window, |_, mut cx| async move {
             state
                 .update(&mut cx, |state, cx| state.reset_api_key(cx))?
                 .await
@@ -448,7 +448,7 @@ impl ConfigurationView {
         cx.notify();
     }
 
-    fn render_api_key_editor(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render_api_key_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
         let settings = ThemeSettings::get_global(cx);
         let text_style = TextStyle {
             color: cx.theme().colors().text,
@@ -476,13 +476,13 @@ impl ConfigurationView {
         )
     }
 
-    fn should_render_editor(&self, cx: &mut ViewContext<Self>) -> bool {
+    fn should_render_editor(&self, cx: &mut Context<Self>) -> bool {
         !self.state.read(cx).is_authenticated()
     }
 }
 
 impl Render for ConfigurationView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         const OPENAI_CONSOLE_URL: &str = "https://platform.openai.com/api-keys";
         const INSTRUCTIONS: [&str; 4] = [
             "To use Zed's assistant with OpenAI, you need to add an API key. Follow these steps:",
@@ -506,7 +506,7 @@ impl Render for ConfigurationView {
                         .icon(IconName::ExternalLink)
                         .icon_size(IconSize::XSmall)
                         .icon_color(Color::Muted)
-                        .on_click(move |_, cx| cx.open_url(OPENAI_CONSOLE_URL))
+                        .on_click(move |_, _, cx| cx.open_url(OPENAI_CONSOLE_URL))
                     )
                 )
                 .children(
@@ -556,9 +556,9 @@ impl Render for ConfigurationView {
                         .icon_position(IconPosition::Start)
                         .disabled(env_var_set)
                         .when(env_var_set, |this| {
-                            this.tooltip(|cx| Tooltip::text(format!("To reset your API key, unset the {OPENAI_API_KEY_VAR} environment variable."), cx))
+                            this.tooltip(Tooltip::text(format!("To reset your API key, unset the {OPENAI_API_KEY_VAR} environment variable.")))
                         })
-                        .on_click(cx.listener(|this, _, cx| this.reset_api_key(cx))),
+                        .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx))),
                 )
                 .into_any()
         }

crates/language_models/src/settings.rs 🔗

@@ -1,7 +1,7 @@
 use std::sync::Arc;
 
 use anyhow::Result;
-use gpui::AppContext;
+use gpui::App;
 use language_model::LanguageModelCacheConfiguration;
 use project::Fs;
 use schemars::JsonSchema;
@@ -20,7 +20,7 @@ use crate::provider::{
 };
 
 /// Initializes the language model settings.
-pub fn init(fs: Arc<dyn Fs>, cx: &mut AppContext) {
+pub fn init(fs: Arc<dyn Fs>, cx: &mut App) {
     AllLanguageModelSettings::register(cx);
 
     if AllLanguageModelSettings::get_global(cx)
@@ -246,7 +246,7 @@ impl settings::Settings for AllLanguageModelSettings {
 
     type FileContent = AllLanguageModelSettingsContent;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
         fn merge<T>(target: &mut T, value: Option<T>) {
             if let Some(value) = value {
                 *target = value;

crates/language_selector/src/active_buffer_language.rs 🔗

@@ -1,5 +1,7 @@
 use editor::Editor;
-use gpui::{div, IntoElement, ParentElement, Render, Subscription, View, ViewContext, WeakView};
+use gpui::{
+    div, Context, Entity, IntoElement, ParentElement, Render, Subscription, WeakEntity, Window,
+};
 use language::LanguageName;
 use ui::{Button, ButtonCommon, Clickable, FluentBuilder, LabelSize, Tooltip};
 use workspace::{item::ItemHandle, StatusItemView, Workspace};
@@ -8,7 +10,7 @@ use crate::{LanguageSelector, Toggle};
 
 pub struct ActiveBufferLanguage {
     active_language: Option<Option<LanguageName>>,
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     _observe_active_editor: Option<Subscription>,
 }
 
@@ -21,7 +23,7 @@ impl ActiveBufferLanguage {
         }
     }
 
-    fn update_language(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
+    fn update_language(&mut self, editor: Entity<Editor>, _: &mut Window, cx: &mut Context<Self>) {
         self.active_language = Some(None);
 
         let editor = editor.read(cx);
@@ -36,7 +38,7 @@ impl ActiveBufferLanguage {
 }
 
 impl Render for ActiveBufferLanguage {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         div().when_some(self.active_language.as_ref(), |el, active_language| {
             let active_language_text = if let Some(active_language_text) = active_language {
                 active_language_text.to_string()
@@ -47,14 +49,16 @@ impl Render for ActiveBufferLanguage {
             el.child(
                 Button::new("change-language", active_language_text)
                     .label_size(LabelSize::Small)
-                    .on_click(cx.listener(|this, _, cx| {
+                    .on_click(cx.listener(|this, _, window, cx| {
                         if let Some(workspace) = this.workspace.upgrade() {
                             workspace.update(cx, |workspace, cx| {
-                                LanguageSelector::toggle(workspace, cx)
+                                LanguageSelector::toggle(workspace, window, cx)
                             });
                         }
                     }))
-                    .tooltip(|cx| Tooltip::for_action("Select Language", &Toggle, cx)),
+                    .tooltip(|window, cx| {
+                        Tooltip::for_action("Select Language", &Toggle, window, cx)
+                    }),
             )
         })
     }
@@ -64,11 +68,13 @@ impl StatusItemView for ActiveBufferLanguage {
     fn set_active_pane_item(
         &mut self,
         active_pane_item: Option<&dyn ItemHandle>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         if let Some(editor) = active_pane_item.and_then(|item| item.downcast::<Editor>()) {
-            self._observe_active_editor = Some(cx.observe(&editor, Self::update_language));
-            self.update_language(editor, cx);
+            self._observe_active_editor =
+                Some(cx.observe_in(&editor, window, Self::update_language));
+            self.update_language(editor, window, cx);
         } else {
             self.active_language = None;
             self._observe_active_editor = None;

crates/language_selector/src/language_selector.rs 🔗

@@ -7,8 +7,8 @@ use file_finder::file_finder_settings::FileFinderSettings;
 use file_icons::FileIcons;
 use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
 use gpui::{
-    actions, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Model,
-    ParentElement, Render, Styled, View, ViewContext, VisualContext, WeakView,
+    actions, App, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable,
+    ParentElement, Render, Styled, WeakEntity, Window,
 };
 use language::{Buffer, LanguageMatcher, LanguageName, LanguageRegistry};
 use picker::{Picker, PickerDelegate};
@@ -21,22 +21,30 @@ use workspace::{ModalView, Workspace};
 
 actions!(language_selector, [Toggle]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(LanguageSelector::register).detach();
+pub fn init(cx: &mut App) {
+    cx.observe_new(LanguageSelector::register).detach();
 }
 
 pub struct LanguageSelector {
-    picker: View<Picker<LanguageSelectorDelegate>>,
+    picker: Entity<Picker<LanguageSelectorDelegate>>,
 }
 
 impl LanguageSelector {
-    fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-        workspace.register_action(move |workspace, _: &Toggle, cx| {
-            Self::toggle(workspace, cx);
+    fn register(
+        workspace: &mut Workspace,
+        _window: Option<&mut Window>,
+        _: &mut Context<Workspace>,
+    ) {
+        workspace.register_action(move |workspace, _: &Toggle, window, cx| {
+            Self::toggle(workspace, window, cx);
         });
     }
 
-    fn toggle(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) -> Option<()> {
+    fn toggle(
+        workspace: &mut Workspace,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Option<()> {
         let registry = workspace.app_state().languages.clone();
         let (_, buffer, _) = workspace
             .active_item(cx)?
@@ -45,38 +53,39 @@ impl LanguageSelector {
             .active_excerpt(cx)?;
         let project = workspace.project().clone();
 
-        workspace.toggle_modal(cx, move |cx| {
-            LanguageSelector::new(buffer, project, registry, cx)
+        workspace.toggle_modal(window, cx, move |window, cx| {
+            LanguageSelector::new(buffer, project, registry, window, cx)
         });
         Some(())
     }
 
     fn new(
-        buffer: Model<Buffer>,
-        project: Model<Project>,
+        buffer: Entity<Buffer>,
+        project: Entity<Project>,
         language_registry: Arc<LanguageRegistry>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let delegate = LanguageSelectorDelegate::new(
-            cx.view().downgrade(),
+            cx.model().downgrade(),
             buffer,
             project,
             language_registry,
         );
 
-        let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
+        let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
         Self { picker }
     }
 }
 
 impl Render for LanguageSelector {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         v_flex().w(rems(34.)).child(self.picker.clone())
     }
 }
 
-impl FocusableView for LanguageSelector {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for LanguageSelector {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
@@ -85,9 +94,9 @@ impl EventEmitter<DismissEvent> for LanguageSelector {}
 impl ModalView for LanguageSelector {}
 
 pub struct LanguageSelectorDelegate {
-    language_selector: WeakView<LanguageSelector>,
-    buffer: Model<Buffer>,
-    project: Model<Project>,
+    language_selector: WeakEntity<LanguageSelector>,
+    buffer: Entity<Buffer>,
+    project: Entity<Project>,
     language_registry: Arc<LanguageRegistry>,
     candidates: Vec<StringMatchCandidate>,
     matches: Vec<StringMatch>,
@@ -96,9 +105,9 @@ pub struct LanguageSelectorDelegate {
 
 impl LanguageSelectorDelegate {
     fn new(
-        language_selector: WeakView<LanguageSelector>,
-        buffer: Model<Buffer>,
-        project: Model<Project>,
+        language_selector: WeakEntity<LanguageSelector>,
+        buffer: Entity<Buffer>,
+        project: Entity<Project>,
         language_registry: Arc<LanguageRegistry>,
     ) -> Self {
         let candidates = language_registry
@@ -126,11 +135,7 @@ impl LanguageSelectorDelegate {
         }
     }
 
-    fn language_data_for_match(
-        &self,
-        mat: &StringMatch,
-        cx: &AppContext,
-    ) -> (String, Option<Icon>) {
+    fn language_data_for_match(&self, mat: &StringMatch, cx: &App) -> (String, Option<Icon>) {
         let mut label = mat.string.clone();
         let buffer_language = self.buffer.read(cx).language();
         let need_icon = FileFinderSettings::get_global(cx).file_icons;
@@ -162,7 +167,7 @@ impl LanguageSelectorDelegate {
         }
     }
 
-    fn language_icon(&self, matcher: &LanguageMatcher, cx: &AppContext) -> Option<Icon> {
+    fn language_icon(&self, matcher: &LanguageMatcher, cx: &App) -> Option<Icon> {
         matcher
             .path_suffixes
             .iter()
@@ -181,7 +186,7 @@ impl LanguageSelectorDelegate {
 impl PickerDelegate for LanguageSelectorDelegate {
     type ListItem = ListItem;
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Select a language…".into()
     }
 
@@ -189,13 +194,13 @@ impl PickerDelegate for LanguageSelectorDelegate {
         self.matches.len()
     }
 
-    fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
         if let Some(mat) = self.matches.get(self.selected_index) {
             let language_name = &self.candidates[mat.candidate_id].string;
             let language = self.language_registry.language_for_name(language_name);
             let project = self.project.downgrade();
             let buffer = self.buffer.downgrade();
-            cx.spawn(|_, mut cx| async move {
+            cx.spawn_in(window, |_, mut cx| async move {
                 let language = language.await?;
                 let project = project
                     .upgrade()
@@ -209,10 +214,10 @@ impl PickerDelegate for LanguageSelectorDelegate {
             })
             .detach_and_log_err(cx);
         }
-        self.dismissed(cx);
+        self.dismissed(window, cx);
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+    fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.language_selector
             .update(cx, |_, cx| cx.emit(DismissEvent))
             .log_err();
@@ -222,18 +227,24 @@ impl PickerDelegate for LanguageSelectorDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        _window: &mut Window,
+        _: &mut Context<Picker<Self>>,
+    ) {
         self.selected_index = ix;
     }
 
     fn update_matches(
         &mut self,
         query: String,
-        cx: &mut ViewContext<Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> gpui::Task<()> {
         let background = cx.background_executor().clone();
         let candidates = self.candidates.clone();
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let matches = if query.is_empty() {
                 candidates
                     .into_iter()
@@ -273,7 +284,8 @@ impl PickerDelegate for LanguageSelectorDelegate {
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let mat = &self.matches[ix];
         let (label, language_icon) = self.language_data_for_match(mat, cx);

crates/language_tools/src/key_context_view.rs 🔗

@@ -1,14 +1,14 @@
 use gpui::{
-    actions, Action, AppContext, EventEmitter, FocusHandle, FocusableView,
+    actions, Action, App, AppContext as _, Entity, EventEmitter, FocusHandle, Focusable,
     KeyBindingContextPredicate, KeyContext, Keystroke, MouseButton, Render, Subscription,
 };
 use itertools::Itertools;
 use serde_json::json;
 use settings::get_key_equivalents;
 use ui::{
-    div, h_flex, px, v_flex, ButtonCommon, Clickable, FluentBuilder, InteractiveElement, Label,
-    LabelCommon, LabelSize, ParentElement, SharedString, StatefulInteractiveElement, Styled,
-    ViewContext, VisualContext, WindowContext,
+    div, h_flex, px, v_flex, ButtonCommon, Clickable, Context, FluentBuilder, InteractiveElement,
+    Label, LabelCommon, LabelSize, ParentElement, SharedString, StatefulInteractiveElement, Styled,
+    Window,
 };
 use ui::{Button, ButtonStyle};
 use workspace::Item;
@@ -16,11 +16,11 @@ use workspace::Workspace;
 
 actions!(debug, [OpenKeyContextView]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(|workspace: &mut Workspace, _| {
-        workspace.register_action(|workspace, _: &OpenKeyContextView, cx| {
-            let key_context_view = cx.new_view(KeyContextView::new);
-            workspace.add_item_to_active_pane(Box::new(key_context_view), None, true, cx)
+pub fn init(cx: &mut App) {
+    cx.observe_new(|workspace: &mut Workspace, _, _| {
+        workspace.register_action(|workspace, _: &OpenKeyContextView, window, cx| {
+            let key_context_view = cx.new(|cx| KeyContextView::new(window, cx));
+            workspace.add_item_to_active_pane(Box::new(key_context_view), None, true, window, cx)
         });
     })
     .detach();
@@ -36,13 +36,13 @@ struct KeyContextView {
 }
 
 impl KeyContextView {
-    pub fn new(cx: &mut ViewContext<Self>) -> Self {
-        let sub1 = cx.observe_keystrokes(|this, e, cx| {
+    pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
+        let sub1 = cx.observe_keystrokes(|this, e, window, cx| {
             let mut pending = this.pending_keystrokes.take().unwrap_or_default();
             pending.push(e.keystroke.clone());
             let mut possibilities = cx.all_bindings_for_input(&pending);
             possibilities.reverse();
-            this.context_stack = cx.context_stack();
+            this.context_stack = window.context_stack();
             this.last_keystrokes = Some(
                 json!(pending.iter().map(|p| p.unparse()).join(" "))
                     .to_string()
@@ -86,8 +86,8 @@ impl KeyContextView {
                 })
                 .collect();
         });
-        let sub2 = cx.observe_pending_input(|this, cx| {
-            this.pending_keystrokes = cx
+        let sub2 = cx.observe_pending_input(window, |this, window, cx| {
+            this.pending_keystrokes = window
                 .pending_input_keystrokes()
                 .map(|k| k.iter().cloned().collect());
             if this.pending_keystrokes.is_some() {
@@ -109,13 +109,13 @@ impl KeyContextView {
 
 impl EventEmitter<()> for KeyContextView {}
 
-impl FocusableView for KeyContextView {
-    fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
+impl Focusable for KeyContextView {
+    fn focus_handle(&self, _: &App) -> gpui::FocusHandle {
         self.focus_handle.clone()
     }
 }
 impl KeyContextView {
-    fn set_context_stack(&mut self, stack: Vec<KeyContext>, cx: &mut ViewContext<Self>) {
+    fn set_context_stack(&mut self, stack: Vec<KeyContext>, cx: &mut Context<Self>) {
         self.context_stack = stack;
         cx.notify()
     }
@@ -145,7 +145,7 @@ impl Item for KeyContextView {
 
     fn to_item_events(_: &Self::Event, _: impl FnMut(workspace::item::ItemEvent)) {}
 
-    fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
+    fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option<SharedString> {
         Some("Keyboard Context".into())
     }
 
@@ -156,17 +156,18 @@ impl Item for KeyContextView {
     fn clone_on_split(
         &self,
         _workspace_id: Option<workspace::WorkspaceId>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<gpui::View<Self>>
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Entity<Self>>
     where
         Self: Sized,
     {
-        Some(cx.new_view(Self::new))
+        Some(cx.new(|cx| KeyContextView::new(window, cx)))
     }
 }
 
 impl Render for KeyContextView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl ui::IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement {
         use itertools::Itertools;
         let key_equivalents = get_key_equivalents(cx.keyboard_layout());
         v_flex()
@@ -180,17 +181,17 @@ impl Render for KeyContextView {
             .key_context("KeyContextView")
             .on_mouse_up_out(
                 MouseButton::Left,
-                cx.listener(|this, _, cx| {
+                cx.listener(|this, _, window, cx| {
                     this.last_keystrokes.take();
-                    this.set_context_stack(cx.context_stack(), cx);
+                    this.set_context_stack(window.context_stack(), cx);
                 }),
             )
             .on_mouse_up_out(
                 MouseButton::Right,
-                cx.listener(|_, _, cx| {
-                    cx.defer(|this, cx| {
+                cx.listener(|_, _, window, cx| {
+                    cx.defer_in(window, |this, window, cx| {
                         this.last_keystrokes.take();
-                        this.set_context_stack(cx.context_stack(), cx);
+                        this.set_context_stack(window.context_stack(), cx);
                     });
                 }),
             )
@@ -203,27 +204,27 @@ impl Render for KeyContextView {
                     .child(
                         Button::new("default", "Open Documentation")
                             .style(ButtonStyle::Filled)
-                            .on_click(|_, cx| cx.open_url("https://zed.dev/docs/key-bindings")),
+                            .on_click(|_, _, cx| cx.open_url("https://zed.dev/docs/key-bindings")),
                     )
                     .child(
                         Button::new("default", "View default keymap")
                             .style(ButtonStyle::Filled)
                             .key_binding(ui::KeyBinding::for_action(
                                 &zed_actions::OpenDefaultKeymap,
-                                cx,
+                                window,
                             ))
-                            .on_click(|_, cx| {
-                                cx.dispatch_action(workspace::SplitRight.boxed_clone());
-                                cx.dispatch_action(zed_actions::OpenDefaultKeymap.boxed_clone());
+                            .on_click(|_, window, cx| {
+                                window.dispatch_action(workspace::SplitRight.boxed_clone(), cx);
+                                window.dispatch_action(zed_actions::OpenDefaultKeymap.boxed_clone(), cx);
                             }),
                     )
                     .child(
                         Button::new("default", "Edit your keymap")
                             .style(ButtonStyle::Filled)
-                            .key_binding(ui::KeyBinding::for_action(&zed_actions::OpenKeymap, cx))
-                            .on_click(|_, cx| {
-                                cx.dispatch_action(workspace::SplitRight.boxed_clone());
-                                cx.dispatch_action(zed_actions::OpenKeymap.boxed_clone());
+                            .key_binding(ui::KeyBinding::for_action(&zed_actions::OpenKeymap, window))
+                            .on_click(|_, window, cx| {
+                                window.dispatch_action(workspace::SplitRight.boxed_clone(), cx);
+                                window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx);
                             }),
                     ),
             )
@@ -233,7 +234,7 @@ impl Render for KeyContextView {
                     .mt_8(),
             )
             .children({
-                cx.context_stack().iter().enumerate().map(|(i, context)| {
+                window.context_stack().iter().enumerate().map(|(i, context)| {
                     let primary = context.primary().map(|e| e.key.clone()).unwrap_or_default();
                     let secondary = context
                         .secondary()

crates/language_tools/src/language_tools.rs 🔗

@@ -5,12 +5,12 @@ mod syntax_tree_view;
 #[cfg(test)]
 mod lsp_log_tests;
 
-use gpui::AppContext;
+use gpui::App;
 
 pub use lsp_log::{LogStore, LspLogToolbarItemView, LspLogView};
 pub use syntax_tree_view::{SyntaxTreeToolbarItemView, SyntaxTreeView};
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     lsp_log::init(cx);
     syntax_tree_view::init(cx);
     key_context_view::init(cx);

crates/language_tools/src/lsp_log.rs 🔗

@@ -3,9 +3,8 @@ use copilot::Copilot;
 use editor::{actions::MoveToEnd, scroll::Autoscroll, Editor, EditorEvent};
 use futures::{channel::mpsc, StreamExt};
 use gpui::{
-    actions, div, AppContext, Context, Corner, EventEmitter, FocusHandle, FocusableView,
-    IntoElement, Model, ModelContext, ParentElement, Render, Styled, Subscription, View,
-    ViewContext, VisualContext, WeakModel, WindowContext,
+    actions, div, App, Context, Corner, Entity, EventEmitter, FocusHandle, Focusable, IntoElement,
+    ParentElement, Render, Styled, Subscription, WeakEntity, Window,
 };
 use language::{language_settings::SoftWrap, LanguageServerId};
 use lsp::{
@@ -26,7 +25,7 @@ const RECEIVE_LINE: &str = "// Receive:";
 const MAX_STORED_LOG_ENTRIES: usize = 2000;
 
 pub struct LogStore {
-    projects: HashMap<WeakModel<Project>, ProjectState>,
+    projects: HashMap<WeakEntity<Project>, ProjectState>,
     language_servers: HashMap<LanguageServerId, LanguageServerState>,
     copilot_log_subscription: Option<lsp::Subscription>,
     _copilot_subscription: Option<gpui::Subscription>,
@@ -113,8 +112,8 @@ struct LanguageServerState {
 
 #[derive(PartialEq, Clone)]
 pub enum LanguageServerKind {
-    Local { project: WeakModel<Project> },
-    Remote { project: WeakModel<Project> },
+    Local { project: WeakEntity<Project> },
+    Remote { project: WeakEntity<Project> },
     Global,
 }
 
@@ -135,7 +134,7 @@ impl std::fmt::Debug for LanguageServerKind {
 }
 
 impl LanguageServerKind {
-    fn project(&self) -> Option<&WeakModel<Project>> {
+    fn project(&self) -> Option<&WeakEntity<Project>> {
         match self {
             Self::Local { project } => Some(project),
             Self::Remote { project } => Some(project),
@@ -150,18 +149,18 @@ struct LanguageServerRpcState {
 }
 
 pub struct LspLogView {
-    pub(crate) editor: View<Editor>,
+    pub(crate) editor: Entity<Editor>,
     editor_subscriptions: Vec<Subscription>,
-    log_store: Model<LogStore>,
+    log_store: Entity<LogStore>,
     current_server_id: Option<LanguageServerId>,
     active_entry_kind: LogKind,
-    project: Model<Project>,
+    project: Entity<Project>,
     focus_handle: FocusHandle,
     _log_store_subscriptions: Vec<Subscription>,
 }
 
 pub struct LspLogToolbarItemView {
-    log_view: Option<View<LspLogView>>,
+    log_view: Option<Entity<LspLogView>>,
     _log_view_subscription: Option<Subscription>,
 }
 
@@ -204,10 +203,10 @@ pub(crate) struct LogMenuItem {
 
 actions!(debug, [OpenLanguageServerLogs]);
 
-pub fn init(cx: &mut AppContext) {
-    let log_store = cx.new_model(LogStore::new);
+pub fn init(cx: &mut App) {
+    let log_store = cx.new(LogStore::new);
 
-    cx.observe_new_views(move |workspace: &mut Workspace, cx| {
+    cx.observe_new(move |workspace: &mut Workspace, _, cx| {
         let project = workspace.project();
         if project.read(cx).is_local() || project.read(cx).is_via_ssh() {
             log_store.update(cx, |store, cx| {
@@ -216,14 +215,15 @@ pub fn init(cx: &mut AppContext) {
         }
 
         let log_store = log_store.clone();
-        workspace.register_action(move |workspace, _: &OpenLanguageServerLogs, cx| {
+        workspace.register_action(move |workspace, _: &OpenLanguageServerLogs, window, cx| {
             let project = workspace.project().read(cx);
             if project.is_local() || project.is_via_ssh() {
                 workspace.split_item(
                     SplitDirection::Right,
-                    Box::new(cx.new_view(|cx| {
-                        LspLogView::new(workspace.project().clone(), log_store.clone(), cx)
+                    Box::new(cx.new(|cx| {
+                        LspLogView::new(workspace.project().clone(), log_store.clone(), window, cx)
                     })),
+                    window,
                     cx,
                 );
             }
@@ -233,7 +233,7 @@ pub fn init(cx: &mut AppContext) {
 }
 
 impl LogStore {
-    pub fn new(cx: &mut ModelContext<Self>) -> Self {
+    pub fn new(cx: &mut Context<Self>) -> Self {
         let (io_tx, mut io_rx) = mpsc::unbounded();
 
         let copilot_subscription = Copilot::global(cx).map(|copilot| {
@@ -294,7 +294,7 @@ impl LogStore {
         this
     }
 
-    pub fn add_project(&mut self, project: &Model<Project>, cx: &mut ModelContext<Self>) {
+    pub fn add_project(&mut self, project: &Entity<Project>, cx: &mut Context<Self>) {
         let weak_project = project.downgrade();
         self.projects.insert(
             project.downgrade(),
@@ -367,7 +367,7 @@ impl LogStore {
         name: Option<LanguageServerName>,
         worktree_id: Option<WorktreeId>,
         server: Option<Arc<LanguageServer>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<&mut LanguageServerState> {
         let server_state = self.language_servers.entry(server_id).or_insert_with(|| {
             cx.notify();
@@ -412,7 +412,7 @@ impl LogStore {
         id: LanguageServerId,
         typ: MessageType,
         message: &str,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<()> {
         let language_server_state = self.get_language_server_state(id)?;
 
@@ -435,7 +435,7 @@ impl LogStore {
         &mut self,
         id: LanguageServerId,
         message: &str,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<()> {
         let language_server_state = self.get_language_server_state(id)?;
 
@@ -459,7 +459,7 @@ impl LogStore {
         message: T,
         current_severity: <T as Message>::Level,
         kind: LogKind,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         while log_lines.len() >= MAX_STORED_LOG_ENTRIES {
             log_lines.pop_front();
@@ -475,7 +475,7 @@ impl LogStore {
         }
     }
 
-    fn remove_language_server(&mut self, id: LanguageServerId, cx: &mut ModelContext<Self>) {
+    fn remove_language_server(&mut self, id: LanguageServerId, cx: &mut Context<Self>) {
         self.language_servers.remove(&id);
         cx.notify();
     }
@@ -490,7 +490,7 @@ impl LogStore {
 
     fn server_ids_for_project<'a>(
         &'a self,
-        lookup_project: &'a WeakModel<Project>,
+        lookup_project: &'a WeakEntity<Project>,
     ) -> impl Iterator<Item = LanguageServerId> + 'a {
         self.language_servers
             .iter()
@@ -534,7 +534,7 @@ impl LogStore {
         language_server_id: LanguageServerId,
         io_kind: IoKind,
         message: &str,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<()> {
         let is_received = match io_kind {
             IoKind::StdOut => true,
@@ -591,9 +591,10 @@ impl LogStore {
 
 impl LspLogView {
     pub fn new(
-        project: Model<Project>,
-        log_store: Model<LogStore>,
-        cx: &mut ViewContext<Self>,
+        project: Entity<Project>,
+        log_store: Entity<LogStore>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let server_id = log_store
             .read(cx)
@@ -603,76 +604,78 @@ impl LspLogView {
             .map(|(id, _)| *id);
 
         let weak_project = project.downgrade();
-        let model_changes_subscription = cx.observe(&log_store, move |this, store, cx| {
-            let first_server_id_for_project =
-                store.read(cx).server_ids_for_project(&weak_project).next();
-            if let Some(current_lsp) = this.current_server_id {
-                if !store.read(cx).language_servers.contains_key(&current_lsp) {
-                    if let Some(server_id) = first_server_id_for_project {
-                        match this.active_entry_kind {
-                            LogKind::Rpc => this.show_rpc_trace_for_server(server_id, cx),
-                            LogKind::Trace => this.show_trace_for_server(server_id, cx),
-                            LogKind::Logs => this.show_logs_for_server(server_id, cx),
-                            LogKind::ServerInfo => this.show_server_info(server_id, cx),
+        let model_changes_subscription =
+            cx.observe_in(&log_store, window, move |this, store, window, cx| {
+                let first_server_id_for_project =
+                    store.read(cx).server_ids_for_project(&weak_project).next();
+                if let Some(current_lsp) = this.current_server_id {
+                    if !store.read(cx).language_servers.contains_key(&current_lsp) {
+                        if let Some(server_id) = first_server_id_for_project {
+                            match this.active_entry_kind {
+                                LogKind::Rpc => {
+                                    this.show_rpc_trace_for_server(server_id, window, cx)
+                                }
+                                LogKind::Trace => this.show_trace_for_server(server_id, window, cx),
+                                LogKind::Logs => this.show_logs_for_server(server_id, window, cx),
+                                LogKind::ServerInfo => this.show_server_info(server_id, window, cx),
+                            }
                         }
-                    } else {
-                        this.current_server_id = None;
-                        this.editor.update(cx, |editor, cx| {
-                            editor.set_read_only(false);
-                            editor.clear(cx);
-                            editor.set_read_only(true);
-                        });
-                        cx.notify();
+                    }
+                } else if let Some(server_id) = first_server_id_for_project {
+                    match this.active_entry_kind {
+                        LogKind::Rpc => this.show_rpc_trace_for_server(server_id, window, cx),
+                        LogKind::Trace => this.show_trace_for_server(server_id, window, cx),
+                        LogKind::Logs => this.show_logs_for_server(server_id, window, cx),
+                        LogKind::ServerInfo => this.show_server_info(server_id, window, cx),
                     }
                 }
-            } else if let Some(server_id) = first_server_id_for_project {
-                match this.active_entry_kind {
-                    LogKind::Rpc => this.show_rpc_trace_for_server(server_id, cx),
-                    LogKind::Trace => this.show_trace_for_server(server_id, cx),
-                    LogKind::Logs => this.show_logs_for_server(server_id, cx),
-                    LogKind::ServerInfo => this.show_server_info(server_id, cx),
-                }
-            }
 
-            cx.notify();
-        });
-        let events_subscriptions = cx.subscribe(&log_store, |log_view, _, e, cx| match e {
-            Event::NewServerLogEntry { id, entry, kind } => {
-                if log_view.current_server_id == Some(*id) && *kind == log_view.active_entry_kind {
-                    log_view.editor.update(cx, |editor, cx| {
-                        editor.set_read_only(false);
-                        let last_point = editor.buffer().read(cx).len(cx);
-                        let newest_cursor_is_at_end =
-                            editor.selections.newest::<usize>(cx).start >= last_point;
-                        editor.edit(
-                            vec![
-                                (last_point..last_point, entry.trim()),
-                                (last_point..last_point, "\n"),
-                            ],
-                            cx,
-                        );
-                        let entry_length = entry.len();
-                        if entry_length > 1024 {
-                            editor.fold_ranges(
-                                vec![last_point + 1024..last_point + entry_length],
-                                false,
+                cx.notify();
+            });
+        let events_subscriptions = cx.subscribe_in(
+            &log_store,
+            window,
+            move |log_view, _, e, window, cx| match e {
+                Event::NewServerLogEntry { id, entry, kind } => {
+                    if log_view.current_server_id == Some(*id)
+                        && *kind == log_view.active_entry_kind
+                    {
+                        log_view.editor.update(cx, |editor, cx| {
+                            editor.set_read_only(false);
+                            let last_point = editor.buffer().read(cx).len(cx);
+                            let newest_cursor_is_at_end =
+                                editor.selections.newest::<usize>(cx).start >= last_point;
+                            editor.edit(
+                                vec![
+                                    (last_point..last_point, entry.trim()),
+                                    (last_point..last_point, "\n"),
+                                ],
                                 cx,
                             );
-                        }
+                            let entry_length = entry.len();
+                            if entry_length > 1024 {
+                                editor.fold_ranges(
+                                    vec![last_point + 1024..last_point + entry_length],
+                                    false,
+                                    window,
+                                    cx,
+                                );
+                            }
 
-                        if newest_cursor_is_at_end {
-                            editor.request_autoscroll(Autoscroll::bottom(), cx);
-                        }
-                        editor.set_read_only(true);
-                    });
+                            if newest_cursor_is_at_end {
+                                editor.request_autoscroll(Autoscroll::bottom(), cx);
+                            }
+                            editor.set_read_only(true);
+                        });
+                    }
                 }
-            }
-        });
-        let (editor, editor_subscriptions) = Self::editor_for_logs(String::new(), cx);
+            },
+        );
+        let (editor, editor_subscriptions) = Self::editor_for_logs(String::new(), window, cx);
 
         let focus_handle = cx.focus_handle();
-        let focus_subscription = cx.on_focus(&focus_handle, |log_view, cx| {
-            cx.focus_view(&log_view.editor);
+        let focus_subscription = cx.on_focus(&focus_handle, window, |log_view, window, cx| {
+            window.focus(&log_view.editor.focus_handle(cx));
         });
 
         let mut this = Self {
@@ -690,41 +693,43 @@ impl LspLogView {
             ],
         };
         if let Some(server_id) = server_id {
-            this.show_logs_for_server(server_id, cx);
+            this.show_logs_for_server(server_id, window, cx);
         }
         this
     }
 
     fn editor_for_logs(
         log_contents: String,
-        cx: &mut ViewContext<Self>,
-    ) -> (View<Editor>, Vec<Subscription>) {
-        let editor = cx.new_view(|cx| {
-            let mut editor = Editor::multi_line(cx);
-            editor.set_text(log_contents, cx);
-            editor.move_to_end(&MoveToEnd, cx);
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> (Entity<Editor>, Vec<Subscription>) {
+        let editor = cx.new(|cx| {
+            let mut editor = Editor::multi_line(window, cx);
+            editor.set_text(log_contents, window, cx);
+            editor.move_to_end(&MoveToEnd, window, cx);
             editor.set_read_only(true);
-            editor.set_show_inline_completions(Some(false), cx);
+            editor.set_show_inline_completions(Some(false), window, cx);
             editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
             editor
         });
         let editor_subscription = cx.subscribe(
             &editor,
-            |_, _, event: &EditorEvent, cx: &mut ViewContext<LspLogView>| cx.emit(event.clone()),
+            |_, _, event: &EditorEvent, cx: &mut Context<LspLogView>| cx.emit(event.clone()),
         );
         let search_subscription = cx.subscribe(
             &editor,
-            |_, _, event: &SearchEvent, cx: &mut ViewContext<LspLogView>| cx.emit(event.clone()),
+            |_, _, event: &SearchEvent, cx: &mut Context<LspLogView>| cx.emit(event.clone()),
         );
         (editor, vec![editor_subscription, search_subscription])
     }
 
     fn editor_for_server_info(
         server: &LanguageServer,
-        cx: &mut ViewContext<Self>,
-    ) -> (View<Editor>, Vec<Subscription>) {
-        let editor = cx.new_view(|cx| {
-            let mut editor = Editor::multi_line(cx);
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> (Entity<Editor>, Vec<Subscription>) {
+        let editor = cx.new(|cx| {
+            let mut editor = Editor::multi_line(window, cx);
             let server_info = format!(
                 "* Server: {NAME} (id {ID})
 
@@ -744,24 +749,24 @@ impl LspLogView {
                 CONFIGURATION = serde_json::to_string_pretty(server.configuration())
                     .unwrap_or_else(|e| format!("Failed to serialize configuration: {e}")),
             );
-            editor.set_text(server_info, cx);
+            editor.set_text(server_info, window, cx);
             editor.set_read_only(true);
-            editor.set_show_inline_completions(Some(false), cx);
+            editor.set_show_inline_completions(Some(false), window, cx);
             editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
             editor
         });
         let editor_subscription = cx.subscribe(
             &editor,
-            |_, _, event: &EditorEvent, cx: &mut ViewContext<LspLogView>| cx.emit(event.clone()),
+            |_, _, event: &EditorEvent, cx: &mut Context<LspLogView>| cx.emit(event.clone()),
         );
         let search_subscription = cx.subscribe(
             &editor,
-            |_, _, event: &SearchEvent, cx: &mut ViewContext<LspLogView>| cx.emit(event.clone()),
+            |_, _, event: &SearchEvent, cx: &mut Context<LspLogView>| cx.emit(event.clone()),
         );
         (editor, vec![editor_subscription, search_subscription])
     }
 
-    pub(crate) fn menu_items<'a>(&'a self, cx: &'a AppContext) -> Option<Vec<LogMenuItem>> {
+    pub(crate) fn menu_items<'a>(&'a self, cx: &'a App) -> Option<Vec<LogMenuItem>> {
         let log_store = self.log_store.read(cx);
 
         let unknown_server = LanguageServerName::new_static("unknown server");
@@ -821,7 +826,12 @@ impl LspLogView {
         Some(rows)
     }
 
-    fn show_logs_for_server(&mut self, server_id: LanguageServerId, cx: &mut ViewContext<Self>) {
+    fn show_logs_for_server(
+        &mut self,
+        server_id: LanguageServerId,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let typ = self
             .log_store
             .read_with(cx, |v, _| {
@@ -836,19 +846,20 @@ impl LspLogView {
         if let Some(log_contents) = log_contents {
             self.current_server_id = Some(server_id);
             self.active_entry_kind = LogKind::Logs;
-            let (editor, editor_subscriptions) = Self::editor_for_logs(log_contents, cx);
+            let (editor, editor_subscriptions) = Self::editor_for_logs(log_contents, window, cx);
             self.editor = editor;
             self.editor_subscriptions = editor_subscriptions;
             cx.notify();
         }
-        cx.focus(&self.focus_handle);
+        window.focus(&self.focus_handle);
     }
 
     fn update_log_level(
         &self,
         server_id: LanguageServerId,
         level: MessageType,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let log_contents = self.log_store.update(cx, |this, _| {
             if let Some(state) = this.get_language_server_state(server_id) {
@@ -859,17 +870,22 @@ impl LspLogView {
         });
 
         if let Some(log_contents) = log_contents {
-            self.editor.update(cx, move |editor, cx| {
-                editor.set_text(log_contents, cx);
-                editor.move_to_end(&MoveToEnd, cx);
+            self.editor.update(cx, |editor, cx| {
+                editor.set_text(log_contents, window, cx);
+                editor.move_to_end(&MoveToEnd, window, cx);
             });
             cx.notify();
         }
 
-        cx.focus(&self.focus_handle);
+        window.focus(&self.focus_handle);
     }
 
-    fn show_trace_for_server(&mut self, server_id: LanguageServerId, cx: &mut ViewContext<Self>) {
+    fn show_trace_for_server(
+        &mut self,
+        server_id: LanguageServerId,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let log_contents = self
             .log_store
             .read(cx)
@@ -878,18 +894,19 @@ impl LspLogView {
         if let Some(log_contents) = log_contents {
             self.current_server_id = Some(server_id);
             self.active_entry_kind = LogKind::Trace;
-            let (editor, editor_subscriptions) = Self::editor_for_logs(log_contents, cx);
+            let (editor, editor_subscriptions) = Self::editor_for_logs(log_contents, window, cx);
             self.editor = editor;
             self.editor_subscriptions = editor_subscriptions;
             cx.notify();
         }
-        cx.focus(&self.focus_handle);
+        window.focus(&self.focus_handle);
     }
 
     fn show_rpc_trace_for_server(
         &mut self,
         server_id: LanguageServerId,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let rpc_log = self.log_store.update(cx, |log_store, _| {
             log_store
@@ -899,7 +916,7 @@ impl LspLogView {
         if let Some(rpc_log) = rpc_log {
             self.current_server_id = Some(server_id);
             self.active_entry_kind = LogKind::Rpc;
-            let (editor, editor_subscriptions) = Self::editor_for_logs(rpc_log, cx);
+            let (editor, editor_subscriptions) = Self::editor_for_logs(rpc_log, window, cx);
             let language = self.project.read(cx).languages().language_for_name("JSON");
             editor
                 .read(cx)
@@ -909,7 +926,7 @@ impl LspLogView {
                 .expect("log buffer should be a singleton")
                 .update(cx, |_, cx| {
                     cx.spawn({
-                        let buffer = cx.handle();
+                        let buffer = cx.model();
                         |_, mut cx| async move {
                             let language = language.await.ok();
                             buffer.update(&mut cx, |buffer, cx| {
@@ -925,14 +942,15 @@ impl LspLogView {
             cx.notify();
         }
 
-        cx.focus(&self.focus_handle);
+        window.focus(&self.focus_handle);
     }
 
     fn toggle_rpc_trace_for_server(
         &mut self,
         server_id: LanguageServerId,
         enabled: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.log_store.update(cx, |log_store, _| {
             if enabled {
@@ -942,7 +960,7 @@ impl LspLogView {
             }
         });
         if !enabled && Some(server_id) == self.current_server_id {
-            self.show_logs_for_server(server_id, cx);
+            self.show_logs_for_server(server_id, window, cx);
             cx.notify();
         }
     }
@@ -951,7 +969,7 @@ impl LspLogView {
         &self,
         server_id: LanguageServerId,
         level: TraceValue,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if let Some(server) = self
             .project
@@ -972,18 +990,23 @@ impl LspLogView {
         }
     }
 
-    fn show_server_info(&mut self, server_id: LanguageServerId, cx: &mut ViewContext<Self>) {
+    fn show_server_info(
+        &mut self,
+        server_id: LanguageServerId,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let lsp_store = self.project.read(cx).lsp_store();
         let Some(server) = lsp_store.read(cx).language_server_for_id(server_id) else {
             return;
         };
         self.current_server_id = Some(server_id);
         self.active_entry_kind = LogKind::ServerInfo;
-        let (editor, editor_subscriptions) = Self::editor_for_server_info(&server, cx);
+        let (editor, editor_subscriptions) = Self::editor_for_server_info(&server, window, cx);
         self.editor = editor;
         self.editor_subscriptions = editor_subscriptions;
         cx.notify();
-        cx.focus(&self.focus_handle);
+        window.focus(&self.focus_handle);
     }
 }
 
@@ -1007,14 +1030,15 @@ fn log_contents<T: Message>(lines: &VecDeque<T>, cmp: <T as Message>::Level) ->
 }
 
 impl Render for LspLogView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        self.editor
-            .update(cx, |editor, cx| editor.render(cx).into_any_element())
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+        self.editor.update(cx, |editor, cx| {
+            editor.render(window, cx).into_any_element()
+        })
     }
 }
 
-impl FocusableView for LspLogView {
-    fn focus_handle(&self, _: &AppContext) -> FocusHandle {
+impl Focusable for LspLogView {
+    fn focus_handle(&self, _: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -1026,7 +1050,7 @@ impl Item for LspLogView {
         Editor::to_item_events(event, f)
     }
 
-    fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
+    fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option<SharedString> {
         Some("LSP Logs".into())
     }
 
@@ -1034,26 +1058,27 @@ impl Item for LspLogView {
         None
     }
 
-    fn as_searchable(&self, handle: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
+    fn as_searchable(&self, handle: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {
         Some(Box::new(handle.clone()))
     }
 
     fn clone_on_split(
         &self,
         _workspace_id: Option<WorkspaceId>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<View<Self>>
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Entity<Self>>
     where
         Self: Sized,
     {
-        Some(cx.new_view(|cx| {
-            let mut new_view = Self::new(self.project.clone(), self.log_store.clone(), cx);
+        Some(cx.new(|cx| {
+            let mut new_view = Self::new(self.project.clone(), self.log_store.clone(), window, cx);
             if let Some(server_id) = self.current_server_id {
                 match self.active_entry_kind {
-                    LogKind::Rpc => new_view.show_rpc_trace_for_server(server_id, cx),
-                    LogKind::Trace => new_view.show_trace_for_server(server_id, cx),
-                    LogKind::Logs => new_view.show_logs_for_server(server_id, cx),
-                    LogKind::ServerInfo => new_view.show_server_info(server_id, cx),
+                    LogKind::Rpc => new_view.show_rpc_trace_for_server(server_id, window, cx),
+                    LogKind::Trace => new_view.show_trace_for_server(server_id, window, cx),
+                    LogKind::Logs => new_view.show_logs_for_server(server_id, window, cx),
+                    LogKind::ServerInfo => new_view.show_server_info(server_id, window, cx),
                 }
             }
             new_view
@@ -1064,43 +1089,63 @@ impl Item for LspLogView {
 impl SearchableItem for LspLogView {
     type Match = <Editor as SearchableItem>::Match;
 
-    fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
-        self.editor.update(cx, |e, cx| e.clear_matches(cx))
+    fn clear_matches(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        self.editor.update(cx, |e, cx| e.clear_matches(window, cx))
     }
 
-    fn update_matches(&mut self, matches: &[Self::Match], cx: &mut ViewContext<Self>) {
+    fn update_matches(
+        &mut self,
+        matches: &[Self::Match],
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.editor
-            .update(cx, |e, cx| e.update_matches(matches, cx))
+            .update(cx, |e, cx| e.update_matches(matches, window, cx))
     }
 
-    fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
-        self.editor.update(cx, |e, cx| e.query_suggestion(cx))
+    fn query_suggestion(&mut self, window: &mut Window, cx: &mut Context<Self>) -> String {
+        self.editor
+            .update(cx, |e, cx| e.query_suggestion(window, cx))
     }
 
     fn activate_match(
         &mut self,
         index: usize,
         matches: &[Self::Match],
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.editor
-            .update(cx, |e, cx| e.activate_match(index, matches, cx))
+            .update(cx, |e, cx| e.activate_match(index, matches, window, cx))
     }
 
-    fn select_matches(&mut self, matches: &[Self::Match], cx: &mut ViewContext<Self>) {
+    fn select_matches(
+        &mut self,
+        matches: &[Self::Match],
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.editor
-            .update(cx, |e, cx| e.select_matches(matches, cx))
+            .update(cx, |e, cx| e.select_matches(matches, window, cx))
     }
 
     fn find_matches(
         &mut self,
         query: Arc<project::search::SearchQuery>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> gpui::Task<Vec<Self::Match>> {
-        self.editor.update(cx, |e, cx| e.find_matches(query, cx))
+        self.editor
+            .update(cx, |e, cx| e.find_matches(query, window, cx))
     }
 
-    fn replace(&mut self, _: &Self::Match, _: &SearchQuery, _: &mut ViewContext<Self>) {
+    fn replace(
+        &mut self,
+        _: &Self::Match,
+        _: &SearchQuery,
+        _window: &mut Window,
+        _: &mut Context<Self>,
+    ) {
         // Since LSP Log is read-only, it doesn't make sense to support replace operation.
     }
     fn supported_options() -> workspace::searchable::SearchOptions {
@@ -1116,10 +1161,11 @@ impl SearchableItem for LspLogView {
     fn active_match_index(
         &mut self,
         matches: &[Self::Match],
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<usize> {
         self.editor
-            .update(cx, |e, cx| e.active_match_index(matches, cx))
+            .update(cx, |e, cx| e.active_match_index(matches, window, cx))
     }
 }
 
@@ -1129,7 +1175,8 @@ impl ToolbarItemView for LspLogToolbarItemView {
     fn set_active_pane_item(
         &mut self,
         active_pane_item: Option<&dyn ItemHandle>,
-        cx: &mut ViewContext<Self>,
+        _: &mut Window,
+        cx: &mut Context<Self>,
     ) -> workspace::ToolbarItemLocation {
         if let Some(item) = active_pane_item {
             if let Some(log_view) = item.downcast::<LspLogView>() {
@@ -1147,7 +1194,7 @@ impl ToolbarItemView for LspLogToolbarItemView {
 }
 
 impl Render for LspLogToolbarItemView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let Some(log_view) = self.log_view.clone() else {
             return div();
         };
@@ -1175,7 +1222,7 @@ impl Render for LspLogToolbarItemView {
                 )
             })
             .collect();
-        let log_toolbar_view = cx.view().clone();
+        let log_toolbar_view = cx.model().clone();
         let lsp_menu = PopoverMenu::new("LspLogView")
             .anchor(Corner::TopLeft)
             .trigger(Button::new(
@@ -1192,9 +1239,9 @@ impl Render for LspLogToolbarItemView {
             ))
             .menu({
                 let log_view = log_view.clone();
-                move |cx| {
+                move |window, cx| {
                     let log_view = log_view.clone();
-                    ContextMenu::build(cx, |mut menu, cx| {
+                    ContextMenu::build(window, cx, |mut menu, window, _| {
                         for (server_id, name, worktree_root, active_entry_kind) in
                             available_language_servers.iter()
                         {
@@ -1204,17 +1251,25 @@ impl Render for LspLogToolbarItemView {
                             menu = menu.entry(
                                 label,
                                 None,
-                                cx.handler_for(&log_view, move |view, cx| {
+                                window.handler_for(&log_view, move |view, window, cx| {
                                     view.current_server_id = Some(server_id);
                                     view.active_entry_kind = active_entry_kind;
                                     match view.active_entry_kind {
                                         LogKind::Rpc => {
-                                            view.toggle_rpc_trace_for_server(server_id, true, cx);
-                                            view.show_rpc_trace_for_server(server_id, cx);
+                                            view.toggle_rpc_trace_for_server(
+                                                server_id, true, window, cx,
+                                            );
+                                            view.show_rpc_trace_for_server(server_id, window, cx);
+                                        }
+                                        LogKind::Trace => {
+                                            view.show_trace_for_server(server_id, window, cx)
+                                        }
+                                        LogKind::Logs => {
+                                            view.show_logs_for_server(server_id, window, cx)
+                                        }
+                                        LogKind::ServerInfo => {
+                                            view.show_server_info(server_id, window, cx)
                                         }
-                                        LogKind::Trace => view.show_trace_for_server(server_id, cx),
-                                        LogKind::Logs => view.show_logs_for_server(server_id, cx),
-                                        LogKind::ServerInfo => view.show_server_info(server_id, cx),
                                     }
                                     cx.notify();
                                 }),
@@ -1236,29 +1291,29 @@ impl Render for LspLogToolbarItemView {
                     "language_server_menu_header",
                     server.selected_entry.label(),
                 ))
-                .menu(move |cx| {
+                .menu(move |window, cx| {
                     let log_toolbar_view = log_toolbar_view.clone();
                     let log_view = log_view.clone();
-                    Some(ContextMenu::build(cx, move |this, cx| {
+                    Some(ContextMenu::build(window, cx, move |this, window, _| {
                         this.entry(
                             SERVER_LOGS,
                             None,
-                            cx.handler_for(&log_view, move |view, cx| {
-                                view.show_logs_for_server(server_id, cx);
+                            window.handler_for(&log_view, move |view, window, cx| {
+                                view.show_logs_for_server(server_id, window, cx);
                             }),
                         )
                         .when(!is_remote, |this| {
                             this.entry(
                                 SERVER_TRACE,
                                 None,
-                                cx.handler_for(&log_view, move |view, cx| {
-                                    view.show_trace_for_server(server_id, cx);
+                                window.handler_for(&log_view, move |view, window, cx| {
+                                    view.show_trace_for_server(server_id, window, cx);
                                 }),
                             )
                             .custom_entry(
                                 {
                                     let log_toolbar_view = log_toolbar_view.clone();
-                                    move |cx| {
+                                    move |window, _| {
                                         h_flex()
                                             .w_full()
                                             .justify_between()
@@ -1273,15 +1328,15 @@ impl Render for LspLogToolbarItemView {
                                                             ToggleState::Unselected
                                                         },
                                                     )
-                                                    .on_click(cx.listener_for(
+                                                    .on_click(window.listener_for(
                                                         &log_toolbar_view,
-                                                        move |view, selection, cx| {
+                                                        move |view, selection, window, cx| {
                                                             let enabled = matches!(
                                                                 selection,
                                                                 ToggleState::Selected
                                                             );
                                                             view.toggle_rpc_logging_for_server(
-                                                                server_id, enabled, cx,
+                                                                server_id, enabled, window, cx,
                                                             );
                                                             cx.stop_propagation();
                                                         },
@@ -1291,16 +1346,16 @@ impl Render for LspLogToolbarItemView {
                                             .into_any_element()
                                     }
                                 },
-                                cx.handler_for(&log_view, move |view, cx| {
-                                    view.show_rpc_trace_for_server(server_id, cx);
+                                window.handler_for(&log_view, move |view, window, cx| {
+                                    view.show_rpc_trace_for_server(server_id, window, cx);
                                 }),
                             )
                         })
                         .entry(
                             SERVER_INFO,
                             None,
-                            cx.handler_for(&log_view, move |view, cx| {
-                                view.show_server_info(server_id, cx);
+                            window.handler_for(&log_view, move |view, window, cx| {
+                                view.show_server_info(server_id, window, cx);
                             }),
                         )
                     }))
@@ -1313,137 +1368,141 @@ impl Render for LspLogToolbarItemView {
                 h_flex()
                     .child(lsp_menu)
                     .children(view_selector)
-                    .child(log_view.update(cx, |this, _| match this.active_entry_kind {
-                        LogKind::Trace => {
-                            let log_view = log_view.clone();
-                            div().child(
-                                PopoverMenu::new("lsp-trace-level-menu")
-                                    .anchor(Corner::TopLeft)
-                                    .trigger(Button::new(
-                                        "language_server_trace_level_selector",
-                                        "Trace level",
-                                    ))
-                                    .menu({
-                                        let log_view = log_view.clone();
-
-                                        move |cx| {
-                                            let id = log_view.read(cx).current_server_id?;
-
-                                            let trace_level = log_view.update(cx, |this, cx| {
-                                                this.log_store.update(cx, |this, _| {
-                                                    Some(
-                                                        this.get_language_server_state(id)?
-                                                            .trace_level,
-                                                    )
-                                                })
-                                            })?;
-
-                                            ContextMenu::build(cx, |mut menu, _| {
-                                                let log_view = log_view.clone();
-
-                                                for (option, label) in [
-                                                    (TraceValue::Off, "Off"),
-                                                    (TraceValue::Messages, "Messages"),
-                                                    (TraceValue::Verbose, "Verbose"),
-                                                ] {
-                                                    menu = menu.entry(label, None, {
-                                                        let log_view = log_view.clone();
-                                                        move |cx| {
-                                                            log_view.update(cx, |this, cx| {
-                                                                if let Some(id) =
-                                                                    this.current_server_id
-                                                                {
-                                                                    this.update_trace_level(
-                                                                        id, option, cx,
-                                                                    );
-                                                                }
-                                                            });
+                    .child(
+                        log_view.update(cx, |this, _cx| match this.active_entry_kind {
+                            LogKind::Trace => {
+                                let log_view = log_view.clone();
+                                div().child(
+                                    PopoverMenu::new("lsp-trace-level-menu")
+                                        .anchor(Corner::TopLeft)
+                                        .trigger(Button::new(
+                                            "language_server_trace_level_selector",
+                                            "Trace level",
+                                        ))
+                                        .menu({
+                                            let log_view = log_view.clone();
+
+                                            move |window, cx| {
+                                                let id = log_view.read(cx).current_server_id?;
+
+                                                let trace_level =
+                                                    log_view.update(cx, |this, cx| {
+                                                        this.log_store.update(cx, |this, _| {
+                                                            Some(
+                                                                this.get_language_server_state(id)?
+                                                                    .trace_level,
+                                                            )
+                                                        })
+                                                    })?;
+
+                                                ContextMenu::build(window, cx, |mut menu, _, _| {
+                                                    let log_view = log_view.clone();
+
+                                                    for (option, label) in [
+                                                        (TraceValue::Off, "Off"),
+                                                        (TraceValue::Messages, "Messages"),
+                                                        (TraceValue::Verbose, "Verbose"),
+                                                    ] {
+                                                        menu = menu.entry(label, None, {
+                                                            let log_view = log_view.clone();
+                                                            move |_, cx| {
+                                                                log_view.update(cx, |this, cx| {
+                                                                    if let Some(id) =
+                                                                        this.current_server_id
+                                                                    {
+                                                                        this.update_trace_level(
+                                                                            id, option, cx,
+                                                                        );
+                                                                    }
+                                                                });
+                                                            }
+                                                        });
+                                                        if option == trace_level {
+                                                            menu.select_last();
                                                         }
-                                                    });
-                                                    if option == trace_level {
-                                                        menu.select_last();
                                                     }
-                                                }
 
-                                                menu
-                                            })
-                                            .into()
-                                        }
-                                    }),
-                            )
-                        }
-                        LogKind::Logs => {
-                            let log_view = log_view.clone();
-                            div().child(
-                                PopoverMenu::new("lsp-log-level-menu")
-                                    .anchor(Corner::TopLeft)
-                                    .trigger(Button::new(
-                                        "language_server_log_level_selector",
-                                        "Log level",
-                                    ))
-                                    .menu({
-                                        let log_view = log_view.clone();
-
-                                        move |cx| {
-                                            let id = log_view.read(cx).current_server_id?;
-
-                                            let log_level = log_view.update(cx, |this, cx| {
-                                                this.log_store.update(cx, |this, _| {
-                                                    Some(
-                                                        this.get_language_server_state(id)?
-                                                            .log_level,
-                                                    )
+                                                    menu
                                                 })
-                                            })?;
-
-                                            ContextMenu::build(cx, |mut menu, _| {
-                                                let log_view = log_view.clone();
-
-                                                for (option, label) in [
-                                                    (MessageType::LOG, "Log"),
-                                                    (MessageType::INFO, "Info"),
-                                                    (MessageType::WARNING, "Warning"),
-                                                    (MessageType::ERROR, "Error"),
-                                                ] {
-                                                    menu = menu.entry(label, None, {
-                                                        let log_view = log_view.clone();
-                                                        move |cx| {
-                                                            log_view.update(cx, |this, cx| {
-                                                                if let Some(id) =
-                                                                    this.current_server_id
-                                                                {
-                                                                    this.update_log_level(
-                                                                        id, option, cx,
-                                                                    );
-                                                                }
-                                                            });
+                                                .into()
+                                            }
+                                        }),
+                                )
+                            }
+                            LogKind::Logs => {
+                                let log_view = log_view.clone();
+                                div().child(
+                                    PopoverMenu::new("lsp-log-level-menu")
+                                        .anchor(Corner::TopLeft)
+                                        .trigger(Button::new(
+                                            "language_server_log_level_selector",
+                                            "Log level",
+                                        ))
+                                        .menu({
+                                            let log_view = log_view.clone();
+
+                                            move |window, cx| {
+                                                let id = log_view.read(cx).current_server_id?;
+
+                                                let log_level =
+                                                    log_view.update(cx, |this, cx| {
+                                                        this.log_store.update(cx, |this, _| {
+                                                            Some(
+                                                                this.get_language_server_state(id)?
+                                                                    .log_level,
+                                                            )
+                                                        })
+                                                    })?;
+
+                                                ContextMenu::build(window, cx, |mut menu, _, _| {
+                                                    let log_view = log_view.clone();
+
+                                                    for (option, label) in [
+                                                        (MessageType::LOG, "Log"),
+                                                        (MessageType::INFO, "Info"),
+                                                        (MessageType::WARNING, "Warning"),
+                                                        (MessageType::ERROR, "Error"),
+                                                    ] {
+                                                        menu = menu.entry(label, None, {
+                                                            let log_view = log_view.clone();
+                                                            move |window, cx| {
+                                                                log_view.update(cx, |this, cx| {
+                                                                    if let Some(id) =
+                                                                        this.current_server_id
+                                                                    {
+                                                                        this.update_log_level(
+                                                                            id, option, window, cx,
+                                                                        );
+                                                                    }
+                                                                });
+                                                            }
+                                                        });
+                                                        if option == log_level {
+                                                            menu.select_last();
                                                         }
-                                                    });
-                                                    if option == log_level {
-                                                        menu.select_last();
                                                     }
-                                                }
 
-                                                menu
-                                            })
-                                            .into()
-                                        }
-                                    }),
-                            )
-                        }
-                        _ => div(),
-                    })),
+                                                    menu
+                                                })
+                                                .into()
+                                            }
+                                        }),
+                                )
+                            }
+                            _ => div(),
+                        }),
+                    ),
             )
             .child(
                 div()
                     .child(
                         Button::new("clear_log_button", "Clear").on_click(cx.listener(
-                            |this, _, cx| {
+                            |this, _, window, cx| {
                                 if let Some(log_view) = this.log_view.as_ref() {
                                     log_view.update(cx, |log_view, cx| {
                                         log_view.editor.update(cx, |editor, cx| {
                                             editor.set_read_only(false);
-                                            editor.clear(cx);
+                                            editor.clear(window, cx);
                                             editor.set_read_only(true);
                                         });
                                     })

crates/language_tools/src/lsp_log_tests.rs 🔗

@@ -4,7 +4,7 @@ use crate::lsp_log::LogMenuItem;
 
 use super::*;
 use futures::StreamExt;
-use gpui::{Context, SemanticVersion, TestAppContext, VisualTestContext};
+use gpui::{AppContext as _, SemanticVersion, TestAppContext, VisualTestContext};
 use language::{tree_sitter_rust, FakeLspAdapter, Language, LanguageConfig, LanguageMatcher};
 use lsp::LanguageServerName;
 use lsp_log::LogKind;
@@ -52,7 +52,7 @@ async fn test_lsp_logs(cx: &mut TestAppContext) {
         },
     );
 
-    let log_store = cx.new_model(LogStore::new);
+    let log_store = cx.new(LogStore::new);
     log_store.update(cx, |store, cx| store.add_project(&project, cx));
 
     let _rust_buffer = project
@@ -67,7 +67,8 @@ async fn test_lsp_logs(cx: &mut TestAppContext) {
         .receive_notification::<lsp::notification::DidOpenTextDocument>()
         .await;
 
-    let window = cx.add_window(|cx| LspLogView::new(project.clone(), log_store.clone(), cx));
+    let window =
+        cx.add_window(|window, cx| LspLogView::new(project.clone(), log_store.clone(), window, cx));
     let log_view = window.root(cx).unwrap();
     let mut cx = VisualTestContext::from_window(*window, cx);
 

crates/language_tools/src/syntax_tree_view.rs 🔗

@@ -1,9 +1,9 @@
 use editor::{scroll::Autoscroll, Anchor, Editor, ExcerptId};
 use gpui::{
-    actions, div, rems, uniform_list, AppContext, Div, EventEmitter, FocusHandle, FocusableView,
-    Hsla, InteractiveElement, IntoElement, Model, MouseButton, MouseDownEvent, MouseMoveEvent,
-    ParentElement, Render, ScrollStrategy, SharedString, Styled, UniformListScrollHandle, View,
-    ViewContext, VisualContext, WeakView, WindowContext,
+    actions, div, rems, uniform_list, App, AppContext as _, Context, Div, Entity, EventEmitter,
+    FocusHandle, Focusable, Hsla, InteractiveElement, IntoElement, MouseButton, MouseDownEvent,
+    MouseMoveEvent, ParentElement, Render, ScrollStrategy, SharedString, Styled,
+    UniformListScrollHandle, WeakEntity, Window,
 };
 use language::{Buffer, OwnedSyntaxLayer};
 use std::{mem, ops::Range};
@@ -17,21 +17,26 @@ use workspace::{
 
 actions!(debug, [OpenSyntaxTreeView]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(|workspace: &mut Workspace, _| {
-        workspace.register_action(|workspace, _: &OpenSyntaxTreeView, cx| {
+pub fn init(cx: &mut App) {
+    cx.observe_new(|workspace: &mut Workspace, _, _| {
+        workspace.register_action(|workspace, _: &OpenSyntaxTreeView, window, cx| {
             let active_item = workspace.active_item(cx);
             let workspace_handle = workspace.weak_handle();
             let syntax_tree_view =
-                cx.new_view(|cx| SyntaxTreeView::new(workspace_handle, active_item, cx));
-            workspace.split_item(SplitDirection::Right, Box::new(syntax_tree_view), cx)
+                cx.new(|cx| SyntaxTreeView::new(workspace_handle, active_item, window, cx));
+            workspace.split_item(
+                SplitDirection::Right,
+                Box::new(syntax_tree_view),
+                window,
+                cx,
+            )
         });
     })
     .detach();
 }
 
 pub struct SyntaxTreeView {
-    workspace_handle: WeakView<Workspace>,
+    workspace_handle: WeakEntity<Workspace>,
     editor: Option<EditorState>,
     list_scroll_handle: UniformListScrollHandle,
     selected_descendant_ix: Option<usize>,
@@ -40,28 +45,29 @@ pub struct SyntaxTreeView {
 }
 
 pub struct SyntaxTreeToolbarItemView {
-    tree_view: Option<View<SyntaxTreeView>>,
+    tree_view: Option<Entity<SyntaxTreeView>>,
     subscription: Option<gpui::Subscription>,
 }
 
 struct EditorState {
-    editor: View<Editor>,
+    editor: Entity<Editor>,
     active_buffer: Option<BufferState>,
     _subscription: gpui::Subscription,
 }
 
 #[derive(Clone)]
 struct BufferState {
-    buffer: Model<Buffer>,
+    buffer: Entity<Buffer>,
     excerpt_id: ExcerptId,
     active_layer: Option<OwnedSyntaxLayer>,
 }
 
 impl SyntaxTreeView {
     pub fn new(
-        workspace_handle: WeakView<Workspace>,
+        workspace_handle: WeakEntity<Workspace>,
         active_item: Option<Box<dyn ItemHandle>>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let mut this = Self {
             workspace_handle: workspace_handle.clone(),
@@ -72,11 +78,12 @@ impl SyntaxTreeView {
             focus_handle: cx.focus_handle(),
         };
 
-        this.workspace_updated(active_item, cx);
-        cx.observe(
+        this.workspace_updated(active_item, window, cx);
+        cx.observe_in(
             &workspace_handle.upgrade().unwrap(),
-            |this, workspace, cx| {
-                this.workspace_updated(workspace.read(cx).active_item(cx), cx);
+            window,
+            |this, workspace, window, cx| {
+                this.workspace_updated(workspace.read(cx).active_item(cx), window, cx);
             },
         )
         .detach();
@@ -87,18 +94,19 @@ impl SyntaxTreeView {
     fn workspace_updated(
         &mut self,
         active_item: Option<Box<dyn ItemHandle>>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         if let Some(item) = active_item {
             if item.item_id() != cx.entity_id() {
                 if let Some(editor) = item.act_as::<Editor>(cx) {
-                    self.set_editor(editor, cx);
+                    self.set_editor(editor, window, cx);
                 }
             }
         }
     }
 
-    fn set_editor(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
+    fn set_editor(&mut self, editor: Entity<Editor>, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(state) = &self.editor {
             if state.editor == editor {
                 return;
@@ -108,13 +116,13 @@ impl SyntaxTreeView {
             });
         }
 
-        let subscription = cx.subscribe(&editor, |this, _, event, cx| {
+        let subscription = cx.subscribe_in(&editor, window, |this, _, event, window, cx| {
             let did_reparse = match event {
                 editor::EditorEvent::Reparsed(_) => true,
                 editor::EditorEvent::SelectionsChanged { .. } => false,
                 _ => return,
             };
-            this.editor_updated(did_reparse, cx);
+            this.editor_updated(did_reparse, window, cx);
         });
 
         self.editor = Some(EditorState {
@@ -122,15 +130,20 @@ impl SyntaxTreeView {
             _subscription: subscription,
             active_buffer: None,
         });
-        self.editor_updated(true, cx);
+        self.editor_updated(true, window, cx);
     }
 
-    fn editor_updated(&mut self, did_reparse: bool, cx: &mut ViewContext<Self>) -> Option<()> {
+    fn editor_updated(
+        &mut self,
+        did_reparse: bool,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<()> {
         // Find which excerpt the cursor is in, and the position within that excerpted buffer.
         let editor_state = self.editor.as_mut()?;
         let snapshot = editor_state
             .editor
-            .update(cx, |editor, cx| editor.snapshot(cx));
+            .update(cx, |editor, cx| editor.snapshot(window, cx));
         let (buffer, range, excerpt_id) = editor_state.editor.update(cx, |editor, cx| {
             let selection_range = editor.selections.last::<usize>(cx).range();
             let multi_buffer = editor.buffer().read(cx);
@@ -214,8 +227,9 @@ impl SyntaxTreeView {
     fn update_editor_with_range_for_descendant_ix(
         &self,
         descendant_ix: usize,
-        cx: &mut ViewContext<Self>,
-        mut f: impl FnMut(&mut Editor, Range<Anchor>, &mut ViewContext<Editor>),
+        window: &mut Window,
+        cx: &mut Context<Self>,
+        mut f: impl FnMut(&mut Editor, Range<Anchor>, &mut Window, &mut Context<Editor>),
     ) -> Option<()> {
         let editor_state = self.editor.as_ref()?;
         let buffer_state = editor_state.active_buffer.as_ref()?;
@@ -244,12 +258,12 @@ impl SyntaxTreeView {
 
         // Update the editor with the anchor range.
         editor_state.editor.update(cx, |editor, cx| {
-            f(editor, range, cx);
+            f(editor, range, window, cx);
         });
         Some(())
     }
 
-    fn render_node(cursor: &TreeCursor, depth: u32, selected: bool, cx: &AppContext) -> Div {
+    fn render_node(cursor: &TreeCursor, depth: u32, selected: bool, cx: &App) -> Div {
         let colors = cx.theme().colors();
         let mut row = h_flex();
         if let Some(field_name) = cursor.field_name() {
@@ -278,7 +292,7 @@ impl SyntaxTreeView {
 }
 
 impl Render for SyntaxTreeView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let mut rendered = div().flex_1();
 
         if let Some(layer) = self
@@ -289,10 +303,10 @@ impl Render for SyntaxTreeView {
         {
             let layer = layer.clone();
             rendered = rendered.child(uniform_list(
-                cx.view().clone(),
+                cx.model().clone(),
                 "SyntaxTreeView",
                 layer.node().descendant_count(),
-                move |this, range, cx| {
+                move |this, range, _, cx| {
                     let mut items = Vec::new();
                     let mut cursor = layer.node().walk();
                     let mut descendant_ix = range.start;
@@ -318,17 +332,17 @@ impl Render for SyntaxTreeView {
                                 )
                                 .on_mouse_down(
                                     MouseButton::Left,
-                                    cx.listener(move |tree_view, _: &MouseDownEvent, cx| {
+                                    cx.listener(move |tree_view, _: &MouseDownEvent, window, cx| {
                                         tree_view.update_editor_with_range_for_descendant_ix(
                                             descendant_ix,
-                                            cx,
-                                            |editor, mut range, cx| {
+                                            window, cx,
+                                            |editor, mut range, window, cx| {
                                                 // Put the cursor at the beginning of the node.
                                                 mem::swap(&mut range.start, &mut range.end);
 
                                                 editor.change_selections(
                                                     Some(Autoscroll::newest()),
-                                                    cx,
+                                                    window, cx,
                                                     |selections| {
                                                         selections.select_ranges(vec![range]);
                                                     },
@@ -338,15 +352,15 @@ impl Render for SyntaxTreeView {
                                     }),
                                 )
                                 .on_mouse_move(cx.listener(
-                                    move |tree_view, _: &MouseMoveEvent, cx| {
+                                    move |tree_view, _: &MouseMoveEvent, window, cx| {
                                         if tree_view.hovered_descendant_ix != Some(descendant_ix) {
                                             tree_view.hovered_descendant_ix = Some(descendant_ix);
-                                            tree_view.update_editor_with_range_for_descendant_ix(descendant_ix, cx, |editor, range, cx| {
-                                                editor.clear_background_highlights::<Self>(cx);
+                                            tree_view.update_editor_with_range_for_descendant_ix(descendant_ix, window, cx, |editor, range, _, cx| {
+                                                editor.clear_background_highlights::<Self>( cx);
                                                 editor.highlight_background::<Self>(
                                                     &[range],
                                                     |theme| theme.editor_document_highlight_write_background,
-                                                    cx,
+                                                     cx,
                                                 );
                                             });
                                             cx.notify();
@@ -376,8 +390,8 @@ impl Render for SyntaxTreeView {
 
 impl EventEmitter<()> for SyntaxTreeView {}
 
-impl FocusableView for SyntaxTreeView {
-    fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
+impl Focusable for SyntaxTreeView {
+    fn focus_handle(&self, _: &App) -> gpui::FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -387,7 +401,7 @@ impl Item for SyntaxTreeView {
 
     fn to_item_events(_: &Self::Event, _: impl FnMut(workspace::item::ItemEvent)) {}
 
-    fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
+    fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option<SharedString> {
         Some("Syntax Tree".into())
     }
 
@@ -398,15 +412,16 @@ impl Item for SyntaxTreeView {
     fn clone_on_split(
         &self,
         _: Option<workspace::WorkspaceId>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<View<Self>>
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Entity<Self>>
     where
         Self: Sized,
     {
-        Some(cx.new_view(|cx| {
-            let mut clone = Self::new(self.workspace_handle.clone(), None, cx);
+        Some(cx.new(|cx| {
+            let mut clone = Self::new(self.workspace_handle.clone(), None, window, cx);
             if let Some(editor) = &self.editor {
-                clone.set_editor(editor.editor.clone(), cx)
+                clone.set_editor(editor.editor.clone(), window, cx)
             }
             clone
         }))
@@ -427,7 +442,7 @@ impl SyntaxTreeToolbarItemView {
         }
     }
 
-    fn render_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<PopoverMenu<ContextMenu>> {
+    fn render_menu(&mut self, cx: &mut Context<Self>) -> Option<PopoverMenu<ContextMenu>> {
         let tree_view = self.tree_view.as_ref()?;
         let tree_view = tree_view.read(cx);
 
@@ -436,12 +451,12 @@ impl SyntaxTreeToolbarItemView {
         let active_layer = buffer_state.active_layer.clone()?;
         let active_buffer = buffer_state.buffer.read(cx).snapshot();
 
-        let view = cx.view().clone();
+        let view = cx.model().clone();
         Some(
             PopoverMenu::new("Syntax Tree")
                 .trigger(Self::render_header(&active_layer))
-                .menu(move |cx| {
-                    ContextMenu::build(cx, |mut menu, cx| {
+                .menu(move |window, cx| {
+                    ContextMenu::build(window, cx, |mut menu, window, _| {
                         for (layer_ix, layer) in active_buffer.syntax_layers().enumerate() {
                             menu = menu.entry(
                                 format!(
@@ -450,8 +465,8 @@ impl SyntaxTreeToolbarItemView {
                                     format_node_range(layer.node())
                                 ),
                                 None,
-                                cx.handler_for(&view, move |view, cx| {
-                                    view.select_layer(layer_ix, cx);
+                                window.handler_for(&view, move |view, window, cx| {
+                                    view.select_layer(layer_ix, window, cx);
                                 }),
                             );
                         }
@@ -462,7 +477,12 @@ impl SyntaxTreeToolbarItemView {
         )
     }
 
-    fn select_layer(&mut self, layer_ix: usize, cx: &mut ViewContext<Self>) -> Option<()> {
+    fn select_layer(
+        &mut self,
+        layer_ix: usize,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<()> {
         let tree_view = self.tree_view.as_ref()?;
         tree_view.update(cx, |view, cx| {
             let editor_state = view.editor.as_mut()?;
@@ -472,7 +492,7 @@ impl SyntaxTreeToolbarItemView {
             buffer_state.active_layer = Some(layer.to_owned());
             view.selected_descendant_ix = None;
             cx.notify();
-            view.focus_handle.focus(cx);
+            view.focus_handle.focus(window);
             Some(())
         })
     }
@@ -497,7 +517,7 @@ fn format_node_range(node: Node) -> String {
 }
 
 impl Render for SyntaxTreeToolbarItemView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         self.render_menu(cx)
             .unwrap_or_else(|| PopoverMenu::new("Empty Syntax Tree"))
     }
@@ -509,12 +529,13 @@ impl ToolbarItemView for SyntaxTreeToolbarItemView {
     fn set_active_pane_item(
         &mut self,
         active_pane_item: Option<&dyn ItemHandle>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> ToolbarItemLocation {
         if let Some(item) = active_pane_item {
             if let Some(view) = item.downcast::<SyntaxTreeView>() {
                 self.tree_view = Some(view.clone());
-                self.subscription = Some(cx.observe(&view, |_, _, cx| cx.notify()));
+                self.subscription = Some(cx.observe_in(&view, window, |_, _, _, cx| cx.notify()));
                 return ToolbarItemLocation::PrimaryLeft;
             }
         }

crates/languages/src/c.rs 🔗

@@ -311,7 +311,7 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
 
 #[cfg(test)]
 mod tests {
-    use gpui::{BorrowAppContext, Context, TestAppContext};
+    use gpui::{AppContext as _, BorrowAppContext, TestAppContext};
     use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer};
     use settings::SettingsStore;
     use std::num::NonZeroU32;
@@ -331,7 +331,7 @@ mod tests {
         });
         let language = crate::language("c", tree_sitter_c::LANGUAGE.into());
 
-        cx.new_model(|cx| {
+        cx.new(|cx| {
             let mut buffer = Buffer::local("", cx).with_language(language, cx);
 
             // empty function

crates/languages/src/css.rs 🔗

@@ -151,7 +151,7 @@ async fn get_cached_server_binary(
 
 #[cfg(test)]
 mod tests {
-    use gpui::{Context, TestAppContext};
+    use gpui::{AppContext as _, TestAppContext};
     use unindent::Unindent;
 
     #[gpui::test]
@@ -183,8 +183,7 @@ mod tests {
         "#
         .unindent();
 
-        let buffer =
-            cx.new_model(|cx| language::Buffer::local(text, cx).with_language(language, cx));
+        let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
         let outline = buffer.update(cx, |buffer, _| buffer.snapshot().outline(None).unwrap());
         assert_eq!(
             outline

crates/languages/src/go.rs 🔗

@@ -1,8 +1,8 @@
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use async_trait::async_trait;
 use collections::HashMap;
 use futures::StreamExt;
-use gpui::{AppContext, AsyncAppContext, Task};
+use gpui::{App, AsyncAppContext, Task};
 use http_client::github::latest_github_release;
 pub use language::*;
 use lsp::{LanguageServerBinary, LanguageServerName};
@@ -443,7 +443,7 @@ impl ContextProvider for GoContextProvider {
         location: &Location,
         _: Option<HashMap<String, String>>,
         _: Arc<dyn LanguageToolchainStore>,
-        cx: &mut gpui::AppContext,
+        cx: &mut gpui::App,
     ) -> Task<Result<TaskVariables>> {
         let local_abs_path = location
             .buffer
@@ -506,7 +506,7 @@ impl ContextProvider for GoContextProvider {
     fn associated_tasks(
         &self,
         _: Option<Arc<dyn language::File>>,
-        _: &AppContext,
+        _: &App,
     ) -> Option<TaskTemplates> {
         let package_cwd = if GO_PACKAGE_TASK_VARIABLE.template_value() == "." {
             None

crates/languages/src/json.rs 🔗

@@ -4,7 +4,7 @@ use async_tar::Archive;
 use async_trait::async_trait;
 use collections::HashMap;
 use futures::StreamExt;
-use gpui::{AppContext, AsyncAppContext};
+use gpui::{App, AsyncAppContext};
 use http_client::github::{latest_github_release, GitHubLspBinaryVersion};
 use language::{LanguageRegistry, LanguageToolchainStore, LspAdapter, LspAdapterDelegate};
 use lsp::{LanguageServerBinary, LanguageServerName};
@@ -74,7 +74,7 @@ impl JsonLspAdapter {
         }
     }
 
-    fn get_workspace_config(language_names: Vec<String>, cx: &mut AppContext) -> Value {
+    fn get_workspace_config(language_names: Vec<String>, cx: &mut App) -> Value {
         let keymap_schema = KeymapFile::generate_json_schema_for_registered_actions(cx);
         let font_names = &cx.text_system().all_font_names();
         let settings_schema = cx.global::<SettingsStore>().json_schema(

crates/languages/src/lib.rs 🔗

@@ -1,5 +1,5 @@
-use anyhow::Context;
-use gpui::{AppContext, UpdateGlobal};
+use anyhow::Context as _;
+use gpui::{App, UpdateGlobal};
 use json::json_task_context;
 pub use language::*;
 use lsp::LanguageServerName;
@@ -31,7 +31,7 @@ mod yaml;
 #[exclude = "*.rs"]
 struct LanguageDir;
 
-pub fn init(languages: Arc<LanguageRegistry>, node_runtime: NodeRuntime, cx: &mut AppContext) {
+pub fn init(languages: Arc<LanguageRegistry>, node_runtime: NodeRuntime, cx: &mut App) {
     #[cfg(feature = "load-grammars")]
     languages.register_native_grammars([
         ("bash", tree_sitter_bash::LANGUAGE),

crates/languages/src/python.rs 🔗

@@ -2,7 +2,7 @@ use anyhow::ensure;
 use anyhow::{anyhow, Result};
 use async_trait::async_trait;
 use collections::HashMap;
-use gpui::{AppContext, Task};
+use gpui::{App, Task};
 use gpui::{AsyncAppContext, SharedString};
 use language::language_settings::language_settings;
 use language::LanguageName;
@@ -317,7 +317,7 @@ impl ContextProvider for PythonContextProvider {
         location: &project::Location,
         _: Option<HashMap<String, String>>,
         toolchains: Arc<dyn LanguageToolchainStore>,
-        cx: &mut gpui::AppContext,
+        cx: &mut gpui::App,
     ) -> Task<Result<task::TaskVariables>> {
         let test_target = {
             let test_runner = selected_test_runner(location.buffer.read(cx).file(), cx);
@@ -350,7 +350,7 @@ impl ContextProvider for PythonContextProvider {
     fn associated_tasks(
         &self,
         file: Option<Arc<dyn language::File>>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<TaskTemplates> {
         let test_runner = selected_test_runner(file.as_ref(), cx);
 
@@ -441,7 +441,7 @@ impl ContextProvider for PythonContextProvider {
     }
 }
 
-fn selected_test_runner(location: Option<&Arc<dyn language::File>>, cx: &AppContext) -> TestRunner {
+fn selected_test_runner(location: Option<&Arc<dyn language::File>>, cx: &App) -> TestRunner {
     const TEST_RUNNER_VARIABLE: &str = "TEST_RUNNER";
     language_settings(Some(LanguageName::new("Python")), location, cx)
         .tasks
@@ -1005,7 +1005,7 @@ impl LspAdapter for PyLspAdapter {
 
 #[cfg(test)]
 mod tests {
-    use gpui::{BorrowAppContext, Context, ModelContext, TestAppContext};
+    use gpui::{AppContext as _, BorrowAppContext, Context, TestAppContext};
     use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer};
     use settings::SettingsStore;
     use std::num::NonZeroU32;
@@ -1025,9 +1025,9 @@ mod tests {
             });
         });
 
-        cx.new_model(|cx| {
+        cx.new(|cx| {
             let mut buffer = Buffer::local("", cx).with_language(language, cx);
-            let append = |buffer: &mut Buffer, text: &str, cx: &mut ModelContext<Buffer>| {
+            let append = |buffer: &mut Buffer, text: &str, cx: &mut Context<Buffer>| {
                 let ix = buffer.len();
                 buffer.edit([(ix..ix, text)], Some(AutoindentMode::EachLine), cx);
             };

crates/languages/src/rust.rs 🔗

@@ -1,9 +1,9 @@
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use async_compression::futures::bufread::GzipDecoder;
 use async_trait::async_trait;
 use collections::HashMap;
 use futures::{io::BufReader, StreamExt};
-use gpui::{AppContext, AsyncAppContext, Task};
+use gpui::{App, AsyncAppContext, Task};
 use http_client::github::AssetKind;
 use http_client::github::{latest_github_release, GitHubLspBinaryVersion};
 pub use language::*;
@@ -462,7 +462,7 @@ impl ContextProvider for RustContextProvider {
         location: &Location,
         project_env: Option<HashMap<String, String>>,
         _: Arc<dyn LanguageToolchainStore>,
-        cx: &mut gpui::AppContext,
+        cx: &mut gpui::App,
     ) -> Task<Result<TaskVariables>> {
         let local_abs_path = location
             .buffer
@@ -507,7 +507,7 @@ impl ContextProvider for RustContextProvider {
     fn associated_tasks(
         &self,
         file: Option<Arc<dyn language::File>>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<TaskTemplates> {
         const DEFAULT_RUN_NAME_STR: &str = "RUST_DEFAULT_PACKAGE_RUN";
         let package_to_run = language_settings(Some("Rust".into()), file.as_ref(), cx)
@@ -813,7 +813,7 @@ mod tests {
 
     use super::*;
     use crate::language;
-    use gpui::{BorrowAppContext, Context, Hsla, TestAppContext};
+    use gpui::{AppContext as _, BorrowAppContext, Hsla, TestAppContext};
     use language::language_settings::AllLanguageSettings;
     use lsp::CompletionItemLabelDetails;
     use settings::SettingsStore;
@@ -1042,7 +1042,7 @@ mod tests {
 
         let language = crate::language("rust", tree_sitter_rust::LANGUAGE.into());
 
-        cx.new_model(|cx| {
+        cx.new(|cx| {
             let mut buffer = Buffer::local("", cx).with_language(language, cx);
 
             // indent between braces

crates/languages/src/typescript.rs 🔗

@@ -593,7 +593,7 @@ async fn handle_symlink(src_dir: PathBuf, dest_dir: PathBuf) -> Result<()> {
 
 #[cfg(test)]
 mod tests {
-    use gpui::{Context, TestAppContext};
+    use gpui::{AppContext as _, TestAppContext};
     use unindent::Unindent;
 
     #[gpui::test]
@@ -618,8 +618,7 @@ mod tests {
         "#
         .unindent();
 
-        let buffer =
-            cx.new_model(|cx| language::Buffer::local(text, cx).with_language(language, cx));
+        let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
         let outline = buffer.update(cx, |buffer, _| buffer.snapshot().outline(None).unwrap());
         assert_eq!(
             outline

crates/livekit_client/examples/test_app.rs 🔗

@@ -6,10 +6,10 @@
 use gpui::{
     actions, bounds, div, point,
     prelude::{FluentBuilder as _, IntoElement},
-    px, rgb, size, AsyncAppContext, Bounds, InteractiveElement, KeyBinding, Menu, MenuItem,
-    ParentElement, Pixels, Render, ScreenCaptureStream, SharedString,
-    StatefulInteractiveElement as _, Styled, Task, View, ViewContext, VisualContext, WindowBounds,
-    WindowHandle, WindowOptions,
+    px, rgb, size, AppContext as _, AsyncAppContext, Bounds, Context, Entity, InteractiveElement,
+    KeyBinding, Menu, MenuItem, ParentElement, Pixels, Render, ScreenCaptureStream, SharedString,
+    StatefulInteractiveElement as _, Styled, Task, Window, WindowBounds, WindowHandle,
+    WindowOptions,
 };
 #[cfg(not(target_os = "windows"))]
 use livekit_client::{
@@ -46,7 +46,7 @@ fn main() {}
 fn main() {
     SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
 
-    gpui::App::new().run(|cx| {
+    gpui::Application::new().run(|cx| {
         livekit_client::init(
             cx.background_executor().dispatcher.clone(),
             cx.http_client(),
@@ -98,7 +98,7 @@ fn main() {
     });
 }
 
-fn quit(_: &Quit, cx: &mut gpui::AppContext) {
+fn quit(_: &Quit, cx: &mut gpui::App) {
     cx.quit();
 }
 
@@ -117,7 +117,7 @@ struct LivekitWindow {
 struct ParticipantState {
     audio_output_stream: Option<(RemoteTrackPublication, AudioStream)>,
     muted: bool,
-    screen_share_output_view: Option<(RemoteVideoTrack, View<RemoteVideoTrackView>)>,
+    screen_share_output_view: Option<(RemoteVideoTrack, Entity<RemoteVideoTrackView>)>,
     speaking: bool,
 }
 
@@ -139,12 +139,14 @@ impl LivekitWindow {
                     window_bounds: Some(WindowBounds::Windowed(bounds)),
                     ..Default::default()
                 },
-                |cx| {
-                    cx.new_view(|cx| {
-                        let _events_task = cx.spawn(|this, mut cx| async move {
+                |window, cx| {
+                    cx.new(|cx| {
+                        let _events_task = cx.spawn_in(window, |this, mut cx| async move {
                             while let Some(event) = events.recv().await {
-                                this.update(&mut cx, |this: &mut LivekitWindow, cx| {
-                                    this.handle_room_event(event, cx)
+                                cx.update(|window, cx| {
+                                    this.update(cx, |this: &mut LivekitWindow, cx| {
+                                        this.handle_room_event(event, window, cx)
+                                    })
                                 })
                                 .ok();
                             }
@@ -167,7 +169,7 @@ impl LivekitWindow {
         .unwrap()
     }
 
-    fn handle_room_event(&mut self, event: RoomEvent, cx: &mut ViewContext<Self>) {
+    fn handle_room_event(&mut self, event: RoomEvent, window: &mut Window, cx: &mut Context<Self>) {
         eprintln!("event: {event:?}");
 
         match event {
@@ -210,7 +212,7 @@ impl LivekitWindow {
                     RemoteTrack::Video(track) => {
                         output.screen_share_output_view = Some((
                             track.clone(),
-                            cx.new_view(|cx| RemoteVideoTrackView::new(track, cx)),
+                            cx.new(|cx| RemoteVideoTrackView::new(track, window, cx)),
                         ));
                     }
                 }
@@ -264,7 +266,7 @@ impl LivekitWindow {
         }
     }
 
-    fn toggle_mute(&mut self, cx: &mut ViewContext<Self>) {
+    fn toggle_mute(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(track) = &self.microphone_track {
             if track.is_muted() {
                 track.unmute();
@@ -274,7 +276,7 @@ impl LivekitWindow {
             cx.notify();
         } else {
             let participant = self.room.local_participant();
-            cx.spawn(|this, mut cx| async move {
+            cx.spawn_in(window, |this, mut cx| async move {
                 let (track, stream) = capture_local_audio_track(cx.background_executor())?.await;
                 let publication = participant
                     .publish_track(
@@ -296,7 +298,7 @@ impl LivekitWindow {
         }
     }
 
-    fn toggle_screen_share(&mut self, cx: &mut ViewContext<Self>) {
+    fn toggle_screen_share(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(track) = self.screen_share_track.take() {
             self.screen_share_stream.take();
             let participant = self.room.local_participant();
@@ -309,7 +311,7 @@ impl LivekitWindow {
         } else {
             let participant = self.room.local_participant();
             let sources = cx.screen_capture_sources();
-            cx.spawn(|this, mut cx| async move {
+            cx.spawn_in(window, |this, mut cx| async move {
                 let sources = sources.await.unwrap()?;
                 let source = sources.into_iter().next().unwrap();
                 let (track, stream) = capture_local_video_track(&*source).await?;
@@ -337,7 +339,8 @@ impl LivekitWindow {
     fn toggle_remote_audio_for_participant(
         &mut self,
         identity: &ParticipantIdentity,
-        cx: &mut ViewContext<Self>,
+
+        cx: &mut Context<Self>,
     ) -> Option<()> {
         let participant = self.remote_participants.iter().find_map(|(id, state)| {
             if id == identity {
@@ -355,7 +358,7 @@ impl LivekitWindow {
 
 #[cfg(not(windows))]
 impl Render for LivekitWindow {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         fn button() -> gpui::Div {
             div()
                 .w(px(180.0))
@@ -383,7 +386,7 @@ impl Render for LivekitWindow {
                         } else {
                             "Publish mic"
                         })
-                        .on_click(cx.listener(|this, _, cx| this.toggle_mute(cx))),
+                        .on_click(cx.listener(|this, _, window, cx| this.toggle_mute(window, cx))),
                     button()
                         .id("toggle-screen-share")
                         .child(if self.screen_share_track.is_none() {
@@ -391,7 +394,9 @@ impl Render for LivekitWindow {
                         } else {
                             "Unshare screen"
                         })
-                        .on_click(cx.listener(|this, _, cx| this.toggle_screen_share(cx))),
+                        .on_click(
+                            cx.listener(|this, _, window, cx| this.toggle_screen_share(window, cx)),
+                        ),
                 ]),
             )
             .child(
@@ -427,7 +432,7 @@ impl Render for LivekitWindow {
                                         })
                                         .on_click(cx.listener({
                                             let identity = identity.clone();
-                                            move |this, _, cx| {
+                                            move |this, _, _, cx| {
                                                 this.toggle_remote_audio_for_participant(
                                                     &identity, cx,
                                                 );

crates/livekit_client/src/remote_video_track_view.rs 🔗

@@ -1,7 +1,7 @@
 use crate::track::RemoteVideoTrack;
 use anyhow::Result;
 use futures::StreamExt as _;
-use gpui::{Empty, EventEmitter, IntoElement, Render, Task, View, ViewContext, VisualContext as _};
+use gpui::{AppContext, Context, Empty, Entity, EventEmitter, IntoElement, Render, Task, Window};
 
 pub struct RemoteVideoTrackView {
     track: RemoteVideoTrack,
@@ -19,14 +19,15 @@ pub enum RemoteVideoTrackViewEvent {
 }
 
 impl RemoteVideoTrackView {
-    pub fn new(track: RemoteVideoTrack, cx: &mut ViewContext<Self>) -> Self {
+    pub fn new(track: RemoteVideoTrack, window: &mut Window, cx: &mut Context<Self>) -> Self {
         cx.focus_handle();
         let frames = super::play_remote_video_track(&track);
+        let _window_handle = window.window_handle();
 
         Self {
             track,
             latest_frame: None,
-            _maintain_frame: cx.spawn(|this, mut cx| async move {
+            _maintain_frame: cx.spawn_in(window, |this, mut cx| async move {
                 futures::pin_mut!(frames);
                 while let Some(frame) = frames.next().await {
                     this.update(&mut cx, |this, cx| {
@@ -39,12 +40,16 @@ impl RemoteVideoTrackView {
                     {
                         use util::ResultExt as _;
                         if let Some(frame) = _this.previous_rendered_frame.take() {
-                            cx.window_context().drop_image(frame).log_err();
+                            _window_handle
+                                .update(cx, |_, window, _cx| window.drop_image(frame).log_err())
+                                .ok();
                         }
                         // TODO(mgsloan): This might leak the last image of the screenshare if
                         // render is called after the screenshare ends.
                         if let Some(frame) = _this.current_rendered_frame.take() {
-                            cx.window_context().drop_image(frame).log_err();
+                            _window_handle
+                                .update(cx, |_, window, _cx| window.drop_image(frame).log_err())
+                                .ok();
                         }
                     }
                     cx.emit(RemoteVideoTrackViewEvent::Close)
@@ -58,15 +63,15 @@ impl RemoteVideoTrackView {
         }
     }
 
-    pub fn clone(&self, cx: &mut ViewContext<Self>) -> View<Self> {
-        cx.new_view(|cx| Self::new(self.track.clone(), cx))
+    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Entity<Self> {
+        cx.new(|cx| Self::new(self.track.clone(), window, cx))
     }
 }
 
 impl EventEmitter<RemoteVideoTrackViewEvent> for RemoteVideoTrackView {}
 
 impl Render for RemoteVideoTrackView {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         #[cfg(target_os = "macos")]
         if let Some(latest_frame) = &self.latest_frame {
             use gpui::Styled as _;
@@ -83,7 +88,7 @@ impl Render for RemoteVideoTrackView {
                     // Only drop the frame if it's not also the current frame.
                     if frame.id != current_rendered_frame.id {
                         use util::ResultExt as _;
-                        _cx.window_context().drop_image(frame).log_err();
+                        _window.drop_image(frame).log_err();
                     }
                 }
                 self.previous_rendered_frame = Some(current_rendered_frame)

crates/livekit_client/src/test.rs 🔗

@@ -5,7 +5,7 @@ pub mod webrtc;
 
 use self::id::*;
 use self::{participant::*, publication::*, track::*};
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use async_trait::async_trait;
 use collections::{btree_map::Entry as BTreeEntry, hash_map::Entry, BTreeMap, HashMap, HashSet};
 use gpui::BackgroundExecutor;

crates/livekit_client_macos/examples/test_app_macos.rs 🔗

@@ -12,7 +12,7 @@ actions!(livekit_client_macos, [Quit]);
 fn main() {
     SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
 
-    gpui::App::new().run(|cx| {
+    gpui::Application::new().run(|cx| {
         #[cfg(any(test, feature = "test-support"))]
         println!("USING TEST LIVEKIT");
 
@@ -167,6 +167,6 @@ fn main() {
     });
 }
 
-fn quit(_: &Quit, cx: &mut gpui::AppContext) {
+fn quit(_: &Quit, cx: &mut gpui::App) {
     cx.quit();
 }

crates/livekit_client_macos/src/prod.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{ConnectionState, RoomUpdate, Sid};
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use core_foundation::{
     array::{CFArray, CFArrayRef},
     base::{CFRelease, CFRetain, TCFType},

crates/livekit_client_macos/src/test.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{ConnectionState, RoomUpdate, Sid};
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use async_trait::async_trait;
 use collections::{btree_map::Entry as BTreeEntry, hash_map::Entry, BTreeMap, HashMap, HashSet};
 use futures::Stream;

crates/lmstudio/src/lmstudio.rs 🔗

@@ -1,4 +1,4 @@
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use futures::{io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, StreamExt};
 use http_client::{http, AsyncBody, HttpClient, Method, Request as HttpRequest};
 use serde::{Deserialize, Serialize};

crates/lsp/src/lsp.rs 🔗

@@ -3,10 +3,10 @@ mod input_handler;
 pub use lsp_types::request::*;
 pub use lsp_types::*;
 
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use collections::HashMap;
 use futures::{channel::oneshot, io::BufWriter, select, AsyncRead, AsyncWrite, Future, FutureExt};
-use gpui::{AppContext, AsyncAppContext, BackgroundExecutor, SharedString, Task};
+use gpui::{App, AsyncAppContext, BackgroundExecutor, SharedString, Task};
 use parking_lot::{Mutex, RwLock};
 use postage::{barrier, prelude::Stream};
 use schemars::{
@@ -614,7 +614,7 @@ impl LanguageServer {
         Ok(())
     }
 
-    pub fn default_initialize_params(&self, cx: &AppContext) -> InitializeParams {
+    pub fn default_initialize_params(&self, cx: &App) -> InitializeParams {
         let root_uri = Url::from_file_path(&self.working_dir).unwrap();
         #[allow(deprecated)]
         InitializeParams {
@@ -811,7 +811,7 @@ impl LanguageServer {
         mut self,
         initialize_params: Option<InitializeParams>,
         configuration: Arc<DidChangeConfigurationParams>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Result<Arc<Self>>> {
         let params = if let Some(params) = initialize_params {
             params

crates/markdown/examples/markdown.rs 🔗

@@ -1,5 +1,5 @@
 use assets::Assets;
-use gpui::{prelude::*, rgb, App, KeyBinding, StyleRefinement, View, WindowOptions};
+use gpui::{prelude::*, rgb, Application, Entity, KeyBinding, StyleRefinement, WindowOptions};
 use language::{language_settings::AllLanguageSettings, LanguageRegistry};
 use markdown::{Markdown, MarkdownStyle};
 use node_runtime::NodeRuntime;
@@ -7,7 +7,7 @@ use settings::SettingsStore;
 use std::sync::Arc;
 use theme::LoadThemes;
 use ui::prelude::*;
-use ui::{div, WindowContext};
+use ui::{div, App, Window};
 
 const MARKDOWN_EXAMPLE: &str = r#"
 # Markdown Example Document
@@ -99,7 +99,7 @@ Remember, markdown processors may have slight differences and extensions, so alw
 
 pub fn main() {
     env_logger::init();
-    App::new().with_assets(Assets).run(|cx| {
+    Application::new().with_assets(Assets).run(|cx| {
         let store = SettingsStore::test(cx);
         cx.set_global(store);
         language::init(cx);
@@ -118,8 +118,8 @@ pub fn main() {
         Assets.load_fonts(cx).unwrap();
 
         cx.activate(true);
-        cx.open_window(WindowOptions::default(), |cx| {
-            cx.new_view(|cx| {
+        cx.open_window(WindowOptions::default(), |window, cx| {
+            cx.new(|cx| {
                 let markdown_style = MarkdownStyle {
                     base_text_style: gpui::TextStyle {
                         font_family: "Zed Plex Sans".into(),
@@ -164,6 +164,7 @@ pub fn main() {
                     MARKDOWN_EXAMPLE.to_string(),
                     markdown_style,
                     language_registry,
+                    window,
                     cx,
                 )
             })
@@ -173,7 +174,7 @@ pub fn main() {
 }
 
 struct MarkdownExample {
-    markdown: View<Markdown>,
+    markdown: Entity<Markdown>,
 }
 
 impl MarkdownExample {
@@ -181,16 +182,17 @@ impl MarkdownExample {
         text: String,
         style: MarkdownStyle,
         language_registry: Arc<LanguageRegistry>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Self {
         let markdown =
-            cx.new_view(|cx| Markdown::new(text, style, Some(language_registry), None, cx));
+            cx.new(|cx| Markdown::new(text, style, Some(language_registry), None, window, cx));
         Self { markdown }
     }
 }
 
 impl Render for MarkdownExample {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         div()
             .id("markdown-example")
             .debug_selector(|| "foo".into())

crates/markdown/examples/markdown_as_child.rs 🔗

@@ -1,5 +1,5 @@
 use assets::Assets;
-use gpui::{rgb, App, KeyBinding, Length, StyleRefinement, View, WindowOptions};
+use gpui::{rgb, Application, Entity, KeyBinding, Length, StyleRefinement, WindowOptions};
 use language::{language_settings::AllLanguageSettings, LanguageRegistry};
 use markdown::{Markdown, MarkdownStyle};
 use node_runtime::NodeRuntime;
@@ -19,7 +19,7 @@ wow so cool
 pub fn main() {
     env_logger::init();
 
-    App::new().with_assets(Assets).run(|cx| {
+    Application::new().with_assets(Assets).run(|cx| {
         let store = SettingsStore::test(cx);
         cx.set_global(store);
         language::init(cx);
@@ -35,8 +35,8 @@ pub fn main() {
         Assets.load_fonts(cx).unwrap();
 
         cx.activate(true);
-        let _ = cx.open_window(WindowOptions::default(), |cx| {
-            cx.new_view(|cx| {
+        let _ = cx.open_window(WindowOptions::default(), |window, cx| {
+            cx.new(|cx| {
                 let markdown_style = MarkdownStyle {
                     base_text_style: gpui::TextStyle {
                         font_family: "Zed Mono".into(),
@@ -86,8 +86,15 @@ pub fn main() {
                     break_style: Default::default(),
                     heading: Default::default(),
                 };
-                let markdown = cx.new_view(|cx| {
-                    Markdown::new(MARKDOWN_EXAMPLE.into(), markdown_style, None, None, cx)
+                let markdown = cx.new(|cx| {
+                    Markdown::new(
+                        MARKDOWN_EXAMPLE.into(),
+                        markdown_style,
+                        None,
+                        None,
+                        window,
+                        cx,
+                    )
                 });
 
                 HelloWorld { markdown }
@@ -96,11 +103,11 @@ pub fn main() {
     });
 }
 struct HelloWorld {
-    markdown: View<Markdown>,
+    markdown: Entity<Markdown>,
 }
 
 impl Render for HelloWorld {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         div()
             .flex()
             .bg(rgb(0x2e7d32))

crates/markdown/src/markdown.rs 🔗

@@ -3,11 +3,11 @@ pub mod parser;
 use crate::parser::CodeBlockKind;
 use futures::FutureExt;
 use gpui::{
-    actions, point, quad, AnyElement, AppContext, Bounds, ClipboardItem, CursorStyle,
-    DispatchPhase, Edges, FocusHandle, FocusableView, FontStyle, FontWeight, GlobalElementId,
-    Hitbox, Hsla, KeyContext, Length, MouseDownEvent, MouseEvent, MouseMoveEvent, MouseUpEvent,
-    Point, Render, Stateful, StrikethroughStyle, StyleRefinement, StyledText, Task, TextLayout,
-    TextRun, TextStyle, TextStyleRefinement, View,
+    actions, point, quad, AnyElement, App, Bounds, ClipboardItem, CursorStyle, DispatchPhase,
+    Edges, Entity, FocusHandle, Focusable, FontStyle, FontWeight, GlobalElementId, Hitbox, Hsla,
+    KeyContext, Length, MouseDownEvent, MouseEvent, MouseMoveEvent, MouseUpEvent, Point, Render,
+    Stateful, StrikethroughStyle, StyleRefinement, StyledText, Task, TextLayout, TextRun,
+    TextStyle, TextStyleRefinement,
 };
 use language::{Language, LanguageRegistry, Rope};
 use parser::{parse_links_only, parse_markdown, MarkdownEvent, MarkdownTag, MarkdownTagEnd};
@@ -78,7 +78,8 @@ impl Markdown {
         style: MarkdownStyle,
         language_registry: Option<Arc<LanguageRegistry>>,
         fallback_code_block_language: Option<String>,
-        cx: &ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let focus_handle = cx.focus_handle();
         let mut this = Self {
@@ -98,7 +99,7 @@ impl Markdown {
                 copy_code_block_buttons: true,
             },
         };
-        this.parse(cx);
+        this.parse(window, cx);
         this
     }
 
@@ -107,7 +108,8 @@ impl Markdown {
         style: MarkdownStyle,
         language_registry: Option<Arc<LanguageRegistry>>,
         fallback_code_block_language: Option<String>,
-        cx: &ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let focus_handle = cx.focus_handle();
         let mut this = Self {
@@ -127,7 +129,7 @@ impl Markdown {
                 copy_code_block_buttons: true,
             },
         };
-        this.parse(cx);
+        this.parse(window, cx);
         this
     }
 
@@ -135,12 +137,12 @@ impl Markdown {
         &self.source
     }
 
-    pub fn append(&mut self, text: &str, cx: &ViewContext<Self>) {
+    pub fn append(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
         self.source.push_str(text);
-        self.parse(cx);
+        self.parse(window, cx);
     }
 
-    pub fn reset(&mut self, source: String, cx: &ViewContext<Self>) {
+    pub fn reset(&mut self, source: String, window: &mut Window, cx: &mut Context<Self>) {
         if source == self.source() {
             return;
         }
@@ -150,14 +152,14 @@ impl Markdown {
         self.pending_parse = None;
         self.should_reparse = false;
         self.parsed_markdown = ParsedMarkdown::default();
-        self.parse(cx);
+        self.parse(window, cx);
     }
 
     pub fn parsed_markdown(&self) -> &ParsedMarkdown {
         &self.parsed_markdown
     }
 
-    fn copy(&self, text: &RenderedText, cx: &ViewContext<Self>) {
+    fn copy(&self, text: &RenderedText, _: &mut Window, cx: &mut Context<Self>) {
         if self.selection.end <= self.selection.start {
             return;
         }
@@ -165,7 +167,7 @@ impl Markdown {
         cx.write_to_clipboard(ClipboardItem::new_string(text));
     }
 
-    fn parse(&mut self, cx: &ViewContext<Self>) {
+    fn parse(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if self.source.is_empty() {
             return;
         }
@@ -190,14 +192,14 @@ impl Markdown {
         });
 
         self.should_reparse = false;
-        self.pending_parse = Some(cx.spawn(|this, mut cx| {
+        self.pending_parse = Some(cx.spawn_in(window, |this, mut cx| {
             async move {
                 let parsed = parsed.await?;
-                this.update(&mut cx, |this, cx| {
+                this.update_in(&mut cx, |this, window, cx| {
                     this.parsed_markdown = parsed;
                     this.pending_parse.take();
                     if this.should_reparse {
-                        this.parse(cx);
+                        this.parse(window, cx);
                     }
                     cx.notify();
                 })
@@ -215,9 +217,9 @@ impl Markdown {
 }
 
 impl Render for Markdown {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         MarkdownElement::new(
-            cx.view().clone(),
+            cx.model().clone(),
             self.style.clone(),
             self.language_registry.clone(),
             self.fallback_code_block_language.clone(),
@@ -225,8 +227,8 @@ impl Render for Markdown {
     }
 }
 
-impl FocusableView for Markdown {
-    fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
+impl Focusable for Markdown {
+    fn focus_handle(&self, _cx: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -282,7 +284,7 @@ impl ParsedMarkdown {
 }
 
 pub struct MarkdownElement {
-    markdown: View<Markdown>,
+    markdown: Entity<Markdown>,
     style: MarkdownStyle,
     language_registry: Option<Arc<LanguageRegistry>>,
     fallback_code_block_language: Option<String>,
@@ -290,7 +292,7 @@ pub struct MarkdownElement {
 
 impl MarkdownElement {
     fn new(
-        markdown: View<Markdown>,
+        markdown: Entity<Markdown>,
         style: MarkdownStyle,
         language_registry: Option<Arc<LanguageRegistry>>,
         fallback_code_block_language: Option<String>,
@@ -303,7 +305,12 @@ impl MarkdownElement {
         }
     }
 
-    fn load_language(&self, name: &str, cx: &mut WindowContext) -> Option<Arc<Language>> {
+    fn load_language(
+        &self,
+        name: &str,
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Option<Arc<Language>> {
         let language_test = self.language_registry.as_ref()?.language_for_name(name);
 
         let language_name = match language_test.now_or_never() {
@@ -325,11 +332,12 @@ impl MarkdownElement {
             Some(language) => language,
             None => {
                 let markdown = self.markdown.downgrade();
-                cx.spawn(|mut cx| async move {
-                    language.await;
-                    markdown.update(&mut cx, |_, cx| cx.notify())
-                })
-                .detach_and_log_err(cx);
+                window
+                    .spawn(cx, |mut cx| async move {
+                        language.await;
+                        markdown.update(&mut cx, |_, cx| cx.notify())
+                    })
+                    .detach_and_log_err(cx);
                 None
             }
         }
@@ -339,7 +347,8 @@ impl MarkdownElement {
         &self,
         bounds: Bounds<Pixels>,
         rendered_text: &RenderedText,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let selection = self.markdown.read(cx).selection;
         let selection_start = rendered_text.position_for_source_index(selection.start);
@@ -349,7 +358,7 @@ impl MarkdownElement {
             selection_start.zip(selection_end)
         {
             if start_position.y == end_position.y {
-                cx.paint_quad(quad(
+                window.paint_quad(quad(
                     Bounds::from_corners(
                         start_position,
                         point(end_position.x, end_position.y + end_line_height),
@@ -360,7 +369,7 @@ impl MarkdownElement {
                     Hsla::transparent_black(),
                 ));
             } else {
-                cx.paint_quad(quad(
+                window.paint_quad(quad(
                     Bounds::from_corners(
                         start_position,
                         point(bounds.right(), start_position.y + start_line_height),
@@ -372,7 +381,7 @@ impl MarkdownElement {
                 ));
 
                 if end_position.y > start_position.y + start_line_height {
-                    cx.paint_quad(quad(
+                    window.paint_quad(quad(
                         Bounds::from_corners(
                             point(bounds.left(), start_position.y + start_line_height),
                             point(bounds.right(), end_position.y),
@@ -384,7 +393,7 @@ impl MarkdownElement {
                     ));
                 }
 
-                cx.paint_quad(quad(
+                window.paint_quad(quad(
                     Bounds::from_corners(
                         point(bounds.left(), end_position.y),
                         point(end_position.x, end_position.y + end_line_height),
@@ -402,25 +411,26 @@ impl MarkdownElement {
         &self,
         hitbox: &Hitbox,
         rendered_text: &RenderedText,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        let is_hovering_link = hitbox.is_hovered(cx)
+        let is_hovering_link = hitbox.is_hovered(window)
             && !self.markdown.read(cx).selection.pending
             && rendered_text
-                .link_for_position(cx.mouse_position())
+                .link_for_position(window.mouse_position())
                 .is_some();
 
         if is_hovering_link {
-            cx.set_cursor_style(CursorStyle::PointingHand, hitbox);
+            window.set_cursor_style(CursorStyle::PointingHand, hitbox);
         } else {
-            cx.set_cursor_style(CursorStyle::IBeam, hitbox);
+            window.set_cursor_style(CursorStyle::IBeam, hitbox);
         }
 
-        self.on_mouse_event(cx, {
+        self.on_mouse_event(window, cx, {
             let rendered_text = rendered_text.clone();
             let hitbox = hitbox.clone();
-            move |markdown, event: &MouseDownEvent, phase, cx| {
-                if hitbox.is_hovered(cx) {
+            move |markdown, event: &MouseDownEvent, phase, window, cx| {
+                if hitbox.is_hovered(window) {
                     if phase.bubble() {
                         if let Some(link) = rendered_text.link_for_position(event.position) {
                             markdown.pressed_link = Some(link.clone());
@@ -442,8 +452,8 @@ impl MarkdownElement {
                                 reversed: false,
                                 pending: true,
                             };
-                            cx.focus(&markdown.focus_handle);
-                            cx.prevent_default()
+                            window.focus(&markdown.focus_handle);
+                            window.prevent_default()
                         }
 
                         cx.notify();
@@ -455,11 +465,11 @@ impl MarkdownElement {
                 }
             }
         });
-        self.on_mouse_event(cx, {
+        self.on_mouse_event(window, cx, {
             let rendered_text = rendered_text.clone();
             let hitbox = hitbox.clone();
             let was_hovering_link = is_hovering_link;
-            move |markdown, event: &MouseMoveEvent, phase, cx| {
+            move |markdown, event: &MouseMoveEvent, phase, window, cx| {
                 if phase.capture() {
                     return;
                 }
@@ -473,7 +483,7 @@ impl MarkdownElement {
                     markdown.autoscroll_request = Some(source_index);
                     cx.notify();
                 } else {
-                    let is_hovering_link = hitbox.is_hovered(cx)
+                    let is_hovering_link = hitbox.is_hovered(window)
                         && rendered_text.link_for_position(event.position).is_some();
                     if is_hovering_link != was_hovering_link {
                         cx.notify();
@@ -481,9 +491,9 @@ impl MarkdownElement {
                 }
             }
         });
-        self.on_mouse_event(cx, {
+        self.on_mouse_event(window, cx, {
             let rendered_text = rendered_text.clone();
-            move |markdown, event: &MouseUpEvent, phase, cx| {
+            move |markdown, event: &MouseUpEvent, phase, _, cx| {
                 if phase.bubble() {
                     if let Some(pressed_link) = markdown.pressed_link.take() {
                         if Some(&pressed_link) == rendered_text.link_for_position(event.position) {
@@ -504,22 +514,27 @@ impl MarkdownElement {
         });
     }
 
-    fn autoscroll(&self, rendered_text: &RenderedText, cx: &mut WindowContext) -> Option<()> {
+    fn autoscroll(
+        &self,
+        rendered_text: &RenderedText,
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Option<()> {
         let autoscroll_index = self
             .markdown
             .update(cx, |markdown, _| markdown.autoscroll_request.take())?;
         let (position, line_height) = rendered_text.position_for_source_index(autoscroll_index)?;
 
         let text_style = self.style.base_text_style.clone();
-        let font_id = cx.text_system().resolve_font(&text_style.font());
-        let font_size = text_style.font_size.to_pixels(cx.rem_size());
-        let em_width = cx
+        let font_id = window.text_system().resolve_font(&text_style.font());
+        let font_size = text_style.font_size.to_pixels(window.rem_size());
+        let em_width = window
             .text_system()
             .typographic_bounds(font_id, font_size, 'm')
             .unwrap()
             .size
             .width;
-        cx.request_autoscroll(Bounds::from_corners(
+        window.request_autoscroll(Bounds::from_corners(
             point(position.x - 3. * em_width, position.y - 3. * line_height),
             point(position.x + 3. * em_width, position.y + 3. * line_height),
         ));
@@ -528,14 +543,16 @@ impl MarkdownElement {
 
     fn on_mouse_event<T: MouseEvent>(
         &self,
-        cx: &mut WindowContext,
-        mut f: impl 'static + FnMut(&mut Markdown, &T, DispatchPhase, &mut ViewContext<Markdown>),
+        window: &mut Window,
+        _cx: &mut App,
+        mut f: impl 'static
+            + FnMut(&mut Markdown, &T, DispatchPhase, &mut Window, &mut Context<Markdown>),
     ) {
-        cx.on_mouse_event({
+        window.on_mouse_event({
             let markdown = self.markdown.downgrade();
-            move |event, phase, cx| {
+            move |event, phase, window, cx| {
                 markdown
-                    .update(cx, |markdown, cx| f(markdown, event, phase, cx))
+                    .update(cx, |markdown, cx| f(markdown, event, phase, window, cx))
                     .log_err();
             }
         });
@@ -553,7 +570,8 @@ impl Element for MarkdownElement {
     fn request_layout(
         &mut self,
         _id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (gpui::LayoutId, Self::RequestLayoutState) {
         let mut builder = MarkdownElementBuilder::new(
             self.style.base_text_style.clone(),
@@ -605,7 +623,7 @@ impl Element for MarkdownElement {
                         }
                         MarkdownTag::CodeBlock(kind) => {
                             let language = if let CodeBlockKind::Fenced(language) = kind {
-                                self.load_language(language.as_ref(), cx)
+                                self.load_language(language.as_ref(), window, cx)
                             } else {
                                 None
                             };
@@ -694,14 +712,14 @@ impl Element for MarkdownElement {
                                     IconButton::new(id, IconName::Copy)
                                         .icon_color(Color::Muted)
                                         .shape(ui::IconButtonShape::Square)
-                                        .tooltip(|cx| Tooltip::text("Copy Code Block", cx))
+                                        .tooltip(Tooltip::text("Copy Code Block"))
                                         .on_click({
                                             let code = without_fences(
                                                 parsed_markdown.source()[range.clone()].trim(),
                                             )
                                             .to_string();
 
-                                            move |_, cx| {
+                                            move |_, _, cx| {
                                                 cx.write_to_clipboard(ClipboardItem::new_string(
                                                     code.clone(),
                                                 ))
@@ -774,8 +792,8 @@ impl Element for MarkdownElement {
             }
         }
         let mut rendered_markdown = builder.build();
-        let child_layout_id = rendered_markdown.element.request_layout(cx);
-        let layout_id = cx.request_layout(gpui::Style::default(), [child_layout_id]);
+        let child_layout_id = rendered_markdown.element.request_layout(window, cx);
+        let layout_id = window.request_layout(gpui::Style::default(), [child_layout_id], cx);
         (layout_id, rendered_markdown)
     }
 
@@ -784,14 +802,15 @@ impl Element for MarkdownElement {
         _id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         rendered_markdown: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Self::PrepaintState {
         let focus_handle = self.markdown.read(cx).focus_handle.clone();
-        cx.set_focus_handle(&focus_handle);
+        window.set_focus_handle(&focus_handle, cx);
 
-        let hitbox = cx.insert_hitbox(bounds, false);
-        rendered_markdown.element.prepaint(cx);
-        self.autoscroll(&rendered_markdown.text, cx);
+        let hitbox = window.insert_hitbox(bounds, false);
+        rendered_markdown.element.prepaint(window, cx);
+        self.autoscroll(&rendered_markdown.text, window, cx);
         hitbox
     }
 
@@ -801,25 +820,26 @@ impl Element for MarkdownElement {
         bounds: Bounds<Pixels>,
         rendered_markdown: &mut Self::RequestLayoutState,
         hitbox: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let mut context = KeyContext::default();
         context.add("Markdown");
-        cx.set_key_context(context);
-        let view = self.markdown.clone();
-        cx.on_action(std::any::TypeId::of::<crate::Copy>(), {
+        window.set_key_context(context);
+        let model = self.markdown.clone();
+        window.on_action(std::any::TypeId::of::<crate::Copy>(), {
             let text = rendered_markdown.text.clone();
-            move |_, phase, cx| {
+            move |_, phase, window, cx| {
                 let text = text.clone();
                 if phase == DispatchPhase::Bubble {
-                    view.update(cx, move |this, cx| this.copy(&text, cx))
+                    model.update(cx, move |this, cx| this.copy(&text, window, cx))
                 }
             }
         });
 
-        self.paint_mouse_listeners(hitbox, &rendered_markdown.text, cx);
-        rendered_markdown.element.paint(cx);
-        self.paint_selection(bounds, &rendered_markdown.text, cx);
+        self.paint_mouse_listeners(hitbox, &rendered_markdown.text, window, cx);
+        rendered_markdown.element.paint(window, cx);
+        self.paint_selection(bounds, &rendered_markdown.text, window, cx);
     }
 }
 

crates/markdown_preview/src/markdown_preview.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{actions, AppContext};
+use gpui::{actions, App};
 use workspace::Workspace;
 
 pub mod markdown_elements;
@@ -8,9 +8,12 @@ pub mod markdown_renderer;
 
 actions!(markdown, [OpenPreview, OpenPreviewToTheSide]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(|workspace: &mut Workspace, cx| {
-        markdown_preview_view::MarkdownPreviewView::register(workspace, cx);
+pub fn init(cx: &mut App) {
+    cx.observe_new(|workspace: &mut Workspace, window, cx| {
+        let Some(window) = window else {
+            return;
+        };
+        markdown_preview_view::MarkdownPreviewView::register(workspace, window, cx);
     })
     .detach();
 }

crates/markdown_preview/src/markdown_preview_view.rs 🔗

@@ -6,9 +6,9 @@ use anyhow::Result;
 use editor::scroll::{Autoscroll, AutoscrollStrategy};
 use editor::{Editor, EditorEvent};
 use gpui::{
-    list, AppContext, ClickEvent, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
-    IntoElement, ListState, ParentElement, Render, Styled, Subscription, Task, View, ViewContext,
-    WeakView,
+    list, App, ClickEvent, Context, Entity, EventEmitter, FocusHandle, Focusable,
+    InteractiveElement, IntoElement, ListState, ParentElement, Render, Styled, Subscription, Task,
+    WeakEntity, Window,
 };
 use language::LanguageRegistry;
 use ui::prelude::*;
@@ -27,7 +27,7 @@ use crate::{
 const REPARSE_DEBOUNCE: Duration = Duration::from_millis(200);
 
 pub struct MarkdownPreviewView {
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     active_editor: Option<EditorState>,
     focus_handle: FocusHandle,
     contents: Option<ParsedMarkdown>,
@@ -48,46 +48,47 @@ pub enum MarkdownPreviewMode {
 }
 
 struct EditorState {
-    editor: View<Editor>,
+    editor: Entity<Editor>,
     _subscription: Subscription,
 }
 
 impl MarkdownPreviewView {
-    pub fn register(workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>) {
-        workspace.register_action(move |workspace, _: &OpenPreview, cx| {
+    pub fn register(workspace: &mut Workspace, _window: &mut Window, _cx: &mut Context<Workspace>) {
+        workspace.register_action(move |workspace, _: &OpenPreview, window, cx| {
             if let Some(editor) = Self::resolve_active_item_as_markdown_editor(workspace, cx) {
-                let view = Self::create_markdown_view(workspace, editor, cx);
+                let view = Self::create_markdown_view(workspace, editor, window, cx);
                 workspace.active_pane().update(cx, |pane, cx| {
                     if let Some(existing_view_idx) = Self::find_existing_preview_item_idx(pane) {
-                        pane.activate_item(existing_view_idx, true, true, cx);
+                        pane.activate_item(existing_view_idx, true, true, window, cx);
                     } else {
-                        pane.add_item(Box::new(view.clone()), true, true, None, cx)
+                        pane.add_item(Box::new(view.clone()), true, true, None, window, cx)
                     }
                 });
                 cx.notify();
             }
         });
 
-        workspace.register_action(move |workspace, _: &OpenPreviewToTheSide, cx| {
+        workspace.register_action(move |workspace, _: &OpenPreviewToTheSide, window, cx| {
             if let Some(editor) = Self::resolve_active_item_as_markdown_editor(workspace, cx) {
-                let view = Self::create_markdown_view(workspace, editor.clone(), cx);
+                let view = Self::create_markdown_view(workspace, editor.clone(), window, cx);
                 let pane = workspace
                     .find_pane_in_direction(workspace::SplitDirection::Right, cx)
                     .unwrap_or_else(|| {
                         workspace.split_pane(
                             workspace.active_pane().clone(),
                             workspace::SplitDirection::Right,
+                            window,
                             cx,
                         )
                     });
                 pane.update(cx, |pane, cx| {
                     if let Some(existing_view_idx) = Self::find_existing_preview_item_idx(pane) {
-                        pane.activate_item(existing_view_idx, true, true, cx);
+                        pane.activate_item(existing_view_idx, true, true, window, cx);
                     } else {
-                        pane.add_item(Box::new(view.clone()), false, false, None, cx)
+                        pane.add_item(Box::new(view.clone()), false, false, None, window, cx)
                     }
                 });
-                editor.focus_handle(cx).focus(cx);
+                editor.focus_handle(cx).focus(window);
                 cx.notify();
             }
         });
@@ -101,8 +102,8 @@ impl MarkdownPreviewView {
 
     pub fn resolve_active_item_as_markdown_editor(
         workspace: &Workspace,
-        cx: &mut ViewContext<Workspace>,
-    ) -> Option<View<Editor>> {
+        cx: &mut Context<Workspace>,
+    ) -> Option<Entity<Editor>> {
         if let Some(editor) = workspace
             .active_item(cx)
             .and_then(|item| item.act_as::<Editor>(cx))
@@ -116,9 +117,10 @@ impl MarkdownPreviewView {
 
     fn create_markdown_view(
         workspace: &mut Workspace,
-        editor: View<Editor>,
-        cx: &mut ViewContext<Workspace>,
-    ) -> View<MarkdownPreviewView> {
+        editor: Entity<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Entity<MarkdownPreviewView> {
         let language_registry = workspace.project().read(cx).languages().clone();
         let workspace_handle = workspace.weak_handle();
         MarkdownPreviewView::new(
@@ -127,34 +129,39 @@ impl MarkdownPreviewView {
             workspace_handle,
             language_registry,
             None,
+            window,
             cx,
         )
     }
 
     pub fn new(
         mode: MarkdownPreviewMode,
-        active_editor: View<Editor>,
-        workspace: WeakView<Workspace>,
+        active_editor: Entity<Editor>,
+        workspace: WeakEntity<Workspace>,
         language_registry: Arc<LanguageRegistry>,
         fallback_description: Option<SharedString>,
-        cx: &mut ViewContext<Workspace>,
-    ) -> View<Self> {
-        cx.new_view(|cx: &mut ViewContext<Self>| {
-            let view = cx.view().downgrade();
-
-            let list_state =
-                ListState::new(0, gpui::ListAlignment::Top, px(1000.), move |ix, cx| {
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Entity<Self> {
+        cx.new(|cx| {
+            let view = cx.model().downgrade();
+
+            let list_state = ListState::new(
+                0,
+                gpui::ListAlignment::Top,
+                px(1000.),
+                move |ix, window, cx| {
                     if let Some(view) = view.upgrade() {
-                        view.update(cx, |this, cx| {
+                        view.update(cx, |this: &mut Self, cx| {
                             let Some(contents) = &this.contents else {
                                 return div().into_any();
                             };
 
                             let mut render_cx =
-                                RenderContext::new(Some(this.workspace.clone()), cx)
+                                RenderContext::new(Some(this.workspace.clone()), window, cx)
                                     .with_checkbox_clicked_callback({
                                         let view = view.clone();
-                                        move |checked, source_range, cx| {
+                                        move |checked, source_range, window, cx| {
                                             view.update(cx, |view, cx| {
                                                 if let Some(editor) = view
                                                     .active_editor
@@ -171,7 +178,7 @@ impl MarkdownPreviewView {
                                                         );
                                                     });
                                                     view.parse_markdown_from_active_editor(
-                                                        false, cx,
+                                                        false, window, cx,
                                                     );
                                                     cx.notify();
                                                 }
@@ -190,21 +197,24 @@ impl MarkdownPreviewView {
                                 .id(ix)
                                 .when(should_apply_padding, |this| this.pb_3())
                                 .group("markdown-block")
-                                .on_click(cx.listener(move |this, event: &ClickEvent, cx| {
-                                    if event.down.click_count == 2 {
-                                        if let Some(source_range) = this
-                                            .contents
-                                            .as_ref()
-                                            .and_then(|c| c.children.get(ix))
-                                            .and_then(|block| block.source_range())
-                                        {
-                                            this.move_cursor_to_block(
-                                                cx,
-                                                source_range.start..source_range.start,
-                                            );
+                                .on_click(cx.listener(
+                                    move |this, event: &ClickEvent, window, cx| {
+                                        if event.down.click_count == 2 {
+                                            if let Some(source_range) = this
+                                                .contents
+                                                .as_ref()
+                                                .and_then(|c| c.children.get(ix))
+                                                .and_then(|block| block.source_range())
+                                            {
+                                                this.move_cursor_to_block(
+                                                    window,
+                                                    cx,
+                                                    source_range.start..source_range.start,
+                                                );
+                                            }
                                         }
-                                    }
-                                }))
+                                    },
+                                ))
                                 .map(move |container| {
                                     let indicator = div()
                                         .h_full()
@@ -233,7 +243,8 @@ impl MarkdownPreviewView {
                     } else {
                         div().into_any()
                     }
-                });
+                },
+            );
 
             let mut this = Self {
                 selected_block: 0,
@@ -249,13 +260,13 @@ impl MarkdownPreviewView {
                 parsing_markdown_task: None,
             };
 
-            this.set_editor(active_editor, cx);
+            this.set_editor(active_editor, window, cx);
 
             if mode == MarkdownPreviewMode::Follow {
                 if let Some(workspace) = &workspace.upgrade() {
-                    cx.observe(workspace, |this, workspace, cx| {
+                    cx.observe_in(workspace, window, |this, workspace, window, cx| {
                         let item = workspace.read(cx).active_item(cx);
-                        this.workspace_updated(item, cx);
+                        this.workspace_updated(item, window, cx);
                     })
                     .detach();
                 } else {
@@ -270,20 +281,21 @@ impl MarkdownPreviewView {
     fn workspace_updated(
         &mut self,
         active_item: Option<Box<dyn ItemHandle>>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         if let Some(item) = active_item {
             if item.item_id() != cx.entity_id() {
                 if let Some(editor) = item.act_as::<Editor>(cx) {
                     if Self::is_markdown_file(&editor, cx) {
-                        self.set_editor(editor, cx);
+                        self.set_editor(editor, window, cx);
                     }
                 }
             }
         }
     }
 
-    pub fn is_markdown_file<V>(editor: &View<Editor>, cx: &mut ViewContext<V>) -> bool {
+    pub fn is_markdown_file<V>(editor: &Entity<Editor>, cx: &mut Context<V>) -> bool {
         let buffer = editor.read(cx).buffer().read(cx);
         if let Some(buffer) = buffer.as_singleton() {
             if let Some(language) = buffer.read(cx).language() {
@@ -293,28 +305,32 @@ impl MarkdownPreviewView {
         false
     }
 
-    fn set_editor(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
+    fn set_editor(&mut self, editor: Entity<Editor>, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(active) = &self.active_editor {
             if active.editor == editor {
                 return;
             }
         }
 
-        let subscription = cx.subscribe(&editor, |this, editor, event: &EditorEvent, cx| {
-            match event {
-                EditorEvent::Edited { .. } | EditorEvent::DirtyChanged => {
-                    this.parse_markdown_from_active_editor(true, cx);
-                }
-                EditorEvent::SelectionsChanged { .. } => {
-                    let selection_range =
-                        editor.update(cx, |editor, cx| editor.selections.last::<usize>(cx).range());
-                    this.selected_block = this.get_block_index_under_cursor(selection_range);
-                    this.list_state.scroll_to_reveal_item(this.selected_block);
-                    cx.notify();
-                }
-                _ => {}
-            };
-        });
+        let subscription = cx.subscribe_in(
+            &editor,
+            window,
+            |this, editor, event: &EditorEvent, window, cx| {
+                match event {
+                    EditorEvent::Edited { .. } | EditorEvent::DirtyChanged => {
+                        this.parse_markdown_from_active_editor(true, window, cx);
+                    }
+                    EditorEvent::SelectionsChanged { .. } => {
+                        let selection_range = editor
+                            .update(cx, |editor, cx| editor.selections.last::<usize>(cx).range());
+                        this.selected_block = this.get_block_index_under_cursor(selection_range);
+                        this.list_state.scroll_to_reveal_item(this.selected_block);
+                        cx.notify();
+                    }
+                    _ => {}
+                };
+            },
+        );
 
         self.tab_description = editor
             .read(cx)
@@ -326,18 +342,20 @@ impl MarkdownPreviewView {
             _subscription: subscription,
         });
 
-        self.parse_markdown_from_active_editor(false, cx);
+        self.parse_markdown_from_active_editor(false, window, cx);
     }
 
     fn parse_markdown_from_active_editor(
         &mut self,
         wait_for_debounce: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         if let Some(state) = &self.active_editor {
             self.parsing_markdown_task = Some(self.parse_markdown_in_background(
                 wait_for_debounce,
                 state.editor.clone(),
+                window,
                 cx,
             ));
         }
@@ -346,12 +364,13 @@ impl MarkdownPreviewView {
     fn parse_markdown_in_background(
         &mut self,
         wait_for_debounce: bool,
-        editor: View<Editor>,
-        cx: &mut ViewContext<Self>,
+        editor: Entity<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let language_registry = self.language_registry.clone();
 
-        cx.spawn(move |view, mut cx| async move {
+        cx.spawn_in(window, move |view, mut cx| async move {
             if wait_for_debounce {
                 // Wait for the user to stop typing
                 cx.background_executor().timer(REPARSE_DEBOUNCE).await;
@@ -379,24 +398,27 @@ impl MarkdownPreviewView {
         })
     }
 
-    fn move_cursor_to_block(&self, cx: &mut ViewContext<Self>, selection: Range<usize>) {
+    fn move_cursor_to_block(
+        &self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+        selection: Range<usize>,
+    ) {
         if let Some(state) = &self.active_editor {
             state.editor.update(cx, |editor, cx| {
                 editor.change_selections(
                     Some(Autoscroll::Strategy(AutoscrollStrategy::Center)),
+                    window,
                     cx,
                     |selections| selections.select_ranges(vec![selection]),
                 );
-                editor.focus(cx);
+                window.focus(&editor.focus_handle(cx));
             });
         }
     }
 
     /// The absolute path of the file that is currently being previewed.
-    fn get_folder_for_active_editor(
-        editor: &Editor,
-        cx: &ViewContext<MarkdownPreviewView>,
-    ) -> Option<PathBuf> {
+    fn get_folder_for_active_editor(editor: &Editor, cx: &App) -> Option<PathBuf> {
         if let Some(file) = editor.file_at(0, cx) {
             if let Some(file) = file.as_local() {
                 file.abs_path(cx).parent().map(|p| p.to_path_buf())
@@ -448,8 +470,8 @@ impl MarkdownPreviewView {
     }
 }
 
-impl FocusableView for MarkdownPreviewView {
-    fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
+impl Focusable for MarkdownPreviewView {
+    fn focus_handle(&self, _: &App) -> gpui::FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -462,11 +484,11 @@ impl EventEmitter<PreviewEvent> for MarkdownPreviewView {}
 impl Item for MarkdownPreviewView {
     type Event = PreviewEvent;
 
-    fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
+    fn tab_icon(&self, _window: &Window, _cx: &App) -> Option<Icon> {
         Some(Icon::new(IconName::FileDoc))
     }
 
-    fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
+    fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option<SharedString> {
         Some(if let Some(description) = &self.tab_description {
             description.clone().into()
         } else {
@@ -482,7 +504,7 @@ impl Item for MarkdownPreviewView {
 }
 
 impl Render for MarkdownPreviewView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         v_flex()
             .id("MarkdownPreview")
             .key_context("MarkdownPreview")

crates/markdown_preview/src/markdown_renderer.rs 🔗

@@ -5,10 +5,10 @@ use crate::markdown_elements::{
     ParsedMarkdownTableAlignment, ParsedMarkdownTableRow,
 };
 use gpui::{
-    div, img, px, rems, AbsoluteLength, AnyElement, ClipboardItem, DefiniteLength, Div, Element,
-    ElementId, HighlightStyle, Hsla, ImageSource, InteractiveText, IntoElement, Keystroke, Length,
-    Modifiers, ParentElement, Render, Resource, SharedString, Styled, StyledText, TextStyle, View,
-    WeakView, WindowContext,
+    div, img, px, rems, AbsoluteLength, AnyElement, App, AppContext as _, ClipboardItem, Context,
+    DefiniteLength, Div, Element, ElementId, Entity, HighlightStyle, Hsla, ImageSource,
+    InteractiveText, IntoElement, Keystroke, Length, Modifiers, ParentElement, Render, Resource,
+    SharedString, Styled, StyledText, TextStyle, WeakEntity, Window,
 };
 use settings::Settings;
 use std::{
@@ -21,15 +21,15 @@ use ui::{
     h_flex, relative, tooltip_container, v_flex, ButtonCommon, Checkbox, Clickable, Color,
     FluentBuilder, IconButton, IconName, IconSize, InteractiveElement, Label, LabelCommon,
     LabelSize, LinkPreview, StatefulInteractiveElement, StyledExt, StyledImage, ToggleState,
-    Tooltip, ViewContext, VisibleOnHover, VisualContext as _,
+    Tooltip, VisibleOnHover,
 };
 use workspace::Workspace;
 
-type CheckboxClickedCallback = Arc<Box<dyn Fn(bool, Range<usize>, &mut WindowContext)>>;
+type CheckboxClickedCallback = Arc<Box<dyn Fn(bool, Range<usize>, &mut Window, &mut App)>>;
 
 #[derive(Clone)]
 pub struct RenderContext {
-    workspace: Option<WeakView<Workspace>>,
+    workspace: Option<WeakEntity<Workspace>>,
     next_id: usize,
     buffer_font_family: SharedString,
     buffer_text_style: TextStyle,
@@ -45,12 +45,16 @@ pub struct RenderContext {
 }
 
 impl RenderContext {
-    pub fn new(workspace: Option<WeakView<Workspace>>, cx: &WindowContext) -> RenderContext {
+    pub fn new(
+        workspace: Option<WeakEntity<Workspace>>,
+        window: &mut Window,
+        cx: &mut App,
+    ) -> RenderContext {
         let theme = cx.theme().clone();
 
         let settings = ThemeSettings::get_global(cx);
         let buffer_font_family = settings.buffer_font.family.clone();
-        let mut buffer_text_style = cx.text_style();
+        let mut buffer_text_style = window.text_style();
         buffer_text_style.font_family = buffer_font_family.clone();
 
         RenderContext {
@@ -59,7 +63,7 @@ impl RenderContext {
             indent: 0,
             buffer_font_family,
             buffer_text_style,
-            text_style: cx.text_style(),
+            text_style: window.text_style(),
             syntax_theme: theme.syntax().clone(),
             border_color: theme.colors().border,
             text_color: theme.colors().text,
@@ -72,7 +76,7 @@ impl RenderContext {
 
     pub fn with_checkbox_clicked_callback(
         mut self,
-        callback: impl Fn(bool, Range<usize>, &mut WindowContext) + 'static,
+        callback: impl Fn(bool, Range<usize>, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.checkbox_clicked_callback = Some(Arc::new(Box::new(callback)));
         self
@@ -108,10 +112,11 @@ impl RenderContext {
 
 pub fn render_parsed_markdown(
     parsed: &ParsedMarkdown,
-    workspace: Option<WeakView<Workspace>>,
-    cx: &WindowContext,
+    workspace: Option<WeakEntity<Workspace>>,
+    window: &mut Window,
+    cx: &mut App,
 ) -> Div {
-    let mut cx = RenderContext::new(workspace, cx);
+    let mut cx = RenderContext::new(workspace, window, cx);
 
     v_flex().gap_3().children(
         parsed
@@ -190,15 +195,15 @@ fn render_markdown_list_item(
                     |this, callback| {
                         this.on_click({
                             let range = range.clone();
-                            move |selection, cx| {
+                            move |selection, window, cx| {
                                 let checked = match selection {
                                     ToggleState::Selected => true,
                                     ToggleState::Unselected => false,
                                     _ => return,
                                 };
 
-                                if cx.modifiers().secondary() {
-                                    callback(checked, range.clone(), cx);
+                                if window.modifiers().secondary() {
+                                    callback(checked, range.clone(), window, cx);
                                 }
                             }
                         })
@@ -206,7 +211,7 @@ fn render_markdown_list_item(
                 ),
             )
             .hover(|s| s.cursor_pointer())
-            .tooltip(|cx| {
+            .tooltip(|_, cx| {
                 InteractiveMarkdownElementTooltip::new(None, "toggle checkbox", cx).into()
             })
             .into_any_element(),
@@ -381,11 +386,11 @@ fn render_markdown_code_block(
         .icon_size(IconSize::Small)
         .on_click({
             let contents = parsed.contents.clone();
-            move |_, cx| {
+            move |_, _window, cx| {
                 cx.write_to_clipboard(ClipboardItem::new_string(contents.to_string()));
             }
         })
-        .tooltip(|cx| Tooltip::text("Copy code block", cx))
+        .tooltip(Tooltip::text("Copy code block"))
         .visible_on_hover("markdown-block");
 
     cx.with_common_p(div())
@@ -468,7 +473,7 @@ fn render_markdown_text(parsed_new: &MarkdownParagraph, cx: &mut RenderContext)
                         .tooltip({
                             let links = links.clone();
                             let link_ranges = link_ranges.clone();
-                            move |idx, cx| {
+                            move |idx, _, cx| {
                                 for (ix, range) in link_ranges.iter().enumerate() {
                                     if range.contains(&idx) {
                                         return Some(LinkPreview::new(&links[ix].to_string(), cx));
@@ -479,13 +484,13 @@ fn render_markdown_text(parsed_new: &MarkdownParagraph, cx: &mut RenderContext)
                         })
                         .on_click(
                             link_ranges,
-                            move |clicked_range_ix, window_cx| match &links[clicked_range_ix] {
-                                Link::Web { url } => window_cx.open_url(url),
+                            move |clicked_range_ix, window, cx| match &links[clicked_range_ix] {
+                                Link::Web { url } => cx.open_url(url),
                                 Link::Path { path, .. } => {
                                     if let Some(workspace) = &workspace {
-                                        _ = workspace.update(window_cx, |workspace, cx| {
+                                        _ = workspace.update(cx, |workspace, cx| {
                                             workspace
-                                                .open_abs_path(path.clone(), false, cx)
+                                                .open_abs_path(path.clone(), false, window, cx)
                                                 .detach();
                                         });
                                     }
@@ -516,7 +521,7 @@ fn render_markdown_text(parsed_new: &MarkdownParagraph, cx: &mut RenderContext)
                     }))
                     .tooltip({
                         let link = image.link.clone();
-                        move |cx| {
+                        move |_, cx| {
                             InteractiveMarkdownElementTooltip::new(
                                 Some(link.to_string()),
                                 "open image",
@@ -528,15 +533,15 @@ fn render_markdown_text(parsed_new: &MarkdownParagraph, cx: &mut RenderContext)
                     .on_click({
                         let workspace = workspace_clone.clone();
                         let link = image.link.clone();
-                        move |_, cx| {
-                            if cx.modifiers().secondary() {
+                        move |_, window, cx| {
+                            if window.modifiers().secondary() {
                                 match &link {
                                     Link::Web { url } => cx.open_url(url),
                                     Link::Path { path, .. } => {
                                         if let Some(workspace) = &workspace {
                                             _ = workspace.update(cx, |workspace, cx| {
                                                 workspace
-                                                    .open_abs_path(path.clone(), false, cx)
+                                                    .open_abs_path(path.clone(), false, window, cx)
                                                     .detach();
                                             });
                                         }
@@ -565,14 +570,10 @@ struct InteractiveMarkdownElementTooltip {
 }
 
 impl InteractiveMarkdownElementTooltip {
-    pub fn new(
-        tooltip_text: Option<String>,
-        action_text: &str,
-        cx: &mut WindowContext,
-    ) -> View<Self> {
+    pub fn new(tooltip_text: Option<String>, action_text: &str, cx: &mut App) -> Entity<Self> {
         let tooltip_text = tooltip_text.map(|t| util::truncate_and_trailoff(&t, 50).into());
 
-        cx.new_view(|_| Self {
+        cx.new(|_cx| Self {
             tooltip_text,
             action_text: action_text.to_string(),
         })
@@ -580,8 +581,8 @@ impl InteractiveMarkdownElementTooltip {
 }
 
 impl Render for InteractiveMarkdownElementTooltip {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        tooltip_container(cx, |el, _| {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+        tooltip_container(window, cx, |el, _, _| {
             let secondary_modifier = Keystroke {
                 modifiers: Modifiers::secondary_key(),
                 ..Default::default()

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -11,7 +11,7 @@ use clock::ReplicaId;
 use collections::{BTreeMap, Bound, HashMap, HashSet};
 use futures::{channel::mpsc, SinkExt};
 use git::diff::DiffHunkStatus;
-use gpui::{AppContext, EntityId, EventEmitter, Model, ModelContext, Task};
+use gpui::{App, Context, Entity, EntityId, EventEmitter, Task};
 use itertools::Itertools;
 use language::{
     language_settings::{language_settings, IndentGuideSettings, LanguageSettings},
@@ -49,7 +49,7 @@ use theme::SyntaxTheme;
 use util::post_inc;
 
 #[cfg(any(test, feature = "test-support"))]
-use gpui::Context;
+use gpui::AppContext as _;
 
 const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
 
@@ -78,7 +78,7 @@ pub struct MultiBuffer {
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub enum Event {
     ExcerptsAdded {
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         predecessor: ExcerptId,
         excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
     },
@@ -93,7 +93,7 @@ pub enum Event {
     },
     Edited {
         singleton_buffer_edited: bool,
-        edited_buffer: Option<Model<Buffer>>,
+        edited_buffer: Option<Entity<Buffer>>,
     },
     TransactionUndone {
         transaction_id: TransactionId,
@@ -195,7 +195,7 @@ pub trait ToPointUtf16: 'static + fmt::Debug {
 }
 
 struct BufferState {
-    buffer: Model<Buffer>,
+    buffer: Entity<Buffer>,
     last_version: clock::Global,
     last_non_text_state_update_count: usize,
     excerpts: Vec<Locator>,
@@ -203,7 +203,7 @@ struct BufferState {
 }
 
 struct ChangeSetState {
-    change_set: Model<BufferChangeSet>,
+    change_set: Entity<BufferChangeSet>,
     _subscription: gpui::Subscription,
 }
 
@@ -524,7 +524,7 @@ impl MultiBuffer {
         }
     }
 
-    pub fn clone(&self, new_cx: &mut ModelContext<Self>) -> Self {
+    pub fn clone(&self, new_cx: &mut Context<Self>) -> Self {
         let mut buffers = HashMap::default();
         for (buffer_id, buffer_state) in self.buffers.borrow().iter() {
             buffers.insert(
@@ -574,7 +574,7 @@ impl MultiBuffer {
         self.capability == Capability::ReadOnly
     }
 
-    pub fn singleton(buffer: Model<Buffer>, cx: &mut ModelContext<Self>) -> Self {
+    pub fn singleton(buffer: Entity<Buffer>, cx: &mut Context<Self>) -> Self {
         let mut this = Self::new(buffer.read(cx).capability());
         this.singleton = true;
         this.push_excerpts(
@@ -590,17 +590,17 @@ impl MultiBuffer {
     }
 
     /// Returns an up-to-date snapshot of the MultiBuffer.
-    pub fn snapshot(&self, cx: &AppContext) -> MultiBufferSnapshot {
+    pub fn snapshot(&self, cx: &App) -> MultiBufferSnapshot {
         self.sync(cx);
         self.snapshot.borrow().clone()
     }
 
-    pub fn read(&self, cx: &AppContext) -> Ref<MultiBufferSnapshot> {
+    pub fn read(&self, cx: &App) -> Ref<MultiBufferSnapshot> {
         self.sync(cx);
         self.snapshot.borrow()
     }
 
-    pub fn as_singleton(&self) -> Option<Model<Buffer>> {
+    pub fn as_singleton(&self) -> Option<Entity<Buffer>> {
         if self.singleton {
             return Some(
                 self.buffers
@@ -624,25 +624,25 @@ impl MultiBuffer {
         self.subscriptions.subscribe()
     }
 
-    pub fn is_dirty(&self, cx: &AppContext) -> bool {
+    pub fn is_dirty(&self, cx: &App) -> bool {
         self.read(cx).is_dirty()
     }
 
-    pub fn has_deleted_file(&self, cx: &AppContext) -> bool {
+    pub fn has_deleted_file(&self, cx: &App) -> bool {
         self.read(cx).has_deleted_file()
     }
 
-    pub fn has_conflict(&self, cx: &AppContext) -> bool {
+    pub fn has_conflict(&self, cx: &App) -> bool {
         self.read(cx).has_conflict()
     }
 
     // The `is_empty` signature doesn't match what clippy expects
     #[allow(clippy::len_without_is_empty)]
-    pub fn len(&self, cx: &AppContext) -> usize {
+    pub fn len(&self, cx: &App) -> usize {
         self.read(cx).len()
     }
 
-    pub fn is_empty(&self, cx: &AppContext) -> bool {
+    pub fn is_empty(&self, cx: &App) -> bool {
         self.len(cx) != 0
     }
 
@@ -650,7 +650,7 @@ impl MultiBuffer {
         &self,
         offset: T,
         theme: Option<&SyntaxTheme>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<(BufferId, Vec<OutlineItem<Anchor>>)> {
         self.read(cx).symbols_containing(offset, theme)
     }
@@ -659,7 +659,7 @@ impl MultiBuffer {
         &self,
         edits: I,
         autoindent_mode: Option<AutoindentMode>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) where
         I: IntoIterator<Item = (Range<S>, T)>,
         S: ToOffset,
@@ -685,7 +685,7 @@ impl MultiBuffer {
             snapshot: Ref<MultiBufferSnapshot>,
             edits: Vec<(Range<usize>, Arc<str>)>,
             mut autoindent_mode: Option<AutoindentMode>,
-            cx: &mut ModelContext<MultiBuffer>,
+            cx: &mut Context<MultiBuffer>,
         ) {
             if this.read_only() || this.buffers.borrow().is_empty() {
                 return;
@@ -905,7 +905,7 @@ impl MultiBuffer {
         (buffer_edits, edited_excerpt_ids)
     }
 
-    pub fn autoindent_ranges<I, S>(&self, ranges: I, cx: &mut ModelContext<Self>)
+    pub fn autoindent_ranges<I, S>(&self, ranges: I, cx: &mut Context<Self>)
     where
         I: IntoIterator<Item = Range<S>>,
         S: ToOffset,
@@ -929,7 +929,7 @@ impl MultiBuffer {
             this: &MultiBuffer,
             snapshot: Ref<MultiBufferSnapshot>,
             edits: Vec<(Range<usize>, Arc<str>)>,
-            cx: &mut ModelContext<MultiBuffer>,
+            cx: &mut Context<MultiBuffer>,
         ) {
             if this.read_only() || this.buffers.borrow().is_empty() {
                 return;
@@ -974,7 +974,7 @@ impl MultiBuffer {
         position: impl ToPoint,
         space_above: bool,
         space_below: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Point {
         let multibuffer_point = position.to_point(&self.read(cx));
         let (buffer, buffer_point, _) = self.point_to_buffer_point(multibuffer_point, cx).unwrap();
@@ -986,14 +986,14 @@ impl MultiBuffer {
         multibuffer_point + (empty_line_start - buffer_point)
     }
 
-    pub fn start_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
+    pub fn start_transaction(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> {
         self.start_transaction_at(Instant::now(), cx)
     }
 
     pub fn start_transaction_at(
         &mut self,
         now: Instant,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<TransactionId> {
         if let Some(buffer) = self.as_singleton() {
             return buffer.update(cx, |buffer, _| buffer.start_transaction_at(now));
@@ -1005,14 +1005,14 @@ impl MultiBuffer {
         self.history.start_transaction(now)
     }
 
-    pub fn end_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
+    pub fn end_transaction(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> {
         self.end_transaction_at(Instant::now(), cx)
     }
 
     pub fn end_transaction_at(
         &mut self,
         now: Instant,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<TransactionId> {
         if let Some(buffer) = self.as_singleton() {
             return buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx));
@@ -1038,7 +1038,7 @@ impl MultiBuffer {
     pub fn edited_ranges_for_transaction<D>(
         &self,
         transaction_id: TransactionId,
-        cx: &AppContext,
+        cx: &App,
     ) -> Vec<Range<D>>
     where
         D: TextDimension + Ord + Sub<D, Output = D>,
@@ -1094,7 +1094,7 @@ impl MultiBuffer {
         &mut self,
         transaction: TransactionId,
         destination: TransactionId,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if let Some(buffer) = self.as_singleton() {
             buffer.update(cx, |buffer, _| {
@@ -1124,7 +1124,7 @@ impl MultiBuffer {
         }
     }
 
-    pub fn finalize_last_transaction(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
         self.history.finalize_last_transaction();
         for BufferState { buffer, .. } in self.buffers.borrow().values() {
             buffer.update(cx, |buffer, _| {
@@ -1133,9 +1133,9 @@ impl MultiBuffer {
         }
     }
 
-    pub fn push_transaction<'a, T>(&mut self, buffer_transactions: T, cx: &ModelContext<Self>)
+    pub fn push_transaction<'a, T>(&mut self, buffer_transactions: T, cx: &Context<Self>)
     where
-        T: IntoIterator<Item = (&'a Model<Buffer>, &'a language::Transaction)>,
+        T: IntoIterator<Item = (&'a Entity<Buffer>, &'a language::Transaction)>,
     {
         self.history
             .push_transaction(buffer_transactions, Instant::now(), cx);
@@ -1145,7 +1145,7 @@ impl MultiBuffer {
     pub fn group_until_transaction(
         &mut self,
         transaction_id: TransactionId,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if let Some(buffer) = self.as_singleton() {
             buffer.update(cx, |buffer, _| {
@@ -1161,7 +1161,7 @@ impl MultiBuffer {
         selections: &[Selection<Anchor>],
         line_mode: bool,
         cursor_shape: CursorShape,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let mut selections_by_buffer: HashMap<BufferId, Vec<Selection<text::Anchor>>> =
             Default::default();
@@ -1233,7 +1233,7 @@ impl MultiBuffer {
         }
     }
 
-    pub fn remove_active_selections(&self, cx: &mut ModelContext<Self>) {
+    pub fn remove_active_selections(&self, cx: &mut Context<Self>) {
         for buffer in self.buffers.borrow().values() {
             buffer
                 .buffer
@@ -1241,7 +1241,7 @@ impl MultiBuffer {
         }
     }
 
-    pub fn undo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
+    pub fn undo(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> {
         let mut transaction_id = None;
         if let Some(buffer) = self.as_singleton() {
             transaction_id = buffer.update(cx, |buffer, cx| buffer.undo(cx));
@@ -1274,7 +1274,7 @@ impl MultiBuffer {
         transaction_id
     }
 
-    pub fn redo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
+    pub fn redo(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> {
         if let Some(buffer) = self.as_singleton() {
             return buffer.update(cx, |buffer, cx| buffer.redo(cx));
         }
@@ -1301,7 +1301,7 @@ impl MultiBuffer {
         None
     }
 
-    pub fn undo_transaction(&mut self, transaction_id: TransactionId, cx: &mut ModelContext<Self>) {
+    pub fn undo_transaction(&mut self, transaction_id: TransactionId, cx: &mut Context<Self>) {
         if let Some(buffer) = self.as_singleton() {
             buffer.update(cx, |buffer, cx| buffer.undo_transaction(transaction_id, cx));
         } else if let Some(transaction) = self.history.remove_from_undo(transaction_id) {
@@ -1315,11 +1315,7 @@ impl MultiBuffer {
         }
     }
 
-    pub fn forget_transaction(
-        &mut self,
-        transaction_id: TransactionId,
-        cx: &mut ModelContext<Self>,
-    ) {
+    pub fn forget_transaction(&mut self, transaction_id: TransactionId, cx: &mut Context<Self>) {
         if let Some(buffer) = self.as_singleton() {
             buffer.update(cx, |buffer, _| {
                 buffer.forget_transaction(transaction_id);
@@ -1337,9 +1333,9 @@ impl MultiBuffer {
 
     pub fn push_excerpts<O>(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         ranges: impl IntoIterator<Item = ExcerptRange<O>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Vec<ExcerptId>
     where
         O: text::ToOffset,
@@ -1349,10 +1345,10 @@ impl MultiBuffer {
 
     pub fn push_excerpts_with_context_lines<O>(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         ranges: Vec<Range<O>>,
         context_line_count: u32,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Vec<Range<Anchor>>
     where
         O: text::ToPoint + text::ToOffset,
@@ -1388,9 +1384,9 @@ impl MultiBuffer {
 
     pub fn push_multiple_excerpts_with_context_lines(
         &self,
-        buffers_with_ranges: Vec<(Model<Buffer>, Vec<Range<text::Anchor>>)>,
+        buffers_with_ranges: Vec<(Entity<Buffer>, Vec<Range<text::Anchor>>)>,
         context_line_count: u32,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Vec<Range<Anchor>>> {
         use futures::StreamExt;
 
@@ -1476,9 +1472,9 @@ impl MultiBuffer {
     pub fn insert_excerpts_after<O>(
         &mut self,
         prev_excerpt_id: ExcerptId,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         ranges: impl IntoIterator<Item = ExcerptRange<O>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Vec<ExcerptId>
     where
         O: text::ToOffset,
@@ -1506,9 +1502,9 @@ impl MultiBuffer {
     pub fn insert_excerpts_with_ids_after<O>(
         &mut self,
         prev_excerpt_id: ExcerptId,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         ranges: impl IntoIterator<Item = (ExcerptId, ExcerptRange<O>)>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) where
         O: text::ToOffset,
     {
@@ -1621,7 +1617,7 @@ impl MultiBuffer {
         cx.notify();
     }
 
-    pub fn clear(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn clear(&mut self, cx: &mut Context<Self>) {
         self.sync(cx);
         let ids = self.excerpt_ids();
         self.buffers.borrow_mut().clear();
@@ -1652,8 +1648,8 @@ impl MultiBuffer {
 
     pub fn excerpts_for_buffer(
         &self,
-        buffer: &Model<Buffer>,
-        cx: &AppContext,
+        buffer: &Entity<Buffer>,
+        cx: &App,
     ) -> Vec<(ExcerptId, ExcerptRange<text::Anchor>)> {
         let mut excerpts = Vec::new();
         let snapshot = self.read(cx);
@@ -1676,11 +1672,7 @@ impl MultiBuffer {
         excerpts
     }
 
-    pub fn excerpt_ranges_for_buffer(
-        &self,
-        buffer_id: BufferId,
-        cx: &AppContext,
-    ) -> Vec<Range<Point>> {
+    pub fn excerpt_ranges_for_buffer(&self, buffer_id: BufferId, cx: &App) -> Vec<Range<Point>> {
         let snapshot = self.read(cx);
         let buffers = self.buffers.borrow();
         let mut excerpts = snapshot
@@ -1739,8 +1731,8 @@ impl MultiBuffer {
     pub fn excerpt_containing(
         &self,
         position: impl ToOffset,
-        cx: &AppContext,
-    ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
+        cx: &App,
+    ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
         let snapshot = self.read(cx);
         let offset = position.to_offset(&snapshot);
 
@@ -1767,8 +1759,8 @@ impl MultiBuffer {
     pub fn point_to_buffer_offset<T: ToOffset>(
         &self,
         point: T,
-        cx: &AppContext,
-    ) -> Option<(Model<Buffer>, usize)> {
+        cx: &App,
+    ) -> Option<(Entity<Buffer>, usize)> {
         let snapshot = self.read(cx);
         let (buffer, offset) = snapshot.point_to_buffer_offset(point)?;
         Some((
@@ -1785,8 +1777,8 @@ impl MultiBuffer {
     pub fn point_to_buffer_point<T: ToPoint>(
         &self,
         point: T,
-        cx: &AppContext,
-    ) -> Option<(Model<Buffer>, Point, ExcerptId)> {
+        cx: &App,
+    ) -> Option<(Entity<Buffer>, Point, ExcerptId)> {
         let snapshot = self.read(cx);
         let point = point.to_point(&snapshot);
         let mut cursor = snapshot.cursor::<Point>();
@@ -1808,9 +1800,9 @@ impl MultiBuffer {
 
     pub fn buffer_point_to_anchor(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         point: Point,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<Anchor> {
         let mut found = None;
         let snapshot = buffer.read(cx).snapshot();
@@ -1838,7 +1830,7 @@ impl MultiBuffer {
     pub fn remove_excerpts(
         &mut self,
         excerpt_ids: impl IntoIterator<Item = ExcerptId>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.sync(cx);
         let ids = excerpt_ids.into_iter().collect::<Vec<_>>();
@@ -1929,7 +1921,7 @@ impl MultiBuffer {
     pub fn wait_for_anchors<'a>(
         &self,
         anchors: impl 'a + Iterator<Item = Anchor>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> impl 'static + Future<Output = Result<()>> {
         let borrow = self.buffers.borrow();
         let mut error = None;
@@ -1962,8 +1954,8 @@ impl MultiBuffer {
     pub fn text_anchor_for_position<T: ToOffset>(
         &self,
         position: T,
-        cx: &AppContext,
-    ) -> Option<(Model<Buffer>, language::Anchor)> {
+        cx: &App,
+    ) -> Option<(Entity<Buffer>, language::Anchor)> {
         let snapshot = self.read(cx);
         let anchor = snapshot.anchor_before(position);
         let buffer = self
@@ -1977,9 +1969,9 @@ impl MultiBuffer {
 
     fn on_buffer_event(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         event: &language::BufferEvent,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         cx.emit(match event {
             language::BufferEvent::Edited => Event::Edited {
@@ -2006,11 +1998,7 @@ impl MultiBuffer {
         });
     }
 
-    fn buffer_diff_changed(
-        &mut self,
-        change_set: Model<BufferChangeSet>,
-        cx: &mut ModelContext<Self>,
-    ) {
+    fn buffer_diff_changed(&mut self, change_set: Entity<BufferChangeSet>, cx: &mut Context<Self>) {
         let change_set = change_set.read(cx);
         let buffer_id = change_set.buffer_id;
         let diff = change_set.diff_to_buffer.clone();
@@ -2076,7 +2064,7 @@ impl MultiBuffer {
         });
     }
 
-    pub fn all_buffers(&self) -> HashSet<Model<Buffer>> {
+    pub fn all_buffers(&self) -> HashSet<Entity<Buffer>> {
         self.buffers
             .borrow()
             .values()
@@ -2084,23 +2072,19 @@ impl MultiBuffer {
             .collect()
     }
 
-    pub fn buffer(&self, buffer_id: BufferId) -> Option<Model<Buffer>> {
+    pub fn buffer(&self, buffer_id: BufferId) -> Option<Entity<Buffer>> {
         self.buffers
             .borrow()
             .get(&buffer_id)
             .map(|state| state.buffer.clone())
     }
 
-    pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
+    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
         self.point_to_buffer_offset(point, cx)
             .and_then(|(buffer, offset)| buffer.read(cx).language_at(offset))
     }
 
-    pub fn settings_at<'a, T: ToOffset>(
-        &self,
-        point: T,
-        cx: &'a AppContext,
-    ) -> Cow<'a, LanguageSettings> {
+    pub fn settings_at<'a, T: ToOffset>(&self, point: T, cx: &'a App) -> Cow<'a, LanguageSettings> {
         let mut language = None;
         let mut file = None;
         if let Some((buffer, offset)) = self.point_to_buffer_offset(point, cx) {
@@ -2111,14 +2095,14 @@ impl MultiBuffer {
         language_settings(language.map(|l| l.name()), file, cx)
     }
 
-    pub fn for_each_buffer(&self, mut f: impl FnMut(&Model<Buffer>)) {
+    pub fn for_each_buffer(&self, mut f: impl FnMut(&Entity<Buffer>)) {
         self.buffers
             .borrow()
             .values()
             .for_each(|state| f(&state.buffer))
     }
 
-    pub fn title<'a>(&'a self, cx: &'a AppContext) -> Cow<'a, str> {
+    pub fn title<'a>(&'a self, cx: &'a App) -> Cow<'a, str> {
         if let Some(title) = self.title.as_ref() {
             return title.into();
         }
@@ -2132,13 +2116,13 @@ impl MultiBuffer {
         "untitled".into()
     }
 
-    pub fn set_title(&mut self, title: String, cx: &mut ModelContext<Self>) {
+    pub fn set_title(&mut self, title: String, cx: &mut Context<Self>) {
         self.title = Some(title);
         cx.notify();
     }
 
     /// Preserve preview tabs containing this multibuffer until additional edits occur.
-    pub fn refresh_preview(&self, cx: &mut ModelContext<Self>) {
+    pub fn refresh_preview(&self, cx: &mut Context<Self>) {
         for buffer_state in self.buffers.borrow().values() {
             buffer_state
                 .buffer
@@ -2147,7 +2131,7 @@ impl MultiBuffer {
     }
 
     /// Whether we should preserve the preview status of a tab containing this multi-buffer.
-    pub fn preserve_preview(&self, cx: &AppContext) -> bool {
+    pub fn preserve_preview(&self, cx: &App) -> bool {
         self.buffers
             .borrow()
             .values()
@@ -2155,15 +2139,11 @@ impl MultiBuffer {
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn is_parsing(&self, cx: &AppContext) -> bool {
+    pub fn is_parsing(&self, cx: &App) -> bool {
         self.as_singleton().unwrap().read(cx).is_parsing()
     }
 
-    pub fn add_change_set(
-        &mut self,
-        change_set: Model<BufferChangeSet>,
-        cx: &mut ModelContext<Self>,
-    ) {
+    pub fn add_change_set(&mut self, change_set: Entity<BufferChangeSet>, cx: &mut Context<Self>) {
         let buffer_id = change_set.read(cx).buffer_id;
         self.buffer_diff_changed(change_set.clone(), cx);
         self.diff_bases.insert(
@@ -2175,21 +2155,21 @@ impl MultiBuffer {
         );
     }
 
-    pub fn change_set_for(&self, buffer_id: BufferId) -> Option<Model<BufferChangeSet>> {
+    pub fn change_set_for(&self, buffer_id: BufferId) -> Option<Entity<BufferChangeSet>> {
         self.diff_bases
             .get(&buffer_id)
             .map(|state| state.change_set.clone())
     }
 
-    pub fn expand_diff_hunks(&mut self, ranges: Vec<Range<Anchor>>, cx: &mut ModelContext<Self>) {
+    pub fn expand_diff_hunks(&mut self, ranges: Vec<Range<Anchor>>, cx: &mut Context<Self>) {
         self.expand_or_collapse_diff_hunks(ranges, true, cx);
     }
 
-    pub fn collapse_diff_hunks(&mut self, ranges: Vec<Range<Anchor>>, cx: &mut ModelContext<Self>) {
+    pub fn collapse_diff_hunks(&mut self, ranges: Vec<Range<Anchor>>, cx: &mut Context<Self>) {
         self.expand_or_collapse_diff_hunks(ranges, false, cx);
     }
 
-    pub fn set_all_diff_hunks_expanded(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn set_all_diff_hunks_expanded(&mut self, cx: &mut Context<Self>) {
         self.all_diff_hunks_expanded = true;
         self.expand_or_collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], true, cx);
     }
@@ -2198,18 +2178,14 @@ impl MultiBuffer {
         self.all_diff_hunks_expanded
     }
 
-    pub fn has_multiple_hunks(&self, cx: &AppContext) -> bool {
+    pub fn has_multiple_hunks(&self, cx: &App) -> bool {
         self.read(cx)
             .diff_hunks_in_range(Anchor::min()..Anchor::max())
             .nth(1)
             .is_some()
     }
 
-    pub fn has_expanded_diff_hunks_in_ranges(
-        &self,
-        ranges: &[Range<Anchor>],
-        cx: &AppContext,
-    ) -> bool {
+    pub fn has_expanded_diff_hunks_in_ranges(&self, ranges: &[Range<Anchor>], cx: &App) -> bool {
         let snapshot = self.read(cx);
         let mut cursor = snapshot.diff_transforms.cursor::<usize>(&());
         for range in ranges {
@@ -2236,7 +2212,7 @@ impl MultiBuffer {
         &mut self,
         ranges: Vec<Range<Anchor>>,
         expand: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.sync(cx);
         let snapshot = self.snapshot.borrow_mut();
@@ -2299,7 +2275,7 @@ impl MultiBuffer {
         &mut self,
         id: ExcerptId,
         range: Range<text::Anchor>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.sync(cx);
 
@@ -2367,7 +2343,7 @@ impl MultiBuffer {
         ids: impl IntoIterator<Item = ExcerptId>,
         line_count: u32,
         direction: ExpandExcerptDirection,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if line_count == 0 {
             return;
@@ -2463,7 +2439,7 @@ impl MultiBuffer {
         cx.notify();
     }
 
-    fn sync(&self, cx: &AppContext) {
+    fn sync(&self, cx: &App) {
         let mut snapshot = self.snapshot.borrow_mut();
         let mut excerpts_to_edit = Vec::new();
         let mut non_text_state_updated = false;
@@ -3037,18 +3013,18 @@ impl MultiBuffer {
 
 #[cfg(any(test, feature = "test-support"))]
 impl MultiBuffer {
-    pub fn build_simple(text: &str, cx: &mut gpui::AppContext) -> Model<Self> {
-        let buffer = cx.new_model(|cx| Buffer::local(text, cx));
-        cx.new_model(|cx| Self::singleton(buffer, cx))
+    pub fn build_simple(text: &str, cx: &mut gpui::App) -> Entity<Self> {
+        let buffer = cx.new(|cx| Buffer::local(text, cx));
+        cx.new(|cx| Self::singleton(buffer, cx))
     }
 
     pub fn build_multi<const COUNT: usize>(
         excerpts: [(&str, Vec<Range<Point>>); COUNT],
-        cx: &mut gpui::AppContext,
-    ) -> Model<Self> {
-        let multi = cx.new_model(|_| Self::new(Capability::ReadWrite));
+        cx: &mut gpui::App,
+    ) -> Entity<Self> {
+        let multi = cx.new(|_| Self::new(Capability::ReadWrite));
         for (text, ranges) in excerpts {
-            let buffer = cx.new_model(|cx| Buffer::local(text, cx));
+            let buffer = cx.new(|cx| Buffer::local(text, cx));
             let excerpt_ranges = ranges.into_iter().map(|range| ExcerptRange {
                 context: range,
                 primary: None,
@@ -3061,12 +3037,12 @@ impl MultiBuffer {
         multi
     }
 
-    pub fn build_from_buffer(buffer: Model<Buffer>, cx: &mut gpui::AppContext) -> Model<Self> {
-        cx.new_model(|cx| Self::singleton(buffer, cx))
+    pub fn build_from_buffer(buffer: Entity<Buffer>, cx: &mut gpui::App) -> Entity<Self> {
+        cx.new(|cx| Self::singleton(buffer, cx))
     }
 
-    pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui::AppContext) -> Model<Self> {
-        cx.new_model(|cx| {
+    pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui::App) -> Entity<Self> {
+        cx.new(|cx| {
             let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
             let mutation_count = rng.gen_range(1..=5);
             multibuffer.randomly_edit_excerpts(rng, mutation_count, cx);
@@ -3078,7 +3054,7 @@ impl MultiBuffer {
         &mut self,
         rng: &mut impl rand::Rng,
         edit_count: usize,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         use util::RandomCharIter;
 
@@ -3115,7 +3091,7 @@ impl MultiBuffer {
         &mut self,
         rng: &mut impl rand::Rng,
         mutation_count: usize,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         use rand::prelude::*;
         use std::env;
@@ -3155,7 +3131,7 @@ impl MultiBuffer {
             if excerpt_ids.is_empty() || (rng.gen() && excerpt_ids.len() < max_excerpts) {
                 let buffer_handle = if rng.gen() || self.buffers.borrow().is_empty() {
                     let text = RandomCharIter::new(&mut *rng).take(10).collect::<String>();
-                    buffers.push(cx.new_model(|cx| Buffer::local(text, cx)));
+                    buffers.push(cx.new(|cx| Buffer::local(text, cx)));
                     let buffer = buffers.last().unwrap().read(cx);
                     log::info!(
                         "Creating new buffer {} with text: {:?}",
@@ -3217,7 +3193,7 @@ impl MultiBuffer {
         &mut self,
         rng: &mut impl rand::Rng,
         mutation_count: usize,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         use rand::prelude::*;
 
@@ -3247,7 +3223,7 @@ impl MultiBuffer {
         self.check_invariants(cx);
     }
 
-    fn check_invariants(&self, cx: &AppContext) {
+    fn check_invariants(&self, cx: &App) {
         self.read(cx).check_invariants();
     }
 }
@@ -3949,7 +3925,7 @@ impl MultiBufferSnapshot {
     pub fn suggested_indents(
         &self,
         rows: impl IntoIterator<Item = u32>,
-        cx: &AppContext,
+        cx: &App,
     ) -> BTreeMap<MultiBufferRow, IndentSize> {
         let mut result = BTreeMap::new();
 
@@ -4029,7 +4005,7 @@ impl MultiBufferSnapshot {
         }
     }
 
-    pub fn indent_and_comment_for_line(&self, row: MultiBufferRow, cx: &AppContext) -> String {
+    pub fn indent_and_comment_for_line(&self, row: MultiBufferRow, cx: &App) -> String {
         let mut indent = self.indent_size_for_line(row).chars().collect::<String>();
 
         if self.settings_at(0, cx).extend_comment_on_newline {
@@ -5263,7 +5239,7 @@ impl MultiBufferSnapshot {
         &self,
         range: Range<T>,
         ignore_disabled_for_language: bool,
-        cx: &AppContext,
+        cx: &App,
     ) -> impl Iterator<Item = IndentGuide> {
         let range = range.start.to_point(self)..range.end.to_point(self);
         let start_row = MultiBufferRow(range.start.row);
@@ -5378,7 +5354,7 @@ impl MultiBufferSnapshot {
     pub fn settings_at<'a, T: ToOffset>(
         &'a self,
         point: T,
-        cx: &'a AppContext,
+        cx: &'a App,
     ) -> Cow<'a, LanguageSettings> {
         let mut language = None;
         let mut file = None;
@@ -5403,7 +5379,7 @@ impl MultiBufferSnapshot {
     pub fn language_indent_size_at<T: ToOffset>(
         &self,
         position: T,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<IndentSize> {
         let (buffer_snapshot, offset) = self.point_to_buffer_offset(position)?;
         Some(buffer_snapshot.language_indent_size_at(offset, cx))
@@ -6087,9 +6063,9 @@ impl History {
         &mut self,
         buffer_transactions: T,
         now: Instant,
-        cx: &ModelContext<MultiBuffer>,
+        cx: &Context<MultiBuffer>,
     ) where
-        T: IntoIterator<Item = (&'a Model<Buffer>, &'a language::Transaction)>,
+        T: IntoIterator<Item = (&'a Entity<Buffer>, &'a language::Transaction)>,
     {
         assert_eq!(self.transaction_depth, 0);
         let transaction = Transaction {

crates/multi_buffer/src/multi_buffer_tests.rs 🔗

@@ -1,6 +1,6 @@
 use super::*;
 use git::diff::DiffHunkStatus;
-use gpui::{AppContext, Context, TestAppContext};
+use gpui::{App, TestAppContext};
 use indoc::indoc;
 use language::{Buffer, Rope};
 use parking_lot::RwLock;
@@ -17,9 +17,9 @@ fn init_logger() {
 }
 
 #[gpui::test]
-fn test_empty_singleton(cx: &mut AppContext) {
-    let buffer = cx.new_model(|cx| Buffer::local("", cx));
-    let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
+fn test_empty_singleton(cx: &mut App) {
+    let buffer = cx.new(|cx| Buffer::local("", cx));
+    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
     let snapshot = multibuffer.read(cx).snapshot(cx);
     assert_eq!(snapshot.text(), "");
     assert_eq!(
@@ -33,9 +33,9 @@ fn test_empty_singleton(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_singleton(cx: &mut AppContext) {
-    let buffer = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
-    let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
+fn test_singleton(cx: &mut App) {
+    let buffer = cx.new(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
+    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
 
     let snapshot = multibuffer.read(cx).snapshot(cx);
     assert_eq!(snapshot.text(), buffer.read(cx).text());
@@ -68,9 +68,9 @@ fn test_singleton(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_remote(cx: &mut AppContext) {
-    let host_buffer = cx.new_model(|cx| Buffer::local("a", cx));
-    let guest_buffer = cx.new_model(|cx| {
+fn test_remote(cx: &mut App) {
+    let host_buffer = cx.new(|cx| Buffer::local("a", cx));
+    let guest_buffer = cx.new(|cx| {
         let state = host_buffer.read(cx).to_proto(cx);
         let ops = cx
             .background_executor()
@@ -83,7 +83,7 @@ fn test_remote(cx: &mut AppContext) {
         );
         buffer
     });
-    let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(guest_buffer.clone(), cx));
+    let multibuffer = cx.new(|cx| MultiBuffer::singleton(guest_buffer.clone(), cx));
     let snapshot = multibuffer.read(cx).snapshot(cx);
     assert_eq!(snapshot.text(), "a");
 
@@ -97,10 +97,10 @@ fn test_remote(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_excerpt_boundaries_and_clipping(cx: &mut AppContext) {
-    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
-    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
-    let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
+fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
+    let buffer_1 = cx.new(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
+    let buffer_2 = cx.new(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
+    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
 
     let events = Arc::new(RwLock::new(Vec::<Event>::new()));
     multibuffer.update(cx, |_, cx| {
@@ -354,17 +354,17 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_diff_boundary_anchors(cx: &mut AppContext) {
+fn test_diff_boundary_anchors(cx: &mut App) {
     let base_text = "one\ntwo\nthree\n";
     let text = "one\nthree\n";
-    let buffer = cx.new_model(|cx| Buffer::local(text, cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx));
     let snapshot = buffer.read(cx).snapshot();
-    let change_set = cx.new_model(|cx| {
+    let change_set = cx.new(|cx| {
         let mut change_set = BufferChangeSet::new(&buffer, cx);
         change_set.recalculate_diff_sync(base_text.into(), snapshot.text, true, cx);
         change_set
     });
-    let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
+    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
     multibuffer.update(cx, |multibuffer, cx| {
         multibuffer.add_change_set(change_set, cx)
     });
@@ -406,14 +406,14 @@ fn test_diff_boundary_anchors(cx: &mut AppContext) {
 fn test_diff_hunks_in_range(cx: &mut TestAppContext) {
     let base_text = "one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\n";
     let text = "one\nfour\nseven\n";
-    let buffer = cx.new_model(|cx| Buffer::local(text, cx));
-    let change_set = cx.new_model(|cx| {
+    let buffer = cx.new(|cx| Buffer::local(text, cx));
+    let change_set = cx.new(|cx| {
         let mut change_set = BufferChangeSet::new(&buffer, cx);
         let snapshot = buffer.read(cx).snapshot();
         change_set.recalculate_diff_sync(base_text.into(), snapshot.text, true, cx);
         change_set
     });
-    let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
+    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
     let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| {
         (multibuffer.snapshot(cx), multibuffer.subscribe())
     });
@@ -504,14 +504,14 @@ fn test_diff_hunks_in_range(cx: &mut TestAppContext) {
 fn test_editing_text_in_diff_hunks(cx: &mut TestAppContext) {
     let base_text = "one\ntwo\nfour\nfive\nsix\nseven\n";
     let text = "one\ntwo\nTHREE\nfour\nfive\nseven\n";
-    let buffer = cx.new_model(|cx| Buffer::local(text, cx));
-    let change_set = cx.new_model(|cx| {
+    let buffer = cx.new(|cx| Buffer::local(text, cx));
+    let change_set = cx.new(|cx| {
         let mut change_set = BufferChangeSet::new(&buffer, cx);
         let snapshot = buffer.read(cx).snapshot();
         change_set.recalculate_diff_sync(base_text.into(), snapshot.text, true, cx);
         change_set
     });
-    let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
+    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
 
     let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| {
         multibuffer.add_change_set(change_set.clone(), cx);
@@ -660,12 +660,12 @@ fn test_editing_text_in_diff_hunks(cx: &mut TestAppContext) {
 }
 
 #[gpui::test]
-fn test_excerpt_events(cx: &mut AppContext) {
-    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(10, 3, 'a'), cx));
-    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(10, 3, 'm'), cx));
+fn test_excerpt_events(cx: &mut App) {
+    let buffer_1 = cx.new(|cx| Buffer::local(sample_text(10, 3, 'a'), cx));
+    let buffer_2 = cx.new(|cx| Buffer::local(sample_text(10, 3, 'm'), cx));
 
-    let leader_multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
-    let follower_multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
+    let leader_multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
+    let follower_multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
     let follower_edit_event_count = Arc::new(RwLock::new(0));
 
     follower_multibuffer.update(cx, |_, cx| {
@@ -766,9 +766,9 @@ fn test_excerpt_events(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_expand_excerpts(cx: &mut AppContext) {
-    let buffer = cx.new_model(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
-    let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
+fn test_expand_excerpts(cx: &mut App) {
+    let buffer = cx.new(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
+    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
 
     multibuffer.update(cx, |multibuffer, cx| {
         multibuffer.push_excerpts_with_context_lines(
@@ -842,9 +842,9 @@ fn test_expand_excerpts(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_push_excerpts_with_context_lines(cx: &mut AppContext) {
-    let buffer = cx.new_model(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
-    let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
+fn test_push_excerpts_with_context_lines(cx: &mut App) {
+    let buffer = cx.new(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
+    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
     let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| {
         multibuffer.push_excerpts_with_context_lines(
             buffer.clone(),
@@ -896,8 +896,8 @@ fn test_push_excerpts_with_context_lines(cx: &mut AppContext) {
 
 #[gpui::test(iterations = 100)]
 async fn test_push_multiple_excerpts_with_context_lines(cx: &mut TestAppContext) {
-    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
-    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(15, 4, 'a'), cx));
+    let buffer_1 = cx.new(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
+    let buffer_2 = cx.new(|cx| Buffer::local(sample_text(15, 4, 'a'), cx));
     let snapshot_1 = buffer_1.update(cx, |buffer, _| buffer.snapshot());
     let snapshot_2 = buffer_2.update(cx, |buffer, _| buffer.snapshot());
     let ranges_1 = vec![
@@ -910,7 +910,7 @@ async fn test_push_multiple_excerpts_with_context_lines(cx: &mut TestAppContext)
         snapshot_2.anchor_before(Point::new(10, 0))..snapshot_2.anchor_before(Point::new(10, 2)),
     ];
 
-    let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
+    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
     let anchor_ranges = multibuffer
         .update(cx, |multibuffer, cx| {
             multibuffer.push_multiple_excerpts_with_context_lines(
@@ -972,8 +972,8 @@ async fn test_push_multiple_excerpts_with_context_lines(cx: &mut TestAppContext)
 }
 
 #[gpui::test]
-fn test_empty_multibuffer(cx: &mut AppContext) {
-    let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
+fn test_empty_multibuffer(cx: &mut App) {
+    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
 
     let snapshot = multibuffer.read(cx).snapshot(cx);
     assert_eq!(snapshot.text(), "");
@@ -994,9 +994,9 @@ fn test_empty_multibuffer(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_singleton_multibuffer_anchors(cx: &mut AppContext) {
-    let buffer = cx.new_model(|cx| Buffer::local("abcd", cx));
-    let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
+fn test_singleton_multibuffer_anchors(cx: &mut App) {
+    let buffer = cx.new(|cx| Buffer::local("abcd", cx));
+    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
     let old_snapshot = multibuffer.read(cx).snapshot(cx);
     buffer.update(cx, |buffer, cx| {
         buffer.edit([(0..0, "X")], None, cx);
@@ -1014,10 +1014,10 @@ fn test_singleton_multibuffer_anchors(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_multibuffer_anchors(cx: &mut AppContext) {
-    let buffer_1 = cx.new_model(|cx| Buffer::local("abcd", cx));
-    let buffer_2 = cx.new_model(|cx| Buffer::local("efghi", cx));
-    let multibuffer = cx.new_model(|cx| {
+fn test_multibuffer_anchors(cx: &mut App) {
+    let buffer_1 = cx.new(|cx| Buffer::local("abcd", cx));
+    let buffer_2 = cx.new(|cx| Buffer::local("efghi", cx));
+    let multibuffer = cx.new(|cx| {
         let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
         multibuffer.push_excerpts(
             buffer_1.clone(),
@@ -1072,10 +1072,10 @@ fn test_multibuffer_anchors(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut AppContext) {
-    let buffer_1 = cx.new_model(|cx| Buffer::local("abcd", cx));
-    let buffer_2 = cx.new_model(|cx| Buffer::local("ABCDEFGHIJKLMNOP", cx));
-    let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
+fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut App) {
+    let buffer_1 = cx.new(|cx| Buffer::local("abcd", cx));
+    let buffer_2 = cx.new(|cx| Buffer::local("ABCDEFGHIJKLMNOP", cx));
+    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
 
     // Create an insertion id in buffer 1 that doesn't exist in buffer 2.
     // Add an excerpt from buffer 1 that spans this new insertion.
@@ -1224,12 +1224,12 @@ fn test_basic_diff_hunks(cx: &mut TestAppContext) {
         "
     );
 
-    let buffer = cx.new_model(|cx| Buffer::local(text, cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx));
     let change_set =
-        cx.new_model(|cx| BufferChangeSet::new_with_base_text(base_text.to_string(), &buffer, cx));
+        cx.new(|cx| BufferChangeSet::new_with_base_text(base_text.to_string(), &buffer, cx));
     cx.run_until_parked();
 
-    let multibuffer = cx.new_model(|cx| {
+    let multibuffer = cx.new(|cx| {
         let mut multibuffer = MultiBuffer::singleton(buffer.clone(), cx);
         multibuffer.add_change_set(change_set.clone(), cx);
         multibuffer
@@ -1468,12 +1468,12 @@ fn test_repeatedly_expand_a_diff_hunk(cx: &mut TestAppContext) {
         "
     );
 
-    let buffer = cx.new_model(|cx| Buffer::local(text, cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx));
     let change_set =
-        cx.new_model(|cx| BufferChangeSet::new_with_base_text(base_text.to_string(), &buffer, cx));
+        cx.new(|cx| BufferChangeSet::new_with_base_text(base_text.to_string(), &buffer, cx));
     cx.run_until_parked();
 
-    let multibuffer = cx.new_model(|cx| {
+    let multibuffer = cx.new(|cx| {
         let mut multibuffer = MultiBuffer::singleton(buffer.clone(), cx);
         multibuffer.add_change_set(change_set.clone(), cx);
         multibuffer
@@ -1573,17 +1573,15 @@ fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) {
         "
     );
 
-    let buffer_1 = cx.new_model(|cx| Buffer::local(text_1, cx));
-    let buffer_2 = cx.new_model(|cx| Buffer::local(text_2, cx));
-    let change_set_1 = cx.new_model(|cx| {
-        BufferChangeSet::new_with_base_text(base_text_1.to_string(), &buffer_1, cx)
-    });
-    let change_set_2 = cx.new_model(|cx| {
-        BufferChangeSet::new_with_base_text(base_text_2.to_string(), &buffer_2, cx)
-    });
+    let buffer_1 = cx.new(|cx| Buffer::local(text_1, cx));
+    let buffer_2 = cx.new(|cx| Buffer::local(text_2, cx));
+    let change_set_1 =
+        cx.new(|cx| BufferChangeSet::new_with_base_text(base_text_1.to_string(), &buffer_1, cx));
+    let change_set_2 =
+        cx.new(|cx| BufferChangeSet::new_with_base_text(base_text_2.to_string(), &buffer_2, cx));
     cx.run_until_parked();
 
-    let multibuffer = cx.new_model(|cx| {
+    let multibuffer = cx.new(|cx| {
         let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
         multibuffer.push_excerpts(
             buffer_1.clone(),
@@ -1762,12 +1760,12 @@ fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) {
 #[derive(Default)]
 struct ReferenceMultibuffer {
     excerpts: Vec<ReferenceExcerpt>,
-    change_sets: HashMap<BufferId, Model<BufferChangeSet>>,
+    change_sets: HashMap<BufferId, Entity<BufferChangeSet>>,
 }
 
 struct ReferenceExcerpt {
     id: ExcerptId,
-    buffer: Model<Buffer>,
+    buffer: Entity<Buffer>,
     range: Range<text::Anchor>,
     expanded_diff_hunks: Vec<text::Anchor>,
 }
@@ -1780,7 +1778,7 @@ struct ReferenceRegion {
 }
 
 impl ReferenceMultibuffer {
-    fn expand_excerpts(&mut self, excerpts: &HashSet<ExcerptId>, line_count: u32, cx: &AppContext) {
+    fn expand_excerpts(&mut self, excerpts: &HashSet<ExcerptId>, line_count: u32, cx: &App) {
         if line_count == 0 {
             return;
         }
@@ -1798,7 +1796,7 @@ impl ReferenceMultibuffer {
         }
     }
 
-    fn remove_excerpt(&mut self, id: ExcerptId, cx: &AppContext) {
+    fn remove_excerpt(&mut self, id: ExcerptId, cx: &App) {
         let ix = self
             .excerpts
             .iter()
@@ -1819,7 +1817,7 @@ impl ReferenceMultibuffer {
         &mut self,
         prev_id: ExcerptId,
         new_excerpt_id: ExcerptId,
-        (buffer_handle, anchor_range): (Model<Buffer>, Range<text::Anchor>),
+        (buffer_handle, anchor_range): (Entity<Buffer>, Range<text::Anchor>),
     ) {
         let excerpt_ix = if prev_id == ExcerptId::max() {
             self.excerpts.len()
@@ -1841,12 +1839,7 @@ impl ReferenceMultibuffer {
         );
     }
 
-    fn expand_diff_hunks(
-        &mut self,
-        excerpt_id: ExcerptId,
-        range: Range<text::Anchor>,
-        cx: &AppContext,
-    ) {
+    fn expand_diff_hunks(&mut self, excerpt_id: ExcerptId, range: Range<text::Anchor>, cx: &App) {
         let excerpt = self
             .excerpts
             .iter_mut()
@@ -1894,7 +1887,7 @@ impl ReferenceMultibuffer {
         }
     }
 
-    fn expected_content(&self, cx: &AppContext) -> (String, Vec<RowInfo>, HashSet<MultiBufferRow>) {
+    fn expected_content(&self, cx: &App) -> (String, Vec<RowInfo>, HashSet<MultiBufferRow>) {
         let mut text = String::new();
         let mut regions = Vec::<ReferenceRegion>::new();
         let mut excerpt_boundary_rows = HashSet::default();
@@ -2030,7 +2023,7 @@ impl ReferenceMultibuffer {
         (text, row_infos, excerpt_boundary_rows)
     }
 
-    fn diffs_updated(&mut self, cx: &AppContext) {
+    fn diffs_updated(&mut self, cx: &App) {
         for excerpt in &mut self.excerpts {
             let buffer = excerpt.buffer.read(cx).snapshot();
             let excerpt_range = excerpt.range.to_offset(&buffer);
@@ -2064,20 +2057,20 @@ impl ReferenceMultibuffer {
         }
     }
 
-    fn add_change_set(&mut self, change_set: Model<BufferChangeSet>, cx: &mut AppContext) {
+    fn add_change_set(&mut self, change_set: Entity<BufferChangeSet>, cx: &mut App) {
         let buffer_id = change_set.read(cx).buffer_id;
         self.change_sets.insert(buffer_id, change_set);
     }
 }
 
 #[gpui::test(iterations = 100)]
-fn test_random_multibuffer(cx: &mut AppContext, mut rng: StdRng) {
+fn test_random_multibuffer(cx: &mut App, mut rng: StdRng) {
     let operations = env::var("OPERATIONS")
         .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
         .unwrap_or(10);
 
-    let mut buffers: Vec<Model<Buffer>> = Vec::new();
-    let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
+    let mut buffers: Vec<Entity<Buffer>> = Vec::new();
+    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
     let mut reference = ReferenceMultibuffer::default();
     let mut anchors = Vec::new();
     let mut old_versions = Vec::new();
@@ -2214,9 +2207,9 @@ fn test_random_multibuffer(cx: &mut AppContext, mut rng: StdRng) {
                         .take(256)
                         .collect::<String>();
 
-                    let buffer = cx.new_model(|cx| Buffer::local(base_text.clone(), cx));
+                    let buffer = cx.new(|cx| Buffer::local(base_text.clone(), cx));
                     let snapshot = buffer.read(cx).snapshot();
-                    let change_set = cx.new_model(|cx| {
+                    let change_set = cx.new(|cx| {
                         let mut change_set = BufferChangeSet::new(&buffer, cx);
                         change_set.recalculate_diff_sync(base_text, snapshot.text, true, cx);
                         change_set
@@ -2431,21 +2424,21 @@ fn test_random_multibuffer(cx: &mut AppContext, mut rng: StdRng) {
 }
 
 #[gpui::test]
-fn test_history(cx: &mut AppContext) {
+fn test_history(cx: &mut App) {
     let test_settings = SettingsStore::test(cx);
     cx.set_global(test_settings);
     let group_interval: Duration = Duration::from_millis(1);
-    let buffer_1 = cx.new_model(|cx| {
+    let buffer_1 = cx.new(|cx| {
         let mut buf = Buffer::local("1234", cx);
         buf.set_group_interval(group_interval);
         buf
     });
-    let buffer_2 = cx.new_model(|cx| {
+    let buffer_2 = cx.new(|cx| {
         let mut buf = Buffer::local("5678", cx);
         buf.set_group_interval(group_interval);
         buf
     });
-    let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
+    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
     multibuffer.update(cx, |this, _| {
         this.history.group_interval = group_interval;
     });
@@ -2710,7 +2703,7 @@ fn format_diff(
 
 #[track_caller]
 fn assert_new_snapshot(
-    multibuffer: &Model<MultiBuffer>,
+    multibuffer: &Entity<MultiBuffer>,
     snapshot: &mut MultiBufferSnapshot,
     subscription: &mut Subscription,
     cx: &mut TestAppContext,

crates/notifications/src/notification_store.rs 🔗

@@ -1,31 +1,29 @@
-use anyhow::{Context, Result};
+use anyhow::{Context as _, Result};
 use channel::{ChannelMessage, ChannelMessageId, ChannelStore};
 use client::{ChannelId, Client, UserStore};
 use collections::HashMap;
 use db::smol::stream::StreamExt;
-use gpui::{
-    AppContext, AsyncAppContext, Context as _, EventEmitter, Global, Model, ModelContext, Task,
-};
+use gpui::{App, AppContext as _, AsyncAppContext, Context, Entity, EventEmitter, Global, Task};
 use rpc::{proto, Notification, TypedEnvelope};
 use std::{ops::Range, sync::Arc};
 use sum_tree::{Bias, SumTree};
 use time::OffsetDateTime;
 use util::ResultExt;
 
-pub fn init(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut AppContext) {
-    let notification_store = cx.new_model(|cx| NotificationStore::new(client, user_store, cx));
+pub fn init(client: Arc<Client>, user_store: Entity<UserStore>, cx: &mut App) {
+    let notification_store = cx.new(|cx| NotificationStore::new(client, user_store, cx));
     cx.set_global(GlobalNotificationStore(notification_store));
 }
 
-struct GlobalNotificationStore(Model<NotificationStore>);
+struct GlobalNotificationStore(Entity<NotificationStore>);
 
 impl Global for GlobalNotificationStore {}
 
 pub struct NotificationStore {
     client: Arc<Client>,
-    user_store: Model<UserStore>,
+    user_store: Entity<UserStore>,
     channel_messages: HashMap<u64, ChannelMessage>,
-    channel_store: Model<ChannelStore>,
+    channel_store: Entity<ChannelStore>,
     notifications: SumTree<NotificationEntry>,
     loaded_all_notifications: bool,
     _watch_connection_status: Task<Option<()>>,
@@ -72,15 +70,11 @@ struct Count(usize);
 struct NotificationId(u64);
 
 impl NotificationStore {
-    pub fn global(cx: &AppContext) -> Model<Self> {
+    pub fn global(cx: &App) -> Entity<Self> {
         cx.global::<GlobalNotificationStore>().0.clone()
     }
 
-    pub fn new(
-        client: Arc<Client>,
-        user_store: Model<UserStore>,
-        cx: &mut ModelContext<Self>,
-    ) -> Self {
+    pub fn new(client: Arc<Client>, user_store: Entity<UserStore>, cx: &mut Context<Self>) -> Self {
         let mut connection_status = client.status();
         let watch_connection_status = cx.spawn(|this, mut cx| async move {
             while let Some(status) = connection_status.next().await {
@@ -155,7 +149,7 @@ impl NotificationStore {
     pub fn load_more_notifications(
         &self,
         clear_old: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<Task<Result<()>>> {
         if self.loaded_all_notifications && !clear_old {
             return None;
@@ -191,19 +185,19 @@ impl NotificationStore {
         }))
     }
 
-    fn handle_connect(&mut self, cx: &mut ModelContext<Self>) -> Option<Task<Result<()>>> {
+    fn handle_connect(&mut self, cx: &mut Context<Self>) -> Option<Task<Result<()>>> {
         self.notifications = Default::default();
         self.channel_messages = Default::default();
         cx.notify();
         self.load_more_notifications(true, cx)
     }
 
-    fn handle_disconnect(&mut self, cx: &mut ModelContext<Self>) {
+    fn handle_disconnect(&mut self, cx: &mut Context<Self>) {
         cx.notify()
     }
 
     async fn handle_new_notification(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::AddNotification>,
         cx: AsyncAppContext,
     ) -> Result<()> {
@@ -221,7 +215,7 @@ impl NotificationStore {
     }
 
     async fn handle_delete_notification(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::DeleteNotification>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -232,7 +226,7 @@ impl NotificationStore {
     }
 
     async fn handle_update_notification(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::UpdateNotification>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -262,7 +256,7 @@ impl NotificationStore {
     }
 
     async fn add_notifications(
-        this: Model<Self>,
+        this: Entity<Self>,
         notifications: Vec<proto::Notification>,
         options: AddNotificationsOptions,
         mut cx: AsyncAppContext,
@@ -366,7 +360,7 @@ impl NotificationStore {
         &mut self,
         notifications: impl IntoIterator<Item = (u64, Option<NotificationEntry>)>,
         is_new: bool,
-        cx: &mut ModelContext<'_, NotificationStore>,
+        cx: &mut Context<'_, NotificationStore>,
     ) {
         let mut cursor = self.notifications.cursor::<(NotificationId, Count)>(&());
         let mut new_notifications = SumTree::default();
@@ -425,7 +419,7 @@ impl NotificationStore {
         &mut self,
         notification: Notification,
         response: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match notification {
             Notification::ContactRequest { sender_id } => {

crates/ollama/src/ollama.rs 🔗

@@ -1,4 +1,4 @@
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use futures::{io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, StreamExt};
 use http_client::{http, AsyncBody, HttpClient, Method, Request as HttpRequest};
 use schemars::JsonSchema;

crates/open_ai/src/open_ai.rs 🔗

@@ -1,6 +1,6 @@
 mod supported_countries;
 
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use futures::{
     io::BufReader,
     stream::{self, BoxStream},

crates/outline/src/outline.rs 🔗

@@ -7,9 +7,9 @@ use std::{
 use editor::{scroll::Autoscroll, Anchor, AnchorRangeExt, Editor, EditorMode};
 use fuzzy::StringMatch;
 use gpui::{
-    div, rems, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, HighlightStyle,
-    ParentElement, Point, Render, Styled, StyledText, Task, TextStyle, View, ViewContext,
-    VisualContext, WeakView, WindowContext,
+    div, rems, App, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable,
+    HighlightStyle, ParentElement, Point, Render, Styled, StyledText, Task, TextStyle, WeakEntity,
+    Window,
 };
 use language::{Outline, OutlineItem};
 use ordered_float::OrderedFloat;
@@ -20,23 +20,24 @@ use ui::{prelude::*, ListItem, ListItemSpacing};
 use util::ResultExt;
 use workspace::{DismissDecision, ModalView};
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(OutlineView::register).detach();
+pub fn init(cx: &mut App) {
+    cx.observe_new(OutlineView::register).detach();
     zed_actions::outline::TOGGLE_OUTLINE
-        .set(|view, cx| {
-            let Ok(view) = view.downcast::<Editor>() else {
+        .set(|view, window, cx| {
+            let Ok(editor) = view.downcast::<Editor>() else {
                 return;
             };
 
-            toggle(view, &Default::default(), cx);
+            toggle(editor, &Default::default(), window, cx);
         })
         .ok();
 }
 
 pub fn toggle(
-    editor: View<Editor>,
+    editor: Entity<Editor>,
     _: &zed_actions::outline::ToggleOutline,
-    cx: &mut WindowContext,
+    window: &mut Window,
+    cx: &mut App,
 ) {
     let outline = editor
         .read(cx)
@@ -47,44 +48,51 @@ pub fn toggle(
 
     if let Some((workspace, outline)) = editor.read(cx).workspace().zip(outline) {
         workspace.update(cx, |workspace, cx| {
-            workspace.toggle_modal(cx, |cx| OutlineView::new(outline, editor, cx));
+            workspace.toggle_modal(window, cx, |window, cx| {
+                OutlineView::new(outline, editor, window, cx)
+            });
         })
     }
 }
 
 pub struct OutlineView {
-    picker: View<Picker<OutlineViewDelegate>>,
+    picker: Entity<Picker<OutlineViewDelegate>>,
 }
 
-impl FocusableView for OutlineView {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for OutlineView {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
 
 impl EventEmitter<DismissEvent> for OutlineView {}
 impl ModalView for OutlineView {
-    fn on_before_dismiss(&mut self, cx: &mut ViewContext<Self>) -> DismissDecision {
-        self.picker
-            .update(cx, |picker, cx| picker.delegate.restore_active_editor(cx));
+    fn on_before_dismiss(
+        &mut self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> DismissDecision {
+        self.picker.update(cx, |picker, cx| {
+            picker.delegate.restore_active_editor(window, cx)
+        });
         DismissDecision::Dismiss(true)
     }
 }
 
 impl Render for OutlineView {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         v_flex().w(rems(34.)).child(self.picker.clone())
     }
 }
 
 impl OutlineView {
-    fn register(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
+    fn register(editor: &mut Editor, _: Option<&mut Window>, cx: &mut Context<Editor>) {
         if editor.mode() == EditorMode::Full {
-            let handle = cx.view().downgrade();
+            let handle = cx.model().downgrade();
             editor
-                .register_action(move |action, cx| {
+                .register_action(move |action, window, cx| {
                     if let Some(editor) = handle.upgrade() {
-                        toggle(editor, action, cx);
+                        toggle(editor, action, window, cx);
                     }
                 })
                 .detach();
@@ -93,19 +101,21 @@ impl OutlineView {
 
     fn new(
         outline: Outline<Anchor>,
-        editor: View<Editor>,
-        cx: &mut ViewContext<Self>,
+        editor: Entity<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> OutlineView {
-        let delegate = OutlineViewDelegate::new(cx.view().downgrade(), outline, editor, cx);
-        let picker =
-            cx.new_view(|cx| Picker::uniform_list(delegate, cx).max_height(Some(vh(0.75, cx))));
+        let delegate = OutlineViewDelegate::new(cx.model().downgrade(), outline, editor, cx);
+        let picker = cx.new(|cx| {
+            Picker::uniform_list(delegate, window, cx).max_height(Some(vh(0.75, window)))
+        });
         OutlineView { picker }
     }
 }
 
 struct OutlineViewDelegate {
-    outline_view: WeakView<OutlineView>,
-    active_editor: View<Editor>,
+    outline_view: WeakEntity<OutlineView>,
+    active_editor: Entity<Editor>,
     outline: Outline<Anchor>,
     selected_match_index: usize,
     prev_scroll_position: Option<Point<f32>>,
@@ -117,10 +127,11 @@ enum OutlineRowHighlights {}
 
 impl OutlineViewDelegate {
     fn new(
-        outline_view: WeakView<OutlineView>,
+        outline_view: WeakEntity<OutlineView>,
         outline: Outline<Anchor>,
-        editor: View<Editor>,
-        cx: &mut ViewContext<OutlineView>,
+        editor: Entity<Editor>,
+
+        cx: &mut Context<OutlineView>,
     ) -> Self {
         Self {
             outline_view,
@@ -133,11 +144,11 @@ impl OutlineViewDelegate {
         }
     }
 
-    fn restore_active_editor(&mut self, cx: &mut WindowContext) {
+    fn restore_active_editor(&mut self, window: &mut Window, cx: &mut App) {
         self.active_editor.update(cx, |editor, cx| {
             editor.clear_row_highlights::<OutlineRowHighlights>();
             if let Some(scroll_position) = self.prev_scroll_position {
-                editor.set_scroll_position(scroll_position, cx);
+                editor.set_scroll_position(scroll_position, window, cx);
             }
         })
     }
@@ -146,7 +157,8 @@ impl OutlineViewDelegate {
         &mut self,
         ix: usize,
         navigate: bool,
-        cx: &mut ViewContext<Picker<OutlineViewDelegate>>,
+
+        cx: &mut Context<Picker<OutlineViewDelegate>>,
     ) {
         self.selected_match_index = ix;
 
@@ -171,7 +183,7 @@ impl OutlineViewDelegate {
 impl PickerDelegate for OutlineViewDelegate {
     type ListItem = ListItem;
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Search buffer symbols...".into()
     }
 
@@ -183,18 +195,24 @@ impl PickerDelegate for OutlineViewDelegate {
         self.selected_match_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<OutlineViewDelegate>>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        _: &mut Window,
+        cx: &mut Context<Picker<OutlineViewDelegate>>,
+    ) {
         self.set_selected_index(ix, true, cx);
     }
 
     fn update_matches(
         &mut self,
         query: String,
-        cx: &mut ViewContext<Picker<OutlineViewDelegate>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<OutlineViewDelegate>>,
     ) -> Task<()> {
         let selected_index;
         if query.is_empty() {
-            self.restore_active_editor(cx);
+            self.restore_active_editor(window, cx);
             self.matches = self
                 .outline
                 .items
@@ -252,7 +270,12 @@ impl PickerDelegate for OutlineViewDelegate {
         Task::ready(())
     }
 
-    fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<OutlineViewDelegate>>) {
+    fn confirm(
+        &mut self,
+        _: bool,
+        window: &mut Window,
+        cx: &mut Context<Picker<OutlineViewDelegate>>,
+    ) {
         self.prev_scroll_position.take();
 
         self.active_editor.update(cx, |active_editor, cx| {
@@ -260,29 +283,30 @@ impl PickerDelegate for OutlineViewDelegate {
                 .highlighted_rows::<OutlineRowHighlights>()
                 .next();
             if let Some((rows, _)) = highlight {
-                active_editor.change_selections(Some(Autoscroll::center()), cx, |s| {
+                active_editor.change_selections(Some(Autoscroll::center()), window, cx, |s| {
                     s.select_ranges([rows.start..rows.start])
                 });
                 active_editor.clear_row_highlights::<OutlineRowHighlights>();
-                active_editor.focus(cx);
+                window.focus(&active_editor.focus_handle(cx));
             }
         });
 
-        self.dismissed(cx);
+        self.dismissed(window, cx);
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<OutlineViewDelegate>>) {
+    fn dismissed(&mut self, window: &mut Window, cx: &mut Context<Picker<OutlineViewDelegate>>) {
         self.outline_view
             .update(cx, |_, cx| cx.emit(DismissEvent))
             .log_err();
-        self.restore_active_editor(cx);
+        self.restore_active_editor(window, cx);
     }
 
     fn render_match(
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let mat = self.matches.get(ix)?;
         let outline_item = self.outline.items.get(mat.candidate_id)?;
@@ -305,7 +329,7 @@ impl PickerDelegate for OutlineViewDelegate {
 pub fn render_item<T>(
     outline_item: &OutlineItem<T>,
     match_ranges: impl IntoIterator<Item = Range<usize>>,
-    cx: &AppContext,
+    cx: &App,
 ) -> StyledText {
     let highlight_style = HighlightStyle {
         background_color: Some(color_alpha(cx.theme().colors().text_accent, 0.3)),
@@ -370,7 +394,8 @@ mod tests {
         let project = Project::test(fs, ["/dir".as_ref()], cx).await;
         project.read_with(cx, |project, _| project.languages().add(rust_lang()));
 
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let (workspace, cx) =
+            cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
         let worktree_id = workspace.update(cx, |workspace, cx| {
             workspace.project().update(cx, |project, cx| {
                 project.worktrees(cx).next().unwrap().read(cx).id()
@@ -381,15 +406,15 @@ mod tests {
             .await
             .unwrap();
         let editor = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_path((worktree_id, "a.rs"), None, true, cx)
+            .update_in(cx, |workspace, window, cx| {
+                workspace.open_path((worktree_id, "a.rs"), None, true, window, cx)
             })
             .await
             .unwrap()
             .downcast::<Editor>()
             .unwrap();
         let ensure_outline_view_contents =
-            |outline_view: &View<Picker<OutlineViewDelegate>>, cx: &mut VisualTestContext| {
+            |outline_view: &Entity<Picker<OutlineViewDelegate>>, cx: &mut VisualTestContext| {
                 assert_eq!(query(outline_view, cx), "");
                 assert_eq!(
                     outline_names(outline_view, cx),
@@ -467,9 +492,9 @@ mod tests {
     }
 
     fn open_outline_view(
-        workspace: &View<Workspace>,
+        workspace: &Entity<Workspace>,
         cx: &mut VisualTestContext,
-    ) -> View<Picker<OutlineViewDelegate>> {
+    ) -> Entity<Picker<OutlineViewDelegate>> {
         cx.dispatch_action(zed_actions::outline::ToggleOutline);
         workspace.update(cx, |workspace, cx| {
             workspace
@@ -482,14 +507,14 @@ mod tests {
     }
 
     fn query(
-        outline_view: &View<Picker<OutlineViewDelegate>>,
+        outline_view: &Entity<Picker<OutlineViewDelegate>>,
         cx: &mut VisualTestContext,
     ) -> String {
         outline_view.update(cx, |outline_view, cx| outline_view.query(cx))
     }
 
     fn outline_names(
-        outline_view: &View<Picker<OutlineViewDelegate>>,
+        outline_view: &Entity<Picker<OutlineViewDelegate>>,
         cx: &mut VisualTestContext,
     ) -> Vec<String> {
         outline_view.update(cx, |outline_view, _| {
@@ -503,10 +528,10 @@ mod tests {
         })
     }
 
-    fn highlighted_display_rows(editor: &View<Editor>, cx: &mut VisualTestContext) -> Vec<u32> {
-        editor.update(cx, |editor, cx| {
+    fn highlighted_display_rows(editor: &Entity<Editor>, cx: &mut VisualTestContext) -> Vec<u32> {
+        editor.update_in(cx, |editor, window, cx| {
             editor
-                .highlighted_display_rows(cx)
+                .highlighted_display_rows(window, cx)
                 .into_keys()
                 .map(|r| r.0)
                 .collect()
@@ -610,7 +635,7 @@ mod tests {
 
     #[track_caller]
     fn assert_single_caret_at_row(
-        editor: &View<Editor>,
+        editor: &Entity<Editor>,
         buffer_row: u32,
         cx: &mut VisualTestContext,
     ) {

crates/outline_panel/src/outline_panel.rs 🔗

@@ -10,7 +10,7 @@ use std::{
     u32,
 };
 
-use anyhow::Context;
+use anyhow::Context as _;
 use collections::{hash_map, BTreeSet, HashMap, HashSet};
 use db::kvp::KEY_VALUE_STORE;
 use editor::{
@@ -23,13 +23,13 @@ use editor::{
 use file_icons::FileIcons;
 use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
 use gpui::{
-    actions, anchored, deferred, div, point, px, size, uniform_list, Action, AnyElement,
-    AppContext, AssetSource, AsyncWindowContext, Bounds, ClipboardItem, DismissEvent, Div,
-    ElementId, EventEmitter, FocusHandle, FocusableView, HighlightStyle, InteractiveElement,
-    IntoElement, KeyContext, ListHorizontalSizingBehavior, ListSizingBehavior, Model, MouseButton,
-    MouseDownEvent, ParentElement, Pixels, Point, Render, ScrollStrategy, SharedString, Stateful,
-    StatefulInteractiveElement as _, Styled, Subscription, Task, UniformListScrollHandle, View,
-    ViewContext, VisualContext, WeakView, WindowContext,
+    actions, anchored, deferred, div, point, px, size, uniform_list, Action, AnyElement, App,
+    AppContext as _, AssetSource, AsyncWindowContext, Bounds, ClipboardItem, Context, DismissEvent,
+    Div, ElementId, Entity, EventEmitter, FocusHandle, Focusable, HighlightStyle,
+    InteractiveElement, IntoElement, KeyContext, ListHorizontalSizingBehavior, ListSizingBehavior,
+    MouseButton, MouseDownEvent, ParentElement, Pixels, Point, Render, ScrollStrategy,
+    SharedString, Stateful, StatefulInteractiveElement as _, Styled, Subscription, Task,
+    UniformListScrollHandle, WeakEntity, Window,
 };
 use itertools::Itertools;
 use language::{BufferId, BufferSnapshot, OffsetRangeExt, OutlineItem};
@@ -86,12 +86,12 @@ type HighlightStyleData = Arc<OnceLock<Vec<(Range<usize>, HighlightStyle)>>>;
 pub struct OutlinePanel {
     fs: Arc<dyn Fs>,
     width: Option<Pixels>,
-    project: Model<Project>,
-    workspace: WeakView<Workspace>,
+    project: Entity<Project>,
+    workspace: WeakEntity<Workspace>,
     active: bool,
     pinned: bool,
     scroll_handle: UniformListScrollHandle,
-    context_menu: Option<(View<ContextMenu>, Point<Pixels>, Subscription)>,
+    context_menu: Option<(Entity<ContextMenu>, Point<Pixels>, Subscription)>,
     focus_handle: FocusHandle,
     pending_serialization: Task<Option<()>>,
     fs_entries_depth: HashMap<(WorktreeId, ProjectEntryId), usize>,
@@ -110,7 +110,7 @@ pub struct OutlinePanel {
     outline_fetch_tasks: HashMap<(BufferId, ExcerptId), Task<()>>,
     excerpts: HashMap<BufferId, HashMap<ExcerptId, Excerpt>>,
     cached_entries: Vec<CachedEntry>,
-    filter_editor: View<Editor>,
+    filter_editor: Entity<Editor>,
     mode: ItemsDisplayMode,
     show_scrollbar: bool,
     vertical_scrollbar_state: ScrollbarState,
@@ -149,7 +149,8 @@ impl SearchState {
         previous_matches: HashMap<Range<editor::Anchor>, Arc<OnceLock<SearchData>>>,
         new_matches: Vec<Range<editor::Anchor>>,
         theme: Arc<SyntaxTheme>,
-        cx: &mut ViewContext<OutlinePanel>,
+        window: &mut Window,
+        cx: &mut Context<OutlinePanel>,
     ) -> Self {
         let (highlight_search_match_tx, highlight_search_match_rx) = channel::unbounded();
         let (notify_tx, notify_rx) = channel::unbounded::<()>();
@@ -242,7 +243,7 @@ impl SearchState {
                     );
                 }
             }),
-            _search_match_notify: cx.spawn(|outline_panel, mut cx| async move {
+            _search_match_notify: cx.spawn_in(window, |outline_panel, mut cx| async move {
                 loop {
                     match notify_rx.recv().await {
                         Ok(()) => {}
@@ -640,7 +641,7 @@ enum FsEntry {
 
 struct ActiveItem {
     item_handle: Box<dyn WeakItemHandle>,
-    active_editor: WeakView<Editor>,
+    active_editor: WeakEntity<Editor>,
     _buffer_search_subscription: Subscription,
     _editor_subscrpiption: Subscription,
 }
@@ -656,17 +657,17 @@ struct SerializedOutlinePanel {
     active: Option<bool>,
 }
 
-pub fn init_settings(cx: &mut AppContext) {
+pub fn init_settings(cx: &mut App) {
     OutlinePanelSettings::register(cx);
 }
 
-pub fn init(assets: impl AssetSource, cx: &mut AppContext) {
+pub fn init(assets: impl AssetSource, cx: &mut App) {
     init_settings(cx);
     file_icons::init(assets, cx);
 
-    cx.observe_new_views(|workspace: &mut Workspace, _| {
-        workspace.register_action(|workspace, _: &ToggleFocus, cx| {
-            workspace.toggle_panel_focus::<OutlinePanel>(cx);
+    cx.observe_new(|workspace: &mut Workspace, _, _| {
+        workspace.register_action(|workspace, _: &ToggleFocus, window, cx| {
+            workspace.toggle_panel_focus::<OutlinePanel>(window, cx);
         });
     })
     .detach();
@@ -674,9 +675,9 @@ pub fn init(assets: impl AssetSource, cx: &mut AppContext) {
 
 impl OutlinePanel {
     pub async fn load(
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         mut cx: AsyncWindowContext,
-    ) -> anyhow::Result<View<Self>> {
+    ) -> anyhow::Result<Entity<Self>> {
         let serialized_panel = cx
             .background_executor()
             .spawn(async move { KEY_VALUE_STORE.read_kvp(OUTLINE_PANEL_KEY) })
@@ -689,8 +690,8 @@ impl OutlinePanel {
             .log_err()
             .flatten();
 
-        workspace.update(&mut cx, |workspace, cx| {
-            let panel = Self::new(workspace, cx);
+        workspace.update_in(&mut cx, |workspace, window, cx| {
+            let panel = Self::new(workspace, window, cx);
             if let Some(serialized_panel) = serialized_panel {
                 panel.update(cx, |panel, cx| {
                     panel.width = serialized_panel.width.map(|px| px.round());
@@ -702,33 +703,42 @@ impl OutlinePanel {
         })
     }
 
-    fn new(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) -> View<Self> {
+    fn new(
+        workspace: &mut Workspace,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Entity<Self> {
         let project = workspace.project().clone();
-        let workspace_handle = cx.view().downgrade();
-        let outline_panel = cx.new_view(|cx| {
-            let filter_editor = cx.new_view(|cx| {
-                let mut editor = Editor::single_line(cx);
+        let workspace_handle = cx.model().downgrade();
+        let outline_panel = cx.new(|cx| {
+            let filter_editor = cx.new(|cx| {
+                let mut editor = Editor::single_line(window, cx);
                 editor.set_placeholder_text("Filter...", cx);
                 editor
             });
-            let filter_update_subscription =
-                cx.subscribe(&filter_editor, |outline_panel: &mut Self, _, event, cx| {
+            let filter_update_subscription = cx.subscribe_in(
+                &filter_editor,
+                window,
+                |outline_panel: &mut Self, _, event, window, cx| {
                     if let editor::EditorEvent::BufferEdited = event {
-                        outline_panel.update_cached_entries(Some(UPDATE_DEBOUNCE), cx);
+                        outline_panel.update_cached_entries(Some(UPDATE_DEBOUNCE), window, cx);
                     }
-                });
+                },
+            );
 
             let focus_handle = cx.focus_handle();
-            let focus_subscription = cx.on_focus(&focus_handle, Self::focus_in);
-            let focus_out_subscription = cx.on_focus_out(&focus_handle, |outline_panel, _, cx| {
-                outline_panel.hide_scrollbar(cx);
-            });
-            let workspace_subscription = cx.subscribe(
+            let focus_subscription = cx.on_focus(&focus_handle, window, Self::focus_in);
+            let focus_out_subscription =
+                cx.on_focus_out(&focus_handle, window, |outline_panel, _, window, cx| {
+                    outline_panel.hide_scrollbar(window, cx);
+                });
+            let workspace_subscription = cx.subscribe_in(
                 &workspace
                     .weak_handle()
                     .upgrade()
                     .expect("have a &mut Workspace"),
-                move |outline_panel, workspace, event, cx| {
+                window,
+                move |outline_panel, workspace, event, window, cx| {
                     if let workspace::Event::ActiveItemChanged = event {
                         if let Some((new_active_item, new_active_editor)) =
                             workspace_active_editor(workspace.read(cx), cx)
@@ -737,11 +747,12 @@ impl OutlinePanel {
                                 outline_panel.replace_active_editor(
                                     new_active_item,
                                     new_active_editor,
+                                    window,
                                     cx,
                                 );
                             }
                         } else {
-                            outline_panel.clear_previous(cx);
+                            outline_panel.clear_previous(window, cx);
                             cx.notify();
                         }
                     }
@@ -755,7 +766,7 @@ impl OutlinePanel {
             let mut outline_panel_settings = *OutlinePanelSettings::get_global(cx);
             let mut current_theme = ThemeSettings::get_global(cx).clone();
             let settings_subscription =
-                cx.observe_global::<SettingsStore>(move |outline_panel, cx| {
+                cx.observe_global_in::<SettingsStore>(window, move |outline_panel, window, cx| {
                     let new_settings = OutlinePanelSettings::get_global(cx);
                     let new_theme = ThemeSettings::get_global(cx);
                     if &current_theme != new_theme {
@@ -766,7 +777,7 @@ impl OutlinePanel {
                                 excerpt.invalidate_outlines();
                             }
                         }
-                        outline_panel.update_non_fs_items(cx);
+                        outline_panel.update_non_fs_items(window, cx);
                     } else if &outline_panel_settings != new_settings {
                         outline_panel_settings = *new_settings;
                         cx.notify();
@@ -785,9 +796,9 @@ impl OutlinePanel {
                 show_scrollbar: !Self::should_autohide_scrollbar(cx),
                 hide_scrollbar_task: None,
                 vertical_scrollbar_state: ScrollbarState::new(scroll_handle.clone())
-                    .parent_view(cx.view()),
+                    .parent_model(&cx.model()),
                 horizontal_scrollbar_state: ScrollbarState::new(scroll_handle.clone())
-                    .parent_view(cx.view()),
+                    .parent_model(&cx.model()),
                 max_width_item_index: None,
                 scroll_handle,
                 focus_handle,
@@ -821,7 +832,7 @@ impl OutlinePanel {
                 ],
             };
             if let Some((item, editor)) = workspace_active_editor(workspace, cx) {
-                outline_panel.replace_active_editor(item, editor, cx);
+                outline_panel.replace_active_editor(item, editor, window, cx);
             }
             outline_panel
         });
@@ -829,7 +840,7 @@ impl OutlinePanel {
         outline_panel
     }
 
-    fn serialize(&mut self, cx: &mut ViewContext<Self>) {
+    fn serialize(&mut self, cx: &mut Context<Self>) {
         let width = self.width;
         let active = Some(self.active);
         self.pending_serialization = cx.background_executor().spawn(
@@ -846,11 +857,11 @@ impl OutlinePanel {
         );
     }
 
-    fn dispatch_context(&self, cx: &ViewContext<Self>) -> KeyContext {
+    fn dispatch_context(&self, window: &mut Window, cx: &mut Context<Self>) -> KeyContext {
         let mut dispatch_context = KeyContext::new_with_defaults();
         dispatch_context.add("OutlinePanel");
         dispatch_context.add("menu");
-        let identifier = if self.filter_editor.focus_handle(cx).is_focused(cx) {
+        let identifier = if self.filter_editor.focus_handle(cx).is_focused(window) {
             "editing"
         } else {
             "not_editing"
@@ -859,7 +870,12 @@ impl OutlinePanel {
         dispatch_context
     }
 
-    fn unfold_directory(&mut self, _: &UnfoldDirectory, cx: &mut ViewContext<Self>) {
+    fn unfold_directory(
+        &mut self,
+        _: &UnfoldDirectory,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(PanelEntry::FoldedDirs(FoldedDirsEntry {
             worktree_id,
             entries,
@@ -870,11 +886,11 @@ impl OutlinePanel {
                 .entry(worktree_id)
                 .or_default()
                 .extend(entries.iter().map(|entry| entry.id));
-            self.update_cached_entries(None, cx);
+            self.update_cached_entries(None, window, cx);
         }
     }
 
-    fn fold_directory(&mut self, _: &FoldDirectory, cx: &mut ViewContext<Self>) {
+    fn fold_directory(&mut self, _: &FoldDirectory, window: &mut Window, cx: &mut Context<Self>) {
         let (worktree_id, entry) = match self.selected_entry().cloned() {
             Some(PanelEntry::Fs(FsEntry::Directory(directory))) => {
                 (directory.worktree_id, Some(directory.entry))
@@ -898,23 +914,23 @@ impl OutlinePanel {
         };
 
         unfolded_dirs.remove(&entry.id);
-        self.update_cached_entries(None, cx);
+        self.update_cached_entries(None, window, cx);
     }
 
-    fn open(&mut self, _: &Open, cx: &mut ViewContext<Self>) {
-        if self.filter_editor.focus_handle(cx).is_focused(cx) {
+    fn open(&mut self, _: &Open, window: &mut Window, cx: &mut Context<Self>) {
+        if self.filter_editor.focus_handle(cx).is_focused(window) {
             cx.propagate()
         } else if let Some(selected_entry) = self.selected_entry().cloned() {
-            self.toggle_expanded(&selected_entry, cx);
-            self.scroll_editor_to_entry(&selected_entry, true, false, cx);
+            self.toggle_expanded(&selected_entry, window, cx);
+            self.scroll_editor_to_entry(&selected_entry, true, false, window, cx);
         }
     }
 
-    fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
-        if self.filter_editor.focus_handle(cx).is_focused(cx) {
-            self.focus_handle.focus(cx);
+    fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
+        if self.filter_editor.focus_handle(cx).is_focused(window) {
+            self.focus_handle.focus(window);
         } else {
-            self.filter_editor.focus_handle(cx).focus(cx);
+            self.filter_editor.focus_handle(cx).focus(window);
         }
 
         if self.context_menu.is_some() {
@@ -923,29 +939,37 @@ impl OutlinePanel {
         }
     }
 
-    fn open_excerpts(&mut self, action: &editor::OpenExcerpts, cx: &mut ViewContext<Self>) {
-        if self.filter_editor.focus_handle(cx).is_focused(cx) {
+    fn open_excerpts(
+        &mut self,
+        action: &editor::OpenExcerpts,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        if self.filter_editor.focus_handle(cx).is_focused(window) {
             cx.propagate()
         } else if let Some((active_editor, selected_entry)) =
             self.active_editor().zip(self.selected_entry().cloned())
         {
-            self.scroll_editor_to_entry(&selected_entry, true, true, cx);
-            active_editor.update(cx, |editor, cx| editor.open_excerpts(action, cx));
+            self.scroll_editor_to_entry(&selected_entry, true, true, window, cx);
+            active_editor.update(cx, |editor, cx| editor.open_excerpts(action, window, cx));
         }
     }
 
     fn open_excerpts_split(
         &mut self,
         action: &editor::OpenExcerptsSplit,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
-        if self.filter_editor.focus_handle(cx).is_focused(cx) {
+        if self.filter_editor.focus_handle(cx).is_focused(window) {
             cx.propagate()
         } else if let Some((active_editor, selected_entry)) =
             self.active_editor().zip(self.selected_entry().cloned())
         {
-            self.scroll_editor_to_entry(&selected_entry, true, true, cx);
-            active_editor.update(cx, |editor, cx| editor.open_excerpts_in_split(action, cx));
+            self.scroll_editor_to_entry(&selected_entry, true, true, window, cx);
+            active_editor.update(cx, |editor, cx| {
+                editor.open_excerpts_in_split(action, window, cx)
+            });
         }
     }
 
@@ -954,7 +978,8 @@ impl OutlinePanel {
         entry: &PanelEntry,
         prefer_selection_change: bool,
         change_focus: bool,
-        cx: &mut ViewContext<OutlinePanel>,
+        window: &mut Window,
+        cx: &mut Context<OutlinePanel>,
     ) {
         let Some(active_editor) = self.active_editor() else {
             return;
@@ -1017,18 +1042,23 @@ impl OutlinePanel {
             let activate = self
                 .workspace
                 .update(cx, |workspace, cx| match self.active_item() {
-                    Some(active_item) => {
-                        workspace.activate_item(active_item.as_ref(), true, change_focus, cx)
-                    }
-                    None => workspace.activate_item(&active_editor, true, change_focus, cx),
+                    Some(active_item) => workspace.activate_item(
+                        active_item.as_ref(),
+                        true,
+                        change_focus,
+                        window,
+                        cx,
+                    ),
+                    None => workspace.activate_item(&active_editor, true, change_focus, window, cx),
                 });
 
             if activate.is_ok() {
-                self.select_entry(entry.clone(), true, cx);
+                self.select_entry(entry.clone(), true, window, cx);
                 if change_selection {
                     active_editor.update(cx, |editor, cx| {
                         editor.change_selections(
                             Some(Autoscroll::Strategy(AutoscrollStrategy::Center)),
+                            window,
                             cx,
                             |s| s.select_ranges(Some(anchor..anchor)),
                         );
@@ -1077,20 +1107,20 @@ impl OutlinePanel {
                         }
                     }
                     active_editor.update(cx, |editor, cx| {
-                        editor.set_scroll_anchor(ScrollAnchor { offset, anchor }, cx);
+                        editor.set_scroll_anchor(ScrollAnchor { offset, anchor }, window, cx);
                     });
                 }
 
                 if change_focus {
-                    active_editor.focus_handle(cx).focus(cx);
+                    active_editor.focus_handle(cx).focus(window);
                 } else {
-                    self.focus_handle.focus(cx);
+                    self.focus_handle.focus(window);
                 }
             }
         }
     }
 
-    fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
+    fn select_next(&mut self, _: &SelectNext, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(entry_to_select) = self.selected_entry().and_then(|selected_entry| {
             self.cached_entries
                 .iter()
@@ -1099,16 +1129,16 @@ impl OutlinePanel {
                 .nth(1)
                 .cloned()
         }) {
-            self.select_entry(entry_to_select, true, cx);
+            self.select_entry(entry_to_select, true, window, cx);
         } else {
-            self.select_first(&SelectFirst {}, cx)
+            self.select_first(&SelectFirst {}, window, cx)
         }
         if let Some(selected_entry) = self.selected_entry().cloned() {
-            self.scroll_editor_to_entry(&selected_entry, true, false, cx);
+            self.scroll_editor_to_entry(&selected_entry, true, false, window, cx);
         }
     }
 
-    fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext<Self>) {
+    fn select_prev(&mut self, _: &SelectPrev, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(entry_to_select) = self.selected_entry().and_then(|selected_entry| {
             self.cached_entries
                 .iter()
@@ -1118,16 +1148,16 @@ impl OutlinePanel {
                 .nth(1)
                 .cloned()
         }) {
-            self.select_entry(entry_to_select, true, cx);
+            self.select_entry(entry_to_select, true, window, cx);
         } else {
-            self.select_last(&SelectLast, cx)
+            self.select_last(&SelectLast, window, cx)
         }
         if let Some(selected_entry) = self.selected_entry().cloned() {
-            self.scroll_editor_to_entry(&selected_entry, true, false, cx);
+            self.scroll_editor_to_entry(&selected_entry, true, false, window, cx);
         }
     }
 
-    fn select_parent(&mut self, _: &SelectParent, cx: &mut ViewContext<Self>) {
+    fn select_parent(&mut self, _: &SelectParent, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(entry_to_select) = self.selected_entry().and_then(|selected_entry| {
             let mut previous_entries = self
                 .cached_entries
@@ -1206,19 +1236,19 @@ impl OutlinePanel {
                 }
             }
         }) {
-            self.select_entry(entry_to_select.clone(), true, cx);
+            self.select_entry(entry_to_select.clone(), true, window, cx);
         } else {
-            self.select_first(&SelectFirst {}, cx);
+            self.select_first(&SelectFirst {}, window, cx);
         }
     }
 
-    fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext<Self>) {
+    fn select_first(&mut self, _: &SelectFirst, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(first_entry) = self.cached_entries.first() {
-            self.select_entry(first_entry.entry.clone(), true, cx);
+            self.select_entry(first_entry.entry.clone(), true, window, cx);
         }
     }
 
-    fn select_last(&mut self, _: &SelectLast, cx: &mut ViewContext<Self>) {
+    fn select_last(&mut self, _: &SelectLast, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(new_selection) = self
             .cached_entries
             .iter()
@@ -1226,11 +1256,11 @@ impl OutlinePanel {
             .map(|cached_entry| &cached_entry.entry)
             .next()
         {
-            self.select_entry(new_selection.clone(), true, cx);
+            self.select_entry(new_selection.clone(), true, window, cx);
         }
     }
 
-    fn autoscroll(&mut self, cx: &mut ViewContext<Self>) {
+    fn autoscroll(&mut self, cx: &mut Context<Self>) {
         if let Some(selected_entry) = self.selected_entry() {
             let index = self
                 .cached_entries
@@ -1244,8 +1274,8 @@ impl OutlinePanel {
         }
     }
 
-    fn focus_in(&mut self, cx: &mut ViewContext<Self>) {
-        if !self.focus_handle.contains_focused(cx) {
+    fn focus_in(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        if !self.focus_handle.contains_focused(window, cx) {
             cx.emit(Event::Focus);
         }
     }
@@ -1254,9 +1284,10 @@ impl OutlinePanel {
         &mut self,
         position: Point<Pixels>,
         entry: PanelEntry,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
-        self.select_entry(entry.clone(), true, cx);
+        self.select_entry(entry.clone(), true, window, cx);
         let is_root = match &entry {
             PanelEntry::Fs(FsEntry::File(FsEntryFile {
                 worktree_id, entry, ..
@@ -1300,7 +1331,7 @@ impl OutlinePanel {
         let is_foldable = auto_fold_dirs && !is_root && self.is_foldable(&entry);
         let is_unfoldable = auto_fold_dirs && !is_root && self.is_unfoldable(&entry);
 
-        let context_menu = ContextMenu::build(cx, |menu, _| {
+        let context_menu = ContextMenu::build(window, cx, |menu, _, _| {
             menu.context(self.focus_handle.clone())
                 .when(cfg!(target_os = "macos"), |menu| {
                     menu.action("Reveal in Finder", Box::new(RevealInFileManager))
@@ -1319,7 +1350,7 @@ impl OutlinePanel {
                 .action("Copy Path", Box::new(CopyPath))
                 .action("Copy Relative Path", Box::new(CopyRelativePath))
         });
-        cx.focus_view(&context_menu);
+        window.focus(&context_menu.focus_handle(cx));
         let subscription = cx.subscribe(&context_menu, |outline_panel, _, _: &DismissEvent, cx| {
             outline_panel.context_menu.take();
             cx.notify();
@@ -1365,7 +1396,12 @@ impl OutlinePanel {
         children.may_be_fold_part() && children.dirs > 0
     }
 
-    fn expand_selected_entry(&mut self, _: &ExpandSelectedEntry, cx: &mut ViewContext<Self>) {
+    fn expand_selected_entry(
+        &mut self,
+        _: &ExpandSelectedEntry,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let Some(active_editor) = self.active_editor() else {
             return;
         };
@@ -1422,19 +1458,24 @@ impl OutlinePanel {
             active_editor.update(cx, |editor, cx| {
                 buffers_to_unfold.retain(|buffer_id| editor.is_buffer_folded(*buffer_id, cx));
             });
-            self.select_entry(selected_entry, true, cx);
+            self.select_entry(selected_entry, true, window, cx);
             if buffers_to_unfold.is_empty() {
-                self.update_cached_entries(None, cx);
+                self.update_cached_entries(None, window, cx);
             } else {
-                self.toggle_buffers_fold(buffers_to_unfold, false, cx)
+                self.toggle_buffers_fold(buffers_to_unfold, false, window, cx)
                     .detach();
             }
         } else {
-            self.select_next(&SelectNext, cx)
+            self.select_next(&SelectNext, window, cx)
         }
     }
 
-    fn collapse_selected_entry(&mut self, _: &CollapseSelectedEntry, cx: &mut ViewContext<Self>) {
+    fn collapse_selected_entry(
+        &mut self,
+        _: &CollapseSelectedEntry,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let Some(active_editor) = self.active_editor() else {
             return;
         };
@@ -1508,18 +1549,24 @@ impl OutlinePanel {
             active_editor.update(cx, |editor, cx| {
                 buffers_to_fold.retain(|buffer_id| !editor.is_buffer_folded(*buffer_id, cx));
             });
-            self.select_entry(selected_entry, true, cx);
+            self.select_entry(selected_entry, true, window, cx);
             if buffers_to_fold.is_empty() {
-                self.update_cached_entries(None, cx);
+                self.update_cached_entries(None, window, cx);
             } else {
-                self.toggle_buffers_fold(buffers_to_fold, true, cx).detach();
+                self.toggle_buffers_fold(buffers_to_fold, true, window, cx)
+                    .detach();
             }
         } else {
-            self.select_parent(&SelectParent, cx);
+            self.select_parent(&SelectParent, window, cx);
         }
     }
 
-    pub fn expand_all_entries(&mut self, _: &ExpandAllEntries, cx: &mut ViewContext<Self>) {
+    pub fn expand_all_entries(
+        &mut self,
+        _: &ExpandAllEntries,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let Some(active_editor) = self.active_editor() else {
             return;
         };
@@ -1574,14 +1621,19 @@ impl OutlinePanel {
             buffers_to_unfold.retain(|buffer_id| editor.is_buffer_folded(*buffer_id, cx));
         });
         if buffers_to_unfold.is_empty() {
-            self.update_cached_entries(None, cx);
+            self.update_cached_entries(None, window, cx);
         } else {
-            self.toggle_buffers_fold(buffers_to_unfold, false, cx)
+            self.toggle_buffers_fold(buffers_to_unfold, false, window, cx)
                 .detach();
         }
     }
 
-    pub fn collapse_all_entries(&mut self, _: &CollapseAllEntries, cx: &mut ViewContext<Self>) {
+    pub fn collapse_all_entries(
+        &mut self,
+        _: &CollapseAllEntries,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let Some(active_editor) = self.active_editor() else {
             return;
         };
@@ -1622,13 +1674,14 @@ impl OutlinePanel {
             buffers_to_fold.retain(|buffer_id| !editor.is_buffer_folded(*buffer_id, cx));
         });
         if buffers_to_fold.is_empty() {
-            self.update_cached_entries(None, cx);
+            self.update_cached_entries(None, window, cx);
         } else {
-            self.toggle_buffers_fold(buffers_to_fold, true, cx).detach();
+            self.toggle_buffers_fold(buffers_to_fold, true, window, cx)
+                .detach();
         }
     }
 
-    fn toggle_expanded(&mut self, entry: &PanelEntry, cx: &mut ViewContext<Self>) {
+    fn toggle_expanded(&mut self, entry: &PanelEntry, window: &mut Window, cx: &mut Context<Self>) {
         let Some(active_editor) = self.active_editor() else {
             return;
         };
@@ -1718,11 +1771,11 @@ impl OutlinePanel {
             });
         });
 
-        self.select_entry(entry.clone(), true, cx);
+        self.select_entry(entry.clone(), true, window, cx);
         if buffers_to_toggle.is_empty() {
-            self.update_cached_entries(None, cx);
+            self.update_cached_entries(None, window, cx);
         } else {
-            self.toggle_buffers_fold(buffers_to_toggle, fold, cx)
+            self.toggle_buffers_fold(buffers_to_toggle, fold, window, cx)
                 .detach();
         }
     }
@@ -1731,14 +1784,15 @@ impl OutlinePanel {
         &self,
         buffers: HashSet<BufferId>,
         fold: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<()> {
         let Some(active_editor) = self.active_editor() else {
             return Task::ready(());
         };
-        cx.spawn(|outline_panel, mut cx| async move {
+        cx.spawn_in(window, |outline_panel, mut cx| async move {
             outline_panel
-                .update(&mut cx, |outline_panel, cx| {
+                .update_in(&mut cx, |outline_panel, window, cx| {
                     active_editor.update(cx, |editor, cx| {
                         for buffer_id in buffers {
                             outline_panel
@@ -1752,14 +1806,14 @@ impl OutlinePanel {
                         }
                     });
                     if let Some(selection) = outline_panel.selected_entry().cloned() {
-                        outline_panel.scroll_editor_to_entry(&selection, false, false, cx);
+                        outline_panel.scroll_editor_to_entry(&selection, false, false, window, cx);
                     }
                 })
                 .ok();
         })
     }
 
-    fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
+    fn copy_path(&mut self, _: &CopyPath, _: &mut Window, cx: &mut Context<Self>) {
         if let Some(clipboard_text) = self
             .selected_entry()
             .and_then(|entry| self.abs_path(entry, cx))
@@ -1769,7 +1823,7 @@ impl OutlinePanel {
         }
     }
 
-    fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
+    fn copy_relative_path(&mut self, _: &CopyRelativePath, _: &mut Window, cx: &mut Context<Self>) {
         if let Some(clipboard_text) = self
             .selected_entry()
             .and_then(|entry| match entry {
@@ -1785,7 +1839,12 @@ impl OutlinePanel {
         }
     }
 
-    fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
+    fn reveal_in_finder(
+        &mut self,
+        _: &RevealInFileManager,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(abs_path) = self
             .selected_entry()
             .and_then(|entry| self.abs_path(entry, cx))
@@ -1794,7 +1853,12 @@ impl OutlinePanel {
         }
     }
 
-    fn open_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
+    fn open_in_terminal(
+        &mut self,
+        _: &OpenInTerminal,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let selected_entry = self.selected_entry();
         let abs_path = selected_entry.and_then(|entry| self.abs_path(entry, cx));
         let working_directory = if let (
@@ -1808,23 +1872,32 @@ impl OutlinePanel {
         };
 
         if let Some(working_directory) = working_directory {
-            cx.dispatch_action(workspace::OpenTerminal { working_directory }.boxed_clone())
+            window.dispatch_action(
+                workspace::OpenTerminal { working_directory }.boxed_clone(),
+                cx,
+            )
         }
     }
 
-    fn reveal_entry_for_selection(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
+    fn reveal_entry_for_selection(
+        &mut self,
+        editor: Entity<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if !self.active
             || !OutlinePanelSettings::get_global(cx).auto_reveal_entries
-            || self.focus_handle.contains_focused(cx)
+            || self.focus_handle.contains_focused(window, cx)
         {
             return;
         }
         let project = self.project.clone();
-        self.reveal_selection_task = cx.spawn(|outline_panel, mut cx| async move {
+        self.reveal_selection_task = cx.spawn_in(window, |outline_panel, mut cx| async move {
             cx.background_executor().timer(UPDATE_DEBOUNCE).await;
-            let entry_with_selection = outline_panel.update(&mut cx, |outline_panel, cx| {
-                outline_panel.location_for_editor_selection(&editor, cx)
-            })?;
+            let entry_with_selection =
+                outline_panel.update_in(&mut cx, |outline_panel, window, cx| {
+                    outline_panel.location_for_editor_selection(&editor, window, cx)
+                })?;
             let Some(entry_with_selection) = entry_with_selection else {
                 outline_panel.update(&mut cx, |outline_panel, cx| {
                     outline_panel.selected_entry = SelectedEntry::None;
@@ -1952,9 +2025,9 @@ impl OutlinePanel {
                 })?
             }
 
-            outline_panel.update(&mut cx, |outline_panel, cx| {
-                outline_panel.select_entry(entry_with_selection, false, cx);
-                outline_panel.update_cached_entries(None, cx);
+            outline_panel.update_in(&mut cx, |outline_panel, window, cx| {
+                outline_panel.select_entry(entry_with_selection, false, window, cx);
+                outline_panel.update_cached_entries(None, window, cx);
             })?;
 
             anyhow::Ok(())
@@ -1965,7 +2038,8 @@ impl OutlinePanel {
         &self,
         excerpt: &OutlineEntryExcerpt,
         depth: usize,
-        cx: &mut ViewContext<OutlinePanel>,
+        window: &mut Window,
+        cx: &mut Context<OutlinePanel>,
     ) -> Option<Stateful<Div>> {
         let item_id = ElementId::from(excerpt.id.to_proto() as usize);
         let is_active = match self.selected_entry() {
@@ -2008,6 +2082,7 @@ impl OutlinePanel {
             Some(icon),
             is_active,
             label_element,
+            window,
             cx,
         ))
     }
@@ -2016,7 +2091,7 @@ impl OutlinePanel {
         &self,
         buffer_id: BufferId,
         range: &ExcerptRange<language::Anchor>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<String> {
         let buffer_snapshot = self.buffer_snapshot_for_id(buffer_id, cx)?;
         let excerpt_range = range.context.to_point(&buffer_snapshot);
@@ -2032,7 +2107,8 @@ impl OutlinePanel {
         outline: &OutlineEntryOutline,
         depth: usize,
         string_match: Option<&StringMatch>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Stateful<Div> {
         let item_id = ElementId::from(SharedString::from(format!(
             "{:?}|{:?}{:?}|{:?}",
@@ -2068,6 +2144,7 @@ impl OutlinePanel {
             icon,
             is_active,
             label_element,
+            window,
             cx,
         )
     }
@@ -2077,7 +2154,8 @@ impl OutlinePanel {
         rendered_entry: &FsEntry,
         depth: usize,
         string_match: Option<&StringMatch>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Stateful<Div> {
         let settings = OutlinePanelSettings::get_global(cx);
         let is_active = match self.selected_entry() {
@@ -2183,6 +2261,7 @@ impl OutlinePanel {
             Some(icon),
             is_active,
             label_element,
+            window,
             cx,
         )
     }
@@ -2192,7 +2271,8 @@ impl OutlinePanel {
         folded_dir: &FoldedDirsEntry,
         depth: usize,
         string_match: Option<&StringMatch>,
-        cx: &mut ViewContext<OutlinePanel>,
+        window: &mut Window,
+        cx: &mut Context<OutlinePanel>,
     ) -> Stateful<Div> {
         let settings = OutlinePanelSettings::get_global(cx);
         let is_active = match self.selected_entry() {
@@ -2252,6 +2332,7 @@ impl OutlinePanel {
             Some(icon),
             is_active,
             label_element,
+            window,
             cx,
         )
     }
@@ -2265,7 +2346,8 @@ impl OutlinePanel {
         kind: SearchKind,
         depth: usize,
         string_match: Option<&StringMatch>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<Stateful<Div>> {
         let search_data = match render_data.get() {
             Some(search_data) => search_data,
@@ -2342,6 +2424,7 @@ impl OutlinePanel {
             None,
             is_active,
             entire_label,
+            window,
             cx,
         ))
     }
@@ -2355,7 +2438,8 @@ impl OutlinePanel {
         icon_element: Option<AnyElement>,
         is_active: bool,
         label_element: gpui::AnyElement,
-        cx: &mut ViewContext<OutlinePanel>,
+        window: &mut Window,
+        cx: &mut Context<OutlinePanel>,
     ) -> Stateful<Div> {
         let settings = OutlinePanelSettings::get_global(cx);
         div()
@@ -2363,13 +2447,19 @@ impl OutlinePanel {
             .id(item_id.clone())
             .on_click({
                 let clicked_entry = rendered_entry.clone();
-                cx.listener(move |outline_panel, event: &gpui::ClickEvent, cx| {
+                cx.listener(move |outline_panel, event: &gpui::ClickEvent, window, cx| {
                     if event.down.button == MouseButton::Right || event.down.first_mouse {
                         return;
                     }
                     let change_focus = event.down.click_count > 1;
-                    outline_panel.toggle_expanded(&clicked_entry, cx);
-                    outline_panel.scroll_editor_to_entry(&clicked_entry, true, change_focus, cx);
+                    outline_panel.toggle_expanded(&clicked_entry, window, cx);
+                    outline_panel.scroll_editor_to_entry(
+                        &clicked_entry,
+                        true,
+                        change_focus,
+                        window,
+                        cx,
+                    );
                 })
             })
             .cursor_pointer()
@@ -2383,13 +2473,14 @@ impl OutlinePanel {
                     })
                     .child(h_flex().h_6().child(label_element).ml_1())
                     .on_secondary_mouse_down(cx.listener(
-                        move |outline_panel, event: &MouseDownEvent, cx| {
+                        move |outline_panel, event: &MouseDownEvent, window, cx| {
                             // Stop propagation to prevent the catch-all context menu for the project
                             // panel from being deployed.
                             cx.stop_propagation();
                             outline_panel.deploy_context_menu(
                                 event.position,
                                 rendered_entry.clone(),
+                                window,
                                 cx,
                             )
                         },
@@ -2406,12 +2497,13 @@ impl OutlinePanel {
                     style.bg(hover_color).border_color(hover_color)
                 }
             })
-            .when(is_active && self.focus_handle.contains_focused(cx), |div| {
-                div.border_color(Color::Selected.color(cx))
-            })
+            .when(
+                is_active && self.focus_handle.contains_focused(window, cx),
+                |div| div.border_color(Color::Selected.color(cx)),
+            )
     }
 
-    fn entry_name(&self, worktree_id: &WorktreeId, entry: &Entry, cx: &AppContext) -> String {
+    fn entry_name(&self, worktree_id: &WorktreeId, entry: &Entry, cx: &App) -> String {
         let name = match self.project.read(cx).worktree_for_id(*worktree_id, cx) {
             Some(worktree) => {
                 let worktree = worktree.read(cx);
@@ -2439,9 +2531,10 @@ impl OutlinePanel {
 
     fn update_fs_entries(
         &mut self,
-        active_editor: View<Editor>,
+        active_editor: Entity<Editor>,
         debounce: Option<Duration>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         if !self.active {
             return;
@@ -2451,7 +2544,7 @@ impl OutlinePanel {
         let active_multi_buffer = active_editor.read(cx).buffer().clone();
         let new_entries = self.new_entries_for_fs_update.clone();
         self.updating_fs_entries = true;
-        self.fs_entries_update_task = cx.spawn(|outline_panel, mut cx| async move {
+        self.fs_entries_update_task = cx.spawn_in(window, |outline_panel, mut cx| async move {
             if let Some(debounce) = debounce {
                 cx.background_executor().timer(debounce).await;
             }

crates/outline_panel/src/outline_panel_settings.rs 🔗

@@ -114,7 +114,7 @@ impl Settings for OutlinePanelSettings {
 
     fn load(
         sources: SettingsSources<Self::FileContent>,
-        _: &mut gpui::AppContext,
+        _: &mut gpui::App,
     ) -> anyhow::Result<Self> {
         sources.json_merge()
     }

crates/picker/src/head.rs 🔗

@@ -1,39 +1,43 @@
 use std::sync::Arc;
 
 use editor::{Editor, EditorEvent};
-use gpui::{prelude::*, AppContext, FocusHandle, FocusableView, View};
+use gpui::{prelude::*, App, Entity, FocusHandle, Focusable};
 use ui::prelude::*;
 
 /// The head of a [`Picker`](crate::Picker).
 pub(crate) enum Head {
     /// Picker has an editor that allows the user to filter the list.
-    Editor(View<Editor>),
+    Editor(Entity<Editor>),
 
     /// Picker has no head, it's just a list of items.
-    Empty(View<EmptyHead>),
+    Empty(Entity<EmptyHead>),
 }
 
 impl Head {
     pub fn editor<V: 'static>(
         placeholder_text: Arc<str>,
-        edit_handler: impl FnMut(&mut V, View<Editor>, &EditorEvent, &mut ViewContext<V>) + 'static,
-        cx: &mut ViewContext<V>,
+        edit_handler: impl FnMut(&mut V, &Entity<Editor>, &EditorEvent, &mut Window, &mut Context<V>)
+            + 'static,
+        window: &mut Window,
+        cx: &mut Context<V>,
     ) -> Self {
-        let editor = cx.new_view(|cx| {
-            let mut editor = Editor::single_line(cx);
+        let editor = cx.new(|cx| {
+            let mut editor = Editor::single_line(window, cx);
             editor.set_placeholder_text(placeholder_text, cx);
             editor
         });
-        cx.subscribe(&editor, edit_handler).detach();
+        cx.subscribe_in(&editor, window, edit_handler).detach();
         Self::Editor(editor)
     }
 
     pub fn empty<V: 'static>(
-        blur_handler: impl FnMut(&mut V, &mut ViewContext<V>) + 'static,
-        cx: &mut ViewContext<V>,
+        blur_handler: impl FnMut(&mut V, &mut Window, &mut Context<V>) + 'static,
+        window: &mut Window,
+        cx: &mut Context<V>,
     ) -> Self {
-        let head = cx.new_view(EmptyHead::new);
-        cx.on_blur(&head.focus_handle(cx), blur_handler).detach();
+        let head = cx.new(EmptyHead::new);
+        cx.on_blur(&head.focus_handle(cx), window, blur_handler)
+            .detach();
         Self::Empty(head)
     }
 }
@@ -44,7 +48,7 @@ pub(crate) struct EmptyHead {
 }
 
 impl EmptyHead {
-    fn new(cx: &mut ViewContext<Self>) -> Self {
+    fn new(cx: &mut Context<Self>) -> Self {
         Self {
             focus_handle: cx.focus_handle(),
         }
@@ -52,13 +56,13 @@ impl EmptyHead {
 }
 
 impl Render for EmptyHead {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         div().track_focus(&self.focus_handle(cx))
     }
 }
 
-impl FocusableView for EmptyHead {
-    fn focus_handle(&self, _: &AppContext) -> FocusHandle {
+impl Focusable for EmptyHead {
+    fn focus_handle(&self, _: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }

crates/picker/src/highlighted_match_with_paths.rs 🔗

@@ -49,7 +49,7 @@ impl HighlightedText {
     }
 }
 impl RenderOnce for HighlightedText {
-    fn render(self, _: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _: &mut App) -> impl IntoElement {
         HighlightedLabel::new(self.text, self.highlight_positions).color(self.color)
     }
 }
@@ -65,7 +65,7 @@ impl HighlightedMatchWithPaths {
 }
 
 impl RenderOnce for HighlightedMatchWithPaths {
-    fn render(mut self, _: &mut WindowContext) -> impl IntoElement {
+    fn render(mut self, _window: &mut Window, _: &mut App) -> impl IntoElement {
         v_flex()
             .child(self.match_label.clone())
             .when(!self.paths.is_empty(), |this| {

crates/picker/src/picker.rs 🔗

@@ -1,10 +1,10 @@
 use anyhow::Result;
 use editor::{scroll::Autoscroll, Editor};
 use gpui::{
-    actions, div, impl_actions, list, prelude::*, uniform_list, AnyElement, AppContext, ClickEvent,
-    DismissEvent, EventEmitter, FocusHandle, FocusableView, Length, ListSizingBehavior, ListState,
-    MouseButton, MouseUpEvent, Render, ScrollStrategy, Task, UniformListScrollHandle, View,
-    ViewContext, WindowContext,
+    actions, div, impl_actions, list, prelude::*, uniform_list, AnyElement, App, ClickEvent,
+    Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Length,
+    ListSizingBehavior, ListState, MouseButton, MouseUpEvent, Render, ScrollStrategy, Task,
+    UniformListScrollHandle, Window,
 };
 use head::Head;
 use schemars::JsonSchema;
@@ -69,20 +69,31 @@ pub trait PickerDelegate: Sized + 'static {
     fn separators_after_indices(&self) -> Vec<usize> {
         Vec::new()
     }
-    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>);
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    );
     // Allows binding some optional effect to when the selection changes.
     fn selected_index_changed(
         &self,
         _ix: usize,
-        _cx: &mut ViewContext<Picker<Self>>,
-    ) -> Option<Box<dyn Fn(&mut WindowContext) + 'static>> {
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
+    ) -> Option<Box<dyn Fn(&mut Window, &mut App) + 'static>> {
         None
     }
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str>;
-    fn no_matches_text(&self, _cx: &mut WindowContext) -> SharedString {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str>;
+    fn no_matches_text(&self, _window: &mut Window, _cx: &mut App) -> SharedString {
         "No matches".into()
     }
-    fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()>;
+    fn update_matches(
+        &mut self,
+        query: String,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Task<()>;
 
     // Delegates that support this method (e.g. the CommandPalette) can chose to block on any background
     // work for up to `duration` to try and get a result synchronously.
@@ -92,27 +103,39 @@ pub trait PickerDelegate: Sized + 'static {
         &mut self,
         _query: String,
         _duration: Duration,
-        _cx: &mut ViewContext<Picker<Self>>,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
     ) -> bool {
         false
     }
 
     /// Override if you want to have <enter> update the query instead of confirming.
-    fn confirm_update_query(&mut self, _cx: &mut ViewContext<Picker<Self>>) -> Option<String> {
+    fn confirm_update_query(
+        &mut self,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
+    ) -> Option<String> {
         None
     }
-    fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<Picker<Self>>);
+    fn confirm(&mut self, secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>);
     /// Instead of interacting with currently selected entry, treats editor input literally,
     /// performing some kind of action on it.
-    fn confirm_input(&mut self, _secondary: bool, _: &mut ViewContext<Picker<Self>>) {}
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>);
+    fn confirm_input(
+        &mut self,
+        _secondary: bool,
+        _window: &mut Window,
+        _: &mut Context<Picker<Self>>,
+    ) {
+    }
+    fn dismissed(&mut self, window: &mut Window, cx: &mut Context<Picker<Self>>);
     fn should_dismiss(&self) -> bool {
         true
     }
     fn confirm_completion(
         &mut self,
         _query: String,
-        _: &mut ViewContext<Picker<Self>>,
+        _window: &mut Window,
+        _: &mut Context<Picker<Self>>,
     ) -> Option<String> {
         None
     }
@@ -121,7 +144,12 @@ pub trait PickerDelegate: Sized + 'static {
         PickerEditorPosition::default()
     }
 
-    fn render_editor(&self, editor: &View<Editor>, _cx: &mut ViewContext<Picker<Self>>) -> Div {
+    fn render_editor(
+        &self,
+        editor: &Entity<Editor>,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
+    ) -> Div {
         v_flex()
             .when(
                 self.editor_position() == PickerEditorPosition::End,
@@ -145,18 +173,27 @@ pub trait PickerDelegate: Sized + 'static {
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem>;
-    fn render_header(&self, _: &mut ViewContext<Picker<Self>>) -> Option<AnyElement> {
+    fn render_header(
+        &self,
+        _window: &mut Window,
+        _: &mut Context<Picker<Self>>,
+    ) -> Option<AnyElement> {
         None
     }
-    fn render_footer(&self, _: &mut ViewContext<Picker<Self>>) -> Option<AnyElement> {
+    fn render_footer(
+        &self,
+        _window: &mut Window,
+        _: &mut Context<Picker<Self>>,
+    ) -> Option<AnyElement> {
         None
     }
 }
 
-impl<D: PickerDelegate> FocusableView for Picker<D> {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl<D: PickerDelegate> Focusable for Picker<D> {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         match &self.head {
             Head::Editor(editor) => editor.focus_handle(cx),
             Head::Empty(head) => head.focus_handle(cx),
@@ -174,38 +211,50 @@ impl<D: PickerDelegate> Picker<D> {
     /// A picker, which displays its matches using `gpui::uniform_list`, all matches should have the same height.
     /// The picker allows the user to perform search items by text.
     /// If `PickerDelegate::render_match` can return items with different heights, use `Picker::list`.
-    pub fn uniform_list(delegate: D, cx: &mut ViewContext<Self>) -> Self {
+    pub fn uniform_list(delegate: D, window: &mut Window, cx: &mut Context<Self>) -> Self {
         let head = Head::editor(
-            delegate.placeholder_text(cx),
+            delegate.placeholder_text(window, cx),
             Self::on_input_editor_event,
+            window,
             cx,
         );
 
-        Self::new(delegate, ContainerKind::UniformList, head, cx)
+        Self::new(delegate, ContainerKind::UniformList, head, window, cx)
     }
 
     /// A picker, which displays its matches using `gpui::uniform_list`, all matches should have the same height.
     /// If `PickerDelegate::render_match` can return items with different heights, use `Picker::list`.
-    pub fn nonsearchable_uniform_list(delegate: D, cx: &mut ViewContext<Self>) -> Self {
-        let head = Head::empty(Self::on_empty_head_blur, cx);
+    pub fn nonsearchable_uniform_list(
+        delegate: D,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Self {
+        let head = Head::empty(Self::on_empty_head_blur, window, cx);
 
-        Self::new(delegate, ContainerKind::UniformList, head, cx)
+        Self::new(delegate, ContainerKind::UniformList, head, window, cx)
     }
 
     /// A picker, which displays its matches using `gpui::list`, matches can have different heights.
     /// The picker allows the user to perform search items by text.
     /// If `PickerDelegate::render_match` only returns items with the same height, use `Picker::uniform_list` as its implementation is optimized for that.
-    pub fn list(delegate: D, cx: &mut ViewContext<Self>) -> Self {
+    pub fn list(delegate: D, window: &mut Window, cx: &mut Context<Self>) -> Self {
         let head = Head::editor(
-            delegate.placeholder_text(cx),
+            delegate.placeholder_text(window, cx),
             Self::on_input_editor_event,
+            window,
             cx,
         );
 
-        Self::new(delegate, ContainerKind::List, head, cx)
+        Self::new(delegate, ContainerKind::List, head, window, cx)
     }
 
-    fn new(delegate: D, container: ContainerKind, head: Head, cx: &mut ViewContext<Self>) -> Self {
+    fn new(
+        delegate: D,
+        container: ContainerKind,
+        head: Head,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Self {
         let mut this = Self {
             delegate,
             head,
@@ -216,32 +265,33 @@ impl<D: PickerDelegate> Picker<D> {
             max_height: Some(rems(18.).into()),
             is_modal: true,
         };
-        this.update_matches("".to_string(), cx);
+        this.update_matches("".to_string(), window, cx);
         // give the delegate 4ms to render the first set of suggestions.
         this.delegate
-            .finalize_update_matches("".to_string(), Duration::from_millis(4), cx);
+            .finalize_update_matches("".to_string(), Duration::from_millis(4), window, cx);
         this
     }
 
     fn create_element_container(
         container: ContainerKind,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> ElementContainer {
         match container {
             ContainerKind::UniformList => {
                 ElementContainer::UniformList(UniformListScrollHandle::new())
             }
             ContainerKind::List => {
-                let view = cx.view().downgrade();
+                let model = cx.model().downgrade();
                 ElementContainer::List(ListState::new(
                     0,
                     gpui::ListAlignment::Top,
                     px(1000.),
-                    move |ix, cx| {
-                        view.upgrade()
-                            .map(|view| {
-                                view.update(cx, |this, cx| {
-                                    this.render_element(cx, ix).into_any_element()
+                    move |ix, window, cx| {
+                        model
+                            .upgrade()
+                            .map(|model| {
+                                model.update(cx, |this, cx| {
+                                    this.render_element(window, cx, ix).into_any_element()
                                 })
                             })
                             .unwrap_or_else(|| div().into_any_element())
@@ -266,8 +316,8 @@ impl<D: PickerDelegate> Picker<D> {
         self
     }
 
-    pub fn focus(&self, cx: &mut WindowContext) {
-        self.focus_handle(cx).focus(cx);
+    pub fn focus(&self, window: &mut Window, cx: &mut App) {
+        self.focus_handle(cx).focus(window);
     }
 
     /// Handles the selecting an index, and passing the change to the delegate.
@@ -278,15 +328,16 @@ impl<D: PickerDelegate> Picker<D> {
         &mut self,
         ix: usize,
         scroll_to_index: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let previous_index = self.delegate.selected_index();
-        self.delegate.set_selected_index(ix, cx);
+        self.delegate.set_selected_index(ix, window, cx);
         let current_index = self.delegate.selected_index();
 
         if previous_index != current_index {
-            if let Some(action) = self.delegate.selected_index_changed(ix, cx) {
-                action(cx);
+            if let Some(action) = self.delegate.selected_index_changed(ix, window, cx) {
+                action(window, cx);
             }
             if scroll_to_index {
                 self.scroll_to_item_index(ix);
@@ -294,115 +345,143 @@ impl<D: PickerDelegate> Picker<D> {
         }
     }
 
-    pub fn select_next(&mut self, _: &menu::SelectNext, cx: &mut ViewContext<Self>) {
+    pub fn select_next(
+        &mut self,
+        _: &menu::SelectNext,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let count = self.delegate.match_count();
         if count > 0 {
             let index = self.delegate.selected_index();
             let ix = if index == count - 1 { 0 } else { index + 1 };
-            self.set_selected_index(ix, true, cx);
+            self.set_selected_index(ix, true, window, cx);
             cx.notify();
         }
     }
 
-    fn select_prev(&mut self, _: &menu::SelectPrev, cx: &mut ViewContext<Self>) {
+    fn select_prev(&mut self, _: &menu::SelectPrev, window: &mut Window, cx: &mut Context<Self>) {
         let count = self.delegate.match_count();
         if count > 0 {
             let index = self.delegate.selected_index();
             let ix = if index == 0 { count - 1 } else { index - 1 };
-            self.set_selected_index(ix, true, cx);
+            self.set_selected_index(ix, true, window, cx);
             cx.notify();
         }
     }
 
-    fn select_first(&mut self, _: &menu::SelectFirst, cx: &mut ViewContext<Self>) {
+    fn select_first(&mut self, _: &menu::SelectFirst, window: &mut Window, cx: &mut Context<Self>) {
         let count = self.delegate.match_count();
         if count > 0 {
-            self.set_selected_index(0, true, cx);
+            self.set_selected_index(0, true, window, cx);
             cx.notify();
         }
     }
 
-    fn select_last(&mut self, _: &menu::SelectLast, cx: &mut ViewContext<Self>) {
+    fn select_last(&mut self, _: &menu::SelectLast, window: &mut Window, cx: &mut Context<Self>) {
         let count = self.delegate.match_count();
         if count > 0 {
-            self.set_selected_index(count - 1, true, cx);
+            self.set_selected_index(count - 1, true, window, cx);
             cx.notify();
         }
     }
 
-    pub fn cycle_selection(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn cycle_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let count = self.delegate.match_count();
         let index = self.delegate.selected_index();
         let new_index = if index + 1 == count { 0 } else { index + 1 };
-        self.set_selected_index(new_index, true, cx);
+        self.set_selected_index(new_index, true, window, cx);
         cx.notify();
     }
 
-    pub fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
+    pub fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
         if self.delegate.should_dismiss() {
-            self.delegate.dismissed(cx);
+            self.delegate.dismissed(window, cx);
             cx.emit(DismissEvent);
         }
     }
 
-    fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
+    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
         if self.pending_update_matches.is_some()
-            && !self
-                .delegate
-                .finalize_update_matches(self.query(cx), Duration::from_millis(16), cx)
+            && !self.delegate.finalize_update_matches(
+                self.query(cx),
+                Duration::from_millis(16),
+                window,
+                cx,
+            )
         {
             self.confirm_on_update = Some(false)
         } else {
             self.pending_update_matches.take();
-            self.do_confirm(false, cx);
+            self.do_confirm(false, window, cx);
         }
     }
 
-    fn secondary_confirm(&mut self, _: &menu::SecondaryConfirm, cx: &mut ViewContext<Self>) {
+    fn secondary_confirm(
+        &mut self,
+        _: &menu::SecondaryConfirm,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if self.pending_update_matches.is_some()
-            && !self
-                .delegate
-                .finalize_update_matches(self.query(cx), Duration::from_millis(16), cx)
+            && !self.delegate.finalize_update_matches(
+                self.query(cx),
+                Duration::from_millis(16),
+                window,
+                cx,
+            )
         {
             self.confirm_on_update = Some(true)
         } else {
-            self.do_confirm(true, cx);
+            self.do_confirm(true, window, cx);
         }
     }
 
-    fn confirm_input(&mut self, input: &ConfirmInput, cx: &mut ViewContext<Self>) {
-        self.delegate.confirm_input(input.secondary, cx);
+    fn confirm_input(&mut self, input: &ConfirmInput, window: &mut Window, cx: &mut Context<Self>) {
+        self.delegate.confirm_input(input.secondary, window, cx);
     }
 
-    fn confirm_completion(&mut self, _: &ConfirmCompletion, cx: &mut ViewContext<Self>) {
-        if let Some(new_query) = self.delegate.confirm_completion(self.query(cx), cx) {
-            self.set_query(new_query, cx);
+    fn confirm_completion(
+        &mut self,
+        _: &ConfirmCompletion,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        if let Some(new_query) = self.delegate.confirm_completion(self.query(cx), window, cx) {
+            self.set_query(new_query, window, cx);
         } else {
             cx.propagate()
         }
     }
 
-    fn handle_click(&mut self, ix: usize, secondary: bool, cx: &mut ViewContext<Self>) {
+    fn handle_click(
+        &mut self,
+        ix: usize,
+        secondary: bool,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         cx.stop_propagation();
-        cx.prevent_default();
-        self.set_selected_index(ix, false, cx);
-        self.do_confirm(secondary, cx)
+        window.prevent_default();
+        self.set_selected_index(ix, false, window, cx);
+        self.do_confirm(secondary, window, cx)
     }
 
-    fn do_confirm(&mut self, secondary: bool, cx: &mut ViewContext<Self>) {
-        if let Some(update_query) = self.delegate.confirm_update_query(cx) {
-            self.set_query(update_query, cx);
-            self.delegate.set_selected_index(0, cx);
+    fn do_confirm(&mut self, secondary: bool, window: &mut Window, cx: &mut Context<Self>) {
+        if let Some(update_query) = self.delegate.confirm_update_query(window, cx) {
+            self.set_query(update_query, window, cx);
+            self.delegate.set_selected_index(0, window, cx);
         } else {
-            self.delegate.confirm(secondary, cx)
+            self.delegate.confirm(secondary, window, cx)
         }
     }
 
     fn on_input_editor_event(
         &mut self,
-        _: View<Editor>,
+        _: &Entity<Editor>,
         event: &editor::EditorEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let Head::Editor(ref editor) = &self.head else {
             panic!("unexpected call");
@@ -410,28 +489,28 @@ impl<D: PickerDelegate> Picker<D> {
         match event {
             editor::EditorEvent::BufferEdited => {
                 let query = editor.read(cx).text(cx);
-                self.update_matches(query, cx);
+                self.update_matches(query, window, cx);
             }
             editor::EditorEvent::Blurred => {
-                self.cancel(&menu::Cancel, cx);
+                self.cancel(&menu::Cancel, window, cx);
             }
             _ => {}
         }
     }
 
-    fn on_empty_head_blur(&mut self, cx: &mut ViewContext<Self>) {
+    fn on_empty_head_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let Head::Empty(_) = &self.head else {
             panic!("unexpected call");
         };
-        self.cancel(&menu::Cancel, cx);
+        self.cancel(&menu::Cancel, window, cx);
     }
 
-    pub fn refresh_placeholder(&mut self, cx: &mut WindowContext) {
+    pub fn refresh_placeholder(&mut self, window: &mut Window, cx: &mut App) {
         match &self.head {
-            Head::Editor(view) => {
-                let placeholder = self.delegate.placeholder_text(cx);
-                view.update(cx, |this, cx| {
-                    this.set_placeholder_text(placeholder, cx);
+            Head::Editor(editor) => {
+                let placeholder = self.delegate.placeholder_text(window, cx);
+                editor.update(cx, |editor, cx| {
+                    editor.set_placeholder_text(placeholder, cx);
                     cx.notify();
                 });
             }
@@ -439,15 +518,15 @@ impl<D: PickerDelegate> Picker<D> {
         }
     }
 
-    pub fn refresh(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn refresh(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let query = self.query(cx);
-        self.update_matches(query, cx);
+        self.update_matches(query, window, cx);
     }
 
-    pub fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) {
-        let delegate_pending_update_matches = self.delegate.update_matches(query, cx);
+    pub fn update_matches(&mut self, query: String, window: &mut Window, cx: &mut Context<Self>) {
+        let delegate_pending_update_matches = self.delegate.update_matches(query, window, cx);
 
-        self.matches_updated(cx);
+        self.matches_updated(window, cx);
         // This struct ensures that we can synchronously drop the task returned by the
         // delegate's `update_matches` method and the task that the picker is spawning.
         // If we simply capture the delegate's task into the picker's task, when the picker's
@@ -456,7 +535,7 @@ impl<D: PickerDelegate> Picker<D> {
         // asynchronously.
         self.pending_update_matches = Some(PendingUpdateMatches {
             delegate_update_matches: Some(delegate_pending_update_matches),
-            _task: cx.spawn(|this, mut cx| async move {
+            _task: cx.spawn_in(window, |this, mut cx| async move {
                 let delegate_pending_update_matches = this.update(&mut cx, |this, _| {
                     this.pending_update_matches
                         .as_mut()
@@ -466,14 +545,14 @@ impl<D: PickerDelegate> Picker<D> {
                         .unwrap()
                 })?;
                 delegate_pending_update_matches.await;
-                this.update(&mut cx, |this, cx| {
-                    this.matches_updated(cx);
+                this.update_in(&mut cx, |this, window, cx| {
+                    this.matches_updated(window, cx);
                 })
             }),
         });
     }
 
-    fn matches_updated(&mut self, cx: &mut ViewContext<Self>) {
+    fn matches_updated(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if let ElementContainer::List(state) = &mut self.element_container {
             state.reset(self.delegate.match_count());
         }
@@ -482,24 +561,24 @@ impl<D: PickerDelegate> Picker<D> {
         self.scroll_to_item_index(index);
         self.pending_update_matches = None;
         if let Some(secondary) = self.confirm_on_update.take() {
-            self.do_confirm(secondary, cx);
+            self.do_confirm(secondary, window, cx);
         }
         cx.notify();
     }
 
-    pub fn query(&self, cx: &AppContext) -> String {
+    pub fn query(&self, cx: &App) -> String {
         match &self.head {
             Head::Editor(editor) => editor.read(cx).text(cx),
             Head::Empty(_) => "".to_string(),
         }
     }
 
-    pub fn set_query(&self, query: impl Into<Arc<str>>, cx: &mut WindowContext) {
+    pub fn set_query(&self, query: impl Into<Arc<str>>, window: &mut Window, cx: &mut App) {
         if let Head::Editor(ref editor) = &self.head {
             editor.update(cx, |editor, cx| {
-                editor.set_text(query, cx);
+                editor.set_text(query, window, cx);
                 let editor_offset = editor.buffer().read(cx).len(cx);
-                editor.change_selections(Some(Autoscroll::Next), cx, |s| {
+                editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
                     s.select_ranges(Some(editor_offset..editor_offset))
                 });
             });
@@ -515,12 +594,17 @@ impl<D: PickerDelegate> Picker<D> {
         }
     }
 
-    fn render_element(&self, cx: &mut ViewContext<Self>, ix: usize) -> impl IntoElement {
+    fn render_element(
+        &self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+        ix: usize,
+    ) -> impl IntoElement {
         div()
             .id(("item", ix))
             .cursor_pointer()
-            .on_click(cx.listener(move |this, event: &ClickEvent, cx| {
-                this.handle_click(ix, event.down.modifiers.secondary(), cx)
+            .on_click(cx.listener(move |this, event: &ClickEvent, window, cx| {
+                this.handle_click(ix, event.down.modifiers.secondary(), window, cx)
             }))
             // As of this writing, GPUI intercepts `ctrl-[mouse-event]`s on macOS
             // and produces right mouse button events. This matches platforms norms
@@ -528,16 +612,18 @@ impl<D: PickerDelegate> Picker<D> {
             // switcher) can't be clicked on. Hence, this handler.
             .on_mouse_up(
                 MouseButton::Right,
-                cx.listener(move |this, event: &MouseUpEvent, cx| {
+                cx.listener(move |this, event: &MouseUpEvent, window, cx| {
                     // We specifically want to use the platform key here, as
                     // ctrl will already be held down for the tab switcher.
-                    this.handle_click(ix, event.modifiers.platform, cx)
+                    this.handle_click(ix, event.modifiers.platform, window, cx)
                 }),
             )
-            .children(
-                self.delegate
-                    .render_match(ix, ix == self.delegate.selected_index(), cx),
-            )
+            .children(self.delegate.render_match(
+                ix,
+                ix == self.delegate.selected_index(),
+                window,
+                cx,
+            ))
             .when(
                 self.delegate.separators_after_indices().contains(&ix),
                 |picker| {
@@ -549,7 +635,7 @@ impl<D: PickerDelegate> Picker<D> {
             )
     }
 
-    fn render_element_container(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render_element_container(&self, cx: &mut Context<Self>) -> impl IntoElement {
         let sizing_behavior = if self.max_height.is_some() {
             ListSizingBehavior::Infer
         } else {
@@ -557,12 +643,12 @@ impl<D: PickerDelegate> Picker<D> {
         };
         match &self.element_container {
             ElementContainer::UniformList(scroll_handle) => uniform_list(
-                cx.view().clone(),
+                cx.model().clone(),
                 "candidates",
                 self.delegate.match_count(),
-                move |picker, visible_range, cx| {
+                move |picker, visible_range, window, cx| {
                     visible_range
-                        .map(|ix| picker.render_element(cx, ix))
+                        .map(|ix| picker.render_element(window, cx, ix))
                         .collect()
                 },
             )
@@ -594,7 +680,7 @@ impl<D: PickerDelegate> EventEmitter<DismissEvent> for Picker<D> {}
 impl<D: PickerDelegate> ModalView for Picker<D> {}
 
 impl<D: PickerDelegate> Render for Picker<D> {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let editor_position = self.delegate.editor_position();
 
         v_flex()
@@ -619,7 +705,7 @@ impl<D: PickerDelegate> Render for Picker<D> {
             .children(match &self.head {
                 Head::Editor(editor) => {
                     if editor_position == PickerEditorPosition::Start {
-                        Some(self.delegate.render_editor(&editor.clone(), cx))
+                        Some(self.delegate.render_editor(&editor.clone(), window, cx))
                     } else {
                         None
                     }
@@ -632,7 +718,7 @@ impl<D: PickerDelegate> Render for Picker<D> {
                         .flex_grow()
                         .when_some(self.max_height, |div, max_h| div.max_h(max_h))
                         .overflow_hidden()
-                        .children(self.delegate.render_header(cx))
+                        .children(self.delegate.render_header(window, cx))
                         .child(self.render_element_container(cx)),
                 )
             })
@@ -644,16 +730,17 @@ impl<D: PickerDelegate> Render for Picker<D> {
                             .spacing(ListItemSpacing::Sparse)
                             .disabled(true)
                             .child(
-                                Label::new(self.delegate.no_matches_text(cx)).color(Color::Muted),
+                                Label::new(self.delegate.no_matches_text(window, cx))
+                                    .color(Color::Muted),
                             ),
                     ),
                 )
             })
-            .children(self.delegate.render_footer(cx))
+            .children(self.delegate.render_footer(window, cx))
             .children(match &self.head {
                 Head::Editor(editor) => {
                     if editor_position == PickerEditorPosition::End {
-                        Some(self.delegate.render_editor(&editor.clone(), cx))
+                        Some(self.delegate.render_editor(&editor.clone(), window, cx))
                     } else {
                         None
                     }

crates/prettier/src/prettier.rs 🔗

@@ -1,7 +1,7 @@
-use anyhow::{anyhow, Context};
+use anyhow::{anyhow, Context as _};
 use collections::{HashMap, HashSet};
 use fs::Fs;
-use gpui::{AsyncAppContext, Model};
+use gpui::{AsyncAppContext, Entity};
 use language::{language_settings::language_settings, Buffer, Diff};
 use lsp::{LanguageServer, LanguageServerId};
 use node_runtime::NodeRuntime;
@@ -302,7 +302,7 @@ impl Prettier {
 
     pub async fn format(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         buffer_path: Option<PathBuf>,
         ignore_dir: Option<PathBuf>,
         cx: &mut AsyncAppContext,

crates/project/src/buffer_store.rs 🔗

@@ -12,8 +12,8 @@ use fs::Fs;
 use futures::{channel::oneshot, future::Shared, Future, FutureExt as _, StreamExt};
 use git::{blame::Blame, diff::BufferDiff, repository::RepoPath};
 use gpui::{
-    AppContext, AsyncAppContext, Context as _, EventEmitter, Model, ModelContext, Subscription,
-    Task, WeakModel,
+    App, AppContext as _, AsyncAppContext, Context, Entity, EventEmitter, Subscription, Task,
+    WeakEntity,
 };
 use http_client::Url;
 use language::{
@@ -43,11 +43,11 @@ use worktree::{File, PathChange, ProjectEntryId, UpdatedGitRepositoriesSet, Work
 pub struct BufferStore {
     state: BufferStoreState,
     #[allow(clippy::type_complexity)]
-    loading_buffers: HashMap<ProjectPath, Shared<Task<Result<Model<Buffer>, Arc<anyhow::Error>>>>>,
+    loading_buffers: HashMap<ProjectPath, Shared<Task<Result<Entity<Buffer>, Arc<anyhow::Error>>>>>,
     #[allow(clippy::type_complexity)]
     loading_change_sets:
-        HashMap<BufferId, Shared<Task<Result<Model<BufferChangeSet>, Arc<anyhow::Error>>>>>,
-    worktree_store: Model<WorktreeStore>,
+        HashMap<BufferId, Shared<Task<Result<Entity<BufferChangeSet>, Arc<anyhow::Error>>>>>,
+    worktree_store: Entity<WorktreeStore>,
     opened_buffers: HashMap<BufferId, OpenBuffer>,
     downstream_client: Option<(AnyProtoClient, u64)>,
     shared_buffers: HashMap<proto::PeerId, HashMap<BufferId, SharedBuffer>>,
@@ -55,8 +55,8 @@ pub struct BufferStore {
 
 #[derive(Hash, Eq, PartialEq, Clone)]
 struct SharedBuffer {
-    buffer: Model<Buffer>,
-    unstaged_changes: Option<Model<BufferChangeSet>>,
+    buffer: Entity<Buffer>,
+    unstaged_changes: Option<Entity<BufferChangeSet>>,
     lsp_handle: Option<OpenLspBufferHandle>,
 }
 
@@ -76,50 +76,46 @@ enum BufferStoreState {
 }
 
 struct RemoteBufferStore {
-    shared_with_me: HashSet<Model<Buffer>>,
+    shared_with_me: HashSet<Entity<Buffer>>,
     upstream_client: AnyProtoClient,
     project_id: u64,
-    loading_remote_buffers_by_id: HashMap<BufferId, Model<Buffer>>,
+    loading_remote_buffers_by_id: HashMap<BufferId, Entity<Buffer>>,
     remote_buffer_listeners:
-        HashMap<BufferId, Vec<oneshot::Sender<Result<Model<Buffer>, anyhow::Error>>>>,
-    worktree_store: Model<WorktreeStore>,
+        HashMap<BufferId, Vec<oneshot::Sender<Result<Entity<Buffer>, anyhow::Error>>>>,
+    worktree_store: Entity<WorktreeStore>,
 }
 
 struct LocalBufferStore {
     local_buffer_ids_by_path: HashMap<ProjectPath, BufferId>,
     local_buffer_ids_by_entry_id: HashMap<ProjectEntryId, BufferId>,
-    worktree_store: Model<WorktreeStore>,
+    worktree_store: Entity<WorktreeStore>,
     _subscription: Subscription,
 }
 
 enum OpenBuffer {
     Complete {
-        buffer: WeakModel<Buffer>,
-        unstaged_changes: Option<WeakModel<BufferChangeSet>>,
+        buffer: WeakEntity<Buffer>,
+        unstaged_changes: Option<WeakEntity<BufferChangeSet>>,
     },
     Operations(Vec<Operation>),
 }
 
 pub enum BufferStoreEvent {
-    BufferAdded(Model<Buffer>),
+    BufferAdded(Entity<Buffer>),
     BufferDropped(BufferId),
     BufferChangedFilePath {
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         old_file: Option<Arc<dyn language::File>>,
     },
 }
 
 #[derive(Default, Debug)]
-pub struct ProjectTransaction(pub HashMap<Model<Buffer>, language::Transaction>);
+pub struct ProjectTransaction(pub HashMap<Entity<Buffer>, language::Transaction>);
 
 impl EventEmitter<BufferStoreEvent> for BufferStore {}
 
 impl RemoteBufferStore {
-    fn load_staged_text(
-        &self,
-        buffer_id: BufferId,
-        cx: &AppContext,
-    ) -> Task<Result<Option<String>>> {
+    fn load_staged_text(&self, buffer_id: BufferId, cx: &App) -> Task<Result<Option<String>>> {
         let project_id = self.project_id;
         let client = self.upstream_client.clone();
         cx.background_executor().spawn(async move {
@@ -135,8 +131,8 @@ impl RemoteBufferStore {
     pub fn wait_for_remote_buffer(
         &mut self,
         id: BufferId,
-        cx: &mut ModelContext<BufferStore>,
-    ) -> Task<Result<Model<Buffer>>> {
+        cx: &mut Context<BufferStore>,
+    ) -> Task<Result<Entity<Buffer>>> {
         let (tx, rx) = oneshot::channel();
         self.remote_buffer_listeners.entry(id).or_default().push(tx);
 
@@ -157,9 +153,9 @@ impl RemoteBufferStore {
 
     fn save_remote_buffer(
         &self,
-        buffer_handle: Model<Buffer>,
+        buffer_handle: Entity<Buffer>,
         new_path: Option<proto::ProjectPath>,
-        cx: &ModelContext<BufferStore>,
+        cx: &Context<BufferStore>,
     ) -> Task<Result<()>> {
         let buffer = buffer_handle.read(cx);
         let buffer_id = buffer.remote_id().into();
@@ -191,8 +187,8 @@ impl RemoteBufferStore {
         envelope: TypedEnvelope<proto::CreateBufferForPeer>,
         replica_id: u16,
         capability: Capability,
-        cx: &mut ModelContext<BufferStore>,
-    ) -> Result<Option<Model<Buffer>>> {
+        cx: &mut Context<BufferStore>,
+    ) -> Result<Option<Entity<Buffer>>> {
         match envelope
             .payload
             .variant
@@ -220,7 +216,7 @@ impl RemoteBufferStore {
 
                 match buffer_result {
                     Ok(buffer) => {
-                        let buffer = cx.new_model(|_| buffer);
+                        let buffer = cx.new(|_| buffer);
                         self.loading_remote_buffers_by_id.insert(buffer_id, buffer);
                     }
                     Err(error) => {
@@ -292,7 +288,7 @@ impl RemoteBufferStore {
         &self,
         message: proto::ProjectTransaction,
         push_to_history: bool,
-        cx: &mut ModelContext<BufferStore>,
+        cx: &mut Context<BufferStore>,
     ) -> Task<Result<ProjectTransaction>> {
         cx.spawn(|this, mut cx| async move {
             let mut project_transaction = ProjectTransaction::default();
@@ -329,9 +325,9 @@ impl RemoteBufferStore {
     fn open_buffer(
         &self,
         path: Arc<Path>,
-        worktree: Model<Worktree>,
-        cx: &mut ModelContext<BufferStore>,
-    ) -> Task<Result<Model<Buffer>>> {
+        worktree: Entity<Worktree>,
+        cx: &mut Context<BufferStore>,
+    ) -> Task<Result<Entity<Buffer>>> {
         let worktree_id = worktree.read(cx).id().to_proto();
         let project_id = self.project_id;
         let client = self.upstream_client.clone();
@@ -356,7 +352,7 @@ impl RemoteBufferStore {
         })
     }
 
-    fn create_buffer(&self, cx: &mut ModelContext<BufferStore>) -> Task<Result<Model<Buffer>>> {
+    fn create_buffer(&self, cx: &mut Context<BufferStore>) -> Task<Result<Entity<Buffer>>> {
         let create = self.upstream_client.request(proto::OpenNewBuffer {
             project_id: self.project_id,
         });
@@ -373,9 +369,9 @@ impl RemoteBufferStore {
 
     fn reload_buffers(
         &self,
-        buffers: HashSet<Model<Buffer>>,
+        buffers: HashSet<Entity<Buffer>>,
         push_to_history: bool,
-        cx: &mut ModelContext<BufferStore>,
+        cx: &mut Context<BufferStore>,
     ) -> Task<Result<ProjectTransaction>> {
         let request = self.upstream_client.request(proto::ReloadBuffers {
             project_id: self.project_id,
@@ -399,11 +395,7 @@ impl RemoteBufferStore {
 }
 
 impl LocalBufferStore {
-    fn load_staged_text(
-        &self,
-        buffer: &Model<Buffer>,
-        cx: &AppContext,
-    ) -> Task<Result<Option<String>>> {
+    fn load_staged_text(&self, buffer: &Entity<Buffer>, cx: &App) -> Task<Result<Option<String>>> {
         let Some(file) = buffer.read(cx).file() else {
             return Task::ready(Ok(None));
         };
@@ -422,11 +414,11 @@ impl LocalBufferStore {
 
     fn save_local_buffer(
         &self,
-        buffer_handle: Model<Buffer>,
-        worktree: Model<Worktree>,
+        buffer_handle: Entity<Buffer>,
+        worktree: Entity<Worktree>,
         path: Arc<Path>,
         mut has_changed_file: bool,
-        cx: &mut ModelContext<BufferStore>,
+        cx: &mut Context<BufferStore>,
     ) -> Task<Result<()>> {
         let buffer = buffer_handle.read(cx);
 
@@ -480,8 +472,8 @@ impl LocalBufferStore {
 
     fn subscribe_to_worktree(
         &mut self,
-        worktree: &Model<Worktree>,
-        cx: &mut ModelContext<BufferStore>,
+        worktree: &Entity<Worktree>,
+        cx: &mut Context<BufferStore>,
     ) {
         cx.subscribe(worktree, |this, worktree, event, cx| {
             if worktree.read(cx).is_local() {
@@ -506,9 +498,9 @@ impl LocalBufferStore {
 
     fn local_worktree_entries_changed(
         this: &mut BufferStore,
-        worktree_handle: &Model<Worktree>,
+        worktree_handle: &Entity<Worktree>,
         changes: &[(Arc<Path>, ProjectEntryId, PathChange)],
-        cx: &mut ModelContext<BufferStore>,
+        cx: &mut Context<BufferStore>,
     ) {
         let snapshot = worktree_handle.read(cx).snapshot();
         for (path, entry_id, _) in changes {
@@ -525,9 +517,9 @@ impl LocalBufferStore {
 
     fn local_worktree_git_repos_changed(
         this: &mut BufferStore,
-        worktree_handle: Model<Worktree>,
+        worktree_handle: Entity<Worktree>,
         changed_repos: &UpdatedGitRepositoriesSet,
-        cx: &mut ModelContext<BufferStore>,
+        cx: &mut Context<BufferStore>,
     ) {
         debug_assert!(worktree_handle.read(cx).is_local());
 
@@ -609,9 +601,9 @@ impl LocalBufferStore {
         this: &mut BufferStore,
         entry_id: ProjectEntryId,
         path: &Arc<Path>,
-        worktree: &Model<worktree::Worktree>,
+        worktree: &Entity<worktree::Worktree>,
         snapshot: &worktree::Snapshot,
-        cx: &mut ModelContext<BufferStore>,
+        cx: &mut Context<BufferStore>,
     ) -> Option<()> {
         let project_path = ProjectPath {
             worktree_id: snapshot.id(),
@@ -696,7 +688,7 @@ impl LocalBufferStore {
                     buffer_id,
                 );
                 events.push(BufferStoreEvent::BufferChangedFilePath {
-                    buffer: cx.handle(),
+                    buffer: cx.model(),
                     old_file: buffer.file().cloned(),
                 });
             }
@@ -733,7 +725,7 @@ impl LocalBufferStore {
         None
     }
 
-    fn buffer_changed_file(&mut self, buffer: Model<Buffer>, cx: &mut AppContext) -> Option<()> {
+    fn buffer_changed_file(&mut self, buffer: Entity<Buffer>, cx: &mut App) -> Option<()> {
         let file = File::from_dyn(buffer.read(cx).file())?;
 
         let remote_id = buffer.read(cx).remote_id();
@@ -761,8 +753,8 @@ impl LocalBufferStore {
 
     fn save_buffer(
         &self,
-        buffer: Model<Buffer>,
-        cx: &mut ModelContext<BufferStore>,
+        buffer: Entity<Buffer>,
+        cx: &mut Context<BufferStore>,
     ) -> Task<Result<()>> {
         let Some(file) = File::from_dyn(buffer.read(cx).file()) else {
             return Task::ready(Err(anyhow!("buffer doesn't have a file")));
@@ -773,9 +765,9 @@ impl LocalBufferStore {
 
     fn save_buffer_as(
         &self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         path: ProjectPath,
-        cx: &mut ModelContext<BufferStore>,
+        cx: &mut Context<BufferStore>,
     ) -> Task<Result<()>> {
         let Some(worktree) = self
             .worktree_store
@@ -790,9 +782,9 @@ impl LocalBufferStore {
     fn open_buffer(
         &self,
         path: Arc<Path>,
-        worktree: Model<Worktree>,
-        cx: &mut ModelContext<BufferStore>,
-    ) -> Task<Result<Model<Buffer>>> {
+        worktree: Entity<Worktree>,
+        cx: &mut Context<BufferStore>,
+    ) -> Task<Result<Entity<Buffer>>> {
         let load_buffer = worktree.update(cx, |worktree, cx| {
             let load_file = worktree.load_file(path.as_ref(), cx);
             let reservation = cx.reserve_model();
@@ -812,7 +804,7 @@ impl LocalBufferStore {
         cx.spawn(move |this, mut cx| async move {
             let buffer = match load_buffer.await {
                 Ok(buffer) => Ok(buffer),
-                Err(error) if is_not_found_error(&error) => cx.new_model(|cx| {
+                Err(error) if is_not_found_error(&error) => cx.new(|cx| {
                     let buffer_id = BufferId::from(cx.entity_id().as_non_zero_u64());
                     let text_buffer = text::Buffer::new(0, buffer_id, "".into());
                     Buffer::build(
@@ -856,11 +848,10 @@ impl LocalBufferStore {
         })
     }
 
-    fn create_buffer(&self, cx: &mut ModelContext<BufferStore>) -> Task<Result<Model<Buffer>>> {
+    fn create_buffer(&self, cx: &mut Context<BufferStore>) -> Task<Result<Entity<Buffer>>> {
         cx.spawn(|buffer_store, mut cx| async move {
-            let buffer = cx.new_model(|cx| {
-                Buffer::local("", cx).with_language(language::PLAIN_TEXT.clone(), cx)
-            })?;
+            let buffer =
+                cx.new(|cx| Buffer::local("", cx).with_language(language::PLAIN_TEXT.clone(), cx))?;
             buffer_store.update(&mut cx, |buffer_store, cx| {
                 buffer_store.add_buffer(buffer.clone(), cx).log_err();
             })?;
@@ -870,9 +861,9 @@ impl LocalBufferStore {
 
     fn reload_buffers(
         &self,
-        buffers: HashSet<Model<Buffer>>,
+        buffers: HashSet<Entity<Buffer>>,
         push_to_history: bool,
-        cx: &mut ModelContext<BufferStore>,
+        cx: &mut Context<BufferStore>,
     ) -> Task<Result<ProjectTransaction>> {
         cx.spawn(move |_, mut cx| async move {
             let mut project_transaction = ProjectTransaction::default();
@@ -885,7 +876,7 @@ impl LocalBufferStore {
                         if !push_to_history {
                             buffer.forget_transaction(transaction.id);
                         }
-                        project_transaction.0.insert(cx.handle(), transaction);
+                        project_transaction.0.insert(cx.model(), transaction);
                     }
                 })?;
             }
@@ -909,7 +900,7 @@ impl BufferStore {
     }
 
     /// Creates a buffer store, optionally retaining its buffers.
-    pub fn local(worktree_store: Model<WorktreeStore>, cx: &mut ModelContext<Self>) -> Self {
+    pub fn local(worktree_store: Entity<WorktreeStore>, cx: &mut Context<Self>) -> Self {
         Self {
             state: BufferStoreState::Local(LocalBufferStore {
                 local_buffer_ids_by_path: Default::default(),
@@ -932,10 +923,10 @@ impl BufferStore {
     }
 
     pub fn remote(
-        worktree_store: Model<WorktreeStore>,
+        worktree_store: Entity<WorktreeStore>,
         upstream_client: AnyProtoClient,
         remote_id: u64,
-        _cx: &mut ModelContext<Self>,
+        _cx: &mut Context<Self>,
     ) -> Self {
         Self {
             state: BufferStoreState::Remote(RemoteBufferStore {
@@ -979,8 +970,8 @@ impl BufferStore {
     pub fn open_buffer(
         &mut self,
         project_path: ProjectPath,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<Buffer>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<Buffer>>> {
         if let Some(buffer) = self.get_by_path(&project_path, cx) {
             return Task::ready(Ok(buffer));
         }
@@ -1024,9 +1015,9 @@ impl BufferStore {
 
     pub fn open_unstaged_changes(
         &mut self,
-        buffer: Model<Buffer>,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<BufferChangeSet>>> {
+        buffer: Entity<Buffer>,
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<BufferChangeSet>>> {
         let buffer_id = buffer.read(cx).remote_id();
         if let Some(change_set) = self.get_unstaged_changes(buffer_id) {
             return Task::ready(Ok(change_set));
@@ -1058,17 +1049,17 @@ impl BufferStore {
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn set_change_set(&mut self, buffer_id: BufferId, change_set: Model<BufferChangeSet>) {
+    pub fn set_change_set(&mut self, buffer_id: BufferId, change_set: Entity<BufferChangeSet>) {
         self.loading_change_sets
             .insert(buffer_id, Task::ready(Ok(change_set)).shared());
     }
 
     pub async fn open_unstaged_changes_internal(
-        this: WeakModel<Self>,
+        this: WeakEntity<Self>,
         text: Result<Option<String>>,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
-    ) -> Result<Model<BufferChangeSet>> {
+    ) -> Result<Entity<BufferChangeSet>> {
         let text = match text {
             Err(e) => {
                 this.update(&mut cx, |this, cx| {
@@ -1080,9 +1071,7 @@ impl BufferStore {
             Ok(text) => text,
         };
 
-        let change_set = cx
-            .new_model(|cx| BufferChangeSet::new(&buffer, cx))
-            .unwrap();
+        let change_set = cx.new(|cx| BufferChangeSet::new(&buffer, cx)).unwrap();
 
         if let Some(text) = text {
             change_set
@@ -1108,7 +1097,7 @@ impl BufferStore {
         Ok(change_set)
     }
 
-    pub fn create_buffer(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<Model<Buffer>>> {
+    pub fn create_buffer(&mut self, cx: &mut Context<Self>) -> Task<Result<Entity<Buffer>>> {
         match &self.state {
             BufferStoreState::Local(this) => this.create_buffer(cx),
             BufferStoreState::Remote(this) => this.create_buffer(cx),
@@ -1117,8 +1106,8 @@ impl BufferStore {
 
     pub fn save_buffer(
         &mut self,
-        buffer: Model<Buffer>,
-        cx: &mut ModelContext<Self>,
+        buffer: Entity<Buffer>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         match &mut self.state {
             BufferStoreState::Local(this) => this.save_buffer(buffer, cx),
@@ -1128,9 +1117,9 @@ impl BufferStore {
 
     pub fn save_buffer_as(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         path: ProjectPath,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let old_file = buffer.read(cx).file().cloned();
         let task = match &self.state {
@@ -1149,9 +1138,9 @@ impl BufferStore {
 
     pub fn blame_buffer(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         version: Option<clock::Global>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Result<Option<Blame>>> {
         let buffer = buffer.read(cx);
         let Some(file) = File::from_dyn(buffer.file()) else {
@@ -1211,9 +1200,9 @@ impl BufferStore {
 
     pub fn get_permalink_to_line(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         selection: Range<u32>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Result<url::Url>> {
         let buffer = buffer.read(cx);
         let Some(file) = File::from_dyn(buffer.file()) else {
@@ -1306,7 +1295,7 @@ impl BufferStore {
         }
     }
 
-    fn add_buffer(&mut self, buffer: Model<Buffer>, cx: &mut ModelContext<Self>) -> Result<()> {
+    fn add_buffer(&mut self, buffer: Entity<Buffer>, cx: &mut Context<Self>) -> Result<()> {
         let remote_id = buffer.read(cx).remote_id();
         let is_remote = buffer.read(cx).replica_id() != 0;
         let open_buffer = OpenBuffer::Complete {
@@ -1314,7 +1303,7 @@ impl BufferStore {
             unstaged_changes: None,
         };
 
-        let handle = cx.handle().downgrade();
+        let handle = cx.model().downgrade();
         buffer.update(cx, move |_, cx| {
             cx.on_release(move |buffer, cx| {
                 handle
@@ -1350,7 +1339,7 @@ impl BufferStore {
         Ok(())
     }
 
-    pub fn buffers(&self) -> impl '_ + Iterator<Item = Model<Buffer>> {
+    pub fn buffers(&self) -> impl '_ + Iterator<Item = Entity<Buffer>> {
         self.opened_buffers
             .values()
             .filter_map(|buffer| buffer.upgrade())
@@ -1358,14 +1347,14 @@ impl BufferStore {
 
     pub fn loading_buffers(
         &self,
-    ) -> impl Iterator<Item = (&ProjectPath, impl Future<Output = Result<Model<Buffer>>>)> {
+    ) -> impl Iterator<Item = (&ProjectPath, impl Future<Output = Result<Entity<Buffer>>>)> {
         self.loading_buffers.iter().map(|(path, task)| {
             let task = task.clone();
             (path, async move { task.await.map_err(|e| anyhow!("{e}")) })
         })
     }
 
-    pub fn get_by_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<Model<Buffer>> {
+    pub fn get_by_path(&self, path: &ProjectPath, cx: &App) -> Option<Entity<Buffer>> {
         self.buffers().find_map(|buffer| {
             let file = File::from_dyn(buffer.read(cx).file())?;
             if file.worktree_id(cx) == path.worktree_id && file.path == path.path {
@@ -1376,23 +1365,23 @@ impl BufferStore {
         })
     }
 
-    pub fn get(&self, buffer_id: BufferId) -> Option<Model<Buffer>> {
+    pub fn get(&self, buffer_id: BufferId) -> Option<Entity<Buffer>> {
         self.opened_buffers.get(&buffer_id)?.upgrade()
     }
 
-    pub fn get_existing(&self, buffer_id: BufferId) -> Result<Model<Buffer>> {
+    pub fn get_existing(&self, buffer_id: BufferId) -> Result<Entity<Buffer>> {
         self.get(buffer_id)
             .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))
     }
 
-    pub fn get_possibly_incomplete(&self, buffer_id: BufferId) -> Option<Model<Buffer>> {
+    pub fn get_possibly_incomplete(&self, buffer_id: BufferId) -> Option<Entity<Buffer>> {
         self.get(buffer_id).or_else(|| {
             self.as_remote()
                 .and_then(|remote| remote.loading_remote_buffers_by_id.get(&buffer_id).cloned())
         })
     }
 
-    pub fn get_unstaged_changes(&self, buffer_id: BufferId) -> Option<Model<BufferChangeSet>> {
+    pub fn get_unstaged_changes(&self, buffer_id: BufferId) -> Option<Entity<BufferChangeSet>> {
         if let OpenBuffer::Complete {
             unstaged_changes, ..
         } = self.opened_buffers.get(&buffer_id)?
@@ -1403,10 +1392,7 @@ impl BufferStore {
         }
     }
 
-    pub fn buffer_version_info(
-        &self,
-        cx: &AppContext,
-    ) -> (Vec<proto::BufferVersion>, Vec<BufferId>) {
+    pub fn buffer_version_info(&self, cx: &App) -> (Vec<proto::BufferVersion>, Vec<BufferId>) {
         let buffers = self
             .buffers()
             .map(|buffer| {
@@ -1424,7 +1410,7 @@ impl BufferStore {
         (buffers, incomplete_buffer_ids)
     }
 
-    pub fn disconnected_from_host(&mut self, cx: &mut AppContext) {
+    pub fn disconnected_from_host(&mut self, cx: &mut App) {
         for open_buffer in self.opened_buffers.values_mut() {
             if let Some(buffer) = open_buffer.upgrade() {
                 buffer.update(cx, |buffer, _| buffer.give_up_waiting());
@@ -1444,16 +1430,11 @@ impl BufferStore {
         }
     }
 
-    pub fn shared(
-        &mut self,
-        remote_id: u64,
-        downstream_client: AnyProtoClient,
-        _cx: &mut AppContext,
-    ) {
+    pub fn shared(&mut self, remote_id: u64, downstream_client: AnyProtoClient, _cx: &mut App) {
         self.downstream_client = Some((downstream_client, remote_id));
     }
 
-    pub fn unshared(&mut self, _cx: &mut ModelContext<Self>) {
+    pub fn unshared(&mut self, _cx: &mut Context<Self>) {
         self.downstream_client.take();
         self.forget_shared_buffers();
     }
@@ -1468,8 +1449,8 @@ impl BufferStore {
         query: &SearchQuery,
         mut limit: usize,
         fs: Arc<dyn Fs>,
-        cx: &mut ModelContext<Self>,
-    ) -> Receiver<Model<Buffer>> {
+        cx: &mut Context<Self>,
+    ) -> Receiver<Entity<Buffer>> {
         let (tx, rx) = smol::channel::unbounded();
         let mut open_buffers = HashSet::default();
         let mut unnamed_buffers = Vec::new();
@@ -1520,8 +1501,8 @@ impl BufferStore {
 
     pub fn recalculate_buffer_diffs(
         &mut self,
-        buffers: Vec<Model<Buffer>>,
-        cx: &mut ModelContext<Self>,
+        buffers: Vec<Entity<Buffer>>,
+        cx: &mut Context<Self>,
     ) -> impl Future<Output = ()> {
         let mut futures = Vec::new();
         for buffer in buffers {
@@ -1549,9 +1530,9 @@ impl BufferStore {
 
     fn on_buffer_event(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         event: &BufferEvent,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match event {
             BufferEvent::FileHandleChanged => {
@@ -1579,7 +1560,7 @@ impl BufferStore {
     }
 
     pub async fn handle_update_buffer(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::UpdateBuffer>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::Ack> {
@@ -1626,7 +1607,7 @@ impl BufferStore {
     pub fn handle_synchronize_buffers(
         &mut self,
         envelope: TypedEnvelope<proto::SynchronizeBuffers>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
         client: Arc<Client>,
     ) -> Result<proto::SynchronizeBuffersResponse> {
         let project_id = envelope.payload.project_id;
@@ -1718,7 +1699,7 @@ impl BufferStore {
         envelope: TypedEnvelope<proto::CreateBufferForPeer>,
         replica_id: u16,
         capability: Capability,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Result<()> {
         let Some(remote) = self.as_remote_mut() else {
             return Err(anyhow!("buffer store is not a remote"));
@@ -1734,7 +1715,7 @@ impl BufferStore {
     }
 
     pub async fn handle_update_buffer_file(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::UpdateBufferFile>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -1782,7 +1763,7 @@ impl BufferStore {
     }
 
     pub async fn handle_save_buffer(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::SaveBuffer>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::BufferSaved> {
@@ -1823,7 +1804,7 @@ impl BufferStore {
     }
 
     pub async fn handle_close_buffer(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::CloseBuffer>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -1847,7 +1828,7 @@ impl BufferStore {
     }
 
     pub async fn handle_buffer_saved(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::BufferSaved>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -1875,7 +1856,7 @@ impl BufferStore {
     }
 
     pub async fn handle_buffer_reloaded(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::BufferReloaded>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -1908,7 +1889,7 @@ impl BufferStore {
     }
 
     pub async fn handle_blame_buffer(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::BlameBuffer>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::BlameBufferResponse> {
@@ -1929,7 +1910,7 @@ impl BufferStore {
     }
 
     pub async fn handle_get_permalink_to_line(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::GetPermalinkToLine>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::GetPermalinkToLineResponse> {
@@ -1954,7 +1935,7 @@ impl BufferStore {
     }
 
     pub async fn handle_get_staged_text(
-        this: Model<Self>,
+        this: Entity<Self>,
         request: TypedEnvelope<proto::GetStagedText>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::GetStagedTextResponse> {
@@ -1983,7 +1964,7 @@ impl BufferStore {
     }
 
     pub async fn handle_update_diff_base(
-        this: Model<Self>,
+        this: Entity<Self>,
         request: TypedEnvelope<proto::UpdateDiffBase>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -2014,9 +1995,9 @@ impl BufferStore {
 
     pub fn reload_buffers(
         &self,
-        buffers: HashSet<Model<Buffer>>,
+        buffers: HashSet<Entity<Buffer>>,
         push_to_history: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<ProjectTransaction>> {
         if buffers.is_empty() {
             return Task::ready(Ok(ProjectTransaction::default()));
@@ -2028,7 +2009,7 @@ impl BufferStore {
     }
 
     async fn handle_reload_buffers(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::ReloadBuffers>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::ReloadBuffersResponse> {
@@ -2053,9 +2034,9 @@ impl BufferStore {
 
     pub fn create_buffer_for_peer(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         peer_id: proto::PeerId,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let buffer_id = buffer.read(cx).remote_id();
         let shared_buffers = self.shared_buffers.entry(peer_id).or_default();
@@ -2140,9 +2121,9 @@ impl BufferStore {
         &mut self,
         text: &str,
         language: Option<Arc<Language>>,
-        cx: &mut ModelContext<Self>,
-    ) -> Model<Buffer> {
-        let buffer = cx.new_model(|cx| {
+        cx: &mut Context<Self>,
+    ) -> Entity<Buffer> {
+        let buffer = cx.new(|cx| {
             Buffer::local(text, cx)
                 .with_language(language.unwrap_or_else(|| language::PLAIN_TEXT.clone()), cx)
         });
@@ -2174,7 +2155,7 @@ impl BufferStore {
         &mut self,
         message: proto::ProjectTransaction,
         push_to_history: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<ProjectTransaction>> {
         if let Some(this) = self.as_remote_mut() {
             this.deserialize_project_transaction(message, push_to_history, cx)
@@ -2187,8 +2168,8 @@ impl BufferStore {
     pub fn wait_for_remote_buffer(
         &mut self,
         id: BufferId,
-        cx: &mut ModelContext<BufferStore>,
-    ) -> Task<Result<Model<Buffer>>> {
+        cx: &mut Context<BufferStore>,
+    ) -> Task<Result<Entity<Buffer>>> {
         if let Some(this) = self.as_remote_mut() {
             this.wait_for_remote_buffer(id, cx)
         } else {
@@ -2201,7 +2182,7 @@ impl BufferStore {
         &mut self,
         project_transaction: ProjectTransaction,
         peer_id: proto::PeerId,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> proto::ProjectTransaction {
         let mut serialized_transaction = proto::ProjectTransaction {
             buffer_ids: Default::default(),
@@ -2222,7 +2203,7 @@ impl BufferStore {
 }
 
 impl BufferChangeSet {
-    pub fn new(buffer: &Model<Buffer>, cx: &mut ModelContext<Self>) -> Self {
+    pub fn new(buffer: &Entity<Buffer>, cx: &mut Context<Self>) -> Self {
         cx.subscribe(buffer, |this, buffer, event, cx| match event {
             BufferEvent::LanguageChanged => {
                 this.language = buffer.read(cx).language().cloned();
@@ -2262,8 +2243,8 @@ impl BufferChangeSet {
     #[cfg(any(test, feature = "test-support"))]
     pub fn new_with_base_text(
         base_text: String,
-        buffer: &Model<Buffer>,
-        cx: &mut ModelContext<Self>,
+        buffer: &Entity<Buffer>,
+        cx: &mut Context<Self>,
     ) -> Self {
         let mut this = Self::new(&buffer, cx);
         let _ = this.set_base_text(base_text, buffer.read(cx).text_snapshot(), cx);
@@ -2297,7 +2278,7 @@ impl BufferChangeSet {
         &mut self,
         mut base_text: String,
         buffer_snapshot: text::BufferSnapshot,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> oneshot::Receiver<()> {
         LineEnding::normalize(&mut base_text);
         self.recalculate_diff_internal(base_text, buffer_snapshot, true, cx)
@@ -2306,7 +2287,7 @@ impl BufferChangeSet {
     pub fn unset_base_text(
         &mut self,
         buffer_snapshot: text::BufferSnapshot,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if self.base_text.is_some() {
             self.base_text = None;
@@ -2319,7 +2300,7 @@ impl BufferChangeSet {
     pub fn recalculate_diff(
         &mut self,
         buffer_snapshot: text::BufferSnapshot,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> oneshot::Receiver<()> {
         if let Some(base_text) = self.base_text.clone() {
             self.recalculate_diff_internal(base_text.text(), buffer_snapshot, false, cx)
@@ -2333,7 +2314,7 @@ impl BufferChangeSet {
         base_text: String,
         buffer_snapshot: text::BufferSnapshot,
         base_text_changed: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> oneshot::Receiver<()> {
         let (tx, rx) = oneshot::channel();
         self.diff_updated_futures.push(tx);
@@ -2381,7 +2362,7 @@ impl BufferChangeSet {
         mut base_text: String,
         buffer_snapshot: text::BufferSnapshot,
         base_text_changed: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         LineEnding::normalize(&mut base_text);
         let diff = BufferDiff::build(&base_text, &buffer_snapshot);
@@ -2404,7 +2385,7 @@ impl BufferChangeSet {
 }
 
 impl OpenBuffer {
-    fn upgrade(&self) -> Option<Model<Buffer>> {
+    fn upgrade(&self) -> Option<Entity<Buffer>> {
         match self {
             OpenBuffer::Complete { buffer, .. } => buffer.upgrade(),
             OpenBuffer::Operations(_) => None,

crates/project/src/connection_manager.rs 🔗

@@ -3,25 +3,25 @@ use anyhow::Result;
 use client::Client;
 use collections::{HashMap, HashSet};
 use futures::{FutureExt, StreamExt};
-use gpui::{AppContext, AsyncAppContext, Context, Global, Model, ModelContext, Task, WeakModel};
+use gpui::{App, AppContext as _, AsyncAppContext, Context, Entity, Global, Task, WeakEntity};
 use postage::stream::Stream;
 use rpc::proto;
 use std::{sync::Arc, time::Duration};
 use util::{ResultExt, TryFutureExt};
 
 impl Global for GlobalManager {}
-struct GlobalManager(Model<Manager>);
+struct GlobalManager(Entity<Manager>);
 
 pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30);
 
 pub struct Manager {
     client: Arc<Client>,
     maintain_connection: Option<Task<Option<()>>>,
-    projects: HashSet<WeakModel<Project>>,
+    projects: HashSet<WeakEntity<Project>>,
 }
 
-pub fn init(client: Arc<Client>, cx: &mut AppContext) {
-    let manager = cx.new_model(|_| Manager {
+pub fn init(client: Arc<Client>, cx: &mut App) {
+    let manager = cx.new(|_| Manager {
         client,
         maintain_connection: None,
         projects: HashSet::default(),
@@ -30,14 +30,14 @@ pub fn init(client: Arc<Client>, cx: &mut AppContext) {
 }
 
 impl Manager {
-    pub fn global(cx: &AppContext) -> Model<Manager> {
+    pub fn global(cx: &App) -> Entity<Manager> {
         cx.global::<GlobalManager>().0.clone()
     }
 
     pub fn maintain_project_connection(
         &mut self,
-        project: &Model<Project>,
-        cx: &mut ModelContext<Self>,
+        project: &Entity<Project>,
+        cx: &mut Context<Self>,
     ) {
         let manager = cx.weak_model();
         project.update(cx, |_, cx| {
@@ -70,7 +70,7 @@ impl Manager {
         }
     }
 
-    fn reconnected(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    fn reconnected(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         let mut projects = HashMap::default();
 
         let request = self.client.request_envelope(proto::RejoinRemoteProjects {
@@ -118,7 +118,7 @@ impl Manager {
         })
     }
 
-    fn connection_lost(&mut self, cx: &mut ModelContext<Self>) {
+    fn connection_lost(&mut self, cx: &mut Context<Self>) {
         for project in self.projects.drain() {
             if let Some(project) = project.upgrade() {
                 project.update(cx, |project, cx| {
@@ -131,7 +131,7 @@ impl Manager {
     }
 
     async fn maintain_connection(
-        this: WeakModel<Self>,
+        this: WeakEntity<Self>,
         client: Arc<Client>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {

crates/project/src/debounced_delay.rs 🔗

@@ -1,5 +1,5 @@
 use futures::{channel::oneshot, FutureExt};
-use gpui::{ModelContext, Task};
+use gpui::{Context, Task};
 use std::{marker::PhantomData, time::Duration};
 
 pub struct DebouncedDelay<E: 'static> {
@@ -23,9 +23,9 @@ impl<E: 'static> DebouncedDelay<E> {
         }
     }
 
-    pub fn fire_new<F>(&mut self, delay: Duration, cx: &mut ModelContext<E>, func: F)
+    pub fn fire_new<F>(&mut self, delay: Duration, cx: &mut Context<E>, func: F)
     where
-        F: 'static + Send + FnOnce(&mut E, &mut ModelContext<E>) -> Task<()>,
+        F: 'static + Send + FnOnce(&mut E, &mut Context<E>) -> Task<()>,
     {
         if let Some(channel) = self.cancel_channel.take() {
             _ = channel.send(());

crates/project/src/environment.rs 🔗

@@ -3,7 +3,7 @@ use std::{path::Path, sync::Arc};
 use util::ResultExt;
 
 use collections::HashMap;
-use gpui::{AppContext, Context, Model, ModelContext, Task};
+use gpui::{App, AppContext as _, Context, Entity, Task};
 use settings::Settings as _;
 use worktree::WorktreeId;
 
@@ -13,7 +13,7 @@ use crate::{
 };
 
 pub struct ProjectEnvironment {
-    worktree_store: Model<WorktreeStore>,
+    worktree_store: Entity<WorktreeStore>,
     cli_environment: Option<HashMap<String, String>>,
     environments: HashMap<WorktreeId, Shared<Task<Option<HashMap<String, String>>>>>,
     environment_error_messages: HashMap<WorktreeId, EnvironmentErrorMessage>,
@@ -21,11 +21,11 @@ pub struct ProjectEnvironment {
 
 impl ProjectEnvironment {
     pub fn new(
-        worktree_store: &Model<WorktreeStore>,
+        worktree_store: &Entity<WorktreeStore>,
         cli_environment: Option<HashMap<String, String>>,
-        cx: &mut AppContext,
-    ) -> Model<Self> {
-        cx.new_model(|cx| {
+        cx: &mut App,
+    ) -> Entity<Self> {
+        cx.new(|cx| {
             cx.subscribe(worktree_store, |this: &mut Self, _, event, _| {
                 if let WorktreeStoreEvent::WorktreeRemoved(_, id) = event {
                     this.remove_worktree_environment(*id);
@@ -78,7 +78,7 @@ impl ProjectEnvironment {
         &mut self,
         worktree_id: Option<WorktreeId>,
         worktree_abs_path: Option<Arc<Path>>,
-        cx: &ModelContext<Self>,
+        cx: &Context<Self>,
     ) -> Shared<Task<Option<HashMap<String, String>>>> {
         if cfg!(any(test, feature = "test-support")) {
             return Task::ready(Some(HashMap::default())).shared();
@@ -129,7 +129,7 @@ impl ProjectEnvironment {
         &mut self,
         worktree_id: WorktreeId,
         worktree_abs_path: Arc<Path>,
-        cx: &ModelContext<Self>,
+        cx: &Context<Self>,
     ) -> Task<Option<HashMap<String, String>>> {
         let load_direnv = ProjectSettings::get_global(cx).load_direnv.clone();
 

crates/project/src/git.rs 🔗

@@ -8,8 +8,7 @@ use git::{
     status::{GitSummary, TrackedSummary},
 };
 use gpui::{
-    AppContext, Context as _, EventEmitter, Model, ModelContext, SharedString, Subscription,
-    WeakModel,
+    App, AppContext as _, Context, Entity, EventEmitter, SharedString, Subscription, WeakEntity,
 };
 use language::{Buffer, LanguageRegistry};
 use settings::WorktreeId;
@@ -28,11 +27,11 @@ pub struct GitState {
 
 #[derive(Clone)]
 pub struct RepositoryHandle {
-    git_state: WeakModel<GitState>,
+    git_state: WeakEntity<GitState>,
     worktree_id: WorktreeId,
     repository_entry: RepositoryEntry,
     git_repo: Arc<dyn GitRepository>,
-    commit_message: Model<Buffer>,
+    commit_message: Entity<Buffer>,
     update_sender: mpsc::UnboundedSender<(Message, mpsc::Sender<anyhow::Error>)>,
 }
 
@@ -67,9 +66,9 @@ impl EventEmitter<Event> for GitState {}
 
 impl GitState {
     pub fn new(
-        worktree_store: &Model<WorktreeStore>,
+        worktree_store: &Entity<WorktreeStore>,
         languages: Arc<LanguageRegistry>,
-        cx: &mut ModelContext<'_, Self>,
+        cx: &mut Context<'_, Self>,
     ) -> Self {
         let (update_sender, mut update_receiver) =
             mpsc::unbounded::<(Message, mpsc::Sender<anyhow::Error>)>();
@@ -115,9 +114,9 @@ impl GitState {
 
     fn on_worktree_store_event(
         &mut self,
-        worktree_store: Model<WorktreeStore>,
+        worktree_store: Entity<WorktreeStore>,
         _event: &WorktreeStoreEvent,
-        cx: &mut ModelContext<'_, Self>,
+        cx: &mut Context<'_, Self>,
     ) {
         // TODO inspect the event
 
@@ -150,7 +149,7 @@ impl GitState {
                             existing_handle.repository_entry = repo.clone();
                             existing_handle
                         } else {
-                            let commit_message = cx.new_model(|cx| Buffer::local("", cx));
+                            let commit_message = cx.new(|cx| Buffer::local("", cx));
                             cx.spawn({
                                 let commit_message = commit_message.downgrade();
                                 let languages = self.languages.clone();
@@ -194,7 +193,7 @@ impl GitState {
 }
 
 impl RepositoryHandle {
-    pub fn display_name(&self, project: &Project, cx: &AppContext) -> SharedString {
+    pub fn display_name(&self, project: &Project, cx: &App) -> SharedString {
         maybe!({
             let path = self.unrelativize(&"".into())?;
             Some(
@@ -209,7 +208,7 @@ impl RepositoryHandle {
         .unwrap_or("".into())
     }
 
-    pub fn activate(&self, cx: &mut AppContext) {
+    pub fn activate(&self, cx: &mut App) {
         let Some(git_state) = self.git_state.upgrade() else {
             return;
         };
@@ -236,7 +235,7 @@ impl RepositoryHandle {
         Some((self.worktree_id, path).into())
     }
 
-    pub fn commit_message(&self) -> Model<Buffer> {
+    pub fn commit_message(&self) -> Entity<Buffer> {
         self.commit_message.clone()
     }
 
@@ -304,7 +303,7 @@ impl RepositoryHandle {
         self.repository_entry.status_summary().index != TrackedSummary::UNCHANGED
     }
 
-    pub fn can_commit(&self, commit_all: bool, cx: &AppContext) -> bool {
+    pub fn can_commit(&self, commit_all: bool, cx: &App) -> bool {
         return self
             .commit_message
             .read(cx)
@@ -314,7 +313,7 @@ impl RepositoryHandle {
             && (commit_all || self.have_staged_changes());
     }
 
-    pub fn commit(&self, mut err_sender: mpsc::Sender<anyhow::Error>, cx: &mut AppContext) {
+    pub fn commit(&self, mut err_sender: mpsc::Sender<anyhow::Error>, cx: &mut App) {
         let message = self.commit_message.read(cx).as_rope().clone();
         let result = self.update_sender.unbounded_send((
             Message::Commit(self.git_repo.clone(), message),
@@ -335,7 +334,7 @@ impl RepositoryHandle {
         });
     }
 
-    pub fn commit_all(&self, mut err_sender: mpsc::Sender<anyhow::Error>, cx: &mut AppContext) {
+    pub fn commit_all(&self, mut err_sender: mpsc::Sender<anyhow::Error>, cx: &mut App) {
         let to_stage = self
             .repository_entry
             .status()

crates/project/src/image_store.rs 🔗

@@ -6,8 +6,7 @@ use anyhow::{Context as _, Result};
 use collections::{hash_map, HashMap, HashSet};
 use futures::{channel::oneshot, StreamExt};
 use gpui::{
-    hash, prelude::*, AppContext, EventEmitter, Img, Model, ModelContext, Subscription, Task,
-    WeakModel,
+    hash, prelude::*, App, Context, Entity, EventEmitter, Img, Subscription, Task, WeakEntity,
 };
 use language::{DiskState, File};
 use rpc::{AnyProtoClient, ErrorExt as _};
@@ -42,7 +41,7 @@ pub enum ImageItemEvent {
 impl EventEmitter<ImageItemEvent> for ImageItem {}
 
 pub enum ImageStoreEvent {
-    ImageAdded(Model<ImageItem>),
+    ImageAdded(Entity<ImageItem>),
 }
 
 impl EventEmitter<ImageStoreEvent> for ImageStore {}
@@ -55,7 +54,7 @@ pub struct ImageItem {
 }
 
 impl ImageItem {
-    pub fn project_path(&self, cx: &AppContext) -> ProjectPath {
+    pub fn project_path(&self, cx: &App) -> ProjectPath {
         ProjectPath {
             worktree_id: self.file.worktree_id(cx),
             path: self.file.path().clone(),
@@ -66,7 +65,7 @@ impl ImageItem {
         self.file.path()
     }
 
-    fn file_updated(&mut self, new_file: Arc<dyn File>, cx: &mut ModelContext<Self>) {
+    fn file_updated(&mut self, new_file: Arc<dyn File>, cx: &mut Context<Self>) {
         let mut file_changed = false;
 
         let old_file = self.file.as_ref();
@@ -90,7 +89,7 @@ impl ImageItem {
         }
     }
 
-    fn reload(&mut self, cx: &mut ModelContext<Self>) -> Option<oneshot::Receiver<()>> {
+    fn reload(&mut self, cx: &mut Context<Self>) -> Option<oneshot::Receiver<()>> {
         let local_file = self.file.as_local()?;
         let (tx, rx) = futures::channel::oneshot::channel();
 
@@ -116,10 +115,10 @@ impl ImageItem {
 
 impl ProjectItem for ImageItem {
     fn try_open(
-        project: &Model<Project>,
+        project: &Entity<Project>,
         path: &ProjectPath,
-        cx: &mut AppContext,
-    ) -> Option<Task<gpui::Result<Model<Self>>>> {
+        cx: &mut App,
+    ) -> Option<Task<gpui::Result<Entity<Self>>>> {
         let path = path.clone();
         let project = project.clone();
 
@@ -152,11 +151,11 @@ impl ProjectItem for ImageItem {
         }
     }
 
-    fn entry_id(&self, _: &AppContext) -> Option<ProjectEntryId> {
+    fn entry_id(&self, _: &App) -> Option<ProjectEntryId> {
         worktree::File::from_dyn(Some(&self.file))?.entry_id
     }
 
-    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
+    fn project_path(&self, cx: &App) -> Option<ProjectPath> {
         Some(self.project_path(cx).clone())
     }
 
@@ -169,17 +168,17 @@ trait ImageStoreImpl {
     fn open_image(
         &self,
         path: Arc<Path>,
-        worktree: Model<Worktree>,
-        cx: &mut ModelContext<ImageStore>,
-    ) -> Task<Result<Model<ImageItem>>>;
+        worktree: Entity<Worktree>,
+        cx: &mut Context<ImageStore>,
+    ) -> Task<Result<Entity<ImageItem>>>;
 
     fn reload_images(
         &self,
-        images: HashSet<Model<ImageItem>>,
-        cx: &mut ModelContext<ImageStore>,
+        images: HashSet<Entity<ImageItem>>,
+        cx: &mut Context<ImageStore>,
     ) -> Task<Result<()>>;
 
-    fn as_local(&self) -> Option<Model<LocalImageStore>>;
+    fn as_local(&self) -> Option<Entity<LocalImageStore>>;
 }
 
 struct RemoteImageStore {}
@@ -187,26 +186,26 @@ struct RemoteImageStore {}
 struct LocalImageStore {
     local_image_ids_by_path: HashMap<ProjectPath, ImageId>,
     local_image_ids_by_entry_id: HashMap<ProjectEntryId, ImageId>,
-    image_store: WeakModel<ImageStore>,
+    image_store: WeakEntity<ImageStore>,
     _subscription: Subscription,
 }
 
 pub struct ImageStore {
     state: Box<dyn ImageStoreImpl>,
-    opened_images: HashMap<ImageId, WeakModel<ImageItem>>,
-    worktree_store: Model<WorktreeStore>,
+    opened_images: HashMap<ImageId, WeakEntity<ImageItem>>,
+    worktree_store: Entity<WorktreeStore>,
     #[allow(clippy::type_complexity)]
     loading_images_by_path: HashMap<
         ProjectPath,
-        postage::watch::Receiver<Option<Result<Model<ImageItem>, Arc<anyhow::Error>>>>,
+        postage::watch::Receiver<Option<Result<Entity<ImageItem>, Arc<anyhow::Error>>>>,
     >,
 }
 
 impl ImageStore {
-    pub fn local(worktree_store: Model<WorktreeStore>, cx: &mut ModelContext<Self>) -> Self {
+    pub fn local(worktree_store: Entity<WorktreeStore>, cx: &mut Context<Self>) -> Self {
         let this = cx.weak_model();
         Self {
-            state: Box::new(cx.new_model(|cx| {
+            state: Box::new(cx.new(|cx| {
                 let subscription = cx.subscribe(
                     &worktree_store,
                     |this: &mut LocalImageStore, _, event, cx| {
@@ -230,32 +229,32 @@ impl ImageStore {
     }
 
     pub fn remote(
-        worktree_store: Model<WorktreeStore>,
+        worktree_store: Entity<WorktreeStore>,
         _upstream_client: AnyProtoClient,
         _remote_id: u64,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Self {
         Self {
-            state: Box::new(cx.new_model(|_| RemoteImageStore {})),
+            state: Box::new(cx.new(|_| RemoteImageStore {})),
             opened_images: Default::default(),
             loading_images_by_path: Default::default(),
             worktree_store,
         }
     }
 
-    pub fn images(&self) -> impl '_ + Iterator<Item = Model<ImageItem>> {
+    pub fn images(&self) -> impl '_ + Iterator<Item = Entity<ImageItem>> {
         self.opened_images
             .values()
             .filter_map(|image| image.upgrade())
     }
 
-    pub fn get(&self, image_id: ImageId) -> Option<Model<ImageItem>> {
+    pub fn get(&self, image_id: ImageId) -> Option<Entity<ImageItem>> {
         self.opened_images
             .get(&image_id)
             .and_then(|image| image.upgrade())
     }
 
-    pub fn get_by_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<Model<ImageItem>> {
+    pub fn get_by_path(&self, path: &ProjectPath, cx: &App) -> Option<Entity<ImageItem>> {
         self.images()
             .find(|image| &image.read(cx).project_path(cx) == path)
     }
@@ -263,8 +262,8 @@ impl ImageStore {
     pub fn open_image(
         &mut self,
         project_path: ProjectPath,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<ImageItem>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<ImageItem>>> {
         let existing_image = self.get_by_path(&project_path, cx);
         if let Some(existing_image) = existing_image {
             return Task::ready(Ok(existing_image));
@@ -317,9 +316,9 @@ impl ImageStore {
 
     pub async fn wait_for_loading_image(
         mut receiver: postage::watch::Receiver<
-            Option<Result<Model<ImageItem>, Arc<anyhow::Error>>>,
+            Option<Result<Entity<ImageItem>, Arc<anyhow::Error>>>,
         >,
-    ) -> Result<Model<ImageItem>, Arc<anyhow::Error>> {
+    ) -> Result<Entity<ImageItem>, Arc<anyhow::Error>> {
         loop {
             if let Some(result) = receiver.borrow().as_ref() {
                 match result {
@@ -333,8 +332,8 @@ impl ImageStore {
 
     pub fn reload_images(
         &self,
-        images: HashSet<Model<ImageItem>>,
-        cx: &mut ModelContext<ImageStore>,
+        images: HashSet<Entity<ImageItem>>,
+        cx: &mut Context<ImageStore>,
     ) -> Task<Result<()>> {
         if images.is_empty() {
             return Task::ready(Ok(()));
@@ -343,11 +342,7 @@ impl ImageStore {
         self.state.reload_images(images, cx)
     }
 
-    fn add_image(
-        &mut self,
-        image: Model<ImageItem>,
-        cx: &mut ModelContext<ImageStore>,
-    ) -> Result<()> {
+    fn add_image(&mut self, image: Entity<ImageItem>, cx: &mut Context<ImageStore>) -> Result<()> {
         let image_id = image.read(cx).id;
 
         self.opened_images.insert(image_id, image.downgrade());
@@ -359,9 +354,9 @@ impl ImageStore {
 
     fn on_image_event(
         &mut self,
-        image: Model<ImageItem>,
+        image: Entity<ImageItem>,
         event: &ImageItemEvent,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match event {
             ImageItemEvent::FileHandleChanged => {
@@ -376,13 +371,13 @@ impl ImageStore {
     }
 }
 
-impl ImageStoreImpl for Model<LocalImageStore> {
+impl ImageStoreImpl for Entity<LocalImageStore> {
     fn open_image(
         &self,
         path: Arc<Path>,
-        worktree: Model<Worktree>,
-        cx: &mut ModelContext<ImageStore>,
-    ) -> Task<Result<Model<ImageItem>>> {
+        worktree: Entity<Worktree>,
+        cx: &mut Context<ImageStore>,
+    ) -> Task<Result<Entity<ImageItem>>> {
         let this = self.clone();
 
         let load_file = worktree.update(cx, |worktree, cx| {
@@ -392,7 +387,7 @@ impl ImageStoreImpl for Model<LocalImageStore> {
             let LoadedBinaryFile { file, content } = load_file.await?;
             let image = create_gpui_image(content)?;
 
-            let model = cx.new_model(|cx| ImageItem {
+            let model = cx.new(|cx| ImageItem {
                 id: cx.entity_id().as_non_zero_u64().into(),
                 file: file.clone(),
                 image,
@@ -426,8 +421,8 @@ impl ImageStoreImpl for Model<LocalImageStore> {
 
     fn reload_images(
         &self,
-        images: HashSet<Model<ImageItem>>,
-        cx: &mut ModelContext<ImageStore>,
+        images: HashSet<Entity<ImageItem>>,
+        cx: &mut Context<ImageStore>,
     ) -> Task<Result<()>> {
         cx.spawn(move |_, mut cx| async move {
             for image in images {
@@ -439,13 +434,13 @@ impl ImageStoreImpl for Model<LocalImageStore> {
         })
     }
 
-    fn as_local(&self) -> Option<Model<LocalImageStore>> {
+    fn as_local(&self) -> Option<Entity<LocalImageStore>> {
         Some(self.clone())
     }
 }
 
 impl LocalImageStore {
-    fn subscribe_to_worktree(&mut self, worktree: &Model<Worktree>, cx: &mut ModelContext<Self>) {
+    fn subscribe_to_worktree(&mut self, worktree: &Entity<Worktree>, cx: &mut Context<Self>) {
         cx.subscribe(worktree, |this, worktree, event, cx| {
             if worktree.read(cx).is_local() {
                 match event {
@@ -461,9 +456,9 @@ impl LocalImageStore {
 
     fn local_worktree_entries_changed(
         &mut self,
-        worktree_handle: &Model<Worktree>,
+        worktree_handle: &Entity<Worktree>,
         changes: &[(Arc<Path>, ProjectEntryId, PathChange)],
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let snapshot = worktree_handle.read(cx).snapshot();
         for (path, entry_id, _) in changes {
@@ -475,9 +470,9 @@ impl LocalImageStore {
         &mut self,
         entry_id: ProjectEntryId,
         path: &Arc<Path>,
-        worktree: &Model<worktree::Worktree>,
+        worktree: &Entity<worktree::Worktree>,
         snapshot: &worktree::Snapshot,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<()> {
         let project_path = ProjectPath {
             worktree_id: snapshot.id(),
@@ -576,7 +571,7 @@ impl LocalImageStore {
         None
     }
 
-    fn image_changed_file(&mut self, image: Model<ImageItem>, cx: &mut AppContext) -> Option<()> {
+    fn image_changed_file(&mut self, image: Entity<ImageItem>, cx: &mut App) -> Option<()> {
         let file = worktree::File::from_dyn(Some(&image.read(cx).file))?;
 
         let image_id = image.read(cx).id;
@@ -620,13 +615,13 @@ fn create_gpui_image(content: Vec<u8>) -> anyhow::Result<Arc<gpui::Image>> {
     }))
 }
 
-impl ImageStoreImpl for Model<RemoteImageStore> {
+impl ImageStoreImpl for Entity<RemoteImageStore> {
     fn open_image(
         &self,
         _path: Arc<Path>,
-        _worktree: Model<Worktree>,
-        _cx: &mut ModelContext<ImageStore>,
-    ) -> Task<Result<Model<ImageItem>>> {
+        _worktree: Entity<Worktree>,
+        _cx: &mut Context<ImageStore>,
+    ) -> Task<Result<Entity<ImageItem>>> {
         Task::ready(Err(anyhow::anyhow!(
             "Opening images from remote is not supported"
         )))
@@ -634,15 +629,15 @@ impl ImageStoreImpl for Model<RemoteImageStore> {
 
     fn reload_images(
         &self,
-        _images: HashSet<Model<ImageItem>>,
-        _cx: &mut ModelContext<ImageStore>,
+        _images: HashSet<Entity<ImageItem>>,
+        _cx: &mut Context<ImageStore>,
     ) -> Task<Result<()>> {
         Task::ready(Err(anyhow::anyhow!(
             "Reloading images from remote is not supported"
         )))
     }
 
-    fn as_local(&self) -> Option<Model<LocalImageStore>> {
+    fn as_local(&self) -> Option<Entity<LocalImageStore>> {
         None
     }
 }

crates/project/src/lsp_command.rs 🔗

@@ -6,13 +6,13 @@ use crate::{
     InlayHintLabel, InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location,
     LocationLink, MarkupContent, PrepareRenameResponse, ProjectTransaction, ResolveState,
 };
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use async_trait::async_trait;
 use client::proto::{self, PeerId};
 use clock::Global;
 use collections::HashSet;
 use futures::future;
-use gpui::{AppContext, AsyncAppContext, Entity, Model};
+use gpui::{App, AsyncAppContext, Entity};
 use language::{
     language_settings::{language_settings, InlayHintKind, LanguageSettings},
     point_from_lsp, point_to_lsp,
@@ -87,7 +87,7 @@ pub trait LspCommand: 'static + Sized + Send + std::fmt::Debug {
         path: &Path,
         buffer: &Buffer,
         language_server: &Arc<LanguageServer>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Result<
         LspParamsOrResponse<<Self::LspRequest as lsp::request::Request>::Params, Self::Response>,
     > {
@@ -113,14 +113,14 @@ pub trait LspCommand: 'static + Sized + Send + std::fmt::Debug {
         path: &Path,
         buffer: &Buffer,
         language_server: &Arc<LanguageServer>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Result<<Self::LspRequest as lsp::request::Request>::Params>;
 
     async fn response_from_lsp(
         self,
         message: <Self::LspRequest as lsp::request::Request>::Result,
-        lsp_store: Model<LspStore>,
-        buffer: Model<Buffer>,
+        lsp_store: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         server_id: LanguageServerId,
         cx: AsyncAppContext,
     ) -> Result<Self::Response>;
@@ -129,8 +129,8 @@ pub trait LspCommand: 'static + Sized + Send + std::fmt::Debug {
 
     async fn from_proto(
         message: Self::ProtoRequest,
-        lsp_store: Model<LspStore>,
-        buffer: Model<Buffer>,
+        lsp_store: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         cx: AsyncAppContext,
     ) -> Result<Self>;
 
@@ -139,14 +139,14 @@ pub trait LspCommand: 'static + Sized + Send + std::fmt::Debug {
         lsp_store: &mut LspStore,
         peer_id: PeerId,
         buffer_version: &clock::Global,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> <Self::ProtoRequest as proto::RequestMessage>::Response;
 
     async fn response_from_proto(
         self,
         message: <Self::ProtoRequest as proto::RequestMessage>::Response,
-        lsp_store: Model<LspStore>,
-        buffer: Model<Buffer>,
+        lsp_store: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         cx: AsyncAppContext,
     ) -> Result<Self::Response>;
 
@@ -255,7 +255,7 @@ impl LspCommand for PrepareRename {
         path: &Path,
         buffer: &Buffer,
         language_server: &Arc<LanguageServer>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Result<LspParamsOrResponse<lsp::TextDocumentPositionParams, PrepareRenameResponse>> {
         let rename_provider = language_server
             .adapter_server_capabilities()
@@ -286,7 +286,7 @@ impl LspCommand for PrepareRename {
         path: &Path,
         _: &Buffer,
         _: &Arc<LanguageServer>,
-        _: &AppContext,
+        _: &App,
     ) -> Result<lsp::TextDocumentPositionParams> {
         make_lsp_text_document_position(path, self.position)
     }
@@ -294,8 +294,8 @@ impl LspCommand for PrepareRename {
     async fn response_from_lsp(
         self,
         message: Option<lsp::PrepareRenameResponse>,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         _: LanguageServerId,
         mut cx: AsyncAppContext,
     ) -> Result<PrepareRenameResponse> {
@@ -337,8 +337,8 @@ impl LspCommand for PrepareRename {
 
     async fn from_proto(
         message: proto::PrepareRename,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Self> {
         let position = message
@@ -361,7 +361,7 @@ impl LspCommand for PrepareRename {
         _: &mut LspStore,
         _: PeerId,
         buffer_version: &clock::Global,
-        _: &mut AppContext,
+        _: &mut App,
     ) -> proto::PrepareRenameResponse {
         match response {
             PrepareRenameResponse::Success(range) => proto::PrepareRenameResponse {
@@ -391,8 +391,8 @@ impl LspCommand for PrepareRename {
     async fn response_from_proto(
         self,
         message: proto::PrepareRenameResponse,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<PrepareRenameResponse> {
         if message.can_rename {
@@ -438,7 +438,7 @@ impl LspCommand for PerformRename {
         path: &Path,
         _: &Buffer,
         _: &Arc<LanguageServer>,
-        _: &AppContext,
+        _: &App,
     ) -> Result<lsp::RenameParams> {
         Ok(lsp::RenameParams {
             text_document_position: make_lsp_text_document_position(path, self.position)?,
@@ -450,8 +450,8 @@ impl LspCommand for PerformRename {
     async fn response_from_lsp(
         self,
         message: Option<lsp::WorkspaceEdit>,
-        lsp_store: Model<LspStore>,
-        buffer: Model<Buffer>,
+        lsp_store: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         server_id: LanguageServerId,
         mut cx: AsyncAppContext,
     ) -> Result<ProjectTransaction> {
@@ -486,8 +486,8 @@ impl LspCommand for PerformRename {
 
     async fn from_proto(
         message: proto::PerformRename,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Self> {
         let position = message
@@ -511,7 +511,7 @@ impl LspCommand for PerformRename {
         lsp_store: &mut LspStore,
         peer_id: PeerId,
         _: &clock::Global,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> proto::PerformRenameResponse {
         let transaction = lsp_store.buffer_store().update(cx, |buffer_store, cx| {
             buffer_store.serialize_project_transaction_for_peer(response, peer_id, cx)
@@ -524,8 +524,8 @@ impl LspCommand for PerformRename {
     async fn response_from_proto(
         self,
         message: proto::PerformRenameResponse,
-        lsp_store: Model<LspStore>,
-        _: Model<Buffer>,
+        lsp_store: Entity<LspStore>,
+        _: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<ProjectTransaction> {
         let message = message
@@ -567,7 +567,7 @@ impl LspCommand for GetDefinition {
         path: &Path,
         _: &Buffer,
         _: &Arc<LanguageServer>,
-        _: &AppContext,
+        _: &App,
     ) -> Result<lsp::GotoDefinitionParams> {
         Ok(lsp::GotoDefinitionParams {
             text_document_position_params: make_lsp_text_document_position(path, self.position)?,
@@ -579,8 +579,8 @@ impl LspCommand for GetDefinition {
     async fn response_from_lsp(
         self,
         message: Option<lsp::GotoDefinitionResponse>,
-        lsp_store: Model<LspStore>,
-        buffer: Model<Buffer>,
+        lsp_store: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         server_id: LanguageServerId,
         cx: AsyncAppContext,
     ) -> Result<Vec<LocationLink>> {
@@ -600,8 +600,8 @@ impl LspCommand for GetDefinition {
 
     async fn from_proto(
         message: proto::GetDefinition,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Self> {
         let position = message
@@ -623,7 +623,7 @@ impl LspCommand for GetDefinition {
         lsp_store: &mut LspStore,
         peer_id: PeerId,
         _: &clock::Global,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> proto::GetDefinitionResponse {
         let links = location_links_to_proto(response, lsp_store, peer_id, cx);
         proto::GetDefinitionResponse { links }
@@ -632,8 +632,8 @@ impl LspCommand for GetDefinition {
     async fn response_from_proto(
         self,
         message: proto::GetDefinitionResponse,
-        lsp_store: Model<LspStore>,
-        _: Model<Buffer>,
+        lsp_store: Entity<LspStore>,
+        _: Entity<Buffer>,
         cx: AsyncAppContext,
     ) -> Result<Vec<LocationLink>> {
         location_links_from_proto(message.links, lsp_store, cx).await
@@ -666,7 +666,7 @@ impl LspCommand for GetDeclaration {
         path: &Path,
         _: &Buffer,
         _: &Arc<LanguageServer>,
-        _: &AppContext,
+        _: &App,
     ) -> Result<lsp::GotoDeclarationParams> {
         Ok(lsp::GotoDeclarationParams {
             text_document_position_params: make_lsp_text_document_position(path, self.position)?,
@@ -678,8 +678,8 @@ impl LspCommand for GetDeclaration {
     async fn response_from_lsp(
         self,
         message: Option<lsp::GotoDeclarationResponse>,
-        lsp_store: Model<LspStore>,
-        buffer: Model<Buffer>,
+        lsp_store: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         server_id: LanguageServerId,
         cx: AsyncAppContext,
     ) -> Result<Vec<LocationLink>> {
@@ -699,8 +699,8 @@ impl LspCommand for GetDeclaration {
 
     async fn from_proto(
         message: proto::GetDeclaration,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Self> {
         let position = message
@@ -722,7 +722,7 @@ impl LspCommand for GetDeclaration {
         lsp_store: &mut LspStore,
         peer_id: PeerId,
         _: &clock::Global,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> proto::GetDeclarationResponse {
         let links = location_links_to_proto(response, lsp_store, peer_id, cx);
         proto::GetDeclarationResponse { links }
@@ -731,8 +731,8 @@ impl LspCommand for GetDeclaration {
     async fn response_from_proto(
         self,
         message: proto::GetDeclarationResponse,
-        lsp_store: Model<LspStore>,
-        _: Model<Buffer>,
+        lsp_store: Entity<LspStore>,
+        _: Entity<Buffer>,
         cx: AsyncAppContext,
     ) -> Result<Vec<LocationLink>> {
         location_links_from_proto(message.links, lsp_store, cx).await
@@ -758,7 +758,7 @@ impl LspCommand for GetImplementation {
         path: &Path,
         _: &Buffer,
         _: &Arc<LanguageServer>,
-        _: &AppContext,
+        _: &App,
     ) -> Result<lsp::GotoImplementationParams> {
         Ok(lsp::GotoImplementationParams {
             text_document_position_params: make_lsp_text_document_position(path, self.position)?,
@@ -770,8 +770,8 @@ impl LspCommand for GetImplementation {
     async fn response_from_lsp(
         self,
         message: Option<lsp::GotoImplementationResponse>,
-        lsp_store: Model<LspStore>,
-        buffer: Model<Buffer>,
+        lsp_store: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         server_id: LanguageServerId,
         cx: AsyncAppContext,
     ) -> Result<Vec<LocationLink>> {
@@ -791,8 +791,8 @@ impl LspCommand for GetImplementation {
 
     async fn from_proto(
         message: proto::GetImplementation,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Self> {
         let position = message
@@ -814,7 +814,7 @@ impl LspCommand for GetImplementation {
         lsp_store: &mut LspStore,
         peer_id: PeerId,
         _: &clock::Global,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> proto::GetImplementationResponse {
         let links = location_links_to_proto(response, lsp_store, peer_id, cx);
         proto::GetImplementationResponse { links }
@@ -823,8 +823,8 @@ impl LspCommand for GetImplementation {
     async fn response_from_proto(
         self,
         message: proto::GetImplementationResponse,
-        project: Model<LspStore>,
-        _: Model<Buffer>,
+        project: Entity<LspStore>,
+        _: Entity<Buffer>,
         cx: AsyncAppContext,
     ) -> Result<Vec<LocationLink>> {
         location_links_from_proto(message.links, project, cx).await
@@ -857,7 +857,7 @@ impl LspCommand for GetTypeDefinition {
         path: &Path,
         _: &Buffer,
         _: &Arc<LanguageServer>,
-        _: &AppContext,
+        _: &App,
     ) -> Result<lsp::GotoTypeDefinitionParams> {
         Ok(lsp::GotoTypeDefinitionParams {
             text_document_position_params: make_lsp_text_document_position(path, self.position)?,
@@ -869,8 +869,8 @@ impl LspCommand for GetTypeDefinition {
     async fn response_from_lsp(
         self,
         message: Option<lsp::GotoTypeDefinitionResponse>,
-        project: Model<LspStore>,
-        buffer: Model<Buffer>,
+        project: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         server_id: LanguageServerId,
         cx: AsyncAppContext,
     ) -> Result<Vec<LocationLink>> {
@@ -890,8 +890,8 @@ impl LspCommand for GetTypeDefinition {
 
     async fn from_proto(
         message: proto::GetTypeDefinition,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Self> {
         let position = message
@@ -913,7 +913,7 @@ impl LspCommand for GetTypeDefinition {
         lsp_store: &mut LspStore,
         peer_id: PeerId,
         _: &clock::Global,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> proto::GetTypeDefinitionResponse {
         let links = location_links_to_proto(response, lsp_store, peer_id, cx);
         proto::GetTypeDefinitionResponse { links }
@@ -922,8 +922,8 @@ impl LspCommand for GetTypeDefinition {
     async fn response_from_proto(
         self,
         message: proto::GetTypeDefinitionResponse,
-        project: Model<LspStore>,
-        _: Model<Buffer>,
+        project: Entity<LspStore>,
+        _: Entity<Buffer>,
         cx: AsyncAppContext,
     ) -> Result<Vec<LocationLink>> {
         location_links_from_proto(message.links, project, cx).await
@@ -935,8 +935,8 @@ impl LspCommand for GetTypeDefinition {
 }
 
 fn language_server_for_buffer(
-    lsp_store: &Model<LspStore>,
-    buffer: &Model<Buffer>,
+    lsp_store: &Entity<LspStore>,
+    buffer: &Entity<Buffer>,
     server_id: LanguageServerId,
     cx: &mut AsyncAppContext,
 ) -> Result<(Arc<CachedLspAdapter>, Arc<LanguageServer>)> {
@@ -951,7 +951,7 @@ fn language_server_for_buffer(
 
 async fn location_links_from_proto(
     proto_links: Vec<proto::LocationLink>,
-    lsp_store: Model<LspStore>,
+    lsp_store: Entity<LspStore>,
     mut cx: AsyncAppContext,
 ) -> Result<Vec<LocationLink>> {
     let mut links = Vec::new();
@@ -1015,8 +1015,8 @@ async fn location_links_from_proto(
 
 async fn location_links_from_lsp(
     message: Option<lsp::GotoDefinitionResponse>,
-    lsp_store: Model<LspStore>,
-    buffer: Model<Buffer>,
+    lsp_store: Entity<LspStore>,
+    buffer: Entity<Buffer>,
     server_id: LanguageServerId,
     mut cx: AsyncAppContext,
 ) -> Result<Vec<LocationLink>> {
@@ -1099,7 +1099,7 @@ fn location_links_to_proto(
     links: Vec<LocationLink>,
     lsp_store: &mut LspStore,
     peer_id: PeerId,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) -> Vec<proto::LocationLink> {
     links
         .into_iter()
@@ -1169,7 +1169,7 @@ impl LspCommand for GetReferences {
         path: &Path,
         _: &Buffer,
         _: &Arc<LanguageServer>,
-        _: &AppContext,
+        _: &App,
     ) -> Result<lsp::ReferenceParams> {
         Ok(lsp::ReferenceParams {
             text_document_position: make_lsp_text_document_position(path, self.position)?,
@@ -1184,8 +1184,8 @@ impl LspCommand for GetReferences {
     async fn response_from_lsp(
         self,
         locations: Option<Vec<lsp::Location>>,
-        lsp_store: Model<LspStore>,
-        buffer: Model<Buffer>,
+        lsp_store: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         server_id: LanguageServerId,
         mut cx: AsyncAppContext,
     ) -> Result<Vec<Location>> {
@@ -1238,8 +1238,8 @@ impl LspCommand for GetReferences {
 
     async fn from_proto(
         message: proto::GetReferences,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Self> {
         let position = message
@@ -1261,7 +1261,7 @@ impl LspCommand for GetReferences {
         lsp_store: &mut LspStore,
         peer_id: PeerId,
         _: &clock::Global,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> proto::GetReferencesResponse {
         let locations = response
             .into_iter()
@@ -1286,8 +1286,8 @@ impl LspCommand for GetReferences {
     async fn response_from_proto(
         self,
         message: proto::GetReferencesResponse,
-        project: Model<LspStore>,
-        _: Model<Buffer>,
+        project: Entity<LspStore>,
+        _: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Vec<Location>> {
         let mut locations = Vec::new();
@@ -1344,7 +1344,7 @@ impl LspCommand for GetDocumentHighlights {
         path: &Path,
         _: &Buffer,
         _: &Arc<LanguageServer>,
-        _: &AppContext,
+        _: &App,
     ) -> Result<lsp::DocumentHighlightParams> {
         Ok(lsp::DocumentHighlightParams {
             text_document_position_params: make_lsp_text_document_position(path, self.position)?,
@@ -1356,8 +1356,8 @@ impl LspCommand for GetDocumentHighlights {
     async fn response_from_lsp(
         self,
         lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         _: LanguageServerId,
         mut cx: AsyncAppContext,
     ) -> Result<Vec<DocumentHighlight>> {
@@ -1395,8 +1395,8 @@ impl LspCommand for GetDocumentHighlights {
 
     async fn from_proto(
         message: proto::GetDocumentHighlights,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Self> {
         let position = message
@@ -1418,7 +1418,7 @@ impl LspCommand for GetDocumentHighlights {
         _: &mut LspStore,
         _: PeerId,
         _: &clock::Global,
-        _: &mut AppContext,
+        _: &mut App,
     ) -> proto::GetDocumentHighlightsResponse {
         let highlights = response
             .into_iter()
@@ -1439,8 +1439,8 @@ impl LspCommand for GetDocumentHighlights {
     async fn response_from_proto(
         self,
         message: proto::GetDocumentHighlightsResponse,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Vec<DocumentHighlight>> {
         let mut highlights = Vec::new();
@@ -1497,7 +1497,7 @@ impl LspCommand for GetSignatureHelp {
         path: &Path,
         _: &Buffer,
         _: &Arc<LanguageServer>,
-        _cx: &AppContext,
+        _cx: &App,
     ) -> Result<lsp::SignatureHelpParams> {
         Ok(lsp::SignatureHelpParams {
             text_document_position_params: make_lsp_text_document_position(path, self.position)?,
@@ -1509,8 +1509,8 @@ impl LspCommand for GetSignatureHelp {
     async fn response_from_lsp(
         self,
         message: Option<lsp::SignatureHelp>,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         _: LanguageServerId,
         mut cx: AsyncAppContext,
     ) -> Result<Self::Response> {
@@ -1530,8 +1530,8 @@ impl LspCommand for GetSignatureHelp {
 
     async fn from_proto(
         payload: Self::ProtoRequest,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Self> {
         buffer
@@ -1555,7 +1555,7 @@ impl LspCommand for GetSignatureHelp {
         _: &mut LspStore,
         _: PeerId,
         _: &Global,
-        _: &mut AppContext,
+        _: &mut App,
     ) -> proto::GetSignatureHelpResponse {
         proto::GetSignatureHelpResponse {
             signature_help: response
@@ -1566,8 +1566,8 @@ impl LspCommand for GetSignatureHelp {
     async fn response_from_proto(
         self,
         response: proto::GetSignatureHelpResponse,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Self::Response> {
         let language = buffer.update(&mut cx, |buffer, _| buffer.language().cloned())?;
@@ -1605,7 +1605,7 @@ impl LspCommand for GetHover {
         path: &Path,
         _: &Buffer,
         _: &Arc<LanguageServer>,
-        _: &AppContext,
+        _: &App,
     ) -> Result<lsp::HoverParams> {
         Ok(lsp::HoverParams {
             text_document_position_params: make_lsp_text_document_position(path, self.position)?,
@@ -1616,8 +1616,8 @@ impl LspCommand for GetHover {
     async fn response_from_lsp(
         self,
         message: Option<lsp::Hover>,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         _: LanguageServerId,
         mut cx: AsyncAppContext,
     ) -> Result<Self::Response> {
@@ -1697,8 +1697,8 @@ impl LspCommand for GetHover {
 
     async fn from_proto(
         message: Self::ProtoRequest,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Self> {
         let position = message
@@ -1720,7 +1720,7 @@ impl LspCommand for GetHover {
         _: &mut LspStore,
         _: PeerId,
         _: &clock::Global,
-        _: &mut AppContext,
+        _: &mut App,
     ) -> proto::GetHoverResponse {
         if let Some(response) = response {
             let (start, end) = if let Some(range) = response.range {
@@ -1763,8 +1763,8 @@ impl LspCommand for GetHover {
     async fn response_from_proto(
         self,
         message: proto::GetHoverResponse,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Self::Response> {
         let contents: Vec<_> = message
@@ -1827,7 +1827,7 @@ impl LspCommand for GetCompletions {
         path: &Path,
         _: &Buffer,
         _: &Arc<LanguageServer>,
-        _: &AppContext,
+        _: &App,
     ) -> Result<lsp::CompletionParams> {
         Ok(lsp::CompletionParams {
             text_document_position: make_lsp_text_document_position(path, self.position)?,
@@ -1840,8 +1840,8 @@ impl LspCommand for GetCompletions {
     async fn response_from_lsp(
         self,
         completions: Option<lsp::CompletionResponse>,
-        lsp_store: Model<LspStore>,
-        buffer: Model<Buffer>,
+        lsp_store: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         server_id: LanguageServerId,
         mut cx: AsyncAppContext,
     ) -> Result<Self::Response> {
@@ -2033,8 +2033,8 @@ impl LspCommand for GetCompletions {
 
     async fn from_proto(
         message: proto::GetCompletions,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Self> {
         let version = deserialize_version(&message.version);
@@ -2064,7 +2064,7 @@ impl LspCommand for GetCompletions {
         _: &mut LspStore,
         _: PeerId,
         buffer_version: &clock::Global,
-        _: &mut AppContext,
+        _: &mut App,
     ) -> proto::GetCompletionsResponse {
         proto::GetCompletionsResponse {
             completions: completions
@@ -2078,8 +2078,8 @@ impl LspCommand for GetCompletions {
     async fn response_from_proto(
         self,
         message: proto::GetCompletionsResponse,
-        _project: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _project: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Self::Response> {
         buffer
@@ -2175,7 +2175,7 @@ impl LspCommand for GetCodeActions {
         path: &Path,
         buffer: &Buffer,
         language_server: &Arc<LanguageServer>,
-        _: &AppContext,
+        _: &App,
     ) -> Result<lsp::CodeActionParams> {
         let mut relevant_diagnostics = Vec::new();
         for entry in buffer
@@ -2221,8 +2221,8 @@ impl LspCommand for GetCodeActions {
     async fn response_from_lsp(
         self,
         actions: Option<lsp::CodeActionResponse>,
-        _: Model<LspStore>,
-        _: Model<Buffer>,
+        _: Entity<LspStore>,
+        _: Entity<Buffer>,
         server_id: LanguageServerId,
         _: AsyncAppContext,
     ) -> Result<Vec<CodeAction>> {
@@ -2269,8 +2269,8 @@ impl LspCommand for GetCodeActions {
 
     async fn from_proto(
         message: proto::GetCodeActions,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Self> {
         let start = message
@@ -2298,7 +2298,7 @@ impl LspCommand for GetCodeActions {
         _: &mut LspStore,
         _: PeerId,
         buffer_version: &clock::Global,
-        _: &mut AppContext,
+        _: &mut App,
     ) -> proto::GetCodeActionsResponse {
         proto::GetCodeActionsResponse {
             actions: code_actions
@@ -2312,8 +2312,8 @@ impl LspCommand for GetCodeActions {
     async fn response_from_proto(
         self,
         message: proto::GetCodeActionsResponse,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Vec<CodeAction>> {
         buffer
@@ -2390,7 +2390,7 @@ impl LspCommand for OnTypeFormatting {
         path: &Path,
         _: &Buffer,
         _: &Arc<LanguageServer>,
-        _: &AppContext,
+        _: &App,
     ) -> Result<lsp::DocumentOnTypeFormattingParams> {
         Ok(lsp::DocumentOnTypeFormattingParams {
             text_document_position: make_lsp_text_document_position(path, self.position)?,
@@ -2402,8 +2402,8 @@ impl LspCommand for OnTypeFormatting {
     async fn response_from_lsp(
         self,
         message: Option<Vec<lsp::TextEdit>>,
-        lsp_store: Model<LspStore>,
-        buffer: Model<Buffer>,
+        lsp_store: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         server_id: LanguageServerId,
         mut cx: AsyncAppContext,
     ) -> Result<Option<Transaction>> {
@@ -2439,8 +2439,8 @@ impl LspCommand for OnTypeFormatting {
 
     async fn from_proto(
         message: proto::OnTypeFormatting,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Self> {
         let position = message
@@ -2472,7 +2472,7 @@ impl LspCommand for OnTypeFormatting {
         _: &mut LspStore,
         _: PeerId,
         _: &clock::Global,
-        _: &mut AppContext,
+        _: &mut App,
     ) -> proto::OnTypeFormattingResponse {
         proto::OnTypeFormattingResponse {
             transaction: response
@@ -2483,8 +2483,8 @@ impl LspCommand for OnTypeFormatting {
     async fn response_from_proto(
         self,
         message: proto::OnTypeFormattingResponse,
-        _: Model<LspStore>,
-        _: Model<Buffer>,
+        _: Entity<LspStore>,
+        _: Entity<Buffer>,
         _: AsyncAppContext,
     ) -> Result<Option<Transaction>> {
         let Some(transaction) = message.transaction else {
@@ -2501,7 +2501,7 @@ impl LspCommand for OnTypeFormatting {
 impl InlayHints {
     pub async fn lsp_to_project_hint(
         lsp_hint: lsp::InlayHint,
-        buffer_handle: &Model<Buffer>,
+        buffer_handle: &Entity<Buffer>,
         server_id: LanguageServerId,
         resolve_state: ResolveState,
         force_no_type_left_padding: bool,
@@ -2897,7 +2897,7 @@ impl LspCommand for InlayHints {
         path: &Path,
         buffer: &Buffer,
         _: &Arc<LanguageServer>,
-        _: &AppContext,
+        _: &App,
     ) -> Result<lsp::InlayHintParams> {
         Ok(lsp::InlayHintParams {
             text_document: lsp::TextDocumentIdentifier {
@@ -2911,8 +2911,8 @@ impl LspCommand for InlayHints {
     async fn response_from_lsp(
         self,
         message: Option<Vec<lsp::InlayHint>>,
-        lsp_store: Model<LspStore>,
-        buffer: Model<Buffer>,
+        lsp_store: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         server_id: LanguageServerId,
         mut cx: AsyncAppContext,
     ) -> anyhow::Result<Vec<InlayHint>> {
@@ -2967,8 +2967,8 @@ impl LspCommand for InlayHints {
 
     async fn from_proto(
         message: proto::InlayHints,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Self> {
         let start = message
@@ -2993,7 +2993,7 @@ impl LspCommand for InlayHints {
         _: &mut LspStore,
         _: PeerId,
         buffer_version: &clock::Global,
-        _: &mut AppContext,
+        _: &mut App,
     ) -> proto::InlayHintsResponse {
         proto::InlayHintsResponse {
             hints: response
@@ -3007,8 +3007,8 @@ impl LspCommand for InlayHints {
     async fn response_from_proto(
         self,
         message: proto::InlayHintsResponse,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> anyhow::Result<Vec<InlayHint>> {
         buffer
@@ -3058,7 +3058,7 @@ impl LspCommand for LinkedEditingRange {
         path: &Path,
         buffer: &Buffer,
         _server: &Arc<LanguageServer>,
-        _: &AppContext,
+        _: &App,
     ) -> Result<lsp::LinkedEditingRangeParams> {
         let position = self.position.to_point_utf16(&buffer.snapshot());
         Ok(lsp::LinkedEditingRangeParams {
@@ -3070,8 +3070,8 @@ impl LspCommand for LinkedEditingRange {
     async fn response_from_lsp(
         self,
         message: Option<lsp::LinkedEditingRanges>,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         _server_id: LanguageServerId,
         cx: AsyncAppContext,
     ) -> Result<Vec<Range<Anchor>>> {
@@ -3105,8 +3105,8 @@ impl LspCommand for LinkedEditingRange {
 
     async fn from_proto(
         message: proto::LinkedEditingRange,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Self> {
         let position = message
@@ -3129,7 +3129,7 @@ impl LspCommand for LinkedEditingRange {
         _: &mut LspStore,
         _: PeerId,
         buffer_version: &clock::Global,
-        _: &mut AppContext,
+        _: &mut App,
     ) -> proto::LinkedEditingRangeResponse {
         proto::LinkedEditingRangeResponse {
             items: response
@@ -3146,8 +3146,8 @@ impl LspCommand for LinkedEditingRange {
     async fn response_from_proto(
         self,
         message: proto::LinkedEditingRangeResponse,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> Result<Vec<Range<Anchor>>> {
         buffer

crates/project/src/lsp_ext_command.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{lsp_command::LspCommand, lsp_store::LspStore, make_text_document_identifier};
-use anyhow::{Context, Result};
+use anyhow::{Context as _, Result};
 use async_trait::async_trait;
-use gpui::{AppContext, AsyncAppContext, Model};
+use gpui::{App, AsyncAppContext, Entity};
 use language::{point_to_lsp, proto::deserialize_anchor, Buffer};
 use lsp::{LanguageServer, LanguageServerId};
 use rpc::proto::{self, PeerId};
@@ -56,7 +56,7 @@ impl LspCommand for ExpandMacro {
         path: &Path,
         _: &Buffer,
         _: &Arc<LanguageServer>,
-        _: &AppContext,
+        _: &App,
     ) -> Result<ExpandMacroParams> {
         Ok(ExpandMacroParams {
             text_document: make_text_document_identifier(path)?,
@@ -67,8 +67,8 @@ impl LspCommand for ExpandMacro {
     async fn response_from_lsp(
         self,
         message: Option<ExpandedMacro>,
-        _: Model<LspStore>,
-        _: Model<Buffer>,
+        _: Entity<LspStore>,
+        _: Entity<Buffer>,
         _: LanguageServerId,
         _: AsyncAppContext,
     ) -> anyhow::Result<ExpandedMacro> {
@@ -92,8 +92,8 @@ impl LspCommand for ExpandMacro {
 
     async fn from_proto(
         message: Self::ProtoRequest,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> anyhow::Result<Self> {
         let position = message
@@ -110,7 +110,7 @@ impl LspCommand for ExpandMacro {
         _: &mut LspStore,
         _: PeerId,
         _: &clock::Global,
-        _: &mut AppContext,
+        _: &mut App,
     ) -> proto::LspExtExpandMacroResponse {
         proto::LspExtExpandMacroResponse {
             name: response.name,
@@ -121,8 +121,8 @@ impl LspCommand for ExpandMacro {
     async fn response_from_proto(
         self,
         message: proto::LspExtExpandMacroResponse,
-        _: Model<LspStore>,
-        _: Model<Buffer>,
+        _: Entity<LspStore>,
+        _: Entity<Buffer>,
         _: AsyncAppContext,
     ) -> anyhow::Result<ExpandedMacro> {
         Ok(ExpandedMacro {
@@ -184,7 +184,7 @@ impl LspCommand for OpenDocs {
         path: &Path,
         _: &Buffer,
         _: &Arc<LanguageServer>,
-        _: &AppContext,
+        _: &App,
     ) -> Result<OpenDocsParams> {
         Ok(OpenDocsParams {
             text_document: lsp::TextDocumentIdentifier {
@@ -197,8 +197,8 @@ impl LspCommand for OpenDocs {
     async fn response_from_lsp(
         self,
         message: Option<DocsUrls>,
-        _: Model<LspStore>,
-        _: Model<Buffer>,
+        _: Entity<LspStore>,
+        _: Entity<Buffer>,
         _: LanguageServerId,
         _: AsyncAppContext,
     ) -> anyhow::Result<DocsUrls> {
@@ -222,8 +222,8 @@ impl LspCommand for OpenDocs {
 
     async fn from_proto(
         message: Self::ProtoRequest,
-        _: Model<LspStore>,
-        buffer: Model<Buffer>,
+        _: Entity<LspStore>,
+        buffer: Entity<Buffer>,
         mut cx: AsyncAppContext,
     ) -> anyhow::Result<Self> {
         let position = message
@@ -240,7 +240,7 @@ impl LspCommand for OpenDocs {
         _: &mut LspStore,
         _: PeerId,
         _: &clock::Global,
-        _: &mut AppContext,
+        _: &mut App,
     ) -> proto::LspExtOpenDocsResponse {
         proto::LspExtOpenDocsResponse {
             web: response.web,
@@ -251,8 +251,8 @@ impl LspCommand for OpenDocs {
     async fn response_from_proto(
         self,
         message: proto::LspExtOpenDocsResponse,
-        _: Model<LspStore>,
-        _: Model<Buffer>,
+        _: Entity<LspStore>,
+        _: Entity<Buffer>,
         _: AsyncAppContext,
     ) -> anyhow::Result<DocsUrls> {
         Ok(DocsUrls {
@@ -301,7 +301,7 @@ impl LspCommand for SwitchSourceHeader {
         path: &Path,
         _: &Buffer,
         _: &Arc<LanguageServer>,
-        _: &AppContext,
+        _: &App,
     ) -> Result<SwitchSourceHeaderParams> {
         Ok(SwitchSourceHeaderParams(make_text_document_identifier(
             path,
@@ -311,8 +311,8 @@ impl LspCommand for SwitchSourceHeader {
     async fn response_from_lsp(
         self,
         message: Option<SwitchSourceHeaderResult>,
-        _: Model<LspStore>,
-        _: Model<Buffer>,
+        _: Entity<LspStore>,
+        _: Entity<Buffer>,
         _: LanguageServerId,
         _: AsyncAppContext,
     ) -> anyhow::Result<SwitchSourceHeaderResult> {
@@ -330,8 +330,8 @@ impl LspCommand for SwitchSourceHeader {
 
     async fn from_proto(
         _: Self::ProtoRequest,
-        _: Model<LspStore>,
-        _: Model<Buffer>,
+        _: Entity<LspStore>,
+        _: Entity<Buffer>,
         _: AsyncAppContext,
     ) -> anyhow::Result<Self> {
         Ok(Self {})
@@ -342,7 +342,7 @@ impl LspCommand for SwitchSourceHeader {
         _: &mut LspStore,
         _: PeerId,
         _: &clock::Global,
-        _: &mut AppContext,
+        _: &mut App,
     ) -> proto::LspExtSwitchSourceHeaderResponse {
         proto::LspExtSwitchSourceHeaderResponse {
             target_file: response.0,
@@ -352,8 +352,8 @@ impl LspCommand for SwitchSourceHeader {
     async fn response_from_proto(
         self,
         message: proto::LspExtSwitchSourceHeaderResponse,
-        _: Model<LspStore>,
-        _: Model<Buffer>,
+        _: Entity<LspStore>,
+        _: Entity<Buffer>,
         _: AsyncAppContext,
     ) -> anyhow::Result<SwitchSourceHeaderResult> {
         Ok(SwitchSourceHeaderResult(message.target_file))

crates/project/src/lsp_store.rs 🔗

@@ -25,8 +25,8 @@ use futures::{
 };
 use globset::{Glob, GlobBuilder, GlobMatcher, GlobSet, GlobSetBuilder};
 use gpui::{
-    AppContext, AsyncAppContext, Context, Entity, EventEmitter, Model, ModelContext, PromptLevel,
-    Task, WeakModel,
+    App, AppContext as _, AsyncAppContext, Context, Entity, EventEmitter, PromptLevel, Task,
+    WeakEntity,
 };
 use http_client::HttpClient;
 use itertools::Itertools as _;
@@ -108,7 +108,7 @@ pub enum LspFormatTarget {
 
 // proto::RegisterBufferWithLanguageServer {}
 
-pub type OpenLspBufferHandle = Model<Model<Buffer>>;
+pub type OpenLspBufferHandle = Entity<Entity<Buffer>>;
 
 // Currently, formatting operations are represented differently depending on
 // whether they come from a language server or an external command.
@@ -130,14 +130,14 @@ impl FormatTrigger {
 }
 
 pub struct LocalLspStore {
-    worktree_store: Model<WorktreeStore>,
-    toolchain_store: Model<ToolchainStore>,
+    worktree_store: Entity<WorktreeStore>,
+    toolchain_store: Entity<ToolchainStore>,
     http_client: Arc<dyn HttpClient>,
-    environment: Model<ProjectEnvironment>,
+    environment: Entity<ProjectEnvironment>,
     fs: Arc<dyn Fs>,
     languages: Arc<LanguageRegistry>,
     language_server_ids: HashMap<(WorktreeId, LanguageServerName), LanguageServerId>,
-    yarn: Model<YarnPathStore>,
+    yarn: Entity<YarnPathStore>,
     pub language_servers: HashMap<LanguageServerId, LanguageServerState>,
     buffers_being_formatted: HashSet<BufferId>,
     last_workspace_edits_by_language_server: HashMap<LanguageServerId, ProjectTransaction>,
@@ -148,7 +148,7 @@ pub struct LocalLspStore {
         HashMap<LanguageServerId, HashMap<String, Vec<FileSystemWatcher>>>,
     supplementary_language_servers:
         HashMap<LanguageServerId, (LanguageServerName, Arc<LanguageServer>)>,
-    prettier_store: Model<PrettierStore>,
+    prettier_store: Entity<PrettierStore>,
     current_lsp_settings: HashMap<LanguageServerName, LspSettings>,
     next_diagnostic_group_id: usize,
     diagnostics: HashMap<
@@ -169,10 +169,10 @@ pub struct LocalLspStore {
 impl LocalLspStore {
     fn start_language_server(
         &mut self,
-        worktree_handle: &Model<Worktree>,
+        worktree_handle: &Entity<Worktree>,
         delegate: Arc<LocalLspAdapterDelegate>,
         adapter: Arc<CachedLspAdapter>,
-        cx: &mut ModelContext<LspStore>,
+        cx: &mut Context<LspStore>,
     ) {
         let worktree = worktree_handle.read(cx);
         let worktree_id = worktree.id();
@@ -367,9 +367,9 @@ impl LocalLspStore {
 
     pub fn start_language_servers(
         &mut self,
-        worktree: &Model<Worktree>,
+        worktree: &Entity<Worktree>,
         language: LanguageName,
-        cx: &mut ModelContext<LspStore>,
+        cx: &mut Context<LspStore>,
     ) {
         let root_file = worktree
             .update(cx, |tree, cx| tree.root_file(cx))
@@ -442,7 +442,7 @@ impl LocalLspStore {
         adapter: Arc<CachedLspAdapter>,
         delegate: Arc<dyn LspAdapterDelegate>,
         allow_binary_download: bool,
-        cx: &mut ModelContext<LspStore>,
+        cx: &mut Context<LspStore>,
     ) -> Task<Result<LanguageServerBinary>> {
         let settings = ProjectSettings::get(
             Some(SettingsLocation {
@@ -504,7 +504,7 @@ impl LocalLspStore {
     }
 
     fn setup_lsp_messages(
-        this: WeakModel<LspStore>,
+        this: WeakEntity<LspStore>,
         fs: Arc<dyn Fs>,
         language_server: &LanguageServer,
         delegate: Arc<dyn LspAdapterDelegate>,
@@ -1000,7 +1000,7 @@ impl LocalLspStore {
 
     fn shutdown_language_servers(
         &mut self,
-        _cx: &mut ModelContext<LspStore>,
+        _cx: &mut Context<LspStore>,
     ) -> impl Future<Output = ()> {
         let shutdown_futures = self
             .language_servers
@@ -1040,7 +1040,7 @@ impl LocalLspStore {
     pub(crate) fn language_server_ids_for_buffer(
         &self,
         buffer: &Buffer,
-        cx: &AppContext,
+        cx: &App,
     ) -> Vec<LanguageServerId> {
         if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) {
             let worktree_id = file.worktree_id(cx);
@@ -1060,7 +1060,7 @@ impl LocalLspStore {
     pub(crate) fn language_servers_for_buffer<'a>(
         &'a self,
         buffer: &'a Buffer,
-        cx: &'a AppContext,
+        cx: &'a App,
     ) -> impl Iterator<Item = (&'a Arc<CachedLspAdapter>, &'a Arc<LanguageServer>)> {
         self.language_server_ids_for_buffer(buffer, cx)
             .into_iter()
@@ -1075,7 +1075,7 @@ impl LocalLspStore {
     fn primary_language_server_for_buffer<'a>(
         &'a self,
         buffer: &'a Buffer,
-        cx: &'a AppContext,
+        cx: &'a App,
     ) -> Option<(&'a Arc<CachedLspAdapter>, &'a Arc<LanguageServer>)> {
         // The list of language servers is ordered based on the `language_servers` setting
         // for each language, thus we can consider the first one in the list to be the
@@ -1084,7 +1084,7 @@ impl LocalLspStore {
     }
 
     async fn format_locally(
-        lsp_store: WeakModel<LspStore>,
+        lsp_store: WeakEntity<LspStore>,
         mut buffers: Vec<FormattableBuffer>,
         target: &LspFormatTarget,
         push_to_history: bool,
@@ -1432,7 +1432,7 @@ impl LocalLspStore {
         buffer: &FormattableBuffer,
         ranges: Option<&Vec<Range<Anchor>>>,
         primary_server_and_path: Option<(&Arc<LanguageServer>, &PathBuf)>,
-        lsp_store: WeakModel<LspStore>,
+        lsp_store: WeakEntity<LspStore>,
         settings: &LanguageSettings,
         adapters_and_servers: &[(Arc<CachedLspAdapter>, Arc<LanguageServer>)],
         push_to_history: bool,
@@ -1521,7 +1521,7 @@ impl LocalLspStore {
     }
 
     pub async fn format_ranges_via_lsp(
-        this: &WeakModel<LspStore>,
+        this: &WeakEntity<LspStore>,
         buffer: &FormattableBuffer,
         ranges: &Vec<Range<Anchor>>,
         abs_path: &Path,
@@ -1593,8 +1593,8 @@ impl LocalLspStore {
 
     #[allow(clippy::too_many_arguments)]
     async fn format_via_lsp(
-        this: &WeakModel<LspStore>,
-        buffer: &Model<Buffer>,
+        this: &WeakEntity<LspStore>,
+        buffer: &Entity<Buffer>,
         abs_path: &Path,
         language_server: &Arc<LanguageServer>,
         settings: &LanguageSettings,
@@ -1736,11 +1736,7 @@ impl LocalLspStore {
         anyhow::Ok(())
     }
 
-    fn initialize_buffer(
-        &mut self,
-        buffer_handle: &Model<Buffer>,
-        cx: &mut ModelContext<LspStore>,
-    ) {
+    fn initialize_buffer(&mut self, buffer_handle: &Entity<Buffer>, cx: &mut Context<LspStore>) {
         let buffer = buffer_handle.read(cx);
 
         let Some(file) = File::from_dyn(buffer.file()) else {
@@ -1802,12 +1798,7 @@ impl LocalLspStore {
         }
     }
 
-    pub(crate) fn reset_buffer(
-        &mut self,
-        buffer: &Model<Buffer>,
-        old_file: &File,
-        cx: &mut AppContext,
-    ) {
+    pub(crate) fn reset_buffer(&mut self, buffer: &Entity<Buffer>, old_file: &File, cx: &mut App) {
         buffer.update(cx, |buffer, cx| {
             let worktree_id = old_file.worktree_id(cx);
 
@@ -1826,11 +1817,11 @@ impl LocalLspStore {
 
     fn update_buffer_diagnostics(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         server_id: LanguageServerId,
         version: Option<i32>,
         mut diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
-        cx: &mut ModelContext<LspStore>,
+        cx: &mut Context<LspStore>,
     ) -> Result<()> {
         fn compare_diagnostics(a: &Diagnostic, b: &Diagnostic) -> Ordering {
             Ordering::Equal
@@ -1900,8 +1891,8 @@ impl LocalLspStore {
 
     fn register_buffer_with_language_servers(
         &mut self,
-        buffer_handle: &Model<Buffer>,
-        cx: &mut ModelContext<LspStore>,
+        buffer_handle: &Entity<Buffer>,
+        cx: &mut Context<LspStore>,
     ) {
         let buffer = buffer_handle.read(cx);
         let buffer_id = buffer.remote_id();
@@ -1965,10 +1956,10 @@ impl LocalLspStore {
     }
     pub(crate) fn unregister_old_buffer_from_language_servers(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         old_file: &File,
 
-        cx: &mut AppContext,
+        cx: &mut App,
     ) {
         let old_path = match old_file.as_local() {
             Some(local) => local.abs_path(cx),
@@ -1980,9 +1971,9 @@ impl LocalLspStore {
 
     pub(crate) fn unregister_buffer_from_language_servers(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         file_url: lsp::Url,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) {
         buffer.update(cx, |buffer, cx| {
             self.buffer_snapshots.remove(&buffer.remote_id());
@@ -2000,10 +1991,10 @@ impl LocalLspStore {
 
     fn buffer_snapshot_for_lsp_version(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         server_id: LanguageServerId,
         version: Option<i32>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Result<TextBufferSnapshot> {
         const OLD_VERSIONS_TO_RETAIN: i32 = 10;
 
@@ -2032,10 +2023,10 @@ impl LocalLspStore {
     }
 
     async fn execute_code_actions_on_servers(
-        this: &WeakModel<LspStore>,
+        this: &WeakEntity<LspStore>,
         adapters_and_servers: &[(Arc<CachedLspAdapter>, Arc<LanguageServer>)],
         code_actions: Vec<lsp::CodeActionKind>,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         push_to_history: bool,
         project_transaction: &mut ProjectTransaction,
         cx: &mut AsyncAppContext,
@@ -2110,8 +2101,8 @@ impl LocalLspStore {
     }
 
     pub async fn deserialize_text_edits(
-        this: Model<LspStore>,
-        buffer_to_edit: Model<Buffer>,
+        this: Entity<LspStore>,
+        buffer_to_edit: Entity<Buffer>,
         edits: Vec<lsp::TextEdit>,
         push_to_history: bool,
         _: Arc<CachedLspAdapter>,
@@ -2154,11 +2145,11 @@ impl LocalLspStore {
     #[allow(clippy::type_complexity)]
     pub(crate) fn edits_from_lsp(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         lsp_edits: impl 'static + Send + IntoIterator<Item = lsp::TextEdit>,
         server_id: LanguageServerId,
         version: Option<i32>,
-        cx: &mut ModelContext<LspStore>,
+        cx: &mut Context<LspStore>,
     ) -> Task<Result<Vec<(Range<Anchor>, String)>>> {
         let snapshot = self.buffer_snapshot_for_lsp_version(buffer, server_id, version, cx);
         cx.background_executor().spawn(async move {
@@ -2257,7 +2248,7 @@ impl LocalLspStore {
     }
 
     pub(crate) async fn deserialize_workspace_edit(
-        this: Model<LspStore>,
+        this: Entity<LspStore>,
         edit: lsp::WorkspaceEdit,
         push_to_history: bool,
         lsp_adapter: Arc<CachedLspAdapter>,
@@ -2483,7 +2474,7 @@ impl LocalLspStore {
     }
 
     async fn on_lsp_workspace_edit(
-        this: WeakModel<LspStore>,
+        this: WeakEntity<LspStore>,
         params: lsp::ApplyWorkspaceEditParams,
         server_id: LanguageServerId,
         adapter: Arc<CachedLspAdapter>,
@@ -2523,7 +2514,7 @@ impl LocalLspStore {
         &'a self,
         language_server_id: LanguageServerId,
         watchers: impl Iterator<Item = &'a FileSystemWatcher>,
-        cx: &mut ModelContext<LspStore>,
+        cx: &mut Context<LspStore>,
     ) -> LanguageServerWatchedPathsBuilder {
         let worktrees = self
             .worktree_store
@@ -2705,7 +2696,7 @@ impl LocalLspStore {
     fn rebuild_watched_paths(
         &mut self,
         language_server_id: LanguageServerId,
-        cx: &mut ModelContext<LspStore>,
+        cx: &mut Context<LspStore>,
     ) {
         let Some(watchers) = self
             .language_server_watcher_registrations
@@ -2728,7 +2719,7 @@ impl LocalLspStore {
         language_server_id: LanguageServerId,
         registration_id: &str,
         params: DidChangeWatchedFilesRegistrationOptions,
-        cx: &mut ModelContext<LspStore>,
+        cx: &mut Context<LspStore>,
     ) {
         let registrations = self
             .language_server_watcher_registrations
@@ -2744,7 +2735,7 @@ impl LocalLspStore {
         &mut self,
         language_server_id: LanguageServerId,
         registration_id: &str,
-        cx: &mut ModelContext<LspStore>,
+        cx: &mut Context<LspStore>,
     ) {
         let registrations = self
             .language_server_watcher_registrations
@@ -2772,7 +2763,7 @@ impl LocalLspStore {
 #[derive(Debug)]
 pub struct FormattableBuffer {
     id: BufferId,
-    handle: Model<Buffer>,
+    handle: Entity<Buffer>,
     abs_path: Option<PathBuf>,
     env: Option<HashMap<String, String>>,
 }
@@ -2799,9 +2790,9 @@ pub struct LspStore {
     last_formatting_failure: Option<String>,
     downstream_client: Option<(AnyProtoClient, u64)>,
     nonce: u128,
-    buffer_store: Model<BufferStore>,
-    worktree_store: Model<WorktreeStore>,
-    toolchain_store: Option<Model<ToolchainStore>>,
+    buffer_store: Entity<BufferStore>,
+    worktree_store: Entity<WorktreeStore>,
+    toolchain_store: Option<Entity<ToolchainStore>>,
     pub languages: Arc<LanguageRegistry>,
     pub language_server_statuses: BTreeMap<LanguageServerId, LanguageServerStatus>,
     active_entry: Option<ProjectEntryId>,
@@ -2821,7 +2812,7 @@ pub enum LspStoreEvent {
     LanguageServerLog(LanguageServerId, LanguageServerLogType, String),
     LanguageServerPrompt(LanguageServerPromptRequest),
     LanguageDetected {
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         new_language: Option<Arc<Language>>,
     },
     Notification(String),
@@ -2953,15 +2944,15 @@ impl LspStore {
 
     #[allow(clippy::too_many_arguments)]
     pub fn new_local(
-        buffer_store: Model<BufferStore>,
-        worktree_store: Model<WorktreeStore>,
-        prettier_store: Model<PrettierStore>,
-        toolchain_store: Model<ToolchainStore>,
-        environment: Model<ProjectEnvironment>,
+        buffer_store: Entity<BufferStore>,
+        worktree_store: Entity<WorktreeStore>,
+        prettier_store: Entity<PrettierStore>,
+        toolchain_store: Entity<ToolchainStore>,
+        environment: Entity<ProjectEnvironment>,
         languages: Arc<LanguageRegistry>,
         http_client: Arc<dyn HttpClient>,
         fs: Arc<dyn Fs>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Self {
         let yarn = YarnPathStore::new(fs.clone(), cx);
         cx.subscribe(&buffer_store, Self::on_buffer_store_event)
@@ -3027,11 +3018,11 @@ impl LspStore {
 
     fn send_lsp_proto_request<R: LspCommand>(
         &self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         client: AnyProtoClient,
         upstream_project_id: u64,
         request: R,
-        cx: &mut ModelContext<'_, LspStore>,
+        cx: &mut Context<'_, LspStore>,
     ) -> Task<anyhow::Result<<R as LspCommand>::Response>> {
         let message = request.to_proto(upstream_project_id, buffer.read(cx));
         cx.spawn(move |this, cx| async move {
@@ -3045,14 +3036,14 @@ impl LspStore {
 
     #[allow(clippy::too_many_arguments)]
     pub(super) fn new_remote(
-        buffer_store: Model<BufferStore>,
-        worktree_store: Model<WorktreeStore>,
-        toolchain_store: Option<Model<ToolchainStore>>,
+        buffer_store: Entity<BufferStore>,
+        worktree_store: Entity<WorktreeStore>,
+        toolchain_store: Option<Entity<ToolchainStore>>,
         languages: Arc<LanguageRegistry>,
         upstream_client: AnyProtoClient,
         project_id: u64,
         fs: Arc<dyn Fs>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Self {
         cx.subscribe(&buffer_store, Self::on_buffer_store_event)
             .detach();
@@ -3085,8 +3076,8 @@ impl LspStore {
     fn worktree_for_id(
         &self,
         worktree_id: WorktreeId,
-        cx: &ModelContext<Self>,
-    ) -> Result<Model<Worktree>> {
+        cx: &Context<Self>,
+    ) -> Result<Entity<Worktree>> {
         self.worktree_store
             .read(cx)
             .worktree_for_id(worktree_id, cx)
@@ -3095,9 +3086,9 @@ impl LspStore {
 
     fn on_buffer_store_event(
         &mut self,
-        _: Model<BufferStore>,
+        _: Entity<BufferStore>,
         event: &BufferStoreEvent,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match event {
             BufferStoreEvent::BufferAdded(buffer) => {
@@ -3128,9 +3119,9 @@ impl LspStore {
 
     fn on_worktree_store_event(
         &mut self,
-        _: Model<WorktreeStore>,
+        _: Entity<WorktreeStore>,
         event: &WorktreeStoreEvent,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match event {
             WorktreeStoreEvent::WorktreeAdded(worktree) => {
@@ -3160,9 +3151,9 @@ impl LspStore {
 
     fn on_prettier_store_event(
         &mut self,
-        _: Model<PrettierStore>,
+        _: Entity<PrettierStore>,
         event: &PrettierStoreEvent,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match event {
             PrettierStoreEvent::LanguageServerRemoved(prettier_server_id) => {
@@ -3185,9 +3176,9 @@ impl LspStore {
 
     fn on_toolchain_store_event(
         &mut self,
-        _: Model<ToolchainStore>,
+        _: Entity<ToolchainStore>,
         event: &ToolchainStoreEvent,
-        _: &mut ModelContext<Self>,
+        _: &mut Context<Self>,
     ) {
         match event {
             ToolchainStoreEvent::ToolchainActivated { .. } => {
@@ -3200,15 +3191,15 @@ impl LspStore {
         *self._maintain_workspace_config.1.borrow_mut() = ();
     }
 
-    pub fn prettier_store(&self) -> Option<Model<PrettierStore>> {
+    pub fn prettier_store(&self) -> Option<Entity<PrettierStore>> {
         self.as_local().map(|local| local.prettier_store.clone())
     }
 
     fn on_buffer_event(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         event: &language::BufferEvent,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match event {
             language::BufferEvent::Edited { .. } => {
@@ -3223,11 +3214,7 @@ impl LspStore {
         }
     }
 
-    fn on_buffer_added(
-        &mut self,
-        buffer: &Model<Buffer>,
-        cx: &mut ModelContext<Self>,
-    ) -> Result<()> {
+    fn on_buffer_added(&mut self, buffer: &Entity<Buffer>, cx: &mut Context<Self>) -> Result<()> {
         buffer.update(cx, |buffer, _| {
             buffer.set_language_registry(self.languages.clone())
         });
@@ -3247,12 +3234,12 @@ impl LspStore {
 
     pub fn register_buffer_with_language_servers(
         &mut self,
-        buffer: &Model<Buffer>,
-        cx: &mut ModelContext<Self>,
+        buffer: &Entity<Buffer>,
+        cx: &mut Context<Self>,
     ) -> OpenLspBufferHandle {
         let buffer_id = buffer.read(cx).remote_id();
 
-        let handle = cx.new_model(|_| buffer.clone());
+        let handle = cx.new(|_| buffer.clone());
 
         if let Some(local) = self.as_local_mut() {
             let Some(file) = File::from_dyn(buffer.read(cx).file()) else {
@@ -3302,7 +3289,7 @@ impl LspStore {
 
     fn maintain_buffer_languages(
         languages: Arc<LanguageRegistry>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<()> {
         let mut subscription = languages.subscribe();
         let mut prev_reload_count = languages.reload_count();
@@ -3377,8 +3364,8 @@ impl LspStore {
 
     fn detect_language_for_buffer(
         &mut self,
-        buffer_handle: &Model<Buffer>,
-        cx: &mut ModelContext<Self>,
+        buffer_handle: &Entity<Buffer>,
+        cx: &mut Context<Self>,
     ) -> Option<language::AvailableLanguage> {
         // If the buffer has a language, set it and start the language server if we haven't already.
         let buffer = buffer_handle.read(cx);
@@ -3406,9 +3393,9 @@ impl LspStore {
 
     pub(crate) fn set_language_for_buffer(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         new_language: Arc<Language>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let buffer_file = buffer.read(cx).file().cloned();
         let buffer_id = buffer.read(cx).remote_id();
@@ -3470,7 +3457,7 @@ impl LspStore {
         })
     }
 
-    pub fn buffer_store(&self) -> Model<BufferStore> {
+    pub fn buffer_store(&self) -> Entity<BufferStore> {
         self.buffer_store.clone()
     }
 
@@ -3498,10 +3485,10 @@ impl LspStore {
 
     pub fn request_lsp<R: LspCommand>(
         &mut self,
-        buffer_handle: Model<Buffer>,
+        buffer_handle: Entity<Buffer>,
         server: LanguageServerToQuery,
         request: R,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<R::Response>>
     where
         <R::LspRequest as lsp::request::Request>::Result: Send,
@@ -3625,7 +3612,7 @@ impl LspStore {
         Task::ready(Ok(Default::default()))
     }
 
-    fn on_settings_changed(&mut self, cx: &mut ModelContext<Self>) {
+    fn on_settings_changed(&mut self, cx: &mut Context<Self>) {
         let mut language_servers_to_start = Vec::new();
         let mut language_formatters_to_check = Vec::new();
         for buffer in self.buffer_store.read(cx).buffers() {
@@ -3731,10 +3718,10 @@ impl LspStore {
 
     pub fn apply_code_action(
         &self,
-        buffer_handle: Model<Buffer>,
+        buffer_handle: Entity<Buffer>,
         mut action: CodeAction,
         push_to_history: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<ProjectTransaction>> {
         if let Some((upstream_client, project_id)) = self.upstream_client() {
             let request = proto::ApplyCodeAction {
@@ -3820,9 +3807,9 @@ impl LspStore {
     pub fn resolve_inlay_hint(
         &self,
         hint: InlayHint,
-        buffer_handle: Model<Buffer>,
+        buffer_handle: Entity<Buffer>,
         server_id: LanguageServerId,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<anyhow::Result<InlayHint>> {
         if let Some((upstream_client, project_id)) = self.upstream_client() {
             let request = proto::ResolveInlayHint {
@@ -3879,9 +3866,9 @@ impl LspStore {
 
     pub(crate) fn linked_edit(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         position: Anchor,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<Vec<Range<Anchor>>>> {
         let snapshot = buffer.read(cx).snapshot();
         let scope = snapshot.language_scope_at(position);
@@ -3933,10 +3920,10 @@ impl LspStore {
 
     fn apply_on_type_formatting(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         position: Anchor,
         trigger: String,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<Option<Transaction>>> {
         if let Some((client, project_id)) = self.upstream_client() {
             let request = proto::OnTypeFormatting {
@@ -3989,11 +3976,11 @@ impl LspStore {
 
     pub fn on_type_format<T: ToPointUtf16>(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         position: T,
         trigger: String,
         push_to_history: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<Option<Transaction>>> {
         let position = position.to_point_utf16(buffer.read(cx));
         self.on_type_format_impl(buffer, position, trigger, push_to_history, cx)
@@ -4001,11 +3988,11 @@ impl LspStore {
 
     fn on_type_format_impl(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         position: PointUtf16,
         trigger: String,
         push_to_history: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<Option<Transaction>>> {
         let options = buffer.update(cx, |buffer, cx| {
             lsp_command::lsp_formatting_options(
@@ -4031,10 +4018,10 @@ impl LspStore {
     }
     pub fn code_actions(
         &mut self,
-        buffer_handle: &Model<Buffer>,
+        buffer_handle: &Entity<Buffer>,
         range: Range<Anchor>,
         kinds: Option<Vec<CodeActionKind>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<Vec<CodeAction>>> {
         if let Some((upstream_client, project_id)) = self.upstream_client() {
             let request_task = upstream_client.request(proto::MultiLspQuery {
@@ -4111,10 +4098,10 @@ impl LspStore {
     #[inline(never)]
     pub fn completions(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         position: PointUtf16,
         context: CompletionContext,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<Vec<Completion>>> {
         let language_registry = self.languages.clone();
 
@@ -4213,10 +4200,10 @@ impl LspStore {
 
     pub fn resolve_completions(
         &self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         completion_indices: Vec<usize>,
         completions: Rc<RefCell<Box<[Completion]>>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<bool>> {
         let client = self.upstream_client();
         let language_registry = self.languages.clone();
@@ -4481,11 +4468,11 @@ impl LspStore {
 
     pub fn apply_additional_edits_for_completion(
         &self,
-        buffer_handle: Model<Buffer>,
+        buffer_handle: Entity<Buffer>,
         completions: Rc<RefCell<Box<[Completion]>>>,
         completion_index: usize,
         push_to_history: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<Option<Transaction>>> {
         let buffer = buffer_handle.read(cx);
         let buffer_id = buffer.remote_id();
@@ -4597,9 +4584,9 @@ impl LspStore {
 
     pub fn inlay_hints(
         &mut self,
-        buffer_handle: Model<Buffer>,
+        buffer_handle: Entity<Buffer>,
         range: Range<Anchor>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<anyhow::Result<Vec<InlayHint>>> {
         let buffer = buffer_handle.read(cx);
         let range_start = range.start;
@@ -4651,9 +4638,9 @@ impl LspStore {
 
     pub fn signature_help<T: ToPointUtf16>(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         position: T,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Vec<SignatureHelp>> {
         let position = position.to_point_utf16(buffer.read(cx));
 
@@ -4725,9 +4712,9 @@ impl LspStore {
 
     pub fn hover(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         position: PointUtf16,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Vec<Hover>> {
         if let Some((client, upstream_project_id)) = self.upstream_client() {
             let request_task = client.request(proto::MultiLspQuery {
@@ -4800,7 +4787,7 @@ impl LspStore {
         }
     }
 
-    pub fn symbols(&self, query: &str, cx: &mut ModelContext<Self>) -> Task<Result<Vec<Symbol>>> {
+    pub fn symbols(&self, query: &str, cx: &mut Context<Self>) -> Task<Result<Vec<Symbol>>> {
         let language_registry = self.languages.clone();
 
         if let Some((upstream_client, project_id)) = self.upstream_client().as_ref() {
@@ -4823,7 +4810,7 @@ impl LspStore {
         } else if let Some(local) = self.as_local() {
             struct WorkspaceSymbolsResult {
                 lsp_adapter: Arc<CachedLspAdapter>,
-                worktree: WeakModel<Worktree>,
+                worktree: WeakEntity<Worktree>,
                 worktree_abs_path: Arc<Path>,
                 lsp_symbols: Vec<(String, SymbolKind, lsp::Location)>,
             }
@@ -4957,7 +4944,7 @@ impl LspStore {
         }
     }
 
-    pub fn diagnostic_summary(&self, include_ignored: bool, cx: &AppContext) -> DiagnosticSummary {
+    pub fn diagnostic_summary(&self, include_ignored: bool, cx: &App) -> DiagnosticSummary {
         let mut summary = DiagnosticSummary::default();
         for (_, _, path_summary) in self.diagnostic_summaries(include_ignored, cx) {
             summary.error_count += path_summary.error_count;
@@ -4969,7 +4956,7 @@ impl LspStore {
     pub fn diagnostic_summaries<'a>(
         &'a self,
         include_ignored: bool,
-        cx: &'a AppContext,
+        cx: &'a App,
     ) -> impl Iterator<Item = (ProjectPath, LanguageServerId, DiagnosticSummary)> + 'a {
         self.worktree_store
             .read(cx)
@@ -5005,8 +4992,8 @@ impl LspStore {
 
     pub fn on_buffer_edited(
         &mut self,
-        buffer: Model<Buffer>,
-        cx: &mut ModelContext<Self>,
+        buffer: Entity<Buffer>,
+        cx: &mut Context<Self>,
     ) -> Option<()> {
         let buffer = buffer.read(cx);
         let file = File::from_dyn(buffer.file())?;
@@ -5108,8 +5095,8 @@ impl LspStore {
 
     pub fn on_buffer_saved(
         &mut self,
-        buffer: Model<Buffer>,
-        cx: &mut ModelContext<Self>,
+        buffer: Entity<Buffer>,
+        cx: &mut Context<Self>,
     ) -> Option<()> {
         let file = File::from_dyn(buffer.read(cx).file())?;
         let worktree_id = file.worktree_id(cx);
@@ -5145,7 +5132,7 @@ impl LspStore {
     }
 
     pub(crate) async fn refresh_workspace_configurations(
-        this: &WeakModel<Self>,
+        this: &WeakEntity<Self>,
         fs: Arc<dyn Fs>,
         mut cx: AsyncAppContext,
     ) {
@@ -5213,7 +5200,7 @@ impl LspStore {
         .await;
     }
 
-    fn toolchain_store(&self, cx: &AppContext) -> Arc<dyn LanguageToolchainStore> {
+    fn toolchain_store(&self, cx: &App) -> Arc<dyn LanguageToolchainStore> {
         if let Some(toolchain_store) = self.toolchain_store.as_ref() {
             toolchain_store.read(cx).as_language_toolchain_store()
         } else {
@@ -5223,7 +5210,7 @@ impl LspStore {
     fn maintain_workspace_config(
         fs: Arc<dyn Fs>,
         external_refresh_requests: watch::Receiver<()>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let (mut settings_changed_tx, mut settings_changed_rx) = watch::channel();
         let _ = postage::stream::Stream::try_recv(&mut settings_changed_rx);
@@ -5247,7 +5234,7 @@ impl LspStore {
     pub(crate) fn language_servers_for_local_buffer<'a>(
         &'a self,
         buffer: &'a Buffer,
-        cx: &'a AppContext,
+        cx: &'a App,
     ) -> impl Iterator<Item = (&'a Arc<CachedLspAdapter>, &'a Arc<LanguageServer>)> {
         self.as_local().into_iter().flat_map(|local| {
             local
@@ -5266,14 +5253,14 @@ impl LspStore {
         &'a self,
         buffer: &'a Buffer,
         server_id: LanguageServerId,
-        cx: &'a AppContext,
+        cx: &'a App,
     ) -> Option<(&'a Arc<CachedLspAdapter>, &'a Arc<LanguageServer>)> {
         self.as_local()?
             .language_servers_for_buffer(buffer, cx)
             .find(|(_, s)| s.server_id() == server_id)
     }
 
-    fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut ModelContext<Self>) {
+    fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut Context<Self>) {
         self.diagnostic_summaries.remove(&id_to_remove);
         let to_remove = Vec::new();
         if let Some(local) = self.as_local_mut() {
@@ -5315,7 +5302,7 @@ impl LspStore {
         &mut self,
         project_id: u64,
         downstream_client: AnyProtoClient,
-        _: &mut ModelContext<Self>,
+        _: &mut Context<Self>,
     ) {
         self.downstream_client = Some((downstream_client.clone(), project_id));
 
@@ -5384,7 +5371,7 @@ impl LspStore {
         abs_path: PathBuf,
         version: Option<i32>,
         diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Result<(), anyhow::Error> {
         let Some((worktree, relative_path)) =
             self.worktree_store.read(cx).find_worktree(&abs_path, cx)
@@ -5432,7 +5419,7 @@ impl LspStore {
         server_id: LanguageServerId,
         worktree_path: Arc<Path>,
         diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
-        _: &mut ModelContext<Worktree>,
+        _: &mut Context<Worktree>,
     ) -> Result<bool> {
         let local = match &mut self.mode {
             LspStoreMode::Local(local_lsp_store) => local_lsp_store,
@@ -5495,8 +5482,8 @@ impl LspStore {
     pub fn open_buffer_for_symbol(
         &mut self,
         symbol: &Symbol,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<Buffer>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<Buffer>>> {
         if let Some((client, project_id)) = self.upstream_client() {
             let request = client.request(proto::OpenBufferForSymbol {
                 project_id,
@@ -5554,8 +5541,8 @@ impl LspStore {
         mut abs_path: lsp::Url,
         language_server_id: LanguageServerId,
         language_server_name: LanguageServerName,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<Buffer>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<Buffer>>> {
         cx.spawn(move |lsp_store, mut cx| async move {
             // Escape percent-encoded string.
             let current_scheme = abs_path.scheme().to_owned();
@@ -5638,10 +5625,10 @@ impl LspStore {
 
     fn request_multiple_lsp_locally<P, R>(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         position: Option<P>,
         request: R,
-        cx: &mut ModelContext<'_, Self>,
+        cx: &mut Context<'_, Self>,
     ) -> Task<Vec<R::Response>>
     where
         P: ToOffset,
@@ -5690,7 +5677,7 @@ impl LspStore {
     }
 
     async fn handle_lsp_command<T: LspCommand>(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<T::ProtoRequest>,
         mut cx: AsyncAppContext,
     ) -> Result<<T::ProtoRequest as proto::RequestMessage>::Response>
@@ -5732,7 +5719,7 @@ impl LspStore {
     }
 
     async fn handle_multi_lsp_query(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::MultiLspQuery>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::MultiLspQueryResponse> {
@@ -5883,7 +5870,7 @@ impl LspStore {
     }
 
     async fn handle_apply_code_action(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::ApplyCodeAction>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::ApplyCodeActionResponse> {
@@ -5916,7 +5903,7 @@ impl LspStore {
     }
 
     async fn handle_register_buffer_with_language_servers(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::RegisterBufferWithLanguageServers>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::Ack> {
@@ -5945,7 +5932,7 @@ impl LspStore {
     }
 
     async fn handle_rename_project_entry(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::RenameProjectEntry>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::ProjectEntryResponse> {
@@ -5991,7 +5978,7 @@ impl LspStore {
     }
 
     async fn handle_update_diagnostic_summary(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::UpdateDiagnosticSummary>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -6052,7 +6039,7 @@ impl LspStore {
     }
 
     async fn handle_start_language_server(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::StartLanguageServer>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {

crates/project/src/prettier_store.rs 🔗

@@ -4,7 +4,7 @@ use std::{
     sync::Arc,
 };
 
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use collections::{HashMap, HashSet};
 use fs::Fs;
 use futures::{
@@ -12,7 +12,7 @@ use futures::{
     stream::FuturesUnordered,
     FutureExt,
 };
-use gpui::{AsyncAppContext, EventEmitter, Model, ModelContext, Task, WeakModel};
+use gpui::{AsyncAppContext, Context, Entity, EventEmitter, Task, WeakEntity};
 use language::{
     language_settings::{Formatter, LanguageSettings, SelectedFormatter},
     Buffer, LanguageRegistry, LocalFile,
@@ -33,7 +33,7 @@ pub struct PrettierStore {
     node: NodeRuntime,
     fs: Arc<dyn Fs>,
     languages: Arc<LanguageRegistry>,
-    worktree_store: Model<WorktreeStore>,
+    worktree_store: Entity<WorktreeStore>,
     default_prettier: DefaultPrettier,
     prettiers_per_worktree: HashMap<WorktreeId, HashSet<Option<PathBuf>>>,
     prettier_ignores_per_worktree: HashMap<WorktreeId, HashSet<PathBuf>>,
@@ -56,8 +56,8 @@ impl PrettierStore {
         node: NodeRuntime,
         fs: Arc<dyn Fs>,
         languages: Arc<LanguageRegistry>,
-        worktree_store: Model<WorktreeStore>,
-        _: &mut ModelContext<Self>,
+        worktree_store: Entity<WorktreeStore>,
+        _: &mut Context<Self>,
     ) -> Self {
         Self {
             node,
@@ -71,7 +71,7 @@ impl PrettierStore {
         }
     }
 
-    pub fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut ModelContext<Self>) {
+    pub fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut Context<Self>) {
         self.prettier_ignores_per_worktree.remove(&id_to_remove);
         let mut prettier_instances_to_clean = FuturesUnordered::new();
         if let Some(prettier_paths) = self.prettiers_per_worktree.remove(&id_to_remove) {
@@ -104,8 +104,8 @@ impl PrettierStore {
 
     fn prettier_instance_for_buffer(
         &mut self,
-        buffer: &Model<Buffer>,
-        cx: &mut ModelContext<Self>,
+        buffer: &Entity<Buffer>,
+        cx: &mut Context<Self>,
     ) -> Task<Option<(Option<PathBuf>, PrettierTask)>> {
         let buffer = buffer.read(cx);
         let buffer_file = buffer.file();
@@ -216,8 +216,8 @@ impl PrettierStore {
 
     fn prettier_ignore_for_buffer(
         &mut self,
-        buffer: &Model<Buffer>,
-        cx: &mut ModelContext<Self>,
+        buffer: &Entity<Buffer>,
+        cx: &mut Context<Self>,
     ) -> Task<Option<PathBuf>> {
         let buffer = buffer.read(cx);
         let buffer_file = buffer.file();
@@ -277,7 +277,7 @@ impl PrettierStore {
         node: NodeRuntime,
         prettier_dir: PathBuf,
         worktree_id: Option<WorktreeId>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> PrettierTask {
         cx.spawn(|prettier_store, mut cx| async move {
             log::info!("Starting prettier at path {prettier_dir:?}");
@@ -305,7 +305,7 @@ impl PrettierStore {
     fn start_default_prettier(
         node: NodeRuntime,
         worktree_id: Option<WorktreeId>,
-        cx: &mut ModelContext<PrettierStore>,
+        cx: &mut Context<PrettierStore>,
     ) -> Task<anyhow::Result<PrettierTask>> {
         cx.spawn(|prettier_store, mut cx| async move {
             let installation_task = prettier_store.update(&mut cx, |prettier_store, _| {
@@ -383,7 +383,7 @@ impl PrettierStore {
     }
 
     fn register_new_prettier(
-        prettier_store: &WeakModel<Self>,
+        prettier_store: &WeakEntity<Self>,
         prettier: &Prettier,
         worktree_id: Option<WorktreeId>,
         new_server_id: LanguageServerId,
@@ -442,9 +442,9 @@ impl PrettierStore {
 
     pub fn update_prettier_settings(
         &self,
-        worktree: &Model<Worktree>,
+        worktree: &Entity<Worktree>,
         changes: &[(Arc<Path>, ProjectEntryId, PathChange)],
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let prettier_config_files = Prettier::CONFIG_FILE_NAMES
             .iter()
@@ -516,7 +516,7 @@ impl PrettierStore {
         &mut self,
         worktree: Option<WorktreeId>,
         plugins: impl Iterator<Item = Arc<str>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if cfg!(any(test, feature = "test-support")) {
             self.default_prettier.installed_plugins.extend(plugins);
@@ -668,7 +668,7 @@ impl PrettierStore {
     pub fn on_settings_changed(
         &mut self,
         language_formatters_to_check: Vec<(Option<WorktreeId>, LanguageSettings)>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let mut prettier_plugins_by_worktree = HashMap::default();
         for (worktree, language_settings) in language_formatters_to_check {
@@ -705,8 +705,8 @@ pub fn prettier_plugins_for_language(
 }
 
 pub(super) async fn format_with_prettier(
-    prettier_store: &WeakModel<PrettierStore>,
-    buffer: &Model<Buffer>,
+    prettier_store: &WeakEntity<PrettierStore>,
+    buffer: &Entity<Buffer>,
     cx: &mut AsyncAppContext,
 ) -> Option<Result<crate::lsp_store::FormatOperation>> {
     let prettier_instance = prettier_store
@@ -822,7 +822,7 @@ impl DefaultPrettier {
         &mut self,
         node: &NodeRuntime,
         worktree_id: Option<WorktreeId>,
-        cx: &mut ModelContext<PrettierStore>,
+        cx: &mut Context<PrettierStore>,
     ) -> Option<Task<anyhow::Result<PrettierTask>>> {
         match &mut self.prettier {
             PrettierInstallation::NotInstalled { .. } => Some(
@@ -841,7 +841,7 @@ impl PrettierInstance {
         node: &NodeRuntime,
         prettier_dir: Option<&Path>,
         worktree_id: Option<WorktreeId>,
-        cx: &mut ModelContext<PrettierStore>,
+        cx: &mut Context<PrettierStore>,
     ) -> Option<Task<anyhow::Result<PrettierTask>>> {
         if self.attempt > prettier::FAIL_THRESHOLD {
             match prettier_dir {

crates/project/src/project.rs 🔗

@@ -48,8 +48,8 @@ use ::git::{
     status::FileStatus,
 };
 use gpui::{
-    AnyModel, AppContext, AsyncAppContext, BorrowAppContext, Context as _, EventEmitter, Hsla,
-    Model, ModelContext, SharedString, Task, WeakModel, WindowContext,
+    AnyEntity, App, AppContext as _, AsyncAppContext, BorrowAppContext, Context, Entity,
+    EventEmitter, Hsla, SharedString, Task, WeakEntity, Window,
 };
 use itertools::Itertools;
 use language::{
@@ -121,14 +121,14 @@ const MAX_SEARCH_RESULT_RANGES: usize = 10_000;
 
 pub trait ProjectItem {
     fn try_open(
-        project: &Model<Project>,
+        project: &Entity<Project>,
         path: &ProjectPath,
-        cx: &mut AppContext,
-    ) -> Option<Task<Result<Model<Self>>>>
+        cx: &mut App,
+    ) -> Option<Task<Result<Entity<Self>>>>
     where
         Self: Sized;
-    fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>;
-    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
+    fn entry_id(&self, cx: &App) -> Option<ProjectEntryId>;
+    fn project_path(&self, cx: &App) -> Option<ProjectPath>;
     fn is_dirty(&self) -> bool;
 }
 
@@ -150,20 +150,20 @@ pub struct Project {
     languages: Arc<LanguageRegistry>,
     client: Arc<client::Client>,
     join_project_response_message_id: u32,
-    task_store: Model<TaskStore>,
-    user_store: Model<UserStore>,
+    task_store: Entity<TaskStore>,
+    user_store: Entity<UserStore>,
     fs: Arc<dyn Fs>,
-    ssh_client: Option<Model<SshRemoteClient>>,
+    ssh_client: Option<Entity<SshRemoteClient>>,
     client_state: ProjectClientState,
-    git_state: Option<Model<GitState>>,
+    git_state: Option<Entity<GitState>>,
     collaborators: HashMap<proto::PeerId, Collaborator>,
     client_subscriptions: Vec<client::Subscription>,
-    worktree_store: Model<WorktreeStore>,
-    buffer_store: Model<BufferStore>,
-    image_store: Model<ImageStore>,
-    lsp_store: Model<LspStore>,
+    worktree_store: Entity<WorktreeStore>,
+    buffer_store: Entity<BufferStore>,
+    image_store: Entity<ImageStore>,
+    lsp_store: Entity<LspStore>,
     _subscriptions: Vec<gpui::Subscription>,
-    buffers_needing_diff: HashSet<WeakModel<Buffer>>,
+    buffers_needing_diff: HashSet<WeakEntity<Buffer>>,
     git_diff_debouncer: DebouncedDelay<Self>,
     remotely_created_models: Arc<Mutex<RemotelyCreatedModels>>,
     terminals: Terminals,
@@ -171,16 +171,16 @@ pub struct Project {
     search_history: SearchHistory,
     search_included_history: SearchHistory,
     search_excluded_history: SearchHistory,
-    snippets: Model<SnippetProvider>,
-    environment: Model<ProjectEnvironment>,
-    settings_observer: Model<SettingsObserver>,
-    toolchain_store: Option<Model<ToolchainStore>>,
+    snippets: Entity<SnippetProvider>,
+    environment: Entity<ProjectEnvironment>,
+    settings_observer: Entity<SettingsObserver>,
+    toolchain_store: Option<Entity<ToolchainStore>>,
 }
 
 #[derive(Default)]
 struct RemotelyCreatedModels {
-    worktrees: Vec<Model<Worktree>>,
-    buffers: Vec<Model<Buffer>>,
+    worktrees: Vec<Entity<Worktree>>,
+    buffers: Vec<Entity<Buffer>>,
     retain_count: usize,
 }
 
@@ -246,7 +246,7 @@ pub enum Event {
         notification_id: SharedString,
     },
     LanguageServerPrompt(LanguageServerPromptRequest),
-    LanguageNotFound(Model<Buffer>),
+    LanguageNotFound(Entity<Buffer>),
     ActiveEntryChanged(Option<ProjectEntryId>),
     ActivateProjectPanel,
     WorktreeAdded(WorktreeId),
@@ -374,7 +374,7 @@ pub struct Completion {
     /// Returns, whether new completions should be retriggered after the current one.
     /// If `true` is returned, the editor will show a new completion menu after this completion is confirmed.
     /// if no confirmation is provided or `false` is returned, the completion will be committed.
-    pub confirm: Option<Arc<dyn Send + Sync + Fn(CompletionIntent, &mut WindowContext) -> bool>>,
+    pub confirm: Option<Arc<dyn Send + Sync + Fn(CompletionIntent, &mut Window, &mut App) -> bool>>,
 }
 
 impl std::fmt::Debug for Completion {
@@ -518,19 +518,19 @@ enum EntitySubscription {
 
 #[derive(Clone)]
 pub enum DirectoryLister {
-    Project(Model<Project>),
+    Project(Entity<Project>),
     Local(Arc<dyn Fs>),
 }
 
 impl DirectoryLister {
-    pub fn is_local(&self, cx: &AppContext) -> bool {
+    pub fn is_local(&self, cx: &App) -> bool {
         match self {
             DirectoryLister::Local(_) => true,
             DirectoryLister::Project(project) => project.read(cx).is_local(),
         }
     }
 
-    pub fn resolve_tilde<'a>(&self, path: &'a String, cx: &AppContext) -> Cow<'a, str> {
+    pub fn resolve_tilde<'a>(&self, path: &'a String, cx: &App) -> Cow<'a, str> {
         if self.is_local(cx) {
             shellexpand::tilde(path)
         } else {
@@ -538,7 +538,7 @@ impl DirectoryLister {
         }
     }
 
-    pub fn default_query(&self, cx: &mut AppContext) -> String {
+    pub fn default_query(&self, cx: &mut App) -> String {
         if let DirectoryLister::Project(project) = self {
             if let Some(worktree) = project.read(cx).visible_worktrees(cx).next() {
                 return worktree.read(cx).abs_path().to_string_lossy().to_string();
@@ -547,7 +547,7 @@ impl DirectoryLister {
         "~/".to_string()
     }
 
-    pub fn list_directory(&self, path: String, cx: &mut AppContext) -> Task<Result<Vec<PathBuf>>> {
+    pub fn list_directory(&self, path: String, cx: &mut App) -> Task<Result<Vec<PathBuf>>> {
         match self {
             DirectoryLister::Project(project) => {
                 project.update(cx, |project, cx| project.list_directory(path, cx))
@@ -578,12 +578,12 @@ pub const DEFAULT_COMPLETION_CONTEXT: CompletionContext = CompletionContext {
 };
 
 impl Project {
-    pub fn init_settings(cx: &mut AppContext) {
+    pub fn init_settings(cx: &mut App) {
         WorktreeSettings::register(cx);
         ProjectSettings::register(cx);
     }
 
-    pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
+    pub fn init(client: &Arc<Client>, cx: &mut App) {
         connection_manager::init(client.clone(), cx);
         Self::init_settings(cx);
 
@@ -614,30 +614,30 @@ impl Project {
     pub fn local(
         client: Arc<Client>,
         node: NodeRuntime,
-        user_store: Model<UserStore>,
+        user_store: Entity<UserStore>,
         languages: Arc<LanguageRegistry>,
         fs: Arc<dyn Fs>,
         env: Option<HashMap<String, String>>,
-        cx: &mut AppContext,
-    ) -> Model<Self> {
-        cx.new_model(|cx: &mut ModelContext<Self>| {
+        cx: &mut App,
+    ) -> Entity<Self> {
+        cx.new(|cx: &mut Context<Self>| {
             let (tx, rx) = mpsc::unbounded();
             cx.spawn(move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
                 .detach();
             let snippets = SnippetProvider::new(fs.clone(), BTreeSet::from_iter([]), cx);
-            let worktree_store = cx.new_model(|_| WorktreeStore::local(false, fs.clone()));
+            let worktree_store = cx.new(|_| WorktreeStore::local(false, fs.clone()));
             cx.subscribe(&worktree_store, Self::on_worktree_store_event)
                 .detach();
 
-            let buffer_store = cx.new_model(|cx| BufferStore::local(worktree_store.clone(), cx));
+            let buffer_store = cx.new(|cx| BufferStore::local(worktree_store.clone(), cx));
             cx.subscribe(&buffer_store, Self::on_buffer_store_event)
                 .detach();
 
-            let image_store = cx.new_model(|cx| ImageStore::local(worktree_store.clone(), cx));
+            let image_store = cx.new(|cx| ImageStore::local(worktree_store.clone(), cx));
             cx.subscribe(&image_store, Self::on_image_store_event)
                 .detach();
 
-            let prettier_store = cx.new_model(|cx| {
+            let prettier_store = cx.new(|cx| {
                 PrettierStore::new(
                     node.clone(),
                     fs.clone(),
@@ -648,7 +648,7 @@ impl Project {
             });
 
             let environment = ProjectEnvironment::new(&worktree_store, env, cx);
-            let toolchain_store = cx.new_model(|cx| {
+            let toolchain_store = cx.new(|cx| {
                 ToolchainStore::local(
                     languages.clone(),
                     worktree_store.clone(),
@@ -656,7 +656,7 @@ impl Project {
                     cx,
                 )
             });
-            let task_store = cx.new_model(|cx| {
+            let task_store = cx.new(|cx| {
                 TaskStore::local(
                     fs.clone(),
                     buffer_store.downgrade(),
@@ -667,7 +667,7 @@ impl Project {
                 )
             });
 
-            let settings_observer = cx.new_model(|cx| {
+            let settings_observer = cx.new(|cx| {
                 SettingsObserver::new_local(
                     fs.clone(),
                     worktree_store.clone(),
@@ -678,7 +678,7 @@ impl Project {
             cx.subscribe(&settings_observer, Self::on_settings_observer_event)
                 .detach();
 
-            let lsp_store = cx.new_model(|cx| {
+            let lsp_store = cx.new(|cx| {
                 LspStore::new_local(
                     buffer_store.clone(),
                     worktree_store.clone(),
@@ -693,7 +693,7 @@ impl Project {
             });
 
             let git_state =
-                Some(cx.new_model(|cx| GitState::new(&worktree_store, languages.clone(), cx)));
+                Some(cx.new(|cx| GitState::new(&worktree_store, languages.clone(), cx)));
 
             cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach();
 
@@ -737,15 +737,15 @@ impl Project {
     }
 
     pub fn ssh(
-        ssh: Model<SshRemoteClient>,
+        ssh: Entity<SshRemoteClient>,
         client: Arc<Client>,
         node: NodeRuntime,
-        user_store: Model<UserStore>,
+        user_store: Entity<UserStore>,
         languages: Arc<LanguageRegistry>,
         fs: Arc<dyn Fs>,
-        cx: &mut AppContext,
-    ) -> Model<Self> {
-        cx.new_model(|cx: &mut ModelContext<Self>| {
+        cx: &mut App,
+    ) -> Entity<Self> {
+        cx.new(|cx: &mut Context<Self>| {
             let (tx, rx) = mpsc::unbounded();
             cx.spawn(move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
                 .detach();
@@ -755,11 +755,11 @@ impl Project {
 
             let ssh_proto = ssh.read(cx).proto_client();
             let worktree_store =
-                cx.new_model(|_| WorktreeStore::remote(false, ssh_proto.clone(), SSH_PROJECT_ID));
+                cx.new(|_| WorktreeStore::remote(false, ssh_proto.clone(), SSH_PROJECT_ID));
             cx.subscribe(&worktree_store, Self::on_worktree_store_event)
                 .detach();
 
-            let buffer_store = cx.new_model(|cx| {
+            let buffer_store = cx.new(|cx| {
                 BufferStore::remote(
                     worktree_store.clone(),
                     ssh.read(cx).proto_client(),
@@ -767,7 +767,7 @@ impl Project {
                     cx,
                 )
             });
-            let image_store = cx.new_model(|cx| {
+            let image_store = cx.new(|cx| {
                 ImageStore::remote(
                     worktree_store.clone(),
                     ssh.read(cx).proto_client(),
@@ -777,10 +777,9 @@ impl Project {
             });
             cx.subscribe(&buffer_store, Self::on_buffer_store_event)
                 .detach();
-            let toolchain_store = cx.new_model(|cx| {
-                ToolchainStore::remote(SSH_PROJECT_ID, ssh.read(cx).proto_client(), cx)
-            });
-            let task_store = cx.new_model(|cx| {
+            let toolchain_store = cx
+                .new(|cx| ToolchainStore::remote(SSH_PROJECT_ID, ssh.read(cx).proto_client(), cx));
+            let task_store = cx.new(|cx| {
                 TaskStore::remote(
                     fs.clone(),
                     buffer_store.downgrade(),
@@ -792,7 +791,7 @@ impl Project {
                 )
             });
 
-            let settings_observer = cx.new_model(|cx| {
+            let settings_observer = cx.new(|cx| {
                 SettingsObserver::new_remote(worktree_store.clone(), task_store.clone(), cx)
             });
             cx.subscribe(&settings_observer, Self::on_settings_observer_event)
@@ -800,7 +799,7 @@ impl Project {
 
             let environment = ProjectEnvironment::new(&worktree_store, None, cx);
 
-            let lsp_store = cx.new_model(|cx| {
+            let lsp_store = cx.new(|cx| {
                 LspStore::new_remote(
                     buffer_store.clone(),
                     worktree_store.clone(),
@@ -870,7 +869,7 @@ impl Project {
             };
 
             let ssh = ssh.read(cx);
-            ssh.subscribe_to_entity(SSH_PROJECT_ID, &cx.handle());
+            ssh.subscribe_to_entity(SSH_PROJECT_ID, &cx.model());
             ssh.subscribe_to_entity(SSH_PROJECT_ID, &this.buffer_store);
             ssh.subscribe_to_entity(SSH_PROJECT_ID, &this.worktree_store);
             ssh.subscribe_to_entity(SSH_PROJECT_ID, &this.lsp_store);
@@ -896,11 +895,11 @@ impl Project {
     pub async fn remote(
         remote_id: u64,
         client: Arc<Client>,
-        user_store: Model<UserStore>,
+        user_store: Entity<UserStore>,
         languages: Arc<LanguageRegistry>,
         fs: Arc<dyn Fs>,
         cx: AsyncAppContext,
-    ) -> Result<Model<Self>> {
+    ) -> Result<Entity<Self>> {
         let project =
             Self::in_room(remote_id, client, user_store, languages, fs, cx.clone()).await?;
         cx.update(|cx| {
@@ -914,11 +913,11 @@ impl Project {
     pub async fn in_room(
         remote_id: u64,
         client: Arc<Client>,
-        user_store: Model<UserStore>,
+        user_store: Entity<UserStore>,
         languages: Arc<LanguageRegistry>,
         fs: Arc<dyn Fs>,
         cx: AsyncAppContext,
-    ) -> Result<Model<Self>> {
+    ) -> Result<Entity<Self>> {
         client.authenticate_and_connect(true, &cx).await?;
 
         let subscriptions = [
@@ -956,25 +955,25 @@ impl Project {
         subscriptions: [EntitySubscription; 5],
         client: Arc<Client>,
         run_tasks: bool,
-        user_store: Model<UserStore>,
+        user_store: Entity<UserStore>,
         languages: Arc<LanguageRegistry>,
         fs: Arc<dyn Fs>,
         mut cx: AsyncAppContext,
-    ) -> Result<Model<Self>> {
+    ) -> Result<Entity<Self>> {
         let remote_id = response.payload.project_id;
         let role = response.payload.role();
 
-        let worktree_store = cx.new_model(|_| {
+        let worktree_store = cx.new(|_| {
             WorktreeStore::remote(true, client.clone().into(), response.payload.project_id)
         })?;
-        let buffer_store = cx.new_model(|cx| {
+        let buffer_store = cx.new(|cx| {
             BufferStore::remote(worktree_store.clone(), client.clone().into(), remote_id, cx)
         })?;
-        let image_store = cx.new_model(|cx| {
+        let image_store = cx.new(|cx| {
             ImageStore::remote(worktree_store.clone(), client.clone().into(), remote_id, cx)
         })?;
 
-        let lsp_store = cx.new_model(|cx| {
+        let lsp_store = cx.new(|cx| {
             let mut lsp_store = LspStore::new_remote(
                 buffer_store.clone(),
                 worktree_store.clone(),
@@ -989,7 +988,7 @@ impl Project {
             lsp_store
         })?;
 
-        let task_store = cx.new_model(|cx| {
+        let task_store = cx.new(|cx| {
             if run_tasks {
                 TaskStore::remote(
                     fs.clone(),
@@ -1005,11 +1004,11 @@ impl Project {
             }
         })?;
 
-        let settings_observer = cx.new_model(|cx| {
+        let settings_observer = cx.new(|cx| {
             SettingsObserver::new_remote(worktree_store.clone(), task_store.clone(), cx)
         })?;
 
-        let this = cx.new_model(|cx| {
+        let this = cx.new(|cx| {
             let replica_id = response.payload.replica_id as ReplicaId;
 
             let snippets = SnippetProvider::new(fs.clone(), BTreeSet::from_iter([]), cx);
@@ -1125,7 +1124,7 @@ impl Project {
         )
     }
 
-    fn release(&mut self, cx: &mut AppContext) {
+    fn release(&mut self, cx: &mut App) {
         if let Some(client) = self.ssh_client.take() {
             let shutdown = client
                 .read(cx)
@@ -1158,7 +1157,7 @@ impl Project {
     pub async fn example(
         root_paths: impl IntoIterator<Item = &Path>,
         cx: &mut AsyncAppContext,
-    ) -> Model<Project> {
+    ) -> Entity<Project> {
         use clock::FakeSystemClock;
 
         let fs = Arc::new(RealFs::default());
@@ -1168,9 +1167,7 @@ impl Project {
         let client = cx
             .update(|cx| client::Client::new(clock, http_client.clone(), cx))
             .unwrap();
-        let user_store = cx
-            .new_model(|cx| UserStore::new(client.clone(), cx))
-            .unwrap();
+        let user_store = cx.new(|cx| UserStore::new(client.clone(), cx)).unwrap();
         let project = cx
             .update(|cx| {
                 Project::local(
@@ -1204,15 +1201,14 @@ impl Project {
         fs: Arc<dyn Fs>,
         root_paths: impl IntoIterator<Item = &Path>,
         cx: &mut gpui::TestAppContext,
-    ) -> Model<Project> {
+    ) -> Entity<Project> {
         use clock::FakeSystemClock;
-        use gpui::Context;
 
         let languages = LanguageRegistry::test(cx.executor());
         let clock = Arc::new(FakeSystemClock::new());
         let http_client = http_client::FakeHttpClient::with_404_response();
         let client = cx.update(|cx| client::Client::new(clock, http_client.clone(), cx));
-        let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
+        let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
         let project = cx.update(|cx| {
             Project::local(
                 client,
@@ -1238,15 +1234,15 @@ impl Project {
         project
     }
 
-    pub fn lsp_store(&self) -> Model<LspStore> {
+    pub fn lsp_store(&self) -> Entity<LspStore> {
         self.lsp_store.clone()
     }
 
-    pub fn worktree_store(&self) -> Model<WorktreeStore> {
+    pub fn worktree_store(&self) -> Entity<WorktreeStore> {
         self.worktree_store.clone()
     }
 
-    pub fn buffer_for_id(&self, remote_id: BufferId, cx: &AppContext) -> Option<Model<Buffer>> {
+    pub fn buffer_for_id(&self, remote_id: BufferId, cx: &App) -> Option<Entity<Buffer>> {
         self.buffer_store.read(cx).get(remote_id)
     }
 
@@ -1258,11 +1254,11 @@ impl Project {
         self.client.clone()
     }
 
-    pub fn ssh_client(&self) -> Option<Model<SshRemoteClient>> {
+    pub fn ssh_client(&self) -> Option<Entity<SshRemoteClient>> {
         self.ssh_client.clone()
     }
 
-    pub fn user_store(&self) -> Model<UserStore> {
+    pub fn user_store(&self) -> Entity<UserStore> {
         self.user_store.clone()
     }
 
@@ -1270,37 +1266,33 @@ impl Project {
         self.node.as_ref()
     }
 
-    pub fn opened_buffers(&self, cx: &AppContext) -> Vec<Model<Buffer>> {
+    pub fn opened_buffers(&self, cx: &App) -> Vec<Entity<Buffer>> {
         self.buffer_store.read(cx).buffers().collect()
     }
 
-    pub fn environment(&self) -> &Model<ProjectEnvironment> {
+    pub fn environment(&self) -> &Entity<ProjectEnvironment> {
         &self.environment
     }
 
-    pub fn cli_environment(&self, cx: &AppContext) -> Option<HashMap<String, String>> {
+    pub fn cli_environment(&self, cx: &App) -> Option<HashMap<String, String>> {
         self.environment.read(cx).get_cli_environment()
     }
 
     pub fn shell_environment_errors<'a>(
         &'a self,
-        cx: &'a AppContext,
+        cx: &'a App,
     ) -> impl Iterator<Item = (&'a WorktreeId, &'a EnvironmentErrorMessage)> {
         self.environment.read(cx).environment_errors()
     }
 
-    pub fn remove_environment_error(
-        &mut self,
-        cx: &mut ModelContext<Self>,
-        worktree_id: WorktreeId,
-    ) {
+    pub fn remove_environment_error(&mut self, cx: &mut Context<Self>, worktree_id: WorktreeId) {
         self.environment.update(cx, |environment, _| {
             environment.remove_environment_error(worktree_id);
         });
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn has_open_buffer(&self, path: impl Into<ProjectPath>, cx: &AppContext) -> bool {
+    pub fn has_open_buffer(&self, path: impl Into<ProjectPath>, cx: &App) -> bool {
         self.buffer_store
             .read(cx)
             .get_by_path(&path.into(), cx)
@@ -1319,7 +1311,7 @@ impl Project {
         }
     }
 
-    pub fn supports_terminal(&self, _cx: &AppContext) -> bool {
+    pub fn supports_terminal(&self, _cx: &App) -> bool {
         if self.is_local() {
             return true;
         }
@@ -1330,7 +1322,7 @@ impl Project {
         return false;
     }
 
-    pub fn ssh_connection_string(&self, cx: &AppContext) -> Option<SharedString> {
+    pub fn ssh_connection_string(&self, cx: &App) -> Option<SharedString> {
         if let Some(ssh_state) = &self.ssh_client {
             return Some(ssh_state.read(cx).connection_string().into());
         }
@@ -1338,13 +1330,13 @@ impl Project {
         return None;
     }
 
-    pub fn ssh_connection_state(&self, cx: &AppContext) -> Option<remote::ConnectionState> {
+    pub fn ssh_connection_state(&self, cx: &App) -> Option<remote::ConnectionState> {
         self.ssh_client
             .as_ref()
             .map(|ssh| ssh.read(cx).connection_state())
     }
 
-    pub fn ssh_connection_options(&self, cx: &AppContext) -> Option<SshConnectionOptions> {
+    pub fn ssh_connection_options(&self, cx: &App) -> Option<SshConnectionOptions> {
         self.ssh_client
             .as_ref()
             .map(|ssh| ssh.read(cx).connection_options())
@@ -1363,11 +1355,11 @@ impl Project {
         }
     }
 
-    pub fn task_store(&self) -> &Model<TaskStore> {
+    pub fn task_store(&self) -> &Entity<TaskStore> {
         &self.task_store
     }
 
-    pub fn snippets(&self) -> &Model<SnippetProvider> {
+    pub fn snippets(&self) -> &Entity<SnippetProvider> {
         &self.snippets
     }
 
@@ -1395,7 +1387,7 @@ impl Project {
         self.collaborators.values().find(|c| c.is_host)
     }
 
-    pub fn set_worktrees_reordered(&mut self, worktrees_reordered: bool, cx: &mut AppContext) {
+    pub fn set_worktrees_reordered(&mut self, worktrees_reordered: bool, cx: &mut App) {
         self.worktree_store.update(cx, |store, _| {
             store.set_worktrees_reordered(worktrees_reordered);
         });
@@ -1404,49 +1396,45 @@ impl Project {
     /// Collect all worktrees, including ones that don't appear in the project panel
     pub fn worktrees<'a>(
         &self,
-        cx: &'a AppContext,
-    ) -> impl 'a + DoubleEndedIterator<Item = Model<Worktree>> {
+        cx: &'a App,
+    ) -> impl 'a + DoubleEndedIterator<Item = Entity<Worktree>> {
         self.worktree_store.read(cx).worktrees()
     }
 
     /// Collect all user-visible worktrees, the ones that appear in the project panel.
     pub fn visible_worktrees<'a>(
         &'a self,
-        cx: &'a AppContext,
-    ) -> impl 'a + DoubleEndedIterator<Item = Model<Worktree>> {
+        cx: &'a App,
+    ) -> impl 'a + DoubleEndedIterator<Item = Entity<Worktree>> {
         self.worktree_store.read(cx).visible_worktrees(cx)
     }
 
-    pub fn worktree_root_names<'a>(&'a self, cx: &'a AppContext) -> impl Iterator<Item = &'a str> {
+    pub fn worktree_root_names<'a>(&'a self, cx: &'a App) -> impl Iterator<Item = &'a str> {
         self.visible_worktrees(cx)
             .map(|tree| tree.read(cx).root_name())
     }
 
-    pub fn worktree_for_id(&self, id: WorktreeId, cx: &AppContext) -> Option<Model<Worktree>> {
+    pub fn worktree_for_id(&self, id: WorktreeId, cx: &App) -> Option<Entity<Worktree>> {
         self.worktree_store.read(cx).worktree_for_id(id, cx)
     }
 
     pub fn worktree_for_entry(
         &self,
         entry_id: ProjectEntryId,
-        cx: &AppContext,
-    ) -> Option<Model<Worktree>> {
+        cx: &App,
+    ) -> Option<Entity<Worktree>> {
         self.worktree_store
             .read(cx)
             .worktree_for_entry(entry_id, cx)
     }
 
-    pub fn worktree_id_for_entry(
-        &self,
-        entry_id: ProjectEntryId,
-        cx: &AppContext,
-    ) -> Option<WorktreeId> {
+    pub fn worktree_id_for_entry(&self, entry_id: ProjectEntryId, cx: &App) -> Option<WorktreeId> {
         self.worktree_for_entry(entry_id, cx)
             .map(|worktree| worktree.read(cx).id())
     }
 
     /// Checks if the entry is the root of a worktree.
-    pub fn entry_is_worktree_root(&self, entry_id: ProjectEntryId, cx: &AppContext) -> bool {
+    pub fn entry_is_worktree_root(&self, entry_id: ProjectEntryId, cx: &App) -> bool {
         self.worktree_for_entry(entry_id, cx)
             .map(|worktree| {
                 worktree
@@ -1460,13 +1448,13 @@ impl Project {
     pub fn project_path_git_status(
         &self,
         project_path: &ProjectPath,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<FileStatus> {
         self.worktree_for_id(project_path.worktree_id, cx)
             .and_then(|worktree| worktree.read(cx).status_for_file(&project_path.path))
     }
 
-    pub fn visibility_for_paths(&self, paths: &[PathBuf], cx: &AppContext) -> Option<bool> {
+    pub fn visibility_for_paths(&self, paths: &[PathBuf], cx: &App) -> Option<bool> {
         paths
             .iter()
             .map(|path| self.visibility_for_path(path, cx))
@@ -1474,7 +1462,7 @@ impl Project {
             .flatten()
     }
 
-    pub fn visibility_for_path(&self, path: &Path, cx: &AppContext) -> Option<bool> {
+    pub fn visibility_for_path(&self, path: &Path, cx: &App) -> Option<bool> {
         self.worktrees(cx)
             .filter_map(|worktree| {
                 let worktree = worktree.read(cx);
@@ -1490,7 +1478,7 @@ impl Project {
         &mut self,
         project_path: impl Into<ProjectPath>,
         is_directory: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<CreatedEntry>> {
         let project_path = project_path.into();
         let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) else {
@@ -1508,7 +1496,7 @@ impl Project {
         entry_id: ProjectEntryId,
         relative_worktree_source_path: Option<PathBuf>,
         new_path: impl Into<Arc<Path>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<Option<Entry>>> {
         let Some(worktree) = self.worktree_for_entry(entry_id, cx) else {
             return Task::ready(Ok(None));
@@ -1522,7 +1510,7 @@ impl Project {
         &mut self,
         entry_id: ProjectEntryId,
         new_path: impl Into<Arc<Path>>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<CreatedEntry>> {
         let worktree_store = self.worktree_store.read(cx);
         let new_path = new_path.into();
@@ -1570,7 +1558,7 @@ impl Project {
         &mut self,
         entry_id: ProjectEntryId,
         trash: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<Task<Result<()>>> {
         let worktree = self.worktree_for_entry(entry_id, cx)?;
         cx.emit(Event::DeletedEntry(worktree.read(cx).id(), entry_id));
@@ -1583,13 +1571,13 @@ impl Project {
         &mut self,
         worktree_id: WorktreeId,
         entry_id: ProjectEntryId,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<Task<Result<()>>> {
         let worktree = self.worktree_for_id(worktree_id, cx)?;
         worktree.update(cx, |worktree, cx| worktree.expand_entry(entry_id, cx))
     }
 
-    pub fn shared(&mut self, project_id: u64, cx: &mut ModelContext<Self>) -> Result<()> {
+    pub fn shared(&mut self, project_id: u64, cx: &mut Context<Self>) -> Result<()> {
         if !matches!(self.client_state, ProjectClientState::Local) {
             return Err(anyhow!("project was already shared"));
         }
@@ -1597,7 +1585,7 @@ impl Project {
         self.client_subscriptions.extend([
             self.client
                 .subscribe_to_entity(project_id)?
-                .set_model(&cx.handle(), &mut cx.to_async()),
+                .set_model(&cx.model(), &mut cx.to_async()),
             self.client
                 .subscribe_to_entity(project_id)?
                 .set_model(&self.worktree_store, &mut cx.to_async()),
@@ -1640,7 +1628,7 @@ impl Project {
     pub fn reshared(
         &mut self,
         message: proto::ResharedProject,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Result<()> {
         self.buffer_store
             .update(cx, |buffer_store, _| buffer_store.forget_shared_buffers());
@@ -1658,7 +1646,7 @@ impl Project {
         &mut self,
         message: proto::RejoinedProject,
         message_id: u32,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Result<()> {
         cx.update_global::<SettingsStore, _>(|store, cx| {
             self.worktree_store.update(cx, |worktree_store, cx| {
@@ -1683,13 +1671,13 @@ impl Project {
         Ok(())
     }
 
-    pub fn unshare(&mut self, cx: &mut ModelContext<Self>) -> Result<()> {
+    pub fn unshare(&mut self, cx: &mut Context<Self>) -> Result<()> {
         self.unshare_internal(cx)?;
         cx.notify();
         Ok(())
     }
 
-    fn unshare_internal(&mut self, cx: &mut AppContext) -> Result<()> {
+    fn unshare_internal(&mut self, cx: &mut App) -> Result<()> {
         if self.is_via_collab() {
             return Err(anyhow!("attempted to unshare a remote project"));
         }
@@ -1723,7 +1711,7 @@ impl Project {
         }
     }
 
-    pub fn disconnected_from_host(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn disconnected_from_host(&mut self, cx: &mut Context<Self>) {
         if self.is_disconnected(cx) {
             return;
         }
@@ -1732,7 +1720,7 @@ impl Project {
         cx.notify();
     }
 
-    pub fn set_role(&mut self, role: proto::ChannelRole, cx: &mut ModelContext<Self>) {
+    pub fn set_role(&mut self, role: proto::ChannelRole, cx: &mut Context<Self>) {
         let new_capability =
             if role == proto::ChannelRole::Member || role == proto::ChannelRole::Admin {
                 Capability::ReadWrite
@@ -1751,7 +1739,7 @@ impl Project {
         }
     }
 
-    fn disconnected_from_host_internal(&mut self, cx: &mut AppContext) {
+    fn disconnected_from_host_internal(&mut self, cx: &mut App) {
         if let ProjectClientState::Remote {
             sharing_has_stopped,
             ..
@@ -1770,11 +1758,11 @@ impl Project {
         }
     }
 
-    pub fn close(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn close(&mut self, cx: &mut Context<Self>) {
         cx.emit(Event::Closed);
     }
 
-    pub fn is_disconnected(&self, cx: &AppContext) -> bool {
+    pub fn is_disconnected(&self, cx: &App) -> bool {
         match &self.client_state {
             ProjectClientState::Remote {
                 sharing_has_stopped,
@@ -1785,7 +1773,7 @@ impl Project {
         }
     }
 
-    fn ssh_is_disconnected(&self, cx: &AppContext) -> bool {
+    fn ssh_is_disconnected(&self, cx: &App) -> bool {
         self.ssh_client
             .as_ref()
             .map(|ssh| ssh.read(cx).is_disconnected())
@@ -1799,7 +1787,7 @@ impl Project {
         }
     }
 
-    pub fn is_read_only(&self, cx: &AppContext) -> bool {
+    pub fn is_read_only(&self, cx: &App) -> bool {
         self.is_disconnected(cx) || self.capability() == Capability::ReadOnly
     }
 
@@ -1828,7 +1816,7 @@ impl Project {
         }
     }
 
-    pub fn create_buffer(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<Model<Buffer>>> {
+    pub fn create_buffer(&mut self, cx: &mut Context<Self>) -> Task<Result<Entity<Buffer>>> {
         self.buffer_store
             .update(cx, |buffer_store, cx| buffer_store.create_buffer(cx))
     }
@@ -1837,8 +1825,8 @@ impl Project {
         &mut self,
         text: &str,
         language: Option<Arc<Language>>,
-        cx: &mut ModelContext<Self>,
-    ) -> Model<Buffer> {
+        cx: &mut Context<Self>,
+    ) -> Entity<Buffer> {
         if self.is_via_collab() || self.is_via_ssh() {
             panic!("called create_local_buffer on a remote project")
         }
@@ -1850,8 +1838,8 @@ impl Project {
     pub fn open_path(
         &mut self,
         path: ProjectPath,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<(Option<ProjectEntryId>, AnyModel)>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<(Option<ProjectEntryId>, AnyEntity)>> {
         let task = self.open_buffer(path.clone(), cx);
         cx.spawn(move |_, cx| async move {
             let buffer = task.await?;
@@ -1859,7 +1847,7 @@ impl Project {
                 File::from_dyn(buffer.file()).and_then(|file| file.project_entry_id(cx))
             })?;
 
-            let buffer: &AnyModel = &buffer;
+            let buffer: &AnyEntity = &buffer;
             Ok((project_entry_id, buffer.clone()))
         })
     }
@@ -1867,8 +1855,8 @@ impl Project {
     pub fn open_local_buffer(
         &mut self,
         abs_path: impl AsRef<Path>,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<Buffer>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<Buffer>>> {
         if let Some((worktree, relative_path)) = self.find_worktree(abs_path.as_ref(), cx) {
             self.open_buffer((worktree.read(cx).id(), relative_path), cx)
         } else {
@@ -1880,8 +1868,8 @@ impl Project {
     pub fn open_local_buffer_with_lsp(
         &mut self,
         abs_path: impl AsRef<Path>,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<(Model<Buffer>, lsp_store::OpenLspBufferHandle)>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<(Entity<Buffer>, lsp_store::OpenLspBufferHandle)>> {
         if let Some((worktree, relative_path)) = self.find_worktree(abs_path.as_ref(), cx) {
             self.open_buffer_with_lsp((worktree.read(cx).id(), relative_path), cx)
         } else {
@@ -1892,8 +1880,8 @@ impl Project {
     pub fn open_buffer(
         &mut self,
         path: impl Into<ProjectPath>,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<Buffer>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<Buffer>>> {
         if self.is_disconnected(cx) {
             return Task::ready(Err(anyhow!(ErrorCode::Disconnected)));
         }
@@ -1907,8 +1895,8 @@ impl Project {
     pub fn open_buffer_with_lsp(
         &mut self,
         path: impl Into<ProjectPath>,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<(Model<Buffer>, lsp_store::OpenLspBufferHandle)>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<(Entity<Buffer>, lsp_store::OpenLspBufferHandle)>> {
         let buffer = self.open_buffer(path, cx);
         let lsp_store = self.lsp_store().clone();
         cx.spawn(|_, mut cx| async move {
@@ -1922,9 +1910,9 @@ impl Project {
 
     pub fn open_unstaged_changes(
         &mut self,
-        buffer: Model<Buffer>,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<BufferChangeSet>>> {
+        buffer: Entity<Buffer>,
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<BufferChangeSet>>> {
         if self.is_disconnected(cx) {
             return Task::ready(Err(anyhow!(ErrorCode::Disconnected)));
         }
@@ -1937,8 +1925,8 @@ impl Project {
     pub fn open_buffer_by_id(
         &mut self,
         id: BufferId,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<Buffer>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<Buffer>>> {
         if let Some(buffer) = self.buffer_for_id(id, cx) {
             Task::ready(Ok(buffer))
         } else if self.is_local() || self.is_via_ssh() {
@@ -1962,8 +1950,8 @@ impl Project {
 
     pub fn save_buffers(
         &self,
-        buffers: HashSet<Model<Buffer>>,
-        cx: &mut ModelContext<Self>,
+        buffers: HashSet<Entity<Buffer>>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         cx.spawn(move |this, mut cx| async move {
             let save_tasks = buffers.into_iter().filter_map(|buffer| {
@@ -1975,35 +1963,27 @@ impl Project {
         })
     }
 
-    pub fn save_buffer(
-        &self,
-        buffer: Model<Buffer>,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<()>> {
+    pub fn save_buffer(&self, buffer: Entity<Buffer>, cx: &mut Context<Self>) -> Task<Result<()>> {
         self.buffer_store
             .update(cx, |buffer_store, cx| buffer_store.save_buffer(buffer, cx))
     }
 
     pub fn save_buffer_as(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         path: ProjectPath,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         self.buffer_store.update(cx, |buffer_store, cx| {
             buffer_store.save_buffer_as(buffer.clone(), path, cx)
         })
     }
 
-    pub fn get_open_buffer(&self, path: &ProjectPath, cx: &AppContext) -> Option<Model<Buffer>> {
+    pub fn get_open_buffer(&self, path: &ProjectPath, cx: &App) -> Option<Entity<Buffer>> {
         self.buffer_store.read(cx).get_by_path(path, cx)
     }
 
-    fn register_buffer(
-        &mut self,
-        buffer: &Model<Buffer>,
-        cx: &mut ModelContext<Self>,
-    ) -> Result<()> {
+    fn register_buffer(&mut self, buffer: &Entity<Buffer>, cx: &mut Context<Self>) -> Result<()> {
         {
             let mut remotely_created_models = self.remotely_created_models.lock();
             if remotely_created_models.retain_count > 0 {
@@ -2024,8 +2004,8 @@ impl Project {
     pub fn open_image(
         &mut self,
         path: impl Into<ProjectPath>,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<ImageItem>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<ImageItem>>> {
         if self.is_disconnected(cx) {
             return Task::ready(Err(anyhow!(ErrorCode::Disconnected)));
         }

crates/project/src/project_settings.rs 🔗

@@ -1,7 +1,7 @@
-use anyhow::Context;
+use anyhow::Context as _;
 use collections::HashMap;
 use fs::Fs;
-use gpui::{AppContext, AsyncAppContext, BorrowAppContext, EventEmitter, Model, ModelContext};
+use gpui::{App, AsyncAppContext, BorrowAppContext, Context, Entity, EventEmitter};
 use lsp::LanguageServerName;
 use paths::{
     local_settings_file_relative_path, local_tasks_file_relative_path,
@@ -208,10 +208,7 @@ impl Settings for ProjectSettings {
 
     type FileContent = Self;
 
-    fn load(
-        sources: SettingsSources<Self::FileContent>,
-        _: &mut AppContext,
-    ) -> anyhow::Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> anyhow::Result<Self> {
         sources.json_merge()
     }
 }
@@ -231,9 +228,9 @@ impl EventEmitter<SettingsObserverEvent> for SettingsObserver {}
 pub struct SettingsObserver {
     mode: SettingsObserverMode,
     downstream_client: Option<AnyProtoClient>,
-    worktree_store: Model<WorktreeStore>,
+    worktree_store: Entity<WorktreeStore>,
     project_id: u64,
-    task_store: Model<TaskStore>,
+    task_store: Entity<TaskStore>,
 }
 
 /// SettingsObserver observers changes to .zed/{settings, task}.json files in local worktrees
@@ -248,9 +245,9 @@ impl SettingsObserver {
 
     pub fn new_local(
         fs: Arc<dyn Fs>,
-        worktree_store: Model<WorktreeStore>,
-        task_store: Model<TaskStore>,
-        cx: &mut ModelContext<Self>,
+        worktree_store: Entity<WorktreeStore>,
+        task_store: Entity<TaskStore>,
+        cx: &mut Context<Self>,
     ) -> Self {
         cx.subscribe(&worktree_store, Self::on_worktree_store_event)
             .detach();
@@ -265,9 +262,9 @@ impl SettingsObserver {
     }
 
     pub fn new_remote(
-        worktree_store: Model<WorktreeStore>,
-        task_store: Model<TaskStore>,
-        _: &mut ModelContext<Self>,
+        worktree_store: Entity<WorktreeStore>,
+        task_store: Entity<TaskStore>,
+        _: &mut Context<Self>,
     ) -> Self {
         Self {
             worktree_store,
@@ -282,7 +279,7 @@ impl SettingsObserver {
         &mut self,
         project_id: u64,
         downstream_client: AnyProtoClient,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.project_id = project_id;
         self.downstream_client = Some(downstream_client.clone());
@@ -319,12 +316,12 @@ impl SettingsObserver {
         }
     }
 
-    pub fn unshared(&mut self, _: &mut ModelContext<Self>) {
+    pub fn unshared(&mut self, _: &mut Context<Self>) {
         self.downstream_client = None;
     }
 
     async fn handle_update_worktree_settings(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::UpdateWorktreeSettings>,
         mut cx: AsyncAppContext,
     ) -> anyhow::Result<()> {
@@ -358,9 +355,9 @@ impl SettingsObserver {
 
     fn on_worktree_store_event(
         &mut self,
-        _: Model<WorktreeStore>,
+        _: Entity<WorktreeStore>,
         event: &WorktreeStoreEvent,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if let WorktreeStoreEvent::WorktreeAdded(worktree) = event {
             cx.subscribe(worktree, |this, worktree, event, cx| {
@@ -374,9 +371,9 @@ impl SettingsObserver {
 
     fn update_local_worktree_settings(
         &mut self,
-        worktree: &Model<Worktree>,
+        worktree: &Entity<Worktree>,
         changes: &UpdatedEntriesSet,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let SettingsObserverMode::Local(fs) = &self.mode else {
             return;
@@ -496,9 +493,9 @@ impl SettingsObserver {
 
     fn update_settings(
         &mut self,
-        worktree: Model<Worktree>,
+        worktree: Entity<Worktree>,
         settings_contents: impl IntoIterator<Item = (Arc<Path>, LocalSettingsKind, Option<String>)>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let worktree_id = worktree.read(cx).id();
         let remote_worktree_id = worktree.read(cx).id();

crates/project/src/project_tests.rs 🔗

@@ -2,7 +2,7 @@ use crate::{Event, *};
 use ::git::diff::assert_hunks;
 use fs::FakeFs;
 use futures::{future, StreamExt};
-use gpui::{AppContext, SemanticVersion, UpdateGlobal};
+use gpui::{App, SemanticVersion, UpdateGlobal};
 use http_client::Url;
 use language::{
     language_settings::{language_settings, AllLanguageSettings, LanguageSettingsContent},
@@ -2643,10 +2643,7 @@ async fn test_definition(cx: &mut gpui::TestAppContext) {
         assert_eq!(list_worktrees(&project, cx), [("/dir/b.rs".as_ref(), true)]);
     });
 
-    fn list_worktrees<'a>(
-        project: &'a Model<Project>,
-        cx: &'a AppContext,
-    ) -> Vec<(&'a Path, bool)> {
+    fn list_worktrees<'a>(project: &'a Entity<Project>, cx: &'a App) -> Vec<(&'a Path, bool)> {
         project
             .read(cx)
             .worktrees(cx)
@@ -5688,7 +5685,7 @@ async fn test_unstaged_changes_for_buffer(cx: &mut gpui::TestAppContext) {
 }
 
 async fn search(
-    project: &Model<Project>,
+    project: &Entity<Project>,
     query: SearchQuery,
     cx: &mut gpui::TestAppContext,
 ) -> Result<HashMap<String, Vec<Range<usize>>>> {
@@ -5807,10 +5804,10 @@ fn tsx_lang() -> Arc<Language> {
 }
 
 fn get_all_tasks(
-    project: &Model<Project>,
+    project: &Entity<Project>,
     worktree_id: Option<WorktreeId>,
     task_context: &TaskContext,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) -> Vec<(TaskSourceKind, ResolvedTask)> {
     let (mut old, new) = project.update(cx, |project, cx| {
         project

crates/project/src/search.rs 🔗

@@ -2,7 +2,7 @@ use aho_corasick::{AhoCorasick, AhoCorasickBuilder};
 use anyhow::Result;
 use client::proto;
 use fancy_regex::{Captures, Regex, RegexBuilder};
-use gpui::Model;
+use gpui::Entity;
 use language::{Buffer, BufferSnapshot, CharKind};
 use smol::future::yield_now;
 use std::{
@@ -17,7 +17,7 @@ use util::paths::PathMatcher;
 
 pub enum SearchResult {
     Buffer {
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         ranges: Vec<Range<Anchor>>,
     },
     LimitReached,
@@ -35,7 +35,7 @@ pub struct SearchInputs {
     query: Arc<str>,
     files_to_include: PathMatcher,
     files_to_exclude: PathMatcher,
-    buffers: Option<Vec<Model<Buffer>>>,
+    buffers: Option<Vec<Entity<Buffer>>>,
 }
 
 impl SearchInputs {
@@ -48,7 +48,7 @@ impl SearchInputs {
     pub fn files_to_exclude(&self) -> &PathMatcher {
         &self.files_to_exclude
     }
-    pub fn buffers(&self) -> &Option<Vec<Model<Buffer>>> {
+    pub fn buffers(&self) -> &Option<Vec<Entity<Buffer>>> {
         &self.buffers
     }
 }
@@ -88,7 +88,7 @@ impl SearchQuery {
         include_ignored: bool,
         files_to_include: PathMatcher,
         files_to_exclude: PathMatcher,
-        buffers: Option<Vec<Model<Buffer>>>,
+        buffers: Option<Vec<Entity<Buffer>>>,
     ) -> Result<Self> {
         let query = query.to_string();
         let search = AhoCorasickBuilder::new()
@@ -117,7 +117,7 @@ impl SearchQuery {
         include_ignored: bool,
         files_to_include: PathMatcher,
         files_to_exclude: PathMatcher,
-        buffers: Option<Vec<Model<Buffer>>>,
+        buffers: Option<Vec<Entity<Buffer>>>,
     ) -> Result<Self> {
         let mut query = query.to_string();
         let initial_query = Arc::from(query.as_str());
@@ -426,7 +426,7 @@ impl SearchQuery {
         self.as_inner().files_to_exclude()
     }
 
-    pub fn buffers(&self) -> Option<&Vec<Model<Buffer>>> {
+    pub fn buffers(&self) -> Option<&Vec<Entity<Buffer>>> {
         self.as_inner().buffers.as_ref()
     }
 

crates/project/src/task_inventory.rs 🔗

@@ -8,9 +8,9 @@ use std::{
     sync::Arc,
 };
 
-use anyhow::{Context, Result};
+use anyhow::{Context as _, Result};
 use collections::{HashMap, HashSet, VecDeque};
-use gpui::{AppContext, Context as _, Model, Task};
+use gpui::{App, AppContext as _, Entity, Task};
 use itertools::Itertools;
 use language::{ContextProvider, File, Language, LanguageToolchainStore, Location};
 use settings::{parse_json_with_comments, SettingsLocation};
@@ -76,8 +76,8 @@ impl TaskSourceKind {
 }
 
 impl Inventory {
-    pub fn new(cx: &mut AppContext) -> Model<Self> {
-        cx.new_model(|_| Self::default())
+    pub fn new(cx: &mut App) -> Entity<Self> {
+        cx.new(|_| Self::default())
     }
 
     /// Pulls its task sources relevant to the worktree and the language given,
@@ -88,7 +88,7 @@ impl Inventory {
         file: Option<Arc<dyn File>>,
         language: Option<Arc<Language>>,
         worktree: Option<WorktreeId>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Vec<(TaskSourceKind, TaskTemplate)> {
         let task_source_kind = language.as_ref().map(|language| TaskSourceKind::Language {
             name: language.name().0,
@@ -115,7 +115,7 @@ impl Inventory {
         worktree: Option<WorktreeId>,
         location: Option<Location>,
         task_context: &TaskContext,
-        cx: &AppContext,
+        cx: &App,
     ) -> (
         Vec<(TaskSourceKind, ResolvedTask)>,
         Vec<(TaskSourceKind, ResolvedTask)>,
@@ -371,7 +371,7 @@ fn task_variables_preference(task: &ResolvedTask) -> Reverse<usize> {
 
 #[cfg(test)]
 mod test_inventory {
-    use gpui::{Model, TestAppContext};
+    use gpui::{Entity, TestAppContext};
     use itertools::Itertools;
     use task::TaskContext;
     use worktree::WorktreeId;
@@ -381,7 +381,7 @@ mod test_inventory {
     use super::TaskSourceKind;
 
     pub(super) fn task_template_names(
-        inventory: &Model<Inventory>,
+        inventory: &Entity<Inventory>,
         worktree: Option<WorktreeId>,
         cx: &mut TestAppContext,
     ) -> Vec<String> {
@@ -396,7 +396,7 @@ mod test_inventory {
     }
 
     pub(super) fn register_task_used(
-        inventory: &Model<Inventory>,
+        inventory: &Entity<Inventory>,
         task_name: &str,
         cx: &mut TestAppContext,
     ) {
@@ -416,7 +416,7 @@ mod test_inventory {
     }
 
     pub(super) async fn list_tasks(
-        inventory: &Model<Inventory>,
+        inventory: &Entity<Inventory>,
         worktree: Option<WorktreeId>,
         cx: &mut TestAppContext,
     ) -> Vec<(TaskSourceKind, String)> {
@@ -438,11 +438,11 @@ mod test_inventory {
 /// A context provided that tries to provide values for all non-custom [`VariableName`] variants for a currently opened file.
 /// Applied as a base for every custom [`ContextProvider`] unless explicitly oped out.
 pub struct BasicContextProvider {
-    worktree_store: Model<WorktreeStore>,
+    worktree_store: Entity<WorktreeStore>,
 }
 
 impl BasicContextProvider {
-    pub fn new(worktree_store: Model<WorktreeStore>) -> Self {
+    pub fn new(worktree_store: Entity<WorktreeStore>) -> Self {
         Self { worktree_store }
     }
 }
@@ -453,7 +453,7 @@ impl ContextProvider for BasicContextProvider {
         location: &Location,
         _: Option<HashMap<String, String>>,
         _: Arc<dyn LanguageToolchainStore>,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Task<Result<TaskVariables>> {
         let buffer = location.buffer.read(cx);
         let buffer_snapshot = buffer.snapshot();
@@ -553,7 +553,7 @@ impl ContextProvider for ContextProviderWithTasks {
     fn associated_tasks(
         &self,
         _: Option<Arc<dyn language::File>>,
-        _: &AppContext,
+        _: &App,
     ) -> Option<TaskTemplates> {
         Some(self.templates.clone())
     }
@@ -859,7 +859,7 @@ mod tests {
     }
 
     async fn resolved_task_names(
-        inventory: &Model<Inventory>,
+        inventory: &Entity<Inventory>,
         worktree: Option<WorktreeId>,
         cx: &mut TestAppContext,
     ) -> Vec<String> {
@@ -888,7 +888,7 @@ mod tests {
     }
 
     async fn list_tasks_sorted_by_last_used(
-        inventory: &Model<Inventory>,
+        inventory: &Entity<Inventory>,
         worktree: Option<WorktreeId>,
         cx: &mut TestAppContext,
     ) -> Vec<(TaskSourceKind, String)> {

crates/project/src/task_store.rs 🔗

@@ -4,7 +4,7 @@ use anyhow::Context as _;
 use collections::HashMap;
 use fs::Fs;
 use futures::StreamExt as _;
-use gpui::{AppContext, AsyncAppContext, EventEmitter, Model, ModelContext, Task, WeakModel};
+use gpui::{App, AsyncAppContext, Context, Entity, EventEmitter, Task, WeakEntity};
 use language::{
     proto::{deserialize_anchor, serialize_anchor},
     ContextProvider as _, LanguageToolchainStore, Location,
@@ -28,9 +28,9 @@ pub enum TaskStore {
 
 pub struct StoreState {
     mode: StoreMode,
-    task_inventory: Model<Inventory>,
-    buffer_store: WeakModel<BufferStore>,
-    worktree_store: Model<WorktreeStore>,
+    task_inventory: Entity<Inventory>,
+    buffer_store: WeakEntity<BufferStore>,
+    worktree_store: Entity<WorktreeStore>,
     toolchain_store: Arc<dyn LanguageToolchainStore>,
     _global_task_config_watcher: Task<()>,
 }
@@ -38,7 +38,7 @@ pub struct StoreState {
 enum StoreMode {
     Local {
         downstream_client: Option<(AnyProtoClient, u64)>,
-        environment: Model<ProjectEnvironment>,
+        environment: Entity<ProjectEnvironment>,
     },
     Remote {
         upstream_client: AnyProtoClient,
@@ -56,7 +56,7 @@ impl TaskStore {
     }
 
     async fn handle_task_context_for_location(
-        store: Model<Self>,
+        store: Entity<Self>,
         envelope: TypedEnvelope<proto::TaskContextForLocation>,
         mut cx: AsyncAppContext,
     ) -> anyhow::Result<proto::TaskContext> {
@@ -153,11 +153,11 @@ impl TaskStore {
 
     pub fn local(
         fs: Arc<dyn Fs>,
-        buffer_store: WeakModel<BufferStore>,
-        worktree_store: Model<WorktreeStore>,
+        buffer_store: WeakEntity<BufferStore>,
+        worktree_store: Entity<WorktreeStore>,
         toolchain_store: Arc<dyn LanguageToolchainStore>,
-        environment: Model<ProjectEnvironment>,
-        cx: &mut ModelContext<'_, Self>,
+        environment: Entity<ProjectEnvironment>,
+        cx: &mut Context<'_, Self>,
     ) -> Self {
         Self::Functional(StoreState {
             mode: StoreMode::Local {
@@ -174,12 +174,12 @@ impl TaskStore {
 
     pub fn remote(
         fs: Arc<dyn Fs>,
-        buffer_store: WeakModel<BufferStore>,
-        worktree_store: Model<WorktreeStore>,
+        buffer_store: WeakEntity<BufferStore>,
+        worktree_store: Entity<WorktreeStore>,
         toolchain_store: Arc<dyn LanguageToolchainStore>,
         upstream_client: AnyProtoClient,
         project_id: u64,
-        cx: &mut ModelContext<'_, Self>,
+        cx: &mut Context<'_, Self>,
     ) -> Self {
         Self::Functional(StoreState {
             mode: StoreMode::Remote {
@@ -198,7 +198,7 @@ impl TaskStore {
         &self,
         captured_variables: TaskVariables,
         location: Location,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Task<Option<TaskContext>> {
         match self {
             TaskStore::Functional(state) => match &state.mode {
@@ -227,19 +227,14 @@ impl TaskStore {
         }
     }
 
-    pub fn task_inventory(&self) -> Option<&Model<Inventory>> {
+    pub fn task_inventory(&self) -> Option<&Entity<Inventory>> {
         match self {
             TaskStore::Functional(state) => Some(&state.task_inventory),
             TaskStore::Noop => None,
         }
     }
 
-    pub fn shared(
-        &mut self,
-        remote_id: u64,
-        new_downstream_client: AnyProtoClient,
-        _cx: &mut AppContext,
-    ) {
+    pub fn shared(&mut self, remote_id: u64, new_downstream_client: AnyProtoClient, _cx: &mut App) {
         if let Self::Functional(StoreState {
             mode: StoreMode::Local {
                 downstream_client, ..
@@ -251,7 +246,7 @@ impl TaskStore {
         }
     }
 
-    pub fn unshared(&mut self, _: &mut ModelContext<Self>) {
+    pub fn unshared(&mut self, _: &mut Context<Self>) {
         if let Self::Functional(StoreState {
             mode: StoreMode::Local {
                 downstream_client, ..
@@ -267,7 +262,7 @@ impl TaskStore {
         &self,
         location: Option<SettingsLocation<'_>>,
         raw_tasks_json: Option<&str>,
-        cx: &mut ModelContext<'_, Self>,
+        cx: &mut Context<'_, Self>,
     ) -> anyhow::Result<()> {
         let task_inventory = match self {
             TaskStore::Functional(state) => &state.task_inventory,
@@ -284,7 +279,7 @@ impl TaskStore {
 
     fn subscribe_to_global_task_file_changes(
         fs: Arc<dyn Fs>,
-        cx: &mut ModelContext<'_, Self>,
+        cx: &mut Context<'_, Self>,
     ) -> Task<()> {
         let mut user_tasks_file_rx =
             watch_config_file(&cx.background_executor(), fs, paths::tasks_file().clone());
@@ -309,7 +304,7 @@ impl TaskStore {
                             message: format!("Invalid global tasks file\n{err}"),
                         });
                     }
-                    cx.refresh();
+                    cx.refresh_windows();
                 }) else {
                     break; // App dropped
                 };
@@ -319,12 +314,12 @@ impl TaskStore {
 }
 
 fn local_task_context_for_location(
-    worktree_store: Model<WorktreeStore>,
+    worktree_store: Entity<WorktreeStore>,
     toolchain_store: Arc<dyn LanguageToolchainStore>,
-    environment: Model<ProjectEnvironment>,
+    environment: Entity<ProjectEnvironment>,
     captured_variables: TaskVariables,
     location: Location,
-    cx: &AppContext,
+    cx: &App,
 ) -> Task<Option<TaskContext>> {
     let worktree_id = location.buffer.read(cx).file().map(|f| f.worktree_id(cx));
     let worktree_abs_path = worktree_id
@@ -368,11 +363,11 @@ fn local_task_context_for_location(
 fn remote_task_context_for_location(
     project_id: u64,
     upstream_client: AnyProtoClient,
-    worktree_store: Model<WorktreeStore>,
+    worktree_store: Entity<WorktreeStore>,
     captured_variables: TaskVariables,
     location: Location,
     toolchain_store: Arc<dyn LanguageToolchainStore>,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) -> Task<Option<TaskContext>> {
     cx.spawn(|cx| async move {
         // We need to gather a client context, as the headless one may lack certain information (e.g. tree-sitter parsing is disabled there, so symbols are not available).
@@ -434,7 +429,7 @@ fn combine_task_variables(
     project_env: Option<HashMap<String, String>>,
     baseline: BasicContextProvider,
     toolchain_store: Arc<dyn LanguageToolchainStore>,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) -> Task<anyhow::Result<TaskVariables>> {
     let language_context_provider = location
         .buffer

crates/project/src/terminals.rs 🔗

@@ -1,7 +1,7 @@
 use crate::Project;
 use anyhow::{Context as _, Result};
 use collections::HashMap;
-use gpui::{AnyWindowHandle, AppContext, Context, Entity, Model, ModelContext, Task, WeakModel};
+use gpui::{AnyWindowHandle, App, AppContext as _, Context, Entity, Task, WeakEntity};
 use itertools::Itertools;
 use language::LanguageName;
 use settings::{Settings, SettingsLocation};
@@ -24,7 +24,7 @@ use util::ResultExt;
 // use std::os::unix::ffi::OsStrExt;
 
 pub struct Terminals {
-    pub(crate) local_handles: Vec<WeakModel<terminal::Terminal>>,
+    pub(crate) local_handles: Vec<WeakEntity<terminal::Terminal>>,
 }
 
 /// Terminals are opened either for the users shell, or to run a task.
@@ -44,7 +44,7 @@ pub struct SshCommand {
 }
 
 impl Project {
-    pub fn active_project_directory(&self, cx: &AppContext) -> Option<Arc<Path>> {
+    pub fn active_project_directory(&self, cx: &App) -> Option<Arc<Path>> {
         let worktree = self
             .active_entry()
             .and_then(|entry_id| self.worktree_for_entry(entry_id, cx))
@@ -54,7 +54,7 @@ impl Project {
         worktree
     }
 
-    pub fn first_project_directory(&self, cx: &AppContext) -> Option<PathBuf> {
+    pub fn first_project_directory(&self, cx: &App) -> Option<PathBuf> {
         let worktree = self.worktrees(cx).next()?;
         let worktree = worktree.read(cx);
         if worktree.root_entry()?.is_dir() {
@@ -64,7 +64,7 @@ impl Project {
         }
     }
 
-    pub fn ssh_details(&self, cx: &AppContext) -> Option<(String, SshCommand)> {
+    pub fn ssh_details(&self, cx: &App) -> Option<(String, SshCommand)> {
         if let Some(ssh_client) = &self.ssh_client {
             let ssh_client = ssh_client.read(cx);
             if let Some(args) = ssh_client.ssh_args() {
@@ -82,8 +82,8 @@ impl Project {
         &mut self,
         kind: TerminalKind,
         window: AnyWindowHandle,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<Terminal>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<Terminal>>> {
         let path: Option<Arc<Path>> = match &kind {
             TerminalKind::Shell(path) => path.as_ref().map(|path| Arc::from(path.as_ref())),
             TerminalKind::Task(spawn_task) => {
@@ -125,7 +125,7 @@ impl Project {
     pub fn terminal_settings<'a>(
         &'a self,
         path: &'a Option<PathBuf>,
-        cx: &'a AppContext,
+        cx: &'a App,
     ) -> &'a TerminalSettings {
         let mut settings_location = None;
         if let Some(path) = path.as_ref() {
@@ -139,7 +139,7 @@ impl Project {
         TerminalSettings::get(settings_location, cx)
     }
 
-    pub fn exec_in_shell(&self, command: String, cx: &AppContext) -> std::process::Command {
+    pub fn exec_in_shell(&self, command: String, cx: &App) -> std::process::Command {
         let path = self.first_project_directory(cx);
         let ssh_details = self.ssh_details(cx);
         let settings = self.terminal_settings(&path, cx).clone();
@@ -184,8 +184,8 @@ impl Project {
         kind: TerminalKind,
         python_venv_directory: Option<PathBuf>,
         window: AnyWindowHandle,
-        cx: &mut ModelContext<Self>,
-    ) -> Result<Model<Terminal>> {
+        cx: &mut Context<Self>,
+    ) -> Result<Entity<Terminal>> {
         let this = &mut *self;
         let path: Option<Arc<Path>> = match &kind {
             TerminalKind::Shell(path) => path.as_ref().map(|path| Arc::from(path.as_ref())),
@@ -339,7 +339,7 @@ impl Project {
             cx,
         )
         .map(|builder| {
-            let terminal_handle = cx.new_model(|cx| builder.subscribe(cx));
+            let terminal_handle = cx.new(|cx| builder.subscribe(cx));
 
             this.terminals
                 .local_handles
@@ -370,7 +370,7 @@ impl Project {
         &self,
         abs_path: Arc<Path>,
         venv_settings: VenvSettings,
-        cx: &ModelContext<Project>,
+        cx: &Context<Project>,
     ) -> Task<Option<PathBuf>> {
         cx.spawn(move |this, mut cx| async move {
             if let Some((worktree, _)) = this
@@ -409,7 +409,7 @@ impl Project {
         &self,
         abs_path: &Path,
         venv_settings: &terminal_settings::VenvSettingsContent,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<PathBuf> {
         let bin_dir_name = match std::env::consts::OS {
             "windows" => "Scripts",
@@ -433,7 +433,7 @@ impl Project {
         &self,
         abs_path: &Path,
         venv_settings: &terminal_settings::VenvSettingsContent,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<PathBuf> {
         let (worktree, _) = self.find_worktree(abs_path, cx)?;
         let fs = worktree.read(cx).as_local()?.fs();
@@ -503,13 +503,13 @@ impl Project {
     fn activate_python_virtual_environment(
         &self,
         command: String,
-        terminal_handle: &Model<Terminal>,
-        cx: &mut AppContext,
+        terminal_handle: &Entity<Terminal>,
+        cx: &mut App,
     ) {
         terminal_handle.update(cx, |terminal, _| terminal.input_bytes(command.into_bytes()));
     }
 
-    pub fn local_terminal_handles(&self) -> &Vec<WeakModel<terminal::Terminal>> {
+    pub fn local_terminal_handles(&self) -> &Vec<WeakEntity<terminal::Terminal>> {
         &self.terminals.local_handles
     }
 }

crates/project/src/toolchain_store.rs 🔗

@@ -5,8 +5,8 @@ use anyhow::{bail, Result};
 use async_trait::async_trait;
 use collections::BTreeMap;
 use gpui::{
-    AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Subscription, Task,
-    WeakModel,
+    App, AppContext as _, AsyncAppContext, Context, Entity, EventEmitter, Subscription, Task,
+    WeakEntity,
 };
 use language::{LanguageName, LanguageRegistry, LanguageToolchainStore, Toolchain, ToolchainList};
 use rpc::{proto, AnyProtoClient, TypedEnvelope};
@@ -17,8 +17,11 @@ use crate::{worktree_store::WorktreeStore, ProjectEnvironment};
 
 pub struct ToolchainStore(ToolchainStoreInner);
 enum ToolchainStoreInner {
-    Local(Model<LocalToolchainStore>, #[allow(dead_code)] Subscription),
-    Remote(Model<RemoteToolchainStore>),
+    Local(
+        Entity<LocalToolchainStore>,
+        #[allow(dead_code)] Subscription,
+    ),
+    Remote(Entity<RemoteToolchainStore>),
 }
 
 impl EventEmitter<ToolchainStoreEvent> for ToolchainStore {}
@@ -31,11 +34,11 @@ impl ToolchainStore {
 
     pub fn local(
         languages: Arc<LanguageRegistry>,
-        worktree_store: Model<WorktreeStore>,
-        project_environment: Model<ProjectEnvironment>,
-        cx: &mut ModelContext<Self>,
+        worktree_store: Entity<WorktreeStore>,
+        project_environment: Entity<ProjectEnvironment>,
+        cx: &mut Context<Self>,
     ) -> Self {
-        let model = cx.new_model(|_| LocalToolchainStore {
+        let model = cx.new(|_| LocalToolchainStore {
             languages,
             worktree_store,
             project_environment,
@@ -46,16 +49,16 @@ impl ToolchainStore {
         });
         Self(ToolchainStoreInner::Local(model, subscription))
     }
-    pub(super) fn remote(project_id: u64, client: AnyProtoClient, cx: &mut AppContext) -> Self {
+    pub(super) fn remote(project_id: u64, client: AnyProtoClient, cx: &mut App) -> Self {
         Self(ToolchainStoreInner::Remote(
-            cx.new_model(|_| RemoteToolchainStore { client, project_id }),
+            cx.new(|_| RemoteToolchainStore { client, project_id }),
         ))
     }
     pub(crate) fn activate_toolchain(
         &self,
         worktree_id: WorktreeId,
         toolchain: Toolchain,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Task<Option<()>> {
         match &self.0 {
             ToolchainStoreInner::Local(local, _) => local.update(cx, |this, cx| {
@@ -72,7 +75,7 @@ impl ToolchainStore {
         &self,
         worktree_id: WorktreeId,
         language_name: LanguageName,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Option<ToolchainList>> {
         match &self.0 {
             ToolchainStoreInner::Local(local, _) => {
@@ -91,7 +94,7 @@ impl ToolchainStore {
         &self,
         worktree_id: WorktreeId,
         language_name: LanguageName,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Option<Toolchain>> {
         match &self.0 {
             ToolchainStoreInner::Local(local, _) => {
@@ -107,7 +110,7 @@ impl ToolchainStore {
         }
     }
     async fn handle_activate_toolchain(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::ActivateToolchain>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::Ack> {
@@ -129,7 +132,7 @@ impl ToolchainStore {
         Ok(proto::Ack {})
     }
     async fn handle_active_toolchain(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::ActiveToolchain>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::ActiveToolchainResponse> {
@@ -151,7 +154,7 @@ impl ToolchainStore {
     }
 
     async fn handle_list_toolchains(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::ListToolchains>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::ListToolchainsResponse> {
@@ -207,8 +210,8 @@ impl ToolchainStore {
 
 struct LocalToolchainStore {
     languages: Arc<LanguageRegistry>,
-    worktree_store: Model<WorktreeStore>,
-    project_environment: Model<ProjectEnvironment>,
+    worktree_store: Entity<WorktreeStore>,
+    project_environment: Entity<ProjectEnvironment>,
     active_toolchains: BTreeMap<(WorktreeId, LanguageName), Toolchain>,
 }
 
@@ -258,8 +261,8 @@ impl language::LanguageToolchainStore for EmptyToolchainStore {
         None
     }
 }
-struct LocalStore(WeakModel<LocalToolchainStore>);
-struct RemoteStore(WeakModel<RemoteToolchainStore>);
+struct LocalStore(WeakEntity<LocalToolchainStore>);
+struct RemoteStore(WeakEntity<RemoteToolchainStore>);
 
 #[derive(Clone)]
 pub(crate) enum ToolchainStoreEvent {
@@ -273,7 +276,7 @@ impl LocalToolchainStore {
         &self,
         worktree_id: WorktreeId,
         toolchain: Toolchain,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Option<()>> {
         cx.spawn(move |this, mut cx| async move {
             this.update(&mut cx, |this, cx| {
@@ -291,7 +294,7 @@ impl LocalToolchainStore {
         &self,
         worktree_id: WorktreeId,
         language_name: LanguageName,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Option<ToolchainList>> {
         let registry = self.languages.clone();
         let Some(root) = self
@@ -325,7 +328,7 @@ impl LocalToolchainStore {
         &self,
         worktree_id: WorktreeId,
         language_name: LanguageName,
-        _: &AppContext,
+        _: &App,
     ) -> Task<Option<Toolchain>> {
         Task::ready(
             self.active_toolchains
@@ -344,7 +347,7 @@ impl RemoteToolchainStore {
         &self,
         worktree_id: WorktreeId,
         toolchain: Toolchain,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Option<()>> {
         let project_id = self.project_id;
         let client = self.client.clone();
@@ -370,7 +373,7 @@ impl RemoteToolchainStore {
         &self,
         worktree_id: WorktreeId,
         language_name: LanguageName,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Option<ToolchainList>> {
         let project_id = self.project_id;
         let client = self.client.clone();
@@ -416,7 +419,7 @@ impl RemoteToolchainStore {
         &self,
         worktree_id: WorktreeId,
         language_name: LanguageName,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Option<Toolchain>> {
         let project_id = self.project_id;
         let client = self.client.clone();

crates/project/src/worktree_store.rs 🔗

@@ -12,9 +12,7 @@ use futures::{
     future::{BoxFuture, Shared},
     FutureExt, SinkExt,
 };
-use gpui::{
-    AppContext, AsyncAppContext, EntityId, EventEmitter, Model, ModelContext, Task, WeakModel,
-};
+use gpui::{App, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Task, WeakEntity};
 use postage::oneshot;
 use rpc::{
     proto::{self, SSH_PROJECT_ID},
@@ -54,16 +52,16 @@ pub struct WorktreeStore {
     worktrees_reordered: bool,
     #[allow(clippy::type_complexity)]
     loading_worktrees:
-        HashMap<SanitizedPath, Shared<Task<Result<Model<Worktree>, Arc<anyhow::Error>>>>>,
+        HashMap<SanitizedPath, Shared<Task<Result<Entity<Worktree>, Arc<anyhow::Error>>>>>,
     state: WorktreeStoreState,
 }
 
 pub enum WorktreeStoreEvent {
-    WorktreeAdded(Model<Worktree>),
+    WorktreeAdded(Entity<Worktree>),
     WorktreeRemoved(EntityId, WorktreeId),
     WorktreeReleased(EntityId, WorktreeId),
     WorktreeOrderChanged,
-    WorktreeUpdateSent(Model<Worktree>),
+    WorktreeUpdateSent(Entity<Worktree>),
     WorktreeUpdatedEntries(WorktreeId, UpdatedEntriesSet),
     WorktreeUpdatedGitRepositories(WorktreeId),
     WorktreeDeletedEntry(WorktreeId, ProjectEntryId),
@@ -113,7 +111,7 @@ impl WorktreeStore {
     }
 
     /// Iterates through all worktrees, including ones that don't appear in the project panel
-    pub fn worktrees(&self) -> impl '_ + DoubleEndedIterator<Item = Model<Worktree>> {
+    pub fn worktrees(&self) -> impl '_ + DoubleEndedIterator<Item = Entity<Worktree>> {
         self.worktrees
             .iter()
             .filter_map(move |worktree| worktree.upgrade())
@@ -122,18 +120,18 @@ impl WorktreeStore {
     /// Iterates through all user-visible worktrees, the ones that appear in the project panel.
     pub fn visible_worktrees<'a>(
         &'a self,
-        cx: &'a AppContext,
-    ) -> impl 'a + DoubleEndedIterator<Item = Model<Worktree>> {
+        cx: &'a App,
+    ) -> impl 'a + DoubleEndedIterator<Item = Entity<Worktree>> {
         self.worktrees()
             .filter(|worktree| worktree.read(cx).is_visible())
     }
 
-    pub fn worktree_for_id(&self, id: WorktreeId, cx: &AppContext) -> Option<Model<Worktree>> {
+    pub fn worktree_for_id(&self, id: WorktreeId, cx: &App) -> Option<Entity<Worktree>> {
         self.worktrees()
             .find(|worktree| worktree.read(cx).id() == id)
     }
 
-    pub fn current_branch(&self, repository: ProjectPath, cx: &AppContext) -> Option<Arc<str>> {
+    pub fn current_branch(&self, repository: ProjectPath, cx: &App) -> Option<Arc<str>> {
         self.worktree_for_id(repository.worktree_id, cx)?
             .read(cx)
             .git_entry(repository.path)?
@@ -143,8 +141,8 @@ impl WorktreeStore {
     pub fn worktree_for_entry(
         &self,
         entry_id: ProjectEntryId,
-        cx: &AppContext,
-    ) -> Option<Model<Worktree>> {
+        cx: &App,
+    ) -> Option<Entity<Worktree>> {
         self.worktrees()
             .find(|worktree| worktree.read(cx).contains_entry(entry_id))
     }
@@ -152,8 +150,8 @@ impl WorktreeStore {
     pub fn find_worktree(
         &self,
         abs_path: impl Into<SanitizedPath>,
-        cx: &AppContext,
-    ) -> Option<(Model<Worktree>, PathBuf)> {
+        cx: &App,
+    ) -> Option<(Entity<Worktree>, PathBuf)> {
         let abs_path: SanitizedPath = abs_path.into();
         for tree in self.worktrees() {
             if let Ok(relative_path) = abs_path.as_path().strip_prefix(tree.read(cx).abs_path()) {
@@ -167,8 +165,8 @@ impl WorktreeStore {
         &mut self,
         abs_path: impl AsRef<Path>,
         visible: bool,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<(Model<Worktree>, PathBuf)>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<(Entity<Worktree>, PathBuf)>> {
         let abs_path = abs_path.as_ref();
         if let Some((tree, relative_path)) = self.find_worktree(abs_path, cx) {
             Task::ready(Ok((tree, relative_path)))
@@ -179,11 +177,7 @@ impl WorktreeStore {
         }
     }
 
-    pub fn entry_for_id<'a>(
-        &'a self,
-        entry_id: ProjectEntryId,
-        cx: &'a AppContext,
-    ) -> Option<&'a Entry> {
+    pub fn entry_for_id<'a>(&'a self, entry_id: ProjectEntryId, cx: &'a App) -> Option<&'a Entry> {
         self.worktrees()
             .find_map(|worktree| worktree.read(cx).entry_for_id(entry_id))
     }
@@ -191,8 +185,8 @@ impl WorktreeStore {
     pub fn worktree_and_entry_for_id<'a>(
         &'a self,
         entry_id: ProjectEntryId,
-        cx: &'a AppContext,
-    ) -> Option<(Model<Worktree>, &'a Entry)> {
+        cx: &'a App,
+    ) -> Option<(Entity<Worktree>, &'a Entry)> {
         self.worktrees().find_map(|worktree| {
             worktree
                 .read(cx)
@@ -201,7 +195,7 @@ impl WorktreeStore {
         })
     }
 
-    pub fn entry_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<Entry> {
+    pub fn entry_for_path(&self, path: &ProjectPath, cx: &App) -> Option<Entry> {
         self.worktree_for_id(path.worktree_id, cx)?
             .read(cx)
             .entry_for_path(&path.path)
@@ -212,8 +206,8 @@ impl WorktreeStore {
         &mut self,
         abs_path: impl Into<SanitizedPath>,
         visible: bool,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<Worktree>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<Worktree>>> {
         let abs_path: SanitizedPath = abs_path.into();
         if !self.loading_worktrees.contains_key(&abs_path) {
             let task = match &self.state {
@@ -256,8 +250,8 @@ impl WorktreeStore {
         client: AnyProtoClient,
         abs_path: impl Into<SanitizedPath>,
         visible: bool,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<Worktree>, Arc<anyhow::Error>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<Worktree>, Arc<anyhow::Error>>> {
         let mut abs_path = Into::<SanitizedPath>::into(abs_path).to_string();
         // If we start with `/~` that means the ssh path was something like `ssh://user@host/~/home-dir-folder/`
         // in which case want to strip the leading the `/`.
@@ -318,8 +312,8 @@ impl WorktreeStore {
         fs: Arc<dyn Fs>,
         abs_path: impl Into<SanitizedPath>,
         visible: bool,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Model<Worktree>, Arc<anyhow::Error>>> {
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<Worktree>, Arc<anyhow::Error>>> {
         let next_entry_id = self.next_entry_id.clone();
         let path: SanitizedPath = abs_path.into();
 
@@ -341,7 +335,7 @@ impl WorktreeStore {
         })
     }
 
-    pub fn add(&mut self, worktree: &Model<Worktree>, cx: &mut ModelContext<Self>) {
+    pub fn add(&mut self, worktree: &Entity<Worktree>, cx: &mut Context<Self>) {
         let worktree_id = worktree.read(cx).id();
         debug_assert!(self.worktrees().all(|w| w.read(cx).id() != worktree_id));
 
@@ -402,7 +396,7 @@ impl WorktreeStore {
         .detach();
     }
 
-    pub fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut ModelContext<Self>) {
+    pub fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut Context<Self>) {
         self.worktrees.retain(|worktree| {
             if let Some(worktree) = worktree.upgrade() {
                 if worktree.read(cx).id() == id_to_remove {
@@ -440,7 +434,7 @@ impl WorktreeStore {
         &mut self,
         worktrees: Vec<proto::WorktreeMetadata>,
         replica_id: ReplicaId,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Result<()> {
         let mut old_worktrees_by_id = self
             .worktrees
@@ -484,7 +478,7 @@ impl WorktreeStore {
         &mut self,
         source: WorktreeId,
         destination: WorktreeId,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Result<()> {
         if source == destination {
             return Ok(());
@@ -526,7 +520,7 @@ impl WorktreeStore {
         Ok(())
     }
 
-    pub fn disconnected_from_host(&mut self, cx: &mut AppContext) {
+    pub fn disconnected_from_host(&mut self, cx: &mut App) {
         for worktree in &self.worktrees {
             if let Some(worktree) = worktree.upgrade() {
                 worktree.update(cx, |worktree, _| {
@@ -538,7 +532,7 @@ impl WorktreeStore {
         }
     }
 
-    pub fn send_project_updates(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn send_project_updates(&mut self, cx: &mut Context<Self>) {
         let Some((downstream_client, project_id)) = self.downstream_client.clone() else {
             return;
         };
@@ -592,7 +586,7 @@ impl WorktreeStore {
         .detach_and_log_err(cx);
     }
 
-    pub fn worktree_metadata_protos(&self, cx: &AppContext) -> Vec<proto::WorktreeMetadata> {
+    pub fn worktree_metadata_protos(&self, cx: &App) -> Vec<proto::WorktreeMetadata> {
         self.worktrees()
             .map(|worktree| {
                 let worktree = worktree.read(cx);
@@ -610,7 +604,7 @@ impl WorktreeStore {
         &mut self,
         remote_id: u64,
         downstream_client: AnyProtoClient,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.retain_worktrees = true;
         self.downstream_client = Some((downstream_client, remote_id));
@@ -629,7 +623,7 @@ impl WorktreeStore {
         self.send_project_updates(cx);
     }
 
-    pub fn unshared(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn unshared(&mut self, cx: &mut Context<Self>) {
         self.retain_worktrees = false;
         self.downstream_client.take();
 
@@ -654,7 +648,7 @@ impl WorktreeStore {
         limit: usize,
         open_entries: HashSet<ProjectEntryId>,
         fs: Arc<dyn Fs>,
-        cx: &ModelContext<Self>,
+        cx: &Context<Self>,
     ) -> Receiver<ProjectPath> {
         let snapshots = self
             .visible_worktrees(cx)
@@ -890,7 +884,7 @@ impl WorktreeStore {
     pub fn branches(
         &self,
         project_path: ProjectPath,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Result<Vec<git::repository::Branch>>> {
         let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) else {
             return Task::ready(Err(anyhow!("No worktree found for ProjectPath")));
@@ -956,7 +950,7 @@ impl WorktreeStore {
         &self,
         repository: ProjectPath,
         new_branch: String,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Result<()>> {
         let Some(worktree) = self.worktree_for_id(repository.worktree_id, cx) else {
             return Task::ready(Err(anyhow!("No worktree found for ProjectPath")));
@@ -1045,7 +1039,7 @@ impl WorktreeStore {
     }
 
     pub async fn handle_create_project_entry(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::CreateProjectEntry>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::ProjectEntryResponse> {
@@ -1058,7 +1052,7 @@ impl WorktreeStore {
     }
 
     pub async fn handle_copy_project_entry(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::CopyProjectEntry>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::ProjectEntryResponse> {
@@ -1071,7 +1065,7 @@ impl WorktreeStore {
     }
 
     pub async fn handle_delete_project_entry(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::DeleteProjectEntry>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::ProjectEntryResponse> {
@@ -1084,7 +1078,7 @@ impl WorktreeStore {
     }
 
     pub async fn handle_expand_project_entry(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::ExpandProjectEntry>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::ExpandProjectEntryResponse> {
@@ -1096,7 +1090,7 @@ impl WorktreeStore {
     }
 
     pub async fn handle_git_branches(
-        this: Model<Self>,
+        this: Entity<Self>,
         branches: TypedEnvelope<proto::GitBranches>,
         cx: AsyncAppContext,
     ) -> Result<proto::GitBranchesResponse> {
@@ -1127,7 +1121,7 @@ impl WorktreeStore {
     }
 
     pub async fn handle_update_branch(
-        this: Model<Self>,
+        this: Entity<Self>,
         update_branch: TypedEnvelope<proto::UpdateGitBranch>,
         cx: AsyncAppContext,
     ) -> Result<proto::Ack> {
@@ -1153,12 +1147,12 @@ impl WorktreeStore {
 
 #[derive(Clone, Debug)]
 enum WorktreeHandle {
-    Strong(Model<Worktree>),
-    Weak(WeakModel<Worktree>),
+    Strong(Entity<Worktree>),
+    Weak(WeakEntity<Worktree>),
 }
 
 impl WorktreeHandle {
-    fn upgrade(&self) -> Option<Model<Worktree>> {
+    fn upgrade(&self) -> Option<Entity<Worktree>> {
         match self {
             WorktreeHandle::Strong(handle) => Some(handle.clone()),
             WorktreeHandle::Weak(handle) => handle.upgrade(),

crates/project/src/yarn.rs 🔗

@@ -14,7 +14,7 @@ use std::{
 use anyhow::Result;
 use collections::HashMap;
 use fs::Fs;
-use gpui::{AppContext, Context, Model, ModelContext, Task};
+use gpui::{App, AppContext as _, Context, Entity, Task};
 use util::ResultExt;
 
 pub(crate) struct YarnPathStore {
@@ -57,8 +57,8 @@ fn resolve_virtual(path: &Path) -> Option<Arc<Path>> {
 }
 
 impl YarnPathStore {
-    pub(crate) fn new(fs: Arc<dyn Fs>, cx: &mut AppContext) -> Model<Self> {
-        cx.new_model(|_| Self {
+    pub(crate) fn new(fs: Arc<dyn Fs>, cx: &mut App) -> Entity<Self> {
+        cx.new(|_| Self {
             temp_dirs: Default::default(),
             fs,
         })
@@ -67,7 +67,7 @@ impl YarnPathStore {
         &mut self,
         path: &Path,
         protocol: &str,
-        cx: &ModelContext<Self>,
+        cx: &Context<Self>,
     ) -> Task<Option<(Arc<Path>, Arc<Path>)>> {
         let mut is_zip = protocol.eq("zip");
 

crates/project_panel/src/project_panel.rs 🔗

@@ -18,12 +18,11 @@ use file_icons::FileIcons;
 use git::status::GitSummary;
 use gpui::{
     actions, anchored, deferred, div, impl_actions, point, px, size, uniform_list, Action,
-    AnyElement, AppContext, AssetSource, AsyncWindowContext, Bounds, ClipboardItem, DismissEvent,
-    Div, DragMoveEvent, EventEmitter, ExternalPaths, FocusHandle, FocusableView, Hsla,
-    InteractiveElement, KeyContext, ListHorizontalSizingBehavior, ListSizingBehavior, Model,
-    MouseButton, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render, ScrollStrategy,
-    Stateful, Styled, Subscription, Task, UniformListScrollHandle, View, ViewContext,
-    VisualContext as _, WeakView, WindowContext,
+    AnyElement, App, AssetSource, AsyncWindowContext, Bounds, ClipboardItem, Context, DismissEvent,
+    Div, DragMoveEvent, Entity, EventEmitter, ExternalPaths, FocusHandle, Focusable, Hsla,
+    InteractiveElement, KeyContext, ListHorizontalSizingBehavior, ListSizingBehavior, MouseButton,
+    MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render, ScrollStrategy, Stateful,
+    Styled, Subscription, Task, UniformListScrollHandle, WeakEntity, Window,
 };
 use indexmap::IndexMap;
 use language::DiagnosticSeverity;
@@ -68,7 +67,7 @@ const PROJECT_PANEL_KEY: &str = "ProjectPanel";
 const NEW_ENTRY_ID: ProjectEntryId = ProjectEntryId::MAX;
 
 pub struct ProjectPanel {
-    project: Model<Project>,
+    project: Entity<Project>,
     fs: Arc<dyn Fs>,
     focus_handle: FocusHandle,
     scroll_handle: UniformListScrollHandle,
@@ -88,12 +87,12 @@ pub struct ProjectPanel {
     // Currently selected leaf entry (see auto-folding for a definition of that) in a file tree
     selection: Option<SelectedEntry>,
     marked_entries: BTreeSet<SelectedEntry>,
-    context_menu: Option<(View<ContextMenu>, Point<Pixels>, Subscription)>,
+    context_menu: Option<(Entity<ContextMenu>, Point<Pixels>, Subscription)>,
     edit_state: Option<EditState>,
-    filename_editor: View<Editor>,
+    filename_editor: Entity<Editor>,
     clipboard: Option<ClipboardEntry>,
     _dragged_entry_destination: Option<Arc<Path>>,
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     width: Option<Pixels>,
     pending_serialization: Task<Option<()>>,
     show_scrollbar: bool,
@@ -213,17 +212,17 @@ impl FoldedAncestors {
     }
 }
 
-pub fn init_settings(cx: &mut AppContext) {
+pub fn init_settings(cx: &mut App) {
     ProjectPanelSettings::register(cx);
 }
 
-pub fn init(assets: impl AssetSource, cx: &mut AppContext) {
+pub fn init(assets: impl AssetSource, cx: &mut App) {
     init_settings(cx);
     file_icons::init(assets, cx);
 
-    cx.observe_new_views(|workspace: &mut Workspace, _| {
-        workspace.register_action(|workspace, _: &ToggleFocus, cx| {
-            workspace.toggle_panel_focus::<ProjectPanel>(cx);
+    cx.observe_new(|workspace: &mut Workspace, _, _| {
+        workspace.register_action(|workspace, _: &ToggleFocus, window, cx| {
+            workspace.toggle_panel_focus::<ProjectPanel>(window, cx);
         });
     })
     .detach();
@@ -263,7 +262,7 @@ struct ItemColors {
     focused: Hsla,
 }
 
-fn get_item_color(cx: &ViewContext<ProjectPanel>) -> ItemColors {
+fn get_item_color(cx: &App) -> ItemColors {
     let colors = cx.theme().colors();
 
     ItemColors {
@@ -276,24 +275,28 @@ fn get_item_color(cx: &ViewContext<ProjectPanel>) -> ItemColors {
 }
 
 impl ProjectPanel {
-    fn new(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) -> View<Self> {
+    fn new(
+        workspace: &mut Workspace,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Entity<Self> {
         let project = workspace.project().clone();
-        let project_panel = cx.new_view(|cx: &mut ViewContext<Self>| {
+        let project_panel = cx.new(|cx| {
             let focus_handle = cx.focus_handle();
-            cx.on_focus(&focus_handle, Self::focus_in).detach();
-            cx.on_focus_out(&focus_handle, |this, _, cx| {
-                this.focus_out(cx);
-                this.hide_scrollbar(cx);
+            cx.on_focus(&focus_handle, window, Self::focus_in).detach();
+            cx.on_focus_out(&focus_handle, window, |this, _, window, cx| {
+                this.focus_out(window, cx);
+                this.hide_scrollbar(window, cx);
             })
             .detach();
             cx.subscribe(&project, |this, project, event, cx| match event {
                 project::Event::ActiveEntryChanged(Some(entry_id)) => {
                     if ProjectPanelSettings::get_global(cx).auto_reveal_entries {
-                        this.reveal_entry(project, *entry_id, true, cx);
+                        this.reveal_entry(project.clone(), *entry_id, true, cx);
                     }
                 }
                 project::Event::RevealInProjectPanel(entry_id) => {
-                    this.reveal_entry(project, *entry_id, false, cx);
+                    this.reveal_entry(project.clone(), *entry_id, false, cx);
                     cx.emit(PanelEvent::Activate);
                 }
                 project::Event::ActivateProjectPanel => {
@@ -332,7 +335,7 @@ impl ProjectPanel {
                 });
             }
 
-            let filename_editor = cx.new_view(Editor::single_line);
+            let filename_editor = cx.new(|cx| Editor::single_line(window, cx));
 
             cx.subscribe(
                 &filename_editor,
@@ -398,9 +401,9 @@ impl ProjectPanel {
                 show_scrollbar: !Self::should_autohide_scrollbar(cx),
                 hide_scrollbar_task: None,
                 vertical_scrollbar_state: ScrollbarState::new(scroll_handle.clone())
-                    .parent_view(cx.view()),
+                    .parent_model(&cx.model()),
                 horizontal_scrollbar_state: ScrollbarState::new(scroll_handle.clone())
-                    .parent_view(cx.view()),
+                    .parent_model(&cx.model()),
                 max_width_item_index: None,
                 diagnostics: Default::default(),
                 scroll_handle,
@@ -412,9 +415,9 @@ impl ProjectPanel {
             this
         });
 
-        cx.subscribe(&project_panel, {
+        cx.subscribe_in(&project_panel, window, {
             let project_panel = project_panel.downgrade();
-            move |workspace, _, event, cx| match event {
+            move |workspace, _, event, window, cx| match event {
                 &Event::OpenedEntry {
                     entry_id,
                     focus_opened_item,
@@ -436,9 +439,9 @@ impl ProjectPanel {
                                     None,
                                     focus_opened_item,
                                     allow_preview,
-                                    cx,
+                                    window, cx,
                                 )
-                                .detach_and_prompt_err("Failed to open file", cx, move |e, _| {
+                                .detach_and_prompt_err("Failed to open file", window, cx, move |e, _, _| {
                                     match e.error_code() {
                                         ErrorCode::Disconnected => if is_via_ssh {
                                             Some("Disconnected from SSH host".to_string())
@@ -463,7 +466,7 @@ impl ProjectPanel {
                                 });
                                 if !focus_opened_item {
                                     let focus_handle = project_panel.read(cx).focus_handle.clone();
-                                    cx.focus(&focus_handle);
+                                    window.focus(&focus_handle);
                                 }
                             }
                         }
@@ -478,7 +481,7 @@ impl ProjectPanel {
                                         worktree_id: worktree.read(cx).id(),
                                         path: entry.path.clone(),
                                     },
-                                    cx,
+                                    window, cx,
                                 )
                                 .detach_and_log_err(cx);
                         }
@@ -493,9 +496,9 @@ impl ProjectPanel {
     }
 
     pub async fn load(
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         mut cx: AsyncWindowContext,
-    ) -> Result<View<Self>> {
+    ) -> Result<Entity<Self>> {
         let serialized_panel = cx
             .background_executor()
             .spawn(async move { KEY_VALUE_STORE.read_kvp(PROJECT_PANEL_KEY) })
@@ -508,8 +511,8 @@ impl ProjectPanel {
             .log_err()
             .flatten();
 
-        workspace.update(&mut cx, |workspace, cx| {
-            let panel = ProjectPanel::new(workspace, cx);
+        workspace.update_in(&mut cx, |workspace, window, cx| {
+            let panel = ProjectPanel::new(workspace, window, cx);
             if let Some(serialized_panel) = serialized_panel {
                 panel.update(cx, |panel, cx| {
                     panel.width = serialized_panel.width.map(|px| px.round());
@@ -520,7 +523,7 @@ impl ProjectPanel {
         })
     }
 
-    fn update_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
+    fn update_diagnostics(&mut self, cx: &mut Context<Self>) {
         let mut diagnostics: HashMap<(WorktreeId, PathBuf), DiagnosticSeverity> =
             Default::default();
         let show_diagnostics_setting = ProjectPanelSettings::get_global(cx).show_diagnostics;
@@ -578,7 +581,7 @@ impl ProjectPanel {
             .or_insert(diagnostic_severity);
     }
 
-    fn serialize(&mut self, cx: &mut ViewContext<Self>) {
+    fn serialize(&mut self, cx: &mut Context<Self>) {
         let width = self.width;
         self.pending_serialization = cx.background_executor().spawn(
             async move {
@@ -594,15 +597,15 @@ impl ProjectPanel {
         );
     }
 
-    fn focus_in(&mut self, cx: &mut ViewContext<Self>) {
-        if !self.focus_handle.contains_focused(cx) {
+    fn focus_in(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        if !self.focus_handle.contains_focused(window, cx) {
             cx.emit(Event::Focus);
         }
     }
 
-    fn focus_out(&mut self, cx: &mut ViewContext<Self>) {
-        if !self.focus_handle.is_focused(cx) {
-            self.confirm(&Confirm, cx);
+    fn focus_out(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        if !self.focus_handle.is_focused(window) {
+            self.confirm(&Confirm, window, cx);
         }
     }
 
@@ -610,7 +613,8 @@ impl ProjectPanel {
         &mut self,
         position: Point<Pixels>,
         entry_id: ProjectEntryId,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let project = self.project.read(cx);
 
@@ -636,7 +640,7 @@ impl ProjectPanel {
             let is_remote = project.is_via_collab();
             let is_local = project.is_local();
 
-            let context_menu = ContextMenu::build(cx, |menu, _| {
+            let context_menu = ContextMenu::build(window, cx, |menu, _, _| {
                 menu.context(self.focus_handle.clone()).map(|menu| {
                     if is_read_only {
                         menu.when(is_dir, |menu| {
@@ -705,7 +709,7 @@ impl ProjectPanel {
                 })
             });
 
-            cx.focus_view(&context_menu);
+            window.focus(&context_menu.focus_handle(cx));
             let subscription = cx.subscribe(&context_menu, |this, _, _: &DismissEvent, cx| {
                 this.context_menu.take();
                 cx.notify();
@@ -747,7 +751,12 @@ impl ProjectPanel {
         false
     }
 
-    fn expand_selected_entry(&mut self, _: &ExpandSelectedEntry, cx: &mut ViewContext<Self>) {
+    fn expand_selected_entry(
+        &mut self,
+        _: &ExpandSelectedEntry,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some((worktree, entry)) = self.selected_entry(cx) {
             if let Some(folded_ancestors) = self.ancestors.get_mut(&entry.id) {
                 if folded_ancestors.current_ancestor_depth > 0 {
@@ -767,7 +776,7 @@ impl ProjectPanel {
                     };
 
                 match expanded_dir_ids.binary_search(&entry_id) {
-                    Ok(_) => self.select_next(&SelectNext, cx),
+                    Ok(_) => self.select_next(&SelectNext, window, cx),
                     Err(ix) => {
                         self.project.update(cx, |project, cx| {
                             project.expand_entry(worktree_id, entry_id, cx);
@@ -782,19 +791,19 @@ impl ProjectPanel {
         }
     }
 
-    fn collapse_selected_entry(&mut self, _: &CollapseSelectedEntry, cx: &mut ViewContext<Self>) {
+    fn collapse_selected_entry(
+        &mut self,
+        _: &CollapseSelectedEntry,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let Some((worktree, entry)) = self.selected_entry_handle(cx) else {
             return;
         };
         self.collapse_entry(entry.clone(), worktree, cx)
     }
 
-    fn collapse_entry(
-        &mut self,
-        entry: Entry,
-        worktree: Model<Worktree>,
-        cx: &mut ViewContext<Self>,
-    ) {
+    fn collapse_entry(&mut self, entry: Entry, worktree: Entity<Worktree>, cx: &mut Context<Self>) {
         let worktree = worktree.read(cx);
         if let Some(folded_ancestors) = self.ancestors.get_mut(&entry.id) {
             if folded_ancestors.current_ancestor_depth + 1 < folded_ancestors.max_ancestor_depth() {
@@ -834,7 +843,12 @@ impl ProjectPanel {
         }
     }
 
-    pub fn collapse_all_entries(&mut self, _: &CollapseAllEntries, cx: &mut ViewContext<Self>) {
+    pub fn collapse_all_entries(
+        &mut self,
+        _: &CollapseAllEntries,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         // By keeping entries for fully collapsed worktrees, we avoid expanding them within update_visible_entries
         // (which is it's default behavior when there's no entry for a worktree in expanded_dir_ids).
         self.expanded_dir_ids
@@ -843,7 +857,12 @@ impl ProjectPanel {
         cx.notify();
     }
 
-    fn toggle_expanded(&mut self, entry_id: ProjectEntryId, cx: &mut ViewContext<Self>) {
+    fn toggle_expanded(
+        &mut self,
+        entry_id: ProjectEntryId,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(worktree_id) = self.project.read(cx).worktree_id_for_entry(entry_id, cx) {
             if let Some(expanded_dir_ids) = self.expanded_dir_ids.get_mut(&worktree_id) {
                 self.project.update(cx, |project, cx| {
@@ -858,13 +877,13 @@ impl ProjectPanel {
                     }
                 });
                 self.update_visible_entries(Some((worktree_id, entry_id)), cx);
-                cx.focus(&self.focus_handle);
+                window.focus(&self.focus_handle);
                 cx.notify();
             }
         }
     }
 
-    fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext<Self>) {
+    fn select_prev(&mut self, _: &SelectPrev, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(edit_state) = &self.edit_state {
             if edit_state.processing_filename.is_none() {
                 self.filename_editor.update(cx, |editor, cx| {
@@ -872,6 +891,7 @@ impl ProjectPanel {
                         &editor::actions::MoveToBeginningOfLine {
                             stop_at_soft_wraps: false,
                         },
+                        window,
                         cx,
                     );
                 });
@@ -896,49 +916,54 @@ impl ProjectPanel {
                 entry_id: worktree_entries[entry_ix].id,
             };
             self.selection = Some(selection);
-            if cx.modifiers().shift {
+            if window.modifiers().shift {
                 self.marked_entries.insert(selection);
             }
             self.autoscroll(cx);
             cx.notify();
         } else {
-            self.select_first(&SelectFirst {}, cx);
+            self.select_first(&SelectFirst {}, window, cx);
         }
     }
 
-    fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
-        if let Some(task) = self.confirm_edit(cx) {
-            task.detach_and_notify_err(cx);
+    fn confirm(&mut self, _: &Confirm, window: &mut Window, cx: &mut Context<Self>) {
+        if let Some(task) = self.confirm_edit(window, cx) {
+            task.detach_and_notify_err(window, cx);
         }
     }
 
-    fn open(&mut self, _: &Open, cx: &mut ViewContext<Self>) {
+    fn open(&mut self, _: &Open, window: &mut Window, cx: &mut Context<Self>) {
         let preview_tabs_enabled = PreviewTabsSettings::get_global(cx).enabled;
-        self.open_internal(true, !preview_tabs_enabled, cx);
+        self.open_internal(true, !preview_tabs_enabled, window, cx);
     }
 
-    fn open_permanent(&mut self, _: &OpenPermanent, cx: &mut ViewContext<Self>) {
-        self.open_internal(false, true, cx);
+    fn open_permanent(&mut self, _: &OpenPermanent, window: &mut Window, cx: &mut Context<Self>) {
+        self.open_internal(false, true, window, cx);
     }
 
     fn open_internal(
         &mut self,
         allow_preview: bool,
         focus_opened_item: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         if let Some((_, entry)) = self.selected_entry(cx) {
             if entry.is_file() {
                 self.open_entry(entry.id, focus_opened_item, allow_preview, cx);
             } else {
-                self.toggle_expanded(entry.id, cx);
+                self.toggle_expanded(entry.id, window, cx);
             }
         }
     }
 
-    fn confirm_edit(&mut self, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
+    fn confirm_edit(
+        &mut self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Task<Result<()>>> {
         let edit_state = self.edit_state.as_mut()?;
-        cx.focus(&self.focus_handle);
+        window.focus(&self.focus_handle);
 
         let worktree_id = edit_state.worktree_id;
         let is_new_entry = edit_state.is_new_entry();
@@ -984,7 +1009,7 @@ impl ProjectPanel {
         edit_state.processing_filename = Some(filename);
         cx.notify();
 
-        Some(cx.spawn(|project_panel, mut cx| async move {
+        Some(cx.spawn_in(window, |project_panel, mut cx| async move {
             let new_entry = edit_task.await;
             project_panel.update(&mut cx, |project_panel, cx| {
                 project_panel.edit_state = None;
@@ -995,7 +1020,7 @@ impl ProjectPanel {
                 Err(e) => {
                     project_panel.update(&mut cx, |project_panel, cx| {
                         project_panel.marked_entries.clear();
-                        project_panel.update_visible_entries(None, cx);
+                        project_panel.update_visible_entries(None,  cx);
                     }).ok();
                     Err(e)?;
                 }
@@ -1018,9 +1043,9 @@ impl ProjectPanel {
                 }
                 Ok(CreatedEntry::Excluded { abs_path }) => {
                     if let Some(open_task) = project_panel
-                        .update(&mut cx, |project_panel, cx| {
+                        .update_in(&mut cx, |project_panel, window, cx| {
                             project_panel.marked_entries.clear();
-                            project_panel.update_visible_entries(None, cx);
+                            project_panel.update_visible_entries(None,  cx);
 
                             if is_dir {
                                 project_panel.project.update(cx, |_, cx| {
@@ -1034,7 +1059,7 @@ impl ProjectPanel {
                                 project_panel
                                     .workspace
                                     .update(cx, |workspace, cx| {
-                                        workspace.open_abs_path(abs_path, true, cx)
+                                        workspace.open_abs_path(abs_path, true, window, cx)
                                     })
                                     .ok()
                             }
@@ -1050,7 +1075,7 @@ impl ProjectPanel {
         }))
     }
 
-    fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
+    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
         let previous_edit_state = self.edit_state.take();
         self.update_visible_entries(None, cx);
         self.marked_entries.clear();
@@ -1062,7 +1087,7 @@ impl ProjectPanel {
             self.autoscroll(cx);
         }
 
-        cx.focus(&self.focus_handle);
+        window.focus(&self.focus_handle);
         cx.notify();
     }
 
@@ -1071,7 +1096,8 @@ impl ProjectPanel {
         entry_id: ProjectEntryId,
         focus_opened_item: bool,
         allow_preview: bool,
-        cx: &mut ViewContext<Self>,
+
+        cx: &mut Context<Self>,
     ) {
         cx.emit(Event::OpenedEntry {
             entry_id,
@@ -1080,19 +1106,19 @@ impl ProjectPanel {
         });
     }
 
-    fn split_entry(&mut self, entry_id: ProjectEntryId, cx: &mut ViewContext<Self>) {
+    fn split_entry(&mut self, entry_id: ProjectEntryId, cx: &mut Context<Self>) {
         cx.emit(Event::SplitEntry { entry_id });
     }
 
-    fn new_file(&mut self, _: &NewFile, cx: &mut ViewContext<Self>) {
-        self.add_entry(false, cx)
+    fn new_file(&mut self, _: &NewFile, window: &mut Window, cx: &mut Context<Self>) {
+        self.add_entry(false, window, cx)
     }
 
-    fn new_directory(&mut self, _: &NewDirectory, cx: &mut ViewContext<Self>) {
-        self.add_entry(true, cx)
+    fn new_directory(&mut self, _: &NewDirectory, window: &mut Window, cx: &mut Context<Self>) {
+        self.add_entry(true, window, cx)
     }
 
-    fn add_entry(&mut self, is_dir: bool, cx: &mut ViewContext<Self>) {
+    fn add_entry(&mut self, is_dir: bool, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(SelectedEntry {
             worktree_id,
             entry_id,
@@ -1142,8 +1168,8 @@ impl ProjectPanel {
                 depth: 0,
             });
             self.filename_editor.update(cx, |editor, cx| {
-                editor.clear(cx);
-                editor.focus(cx);
+                editor.clear(window, cx);
+                window.focus(&editor.focus_handle(cx));
             });
             self.update_visible_entries(Some((worktree_id, NEW_ENTRY_ID)), cx);
             self.autoscroll(cx);
@@ -1163,7 +1189,12 @@ impl ProjectPanel {
         }
     }
 
-    fn rename_impl(&mut self, selection: Option<Range<usize>>, cx: &mut ViewContext<Self>) {
+    fn rename_impl(
+        &mut self,
+        selection: Option<Range<usize>>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(SelectedEntry {
             worktree_id,
             entry_id,
@@ -1194,11 +1225,11 @@ impl ProjectPanel {
                         0..selection_end
                     });
                     self.filename_editor.update(cx, |editor, cx| {
-                        editor.set_text(file_name, cx);
-                        editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                        editor.set_text(file_name, window, cx);
+                        editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                             s.select_ranges([selection])
                         });
-                        editor.focus(cx);
+                        window.focus(&editor.focus_handle(cx));
                     });
                     self.update_visible_entries(None, cx);
                     self.autoscroll(cx);
@@ -1208,19 +1239,25 @@ impl ProjectPanel {
         }
     }
 
-    fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) {
-        self.rename_impl(None, cx);
+    fn rename(&mut self, _: &Rename, window: &mut Window, cx: &mut Context<Self>) {
+        self.rename_impl(None, window, cx);
     }
 
-    fn trash(&mut self, action: &Trash, cx: &mut ViewContext<Self>) {
-        self.remove(true, action.skip_prompt, cx);
+    fn trash(&mut self, action: &Trash, window: &mut Window, cx: &mut Context<Self>) {
+        self.remove(true, action.skip_prompt, window, cx);
     }
 
-    fn delete(&mut self, action: &Delete, cx: &mut ViewContext<Self>) {
-        self.remove(false, action.skip_prompt, cx);
+    fn delete(&mut self, action: &Delete, window: &mut Window, cx: &mut Context<Self>) {
+        self.remove(false, action.skip_prompt, window, cx);
     }
 
-    fn remove(&mut self, trash: bool, skip_prompt: bool, cx: &mut ViewContext<ProjectPanel>) {
+    fn remove(
+        &mut self,
+        trash: bool,
+        skip_prompt: bool,
+        window: &mut Window,
+        cx: &mut Context<ProjectPanel>,
+    ) {
         maybe!({
             let items_to_delete = self.disjoint_entries(cx);
             if items_to_delete.is_empty() {
@@ -1295,12 +1332,12 @@ impl ProjectPanel {
                         )
                     }
                 };
-                Some(cx.prompt(PromptLevel::Info, &prompt, None, &[operation, "Cancel"]))
+                Some(window.prompt(PromptLevel::Info, &prompt, None, &[operation, "Cancel"], cx))
             } else {
                 None
             };
             let next_selection = self.find_next_selection_after_deletion(items_to_delete, cx);
-            cx.spawn(|panel, mut cx| async move {
+            cx.spawn_in(window, |panel, mut cx| async move {
                 if let Some(answer) = answer {
                     if answer.await != Ok(0) {
                         return anyhow::Ok(());
@@ -1316,12 +1353,12 @@ impl ProjectPanel {
                         })??
                         .await?;
                 }
-                panel.update(&mut cx, |panel, cx| {
+                panel.update_in(&mut cx, |panel, window, cx| {
                     if let Some(next_selection) = next_selection {
                         panel.selection = Some(next_selection);
                         panel.autoscroll(cx);
                     } else {
-                        panel.select_last(&SelectLast {}, cx);
+                        panel.select_last(&SelectLast {}, window, cx);
                     }
                 })?;
                 Ok(())
@@ -1334,7 +1371,7 @@ impl ProjectPanel {
     fn find_next_selection_after_deletion(
         &self,
         sanitized_entries: BTreeSet<SelectedEntry>,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<SelectedEntry> {
         if sanitized_entries.is_empty() {
             return None;
@@ -1414,7 +1451,7 @@ impl ProjectPanel {
         })
     }
 
-    fn unfold_directory(&mut self, _: &UnfoldDirectory, cx: &mut ViewContext<Self>) {
+    fn unfold_directory(&mut self, _: &UnfoldDirectory, _: &mut Window, cx: &mut Context<Self>) {
         if let Some((worktree, entry)) = self.selected_entry(cx) {
             self.unfolded_dir_ids.insert(entry.id);
 
@@ -1441,7 +1478,7 @@ impl ProjectPanel {
         }
     }
 
-    fn fold_directory(&mut self, _: &FoldDirectory, cx: &mut ViewContext<Self>) {
+    fn fold_directory(&mut self, _: &FoldDirectory, _: &mut Window, cx: &mut Context<Self>) {
         if let Some((worktree, entry)) = self.selected_entry(cx) {
             self.unfolded_dir_ids.remove(&entry.id);
 
@@ -1467,7 +1504,7 @@ impl ProjectPanel {
         }
     }
 
-    fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
+    fn select_next(&mut self, _: &SelectNext, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(edit_state) = &self.edit_state {
             if edit_state.processing_filename.is_none() {
                 self.filename_editor.update(cx, |editor, cx| {
@@ -1475,6 +1512,7 @@ impl ProjectPanel {
                         &editor::actions::MoveToEndOfLine {
                             stop_at_soft_wraps: false,
                         },
+                        window,
                         cx,
                     );
                 });
@@ -1501,7 +1539,7 @@ impl ProjectPanel {
                         entry_id: entry.id,
                     };
                     self.selection = Some(selection);
-                    if cx.modifiers().shift {
+                    if window.modifiers().shift {
                         self.marked_entries.insert(selection);
                     }
 
@@ -1510,11 +1548,16 @@ impl ProjectPanel {
                 }
             }
         } else {
-            self.select_first(&SelectFirst {}, cx);
+            self.select_first(&SelectFirst {}, window, cx);
         }
     }
 
-    fn select_prev_diagnostic(&mut self, _: &SelectPrevDiagnostic, cx: &mut ViewContext<Self>) {
+    fn select_prev_diagnostic(
+        &mut self,
+        _: &SelectPrevDiagnostic,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let selection = self.find_entry(
             self.selection.as_ref(),
             true,
@@ -1544,7 +1587,12 @@ impl ProjectPanel {
         }
     }
 
-    fn select_next_diagnostic(&mut self, _: &SelectNextDiagnostic, cx: &mut ViewContext<Self>) {
+    fn select_next_diagnostic(
+        &mut self,
+        _: &SelectNextDiagnostic,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let selection = self.find_entry(
             self.selection.as_ref(),
             false,
@@ -1574,7 +1622,12 @@ impl ProjectPanel {
         }
     }
 
-    fn select_prev_git_entry(&mut self, _: &SelectPrevGitEntry, cx: &mut ViewContext<Self>) {
+    fn select_prev_git_entry(
+        &mut self,
+        _: &SelectPrevGitEntry,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let selection = self.find_entry(
             self.selection.as_ref(),
             true,
@@ -1602,7 +1655,12 @@ impl ProjectPanel {
         }
     }
 
-    fn select_prev_directory(&mut self, _: &SelectPrevDirectory, cx: &mut ViewContext<Self>) {
+    fn select_prev_directory(
+        &mut self,
+        _: &SelectPrevDirectory,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let selection = self.find_visible_entry(
             self.selection.as_ref(),
             true,
@@ -1627,7 +1685,12 @@ impl ProjectPanel {
         }
     }
 
-    fn select_next_directory(&mut self, _: &SelectNextDirectory, cx: &mut ViewContext<Self>) {
+    fn select_next_directory(
+        &mut self,
+        _: &SelectNextDirectory,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let selection = self.find_visible_entry(
             self.selection.as_ref(),
             false,
@@ -1652,7 +1715,12 @@ impl ProjectPanel {
         }
     }
 
-    fn select_next_git_entry(&mut self, _: &SelectNextGitEntry, cx: &mut ViewContext<Self>) {
+    fn select_next_git_entry(
+        &mut self,
+        _: &SelectNextGitEntry,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let selection = self.find_entry(
             self.selection.as_ref(),
             true,
@@ -1680,7 +1748,7 @@ impl ProjectPanel {
         }
     }
 
-    fn select_parent(&mut self, _: &SelectParent, cx: &mut ViewContext<Self>) {
+    fn select_parent(&mut self, _: &SelectParent, window: &mut Window, cx: &mut Context<Self>) {
         if let Some((worktree, entry)) = self.selected_sub_entry(cx) {
             if let Some(parent) = entry.path.parent() {
                 let worktree = worktree.read(cx);
@@ -1694,11 +1762,11 @@ impl ProjectPanel {
                 }
             }
         } else {
-            self.select_first(&SelectFirst {}, cx);
+            self.select_first(&SelectFirst {}, window, cx);
         }
     }
 
-    fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext<Self>) {
+    fn select_first(&mut self, _: &SelectFirst, window: &mut Window, cx: &mut Context<Self>) {
         let worktree = self
             .visible_entries
             .first()
@@ -1714,7 +1782,7 @@ impl ProjectPanel {
                     entry_id: root_entry.id,
                 };
                 self.selection = Some(selection);
-                if cx.modifiers().shift {
+                if window.modifiers().shift {
                     self.marked_entries.insert(selection);
                 }
                 self.autoscroll(cx);
@@ -1723,7 +1791,7 @@ impl ProjectPanel {
         }
     }
 
-    fn select_last(&mut self, _: &SelectLast, cx: &mut ViewContext<Self>) {
+    fn select_last(&mut self, _: &SelectLast, _: &mut Window, cx: &mut Context<Self>) {
         let worktree = self.visible_entries.last().and_then(|(worktree_id, _, _)| {
             self.project.read(cx).worktree_for_id(*worktree_id, cx)
         });
@@ -1741,7 +1809,7 @@ impl ProjectPanel {
         }
     }
 
-    fn autoscroll(&mut self, cx: &mut ViewContext<Self>) {
+    fn autoscroll(&mut self, cx: &mut Context<Self>) {
         if let Some((_, _, index)) = self.selection.and_then(|s| self.index_for_selection(s)) {
             self.scroll_handle
                 .scroll_to_item(index, ScrollStrategy::Center);
@@ -1749,7 +1817,7 @@ impl ProjectPanel {
         }
     }
 
-    fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
+    fn cut(&mut self, _: &Cut, _: &mut Window, cx: &mut Context<Self>) {
         let entries = self.disjoint_entries(cx);
         if !entries.is_empty() {
             self.clipboard = Some(ClipboardEntry::Cut(entries));
@@ -1757,7 +1825,7 @@ impl ProjectPanel {
         }
     }
 
-    fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
+    fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
         let entries = self.disjoint_entries(cx);
         if !entries.is_empty() {
             self.clipboard = Some(ClipboardEntry::Copied(entries));
@@ -1768,8 +1836,8 @@ impl ProjectPanel {
     fn create_paste_path(
         &self,
         source: &SelectedEntry,
-        (worktree, target_entry): (Model<Worktree>, &Entry),
-        cx: &AppContext,
+        (worktree, target_entry): (Entity<Worktree>, &Entry),
+        cx: &App,
     ) -> Option<(PathBuf, Option<Range<usize>>)> {
         let mut new_path = target_entry.path.to_path_buf();
         // If we're pasting into a file, or a directory into itself, go up one level.
@@ -1820,7 +1888,7 @@ impl ProjectPanel {
         Some((new_path, disambiguation_range))
     }
 
-    fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
+    fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
         maybe!({
             let (worktree, entry) = self.selected_entry_handle(cx)?;
             let entry = entry.clone();
@@ -1880,7 +1948,7 @@ impl ProjectPanel {
 
             let item_count = paste_entry_tasks.len();
 
-            cx.spawn(|project_panel, mut cx| async move {
+            cx.spawn_in(window, |project_panel, mut cx| async move {
                 let mut last_succeed = None;
                 let mut need_delete_ids = Vec::new();
                 for ((entry_id, need_delete), task) in paste_entry_tasks.into_iter() {
@@ -1914,7 +1982,7 @@ impl ProjectPanel {
                 // update selection
                 if let Some(entry_id) = last_succeed {
                     project_panel
-                        .update(&mut cx, |project_panel, cx| {
+                        .update_in(&mut cx, |project_panel, window, cx| {
                             project_panel.selection = Some(SelectedEntry {
                                 worktree_id,
                                 entry_id,
@@ -1922,7 +1990,7 @@ impl ProjectPanel {
 
                             // if only one entry was pasted and it was disambiguated, open the rename editor
                             if item_count == 1 && disambiguation_range.is_some() {
-                                project_panel.rename_impl(disambiguation_range, cx);
+                                project_panel.rename_impl(disambiguation_range, window, cx);
                             }
                         })
                         .ok();
@@ -1937,12 +2005,12 @@ impl ProjectPanel {
         });
     }
 
-    fn duplicate(&mut self, _: &Duplicate, cx: &mut ViewContext<Self>) {
-        self.copy(&Copy {}, cx);
-        self.paste(&Paste {}, cx);
+    fn duplicate(&mut self, _: &Duplicate, window: &mut Window, cx: &mut Context<Self>) {
+        self.copy(&Copy {}, window, cx);
+        self.paste(&Paste {}, window, cx);
     }
 
-    fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
+    fn copy_path(&mut self, _: &CopyPath, _: &mut Window, cx: &mut Context<Self>) {
         let abs_file_paths = {
             let project = self.project.read(cx);
             self.effective_entries()
@@ -1966,7 +2034,7 @@ impl ProjectPanel {
         }
     }
 
-    fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
+    fn copy_relative_path(&mut self, _: &CopyRelativePath, _: &mut Window, cx: &mut Context<Self>) {
         let file_paths = {
             let project = self.project.read(cx);
             self.effective_entries()
@@ -1987,13 +2055,23 @@ impl ProjectPanel {
         }
     }
 
-    fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
+    fn reveal_in_finder(
+        &mut self,
+        _: &RevealInFileManager,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some((worktree, entry)) = self.selected_sub_entry(cx) {
             cx.reveal_path(&worktree.read(cx).abs_path().join(&entry.path));
         }
     }
 
-    fn remove_from_project(&mut self, _: &RemoveFromProject, cx: &mut ViewContext<Self>) {
+    fn remove_from_project(
+        &mut self,
+        _: &RemoveFromProject,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         for entry in self.effective_entries().iter() {
             let worktree_id = entry.worktree_id;
             self.project
@@ -2001,14 +2079,19 @@ impl ProjectPanel {
         }
     }
 
-    fn open_system(&mut self, _: &OpenWithSystem, cx: &mut ViewContext<Self>) {
+    fn open_system(&mut self, _: &OpenWithSystem, _: &mut Window, cx: &mut Context<Self>) {
         if let Some((worktree, entry)) = self.selected_entry(cx) {
             let abs_path = worktree.abs_path().join(&entry.path);
             cx.open_with_system(&abs_path);
         }
     }
 
-    fn open_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
+    fn open_in_terminal(
+        &mut self,
+        _: &OpenInTerminal,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some((worktree, entry)) = self.selected_sub_entry(cx) {
             let abs_path = match &entry.canonical_path {
                 Some(canonical_path) => Some(canonical_path.to_path_buf()),
@@ -2021,7 +2104,10 @@ impl ProjectPanel {
                 abs_path.and_then(|path| Some(path.parent()?.to_path_buf()))
             };
             if let Some(working_directory) = working_directory {
-                cx.dispatch_action(workspace::OpenTerminal { working_directory }.boxed_clone())
+                window.dispatch_action(
+                    workspace::OpenTerminal { working_directory }.boxed_clone(),
+                    cx,
+                )
             }
         }
     }
@@ -2029,7 +2115,8 @@ impl ProjectPanel {
     pub fn new_search_in_directory(
         &mut self,
         _: &NewSearchInDirectory,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         if let Some((worktree, entry)) = self.selected_sub_entry(cx) {
             if entry.is_dir() {
@@ -2045,7 +2132,7 @@ impl ProjectPanel {
                 self.workspace
                     .update(cx, |workspace, cx| {
                         search::ProjectSearchView::new_search_in_directory(
-                            workspace, &dir_path, cx,
+                            workspace, &dir_path, window, cx,
                         );
                     })
                     .ok();
@@ -2058,7 +2145,7 @@ impl ProjectPanel {
         entry_to_move: ProjectEntryId,
         destination: ProjectEntryId,
         destination_is_file: bool,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if self
             .project

crates/project_panel/src/project_panel_settings.rs 🔗

@@ -149,7 +149,7 @@ impl Settings for ProjectPanelSettings {
 
     fn load(
         sources: SettingsSources<Self::FileContent>,
-        _: &mut gpui::AppContext,
+        _: &mut gpui::App,
     ) -> anyhow::Result<Self> {
         sources.json_merge()
     }

crates/project_symbols/src/project_symbols.rs 🔗

@@ -1,8 +1,8 @@
 use editor::{scroll::Autoscroll, styled_runs_for_code_label, Bias, Editor};
 use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{
-    rems, AppContext, DismissEvent, FontWeight, Model, ParentElement, StyledText, Task, View,
-    ViewContext, WeakView, WindowContext,
+    rems, App, Context, DismissEvent, Entity, FontWeight, ParentElement, StyledText, Task,
+    WeakEntity, Window,
 };
 use ordered_float::OrderedFloat;
 use picker::{Picker, PickerDelegate};
@@ -15,27 +15,29 @@ use workspace::{
     Workspace,
 };
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(
-        |workspace: &mut Workspace, _: &mut ViewContext<Workspace>| {
-            workspace.register_action(|workspace, _: &workspace::ToggleProjectSymbols, cx| {
-                let project = workspace.project().clone();
-                let handle = cx.view().downgrade();
-                workspace.toggle_modal(cx, move |cx| {
-                    let delegate = ProjectSymbolsDelegate::new(handle, project);
-                    Picker::uniform_list(delegate, cx).width(rems(34.))
-                })
-            });
+pub fn init(cx: &mut App) {
+    cx.observe_new(
+        |workspace: &mut Workspace, _window, _: &mut Context<Workspace>| {
+            workspace.register_action(
+                |workspace, _: &workspace::ToggleProjectSymbols, window, cx| {
+                    let project = workspace.project().clone();
+                    let handle = cx.model().downgrade();
+                    workspace.toggle_modal(window, cx, move |window, cx| {
+                        let delegate = ProjectSymbolsDelegate::new(handle, project);
+                        Picker::uniform_list(delegate, window, cx).width(rems(34.))
+                    })
+                },
+            );
         },
     )
     .detach();
 }
 
-pub type ProjectSymbols = View<Picker<ProjectSymbolsDelegate>>;
+pub type ProjectSymbols = Entity<Picker<ProjectSymbolsDelegate>>;
 
 pub struct ProjectSymbolsDelegate {
-    workspace: WeakView<Workspace>,
-    project: Model<Project>,
+    workspace: WeakEntity<Workspace>,
+    project: Entity<Project>,
     selected_match_index: usize,
     symbols: Vec<Symbol>,
     visible_match_candidates: Vec<StringMatchCandidate>,
@@ -45,7 +47,7 @@ pub struct ProjectSymbolsDelegate {
 }
 
 impl ProjectSymbolsDelegate {
-    fn new(workspace: WeakView<Workspace>, project: Model<Project>) -> Self {
+    fn new(workspace: WeakEntity<Workspace>, project: Entity<Project>) -> Self {
         Self {
             workspace,
             project,
@@ -58,7 +60,7 @@ impl ProjectSymbolsDelegate {
         }
     }
 
-    fn filter(&mut self, query: &str, cx: &mut ViewContext<Picker<Self>>) {
+    fn filter(&mut self, query: &str, window: &mut Window, cx: &mut Context<Picker<Self>>) {
         const MAX_MATCHES: usize = 100;
         let mut visible_matches = cx.background_executor().block(fuzzy::match_strings(
             &self.visible_match_candidates,
@@ -95,17 +97,17 @@ impl ProjectSymbolsDelegate {
         }
 
         self.matches = matches;
-        self.set_selected_index(0, cx);
+        self.set_selected_index(0, window, cx);
     }
 }
 
 impl PickerDelegate for ProjectSymbolsDelegate {
     type ListItem = ListItem;
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Search project symbols...".into()
     }
 
-    fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
         if let Some(symbol) = self
             .matches
             .get(self.selected_match_index)
@@ -116,23 +118,23 @@ impl PickerDelegate for ProjectSymbolsDelegate {
             });
             let symbol = symbol.clone();
             let workspace = self.workspace.clone();
-            cx.spawn(|_, mut cx| async move {
+            cx.spawn_in(window, |_, mut cx| async move {
                 let buffer = buffer.await?;
-                workspace.update(&mut cx, |workspace, cx| {
+                workspace.update_in(&mut cx, |workspace, window, cx| {
                     let position = buffer
                         .read(cx)
                         .clip_point_utf16(symbol.range.start, Bias::Left);
                     let pane = if secondary {
-                        workspace.adjacent_pane(cx)
+                        workspace.adjacent_pane(window, cx)
                     } else {
                         workspace.active_pane().clone()
                     };
 
                     let editor =
-                        workspace.open_project_item::<Editor>(pane, buffer, true, true, cx);
+                        workspace.open_project_item::<Editor>(pane, buffer, true, true, window, cx);
 
                     editor.update(cx, |editor, cx| {
-                        editor.change_selections(Some(Autoscroll::center()), cx, |s| {
+                        editor.change_selections(Some(Autoscroll::center()), window, cx, |s| {
                             s.select_ranges([position..position])
                         });
                     });
@@ -144,7 +146,7 @@ impl PickerDelegate for ProjectSymbolsDelegate {
         }
     }
 
-    fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
+    fn dismissed(&mut self, _window: &mut Window, _cx: &mut Context<Picker<Self>>) {}
 
     fn match_count(&self) -> usize {
         self.matches.len()
@@ -154,20 +156,30 @@ impl PickerDelegate for ProjectSymbolsDelegate {
         self.selected_match_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, _cx: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
+    ) {
         self.selected_match_index = ix;
     }
 
-    fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
-        self.filter(&query, cx);
+    fn update_matches(
+        &mut self,
+        query: String,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Task<()> {
+        self.filter(&query, window, cx);
         self.show_worktree_root_name = self.project.read(cx).visible_worktrees(cx).count() > 1;
         let symbols = self
             .project
             .update(cx, |project, cx| project.symbols(&query, cx));
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let symbols = symbols.await.log_err();
             if let Some(symbols) = symbols {
-                this.update(&mut cx, |this, cx| {
+                this.update_in(&mut cx, |this, window, cx| {
                     let delegate = &mut this.delegate;
                     let project = delegate.project.read(cx);
                     let (visible_match_candidates, external_match_candidates) = symbols
@@ -185,7 +197,7 @@ impl PickerDelegate for ProjectSymbolsDelegate {
                     delegate.visible_match_candidates = visible_match_candidates;
                     delegate.external_match_candidates = external_match_candidates;
                     delegate.symbols = symbols;
-                    delegate.filter(&query, cx);
+                    delegate.filter(&query, window, cx);
                 })
                 .log_err();
             }
@@ -196,7 +208,8 @@ impl PickerDelegate for ProjectSymbolsDelegate {
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let string_match = &self.matches[ix];
         let symbol = &self.symbols[string_match.candidate_id];
@@ -240,7 +253,7 @@ impl PickerDelegate for ProjectSymbolsDelegate {
                         .child(
                             LabelLike::new().child(
                                 StyledText::new(label)
-                                    .with_highlights(&cx.text_style().clone(), highlights),
+                                    .with_highlights(&window.text_style().clone(), highlights),
                             ),
                         )
                         .child(Label::new(path).color(Color::Muted)),
@@ -333,12 +346,14 @@ mod tests {
             },
         );
 
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let (workspace, cx) =
+            cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
         // Create the project symbols view.
-        let symbols = cx.new_view(|cx| {
+        let symbols = cx.new_window_model(|window, cx| {
             Picker::uniform_list(
                 ProjectSymbolsDelegate::new(workspace.downgrade(), project.clone()),
+                window,
                 cx,
             )
         });
@@ -346,10 +361,10 @@ mod tests {
         // Spawn multiples updates before the first update completes,
         // such that in the end, there are no matches. Testing for regression:
         // https://github.com/zed-industries/zed/issues/861
-        symbols.update(cx, |p, cx| {
-            p.update_matches("o".to_string(), cx);
-            p.update_matches("on".to_string(), cx);
-            p.update_matches("onex".to_string(), cx);
+        symbols.update_in(cx, |p, window, cx| {
+            p.update_matches("o".to_string(), window, cx);
+            p.update_matches("on".to_string(), window, cx);
+            p.update_matches("onex".to_string(), window, cx);
         });
 
         cx.run_until_parked();
@@ -358,9 +373,9 @@ mod tests {
         });
 
         // Spawn more updates such that in the end, there are matches.
-        symbols.update(cx, |p, cx| {
-            p.update_matches("one".to_string(), cx);
-            p.update_matches("on".to_string(), cx);
+        symbols.update_in(cx, |p, window, cx| {
+            p.update_matches("one".to_string(), window, cx);
+            p.update_matches("on".to_string(), window, cx);
         });
 
         cx.run_until_parked();
@@ -372,9 +387,9 @@ mod tests {
         });
 
         // Spawn more updates such that in the end, there are again no matches.
-        symbols.update(cx, |p, cx| {
-            p.update_matches("o".to_string(), cx);
-            p.update_matches("".to_string(), cx);
+        symbols.update_in(cx, |p, window, cx| {
+            p.update_matches("o".to_string(), window, cx);
+            p.update_matches("".to_string(), window, cx);
         });
 
         cx.run_until_parked();

crates/prompt_library/src/prompt_library.rs 🔗

@@ -6,8 +6,8 @@ use collections::{HashMap, HashSet};
 use editor::CompletionProvider;
 use editor::{actions::Tab, CurrentLineHighlight, Editor, EditorElement, EditorEvent, EditorStyle};
 use gpui::{
-    actions, point, size, transparent_black, Action, AppContext, Bounds, EventEmitter, PromptLevel,
-    Subscription, Task, TextStyle, TitlebarOptions, View, WindowBounds, WindowHandle,
+    actions, point, size, transparent_black, Action, App, Bounds, Entity, EventEmitter, Focusable,
+    PromptLevel, Subscription, Task, TextStyle, TitlebarOptions, WindowBounds, WindowHandle,
     WindowOptions,
 };
 use language::{language_settings::SoftWrap, Buffer, LanguageRegistry};
@@ -22,8 +22,8 @@ use std::sync::Arc;
 use std::time::Duration;
 use theme::ThemeSettings;
 use ui::{
-    div, prelude::*, IconButtonShape, KeyBinding, ListItem, ListItemSpacing, ParentElement, Render,
-    SharedString, Styled, Tooltip, ViewContext, VisualContext,
+    div, prelude::*, Context, IconButtonShape, KeyBinding, ListItem, ListItemSpacing,
+    ParentElement, Render, SharedString, Styled, Tooltip, Window,
 };
 use util::{ResultExt, TryFutureExt};
 use workspace::Workspace;
@@ -32,7 +32,7 @@ use zed_actions::assistant::InlineAssist;
 pub use crate::prompt_store::*;
 pub use crate::prompts::*;
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     prompt_store::init(cx);
 }
 
@@ -54,16 +54,18 @@ const BUILT_IN_TOOLTIP_TEXT: &'static str = concat!(
 pub trait InlineAssistDelegate {
     fn assist(
         &self,
-        prompt_editor: &View<Editor>,
+        prompt_editor: &Entity<Editor>,
         initial_prompt: Option<String>,
-        cx: &mut ViewContext<PromptLibrary>,
+        window: &mut Window,
+        cx: &mut Context<PromptLibrary>,
     );
 
     /// Returns whether the Assistant panel was focused.
     fn focus_assistant_panel(
         &self,
         workspace: &mut Workspace,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) -> bool;
 }
 
@@ -77,7 +79,7 @@ pub fn open_prompt_library(
     language_registry: Arc<LanguageRegistry>,
     inline_assist_delegate: Box<dyn InlineAssistDelegate>,
     make_completion_provider: Arc<dyn Fn() -> Box<dyn CompletionProvider>>,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) -> Task<Result<WindowHandle<PromptLibrary>>> {
     let existing_window = cx
         .windows()
@@ -85,7 +87,7 @@ pub fn open_prompt_library(
         .find_map(|window| window.downcast::<PromptLibrary>());
     if let Some(existing_window) = existing_window {
         existing_window
-            .update(cx, |_, cx| cx.activate_window())
+            .update(cx, |_, window, _| window.activate_window())
             .ok();
         Task::ready(Ok(existing_window))
     } else {
@@ -106,13 +108,14 @@ pub fn open_prompt_library(
                         window_bounds: Some(WindowBounds::Windowed(bounds)),
                         ..Default::default()
                     },
-                    |cx| {
-                        cx.new_view(|cx| {
+                    |window, cx| {
+                        cx.new(|cx| {
                             PromptLibrary::new(
                                 store,
                                 language_registry,
                                 inline_assist_delegate,
                                 make_completion_provider,
+                                window,
                                 cx,
                             )
                         })
@@ -128,7 +131,7 @@ pub struct PromptLibrary {
     language_registry: Arc<LanguageRegistry>,
     prompt_editors: HashMap<PromptId, PromptEditor>,
     active_prompt_id: Option<PromptId>,
-    picker: View<Picker<PromptPickerDelegate>>,
+    picker: Entity<Picker<PromptPickerDelegate>>,
     pending_load: Task<()>,
     inline_assist_delegate: Box<dyn InlineAssistDelegate>,
     make_completion_provider: Arc<dyn Fn() -> Box<dyn CompletionProvider>>,
@@ -136,8 +139,8 @@ pub struct PromptLibrary {
 }
 
 struct PromptEditor {
-    title_editor: View<Editor>,
-    body_editor: View<Editor>,
+    title_editor: Entity<Editor>,
+    body_editor: Entity<Editor>,
     token_count: Option<usize>,
     pending_token_count: Task<Option<()>>,
     next_title_and_body_to_save: Option<(String, Rope)>,
@@ -167,7 +170,7 @@ impl PickerDelegate for PromptPickerDelegate {
         self.matches.len()
     }
 
-    fn no_matches_text(&self, _cx: &mut WindowContext) -> SharedString {
+    fn no_matches_text(&self, _window: &mut Window, _cx: &mut App) -> SharedString {
         if self.store.prompt_count() == 0 {
             "No prompts.".into()
         } else {
@@ -179,7 +182,7 @@ impl PickerDelegate for PromptPickerDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(&mut self, ix: usize, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.selected_index = ix;
         if let Some(prompt) = self.matches.get(self.selected_index) {
             cx.emit(PromptPickerEvent::Selected {
@@ -188,14 +191,19 @@ impl PickerDelegate for PromptPickerDelegate {
         }
     }
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Search...".into()
     }
 
-    fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
+    fn update_matches(
+        &mut self,
+        query: String,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Task<()> {
         let search = self.store.search(query);
         let prev_prompt_id = self.matches.get(self.selected_index).map(|mat| mat.id);
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let (matches, selected_index) = cx
                 .background_executor()
                 .spawn(async move {
@@ -210,16 +218,16 @@ impl PickerDelegate for PromptPickerDelegate {
                 })
                 .await;
 
-            this.update(&mut cx, |this, cx| {
+            this.update_in(&mut cx, |this, window, cx| {
                 this.delegate.matches = matches;
-                this.delegate.set_selected_index(selected_index, cx);
+                this.delegate.set_selected_index(selected_index, window, cx);
                 cx.notify();
             })
             .ok();
         })
     }
 
-    fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _secondary: bool, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         if let Some(prompt) = self.matches.get(self.selected_index) {
             cx.emit(PromptPickerEvent::Confirmed {
                 prompt_id: prompt.id,
@@ -227,13 +235,14 @@ impl PickerDelegate for PromptPickerDelegate {
         }
     }
 
-    fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
+    fn dismissed(&mut self, _window: &mut Window, _cx: &mut Context<Picker<Self>>) {}
 
     fn render_match(
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let prompt = self.matches.get(ix)?;
         let default = prompt.default;
@@ -250,8 +259,8 @@ impl PickerDelegate for PromptPickerDelegate {
                     .toggle_state(true)
                     .icon_color(Color::Accent)
                     .shape(IconButtonShape::Square)
-                    .tooltip(move |cx| Tooltip::text("Remove from Default Prompt", cx))
-                    .on_click(cx.listener(move |_, _, cx| {
+                    .tooltip(Tooltip::text("Remove from Default Prompt"))
+                    .on_click(cx.listener(move |_, _, _, cx| {
                         cx.emit(PromptPickerEvent::ToggledDefault { prompt_id })
                     }))
             }))
@@ -262,11 +271,12 @@ impl PickerDelegate for PromptPickerDelegate {
                         div()
                             .id("built-in-prompt")
                             .child(Icon::new(IconName::FileLock).color(Color::Muted))
-                            .tooltip(move |cx| {
+                            .tooltip(move |window, cx| {
                                 Tooltip::with_meta(
                                     "Built-in prompt",
                                     None,
                                     BUILT_IN_TOOLTIP_TEXT,
+                                    window,
                                     cx,
                                 )
                             })
@@ -275,8 +285,8 @@ impl PickerDelegate for PromptPickerDelegate {
                         IconButton::new("delete-prompt", IconName::Trash)
                             .icon_color(Color::Muted)
                             .shape(IconButtonShape::Square)
-                            .tooltip(move |cx| Tooltip::text("Delete Prompt", cx))
-                            .on_click(cx.listener(move |_, _, cx| {
+                            .tooltip(Tooltip::text("Delete Prompt"))
+                            .on_click(cx.listener(move |_, _, _, cx| {
                                 cx.emit(PromptPickerEvent::Deleted { prompt_id })
                             }))
                             .into_any_element()
@@ -287,17 +297,12 @@ impl PickerDelegate for PromptPickerDelegate {
                             .selected_icon(IconName::SparkleFilled)
                             .icon_color(if default { Color::Accent } else { Color::Muted })
                             .shape(IconButtonShape::Square)
-                            .tooltip(move |cx| {
-                                Tooltip::text(
-                                    if default {
-                                        "Remove from Default Prompt"
-                                    } else {
-                                        "Add to Default Prompt"
-                                    },
-                                    cx,
-                                )
-                            })
-                            .on_click(cx.listener(move |_, _, cx| {
+                            .tooltip(Tooltip::text(if default {
+                                "Remove from Default Prompt"
+                            } else {
+                                "Add to Default Prompt"
+                            }))
+                            .on_click(cx.listener(move |_, _, _, cx| {
                                 cx.emit(PromptPickerEvent::ToggledDefault { prompt_id })
                             })),
                     ),
@@ -305,7 +310,12 @@ impl PickerDelegate for PromptPickerDelegate {
         Some(element)
     }
 
-    fn render_editor(&self, editor: &View<Editor>, cx: &mut ViewContext<Picker<Self>>) -> Div {
+    fn render_editor(
+        &self,
+        editor: &Entity<Editor>,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Div {
         h_flex()
             .bg(cx.theme().colors().editor_background)
             .rounded_md()
@@ -324,7 +334,8 @@ impl PromptLibrary {
         language_registry: Arc<LanguageRegistry>,
         inline_assist_delegate: Box<dyn InlineAssistDelegate>,
         make_completion_provider: Arc<dyn Fn() -> Box<dyn CompletionProvider>>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let delegate = PromptPickerDelegate {
             store: store.clone(),
@@ -332,11 +343,11 @@ impl PromptLibrary {
             matches: Vec::new(),
         };
 
-        let picker = cx.new_view(|cx| {
-            let picker = Picker::uniform_list(delegate, cx)
+        let picker = cx.new(|cx| {
+            let picker = Picker::uniform_list(delegate, window, cx)
                 .modal(false)
                 .max_height(None);
-            picker.focus(cx);
+            picker.focus(window, cx);
             picker
         });
         Self {
@@ -347,54 +358,63 @@ impl PromptLibrary {
             pending_load: Task::ready(()),
             inline_assist_delegate,
             make_completion_provider,
-            _subscriptions: vec![cx.subscribe(&picker, Self::handle_picker_event)],
+            _subscriptions: vec![cx.subscribe_in(&picker, window, Self::handle_picker_event)],
             picker,
         }
     }
 
     fn handle_picker_event(
         &mut self,
-        _: View<Picker<PromptPickerDelegate>>,
+        _: &Entity<Picker<PromptPickerDelegate>>,
         event: &PromptPickerEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match event {
             PromptPickerEvent::Selected { prompt_id } => {
-                self.load_prompt(*prompt_id, false, cx);
+                self.load_prompt(*prompt_id, false, window, cx);
             }
             PromptPickerEvent::Confirmed { prompt_id } => {
-                self.load_prompt(*prompt_id, true, cx);
+                self.load_prompt(*prompt_id, true, window, cx);
             }
             PromptPickerEvent::ToggledDefault { prompt_id } => {
-                self.toggle_default_for_prompt(*prompt_id, cx);
+                self.toggle_default_for_prompt(*prompt_id, window, cx);
             }
             PromptPickerEvent::Deleted { prompt_id } => {
-                self.delete_prompt(*prompt_id, cx);
+                self.delete_prompt(*prompt_id, window, cx);
             }
         }
     }
 
-    pub fn new_prompt(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn new_prompt(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         // If we already have an untitled prompt, use that instead
         // of creating a new one.
         if let Some(metadata) = self.store.first() {
             if metadata.title.is_none() {
-                self.load_prompt(metadata.id, true, cx);
+                self.load_prompt(metadata.id, true, window, cx);
                 return;
             }
         }
 
         let prompt_id = PromptId::new();
         let save = self.store.save(prompt_id, None, false, "".into());
-        self.picker.update(cx, |picker, cx| picker.refresh(cx));
-        cx.spawn(|this, mut cx| async move {
+        self.picker
+            .update(cx, |picker, cx| picker.refresh(window, cx));
+        cx.spawn_in(window, |this, mut cx| async move {
             save.await?;
-            this.update(&mut cx, |this, cx| this.load_prompt(prompt_id, true, cx))
+            this.update_in(&mut cx, |this, window, cx| {
+                this.load_prompt(prompt_id, true, window, cx)
+            })
         })
         .detach_and_log_err(cx);
     }
 
-    pub fn save_prompt(&mut self, prompt_id: PromptId, cx: &mut ViewContext<Self>) {
+    pub fn save_prompt(
+        &mut self,
+        prompt_id: PromptId,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         const SAVE_THROTTLE: Duration = Duration::from_millis(500);
 
         if prompt_id.is_built_in() {
@@ -420,7 +440,7 @@ impl PromptLibrary {
 
         prompt_editor.next_title_and_body_to_save = Some((title, body));
         if prompt_editor.pending_save.is_none() {
-            prompt_editor.pending_save = Some(cx.spawn(|this, mut cx| {
+            prompt_editor.pending_save = Some(cx.spawn_in(window, |this, mut cx| {
                 async move {
                     loop {
                         let title_and_body = this.update(&mut cx, |this, _| {
@@ -440,8 +460,9 @@ impl PromptLibrary {
                                 .save(prompt_id, title, prompt_metadata.default, body)
                                 .await
                                 .log_err();
-                            this.update(&mut cx, |this, cx| {
-                                this.picker.update(cx, |picker, cx| picker.refresh(cx));
+                            this.update_in(&mut cx, |this, window, cx| {
+                                this.picker
+                                    .update(cx, |picker, cx| picker.refresh(window, cx));
                                 cx.notify();
                             })?;
 
@@ -462,73 +483,89 @@ impl PromptLibrary {
         }
     }
 
-    pub fn delete_active_prompt(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn delete_active_prompt(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(active_prompt_id) = self.active_prompt_id {
-            self.delete_prompt(active_prompt_id, cx);
+            self.delete_prompt(active_prompt_id, window, cx);
         }
     }
 
-    pub fn duplicate_active_prompt(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn duplicate_active_prompt(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(active_prompt_id) = self.active_prompt_id {
-            self.duplicate_prompt(active_prompt_id, cx);
+            self.duplicate_prompt(active_prompt_id, window, cx);
         }
     }
 
-    pub fn toggle_default_for_active_prompt(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn toggle_default_for_active_prompt(
+        &mut self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(active_prompt_id) = self.active_prompt_id {
-            self.toggle_default_for_prompt(active_prompt_id, cx);
+            self.toggle_default_for_prompt(active_prompt_id, window, cx);
         }
     }
 
-    pub fn toggle_default_for_prompt(&mut self, prompt_id: PromptId, cx: &mut ViewContext<Self>) {
+    pub fn toggle_default_for_prompt(
+        &mut self,
+        prompt_id: PromptId,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(prompt_metadata) = self.store.metadata(prompt_id) {
             self.store
                 .save_metadata(prompt_id, prompt_metadata.title, !prompt_metadata.default)
                 .detach_and_log_err(cx);
-            self.picker.update(cx, |picker, cx| picker.refresh(cx));
+            self.picker
+                .update(cx, |picker, cx| picker.refresh(window, cx));
             cx.notify();
         }
     }
 
-    pub fn load_prompt(&mut self, prompt_id: PromptId, focus: bool, cx: &mut ViewContext<Self>) {
+    pub fn load_prompt(
+        &mut self,
+        prompt_id: PromptId,
+        focus: bool,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(prompt_editor) = self.prompt_editors.get(&prompt_id) {
             if focus {
                 prompt_editor
                     .body_editor
-                    .update(cx, |editor, cx| editor.focus(cx));
+                    .update(cx, |editor, cx| window.focus(&editor.focus_handle(cx)));
             }
-            self.set_active_prompt(Some(prompt_id), cx);
+            self.set_active_prompt(Some(prompt_id), window, cx);
         } else if let Some(prompt_metadata) = self.store.metadata(prompt_id) {
             let language_registry = self.language_registry.clone();
             let prompt = self.store.load(prompt_id);
             let make_completion_provider = self.make_completion_provider.clone();
-            self.pending_load = cx.spawn(|this, mut cx| async move {
+            self.pending_load = cx.spawn_in(window, |this, mut cx| async move {
                 let prompt = prompt.await;
                 let markdown = language_registry.language_for_name("Markdown").await;
-                this.update(&mut cx, |this, cx| match prompt {
+                this.update_in(&mut cx, |this, window, cx| match prompt {
                     Ok(prompt) => {
-                        let title_editor = cx.new_view(|cx| {
-                            let mut editor = Editor::auto_width(cx);
+                        let title_editor = cx.new(|cx| {
+                            let mut editor = Editor::auto_width(window, cx);
                             editor.set_placeholder_text("Untitled", cx);
-                            editor.set_text(prompt_metadata.title.unwrap_or_default(), cx);
+                            editor.set_text(prompt_metadata.title.unwrap_or_default(), window, cx);
                             if prompt_id.is_built_in() {
                                 editor.set_read_only(true);
-                                editor.set_show_inline_completions(Some(false), cx);
+                                editor.set_show_inline_completions(Some(false), window, cx);
                             }
                             editor
                         });
-                        let body_editor = cx.new_view(|cx| {
-                            let buffer = cx.new_model(|cx| {
+                        let body_editor = cx.new(|cx| {
+                            let buffer = cx.new(|cx| {
                                 let mut buffer = Buffer::local(prompt, cx);
                                 buffer.set_language(markdown.log_err(), cx);
                                 buffer.set_language_registry(language_registry);
                                 buffer
                             });
 
-                            let mut editor = Editor::for_buffer(buffer, None, cx);
+                            let mut editor = Editor::for_buffer(buffer, None, window, cx);
                             if prompt_id.is_built_in() {
                                 editor.set_read_only(true);
-                                editor.set_show_inline_completions(Some(false), cx);
+                                editor.set_show_inline_completions(Some(false), window, cx);
                             }
                             editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
                             editor.set_show_gutter(false, cx);
@@ -538,17 +575,29 @@ impl PromptLibrary {
                             editor.set_current_line_highlight(Some(CurrentLineHighlight::None));
                             editor.set_completion_provider(Some(make_completion_provider()));
                             if focus {
-                                editor.focus(cx);
+                                window.focus(&editor.focus_handle(cx));
                             }
                             editor
                         });
                         let _subscriptions = vec![
-                            cx.subscribe(&title_editor, move |this, editor, event, cx| {
-                                this.handle_prompt_title_editor_event(prompt_id, editor, event, cx)
-                            }),
-                            cx.subscribe(&body_editor, move |this, editor, event, cx| {
-                                this.handle_prompt_body_editor_event(prompt_id, editor, event, cx)
-                            }),
+                            cx.subscribe_in(
+                                &title_editor,
+                                window,
+                                move |this, editor, event, window, cx| {
+                                    this.handle_prompt_title_editor_event(
+                                        prompt_id, editor, event, window, cx,
+                                    )
+                                },
+                            ),
+                            cx.subscribe_in(
+                                &body_editor,
+                                window,
+                                move |this, editor, event, window, cx| {
+                                    this.handle_prompt_body_editor_event(
+                                        prompt_id, editor, event, window, cx,
+                                    )
+                                },
+                            ),
                         ];
                         this.prompt_editors.insert(
                             prompt_id,
@@ -562,8 +611,8 @@ impl PromptLibrary {
                                 _subscriptions,
                             },
                         );
-                        this.set_active_prompt(Some(prompt_id), cx);
-                        this.count_tokens(prompt_id, cx);
+                        this.set_active_prompt(Some(prompt_id), window, cx);
+                        this.count_tokens(prompt_id, window, cx);
                     }
                     Err(error) => {
                         // TODO: we should show the error in the UI.
@@ -575,7 +624,12 @@ impl PromptLibrary {
         }
     }
 
-    fn set_active_prompt(&mut self, prompt_id: Option<PromptId>, cx: &mut ViewContext<Self>) {
+    fn set_active_prompt(
+        &mut self,
+        prompt_id: Option<PromptId>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.active_prompt_id = prompt_id;
         self.picker.update(cx, |picker, cx| {
             if let Some(prompt_id) = prompt_id {
@@ -593,19 +647,24 @@ impl PromptLibrary {
                         .iter()
                         .position(|mat| mat.id == prompt_id)
                     {
-                        picker.set_selected_index(ix, true, cx);
+                        picker.set_selected_index(ix, true, window, cx);
                     }
                 }
             } else {
-                picker.focus(cx);
+                picker.focus(window, cx);
             }
         });
         cx.notify();
     }
 
-    pub fn delete_prompt(&mut self, prompt_id: PromptId, cx: &mut ViewContext<Self>) {
+    pub fn delete_prompt(
+        &mut self,
+        prompt_id: PromptId,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(metadata) = self.store.metadata(prompt_id) {
-            let confirmation = cx.prompt(
+            let confirmation = window.prompt(
                 PromptLevel::Warning,
                 &format!(
                     "Are you sure you want to delete {}",
@@ -613,17 +672,19 @@ impl PromptLibrary {
                 ),
                 None,
                 &["Delete", "Cancel"],
+                cx,
             );
 
-            cx.spawn(|this, mut cx| async move {
+            cx.spawn_in(window, |this, mut cx| async move {
                 if confirmation.await.ok() == Some(0) {
-                    this.update(&mut cx, |this, cx| {
+                    this.update_in(&mut cx, |this, window, cx| {
                         if this.active_prompt_id == Some(prompt_id) {
-                            this.set_active_prompt(None, cx);
+                            this.set_active_prompt(None, window, cx);
                         }
                         this.prompt_editors.remove(&prompt_id);
                         this.store.delete(prompt_id).detach_and_log_err(cx);
-                        this.picker.update(cx, |picker, cx| picker.refresh(cx));
+                        this.picker
+                            .update(cx, |picker, cx| picker.refresh(window, cx));
                         cx.notify();
                     })?;
                 }
@@ -633,7 +694,12 @@ impl PromptLibrary {
         }
     }
 
-    pub fn duplicate_prompt(&mut self, prompt_id: PromptId, cx: &mut ViewContext<Self>) {
+    pub fn duplicate_prompt(
+        &mut self,
+        prompt_id: PromptId,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(prompt) = self.prompt_editors.get(&prompt_id) {
             const DUPLICATE_SUFFIX: &str = " copy";
             let title_to_duplicate = prompt.title_editor.read(cx).text(cx);
@@ -663,31 +729,38 @@ impl PromptLibrary {
             let save = self
                 .store
                 .save(new_id, Some(title.into()), false, body.into());
-            self.picker.update(cx, |picker, cx| picker.refresh(cx));
-            cx.spawn(|this, mut cx| async move {
+            self.picker
+                .update(cx, |picker, cx| picker.refresh(window, cx));
+            cx.spawn_in(window, |this, mut cx| async move {
                 save.await?;
-                this.update(&mut cx, |prompt_library, cx| {
-                    prompt_library.load_prompt(new_id, true, cx)
+                this.update_in(&mut cx, |prompt_library, window, cx| {
+                    prompt_library.load_prompt(new_id, true, window, cx)
                 })
             })
             .detach_and_log_err(cx);
         }
     }
 
-    fn focus_active_prompt(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
+    fn focus_active_prompt(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(active_prompt) = self.active_prompt_id {
             self.prompt_editors[&active_prompt]
                 .body_editor
-                .update(cx, |editor, cx| editor.focus(cx));
+                .update(cx, |editor, cx| window.focus(&editor.focus_handle(cx)));
             cx.stop_propagation();
         }
     }
 
-    fn focus_picker(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
-        self.picker.update(cx, |picker, cx| picker.focus(cx));
+    fn focus_picker(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
+        self.picker
+            .update(cx, |picker, cx| picker.focus(window, cx));
     }
 
-    pub fn inline_assist(&mut self, action: &InlineAssist, cx: &mut ViewContext<Self>) {
+    pub fn inline_assist(
+        &mut self,
+        action: &InlineAssist,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let Some(active_prompt_id) = self.active_prompt_id else {
             cx.propagate();
             return;
@@ -701,15 +774,15 @@ impl PromptLibrary {
         let initial_prompt = action.prompt.clone();
         if provider.is_authenticated(cx) {
             self.inline_assist_delegate
-                .assist(prompt_editor, initial_prompt, cx);
+                .assist(prompt_editor, initial_prompt, window, cx);
         } else {
             for window in cx.windows() {
                 if let Some(workspace) = window.downcast::<Workspace>() {
                     let panel = workspace
-                        .update(cx, |workspace, cx| {
-                            cx.activate_window();
+                        .update(cx, |workspace, window, cx| {
+                            window.activate_window();
                             self.inline_assist_delegate
-                                .focus_assistant_panel(workspace, cx)
+                                .focus_assistant_panel(workspace, window, cx)
                         })
                         .ok();
                     if panel == Some(true) {
@@ -720,18 +793,28 @@ impl PromptLibrary {
         }
     }
 
-    fn move_down_from_title(&mut self, _: &editor::actions::MoveDown, cx: &mut ViewContext<Self>) {
+    fn move_down_from_title(
+        &mut self,
+        _: &editor::actions::MoveDown,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(prompt_id) = self.active_prompt_id {
             if let Some(prompt_editor) = self.prompt_editors.get(&prompt_id) {
-                cx.focus_view(&prompt_editor.body_editor);
+                window.focus(&prompt_editor.body_editor.focus_handle(cx));
             }
         }
     }
 
-    fn move_up_from_body(&mut self, _: &editor::actions::MoveUp, cx: &mut ViewContext<Self>) {
+    fn move_up_from_body(
+        &mut self,
+        _: &editor::actions::MoveUp,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(prompt_id) = self.active_prompt_id {
             if let Some(prompt_editor) = self.prompt_editors.get(&prompt_id) {
-                cx.focus_view(&prompt_editor.title_editor);
+                window.focus(&prompt_editor.title_editor.focus_handle(cx));
             }
         }
     }
@@ -739,18 +822,19 @@ impl PromptLibrary {
     fn handle_prompt_title_editor_event(
         &mut self,
         prompt_id: PromptId,
-        title_editor: View<Editor>,
+        title_editor: &Entity<Editor>,
         event: &EditorEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match event {
             EditorEvent::BufferEdited => {
-                self.save_prompt(prompt_id, cx);
-                self.count_tokens(prompt_id, cx);
+                self.save_prompt(prompt_id, window, cx);
+                self.count_tokens(prompt_id, window, cx);
             }
             EditorEvent::Blurred => {
                 title_editor.update(cx, |title_editor, cx| {
-                    title_editor.change_selections(None, cx, |selections| {
+                    title_editor.change_selections(None, window, cx, |selections| {
                         let cursor = selections.oldest_anchor().head();
                         selections.select_anchor_ranges([cursor..cursor]);
                     });
@@ -763,18 +847,19 @@ impl PromptLibrary {
     fn handle_prompt_body_editor_event(
         &mut self,
         prompt_id: PromptId,
-        body_editor: View<Editor>,
+        body_editor: &Entity<Editor>,
         event: &EditorEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match event {
             EditorEvent::BufferEdited => {
-                self.save_prompt(prompt_id, cx);
-                self.count_tokens(prompt_id, cx);
+                self.save_prompt(prompt_id, window, cx);
+                self.count_tokens(prompt_id, window, cx);
             }
             EditorEvent::Blurred => {
                 body_editor.update(cx, |body_editor, cx| {
-                    body_editor.change_selections(None, cx, |selections| {
+                    body_editor.change_selections(None, window, cx, |selections| {
                         let cursor = selections.oldest_anchor().head();
                         selections.select_anchor_ranges([cursor..cursor]);
                     });
@@ -784,7 +869,7 @@ impl PromptLibrary {
         }
     }
 
-    fn count_tokens(&mut self, prompt_id: PromptId, cx: &mut ViewContext<Self>) {
+    fn count_tokens(&mut self, prompt_id: PromptId, window: &mut Window, cx: &mut Context<Self>) {
         let Some(model) = LanguageModelRegistry::read_global(cx).active_model() else {
             return;
         };
@@ -792,13 +877,13 @@ impl PromptLibrary {
             let editor = &prompt.body_editor.read(cx);
             let buffer = &editor.buffer().read(cx).as_singleton().unwrap().read(cx);
             let body = buffer.as_rope().clone();
-            prompt.pending_token_count = cx.spawn(|this, mut cx| {
+            prompt.pending_token_count = cx.spawn_in(window, |this, mut cx| {
                 async move {
                     const DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
 
                     cx.background_executor().timer(DEBOUNCE_TIMEOUT).await;
                     let token_count = cx
-                        .update(|cx| {
+                        .update(|_, cx| {
                             model.count_tokens(
                                 LanguageModelRequest {
                                     messages: vec![LanguageModelRequestMessage {
@@ -826,7 +911,7 @@ impl PromptLibrary {
         }
     }
 
-    fn render_prompt_list(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render_prompt_list(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
         v_flex()
             .id("prompt-list")
             .capture_action(cx.listener(Self::focus_active_prompt))
@@ -846,16 +931,18 @@ impl PromptLibrary {
                         IconButton::new("new-prompt", IconName::Plus)
                             .style(ButtonStyle::Transparent)
                             .shape(IconButtonShape::Square)
-                            .tooltip(move |cx| Tooltip::for_action("New Prompt", &NewPrompt, cx))
-                            .on_click(|_, cx| {
-                                cx.dispatch_action(Box::new(NewPrompt));
+                            .tooltip(move |window, cx| {
+                                Tooltip::for_action("New Prompt", &NewPrompt, window, cx)
+                            })
+                            .on_click(|_, window, cx| {
+                                window.dispatch_action(Box::new(NewPrompt), cx);
                             }),
                     ),
             )
             .child(div().flex_grow().child(self.picker.clone()))
     }
 
-    fn render_active_prompt(&mut self, cx: &mut ViewContext<PromptLibrary>) -> gpui::Stateful<Div> {
+    fn render_active_prompt(&mut self, cx: &mut Context<PromptLibrary>) -> gpui::Stateful<Div> {
         div()
             .w_2_3()
             .h_full()
@@ -880,8 +967,8 @@ impl PromptLibrary {
                         .overflow_hidden()
                         .pl(DynamicSpacing::Base16.rems(cx))
                         .pt(DynamicSpacing::Base08.rems(cx))
-                        .on_click(cx.listener(move |_, _, cx| {
-                            cx.focus(&focus_handle);
+                        .on_click(cx.listener(move |_, _, window, _| {
+                            window.focus(&focus_handle);
                         }))
                         .child(
                             h_flex()
@@ -964,7 +1051,7 @@ impl PromptLibrary {
 
                                                         h_flex()
                                                             .id("token_count")
-                                                            .tooltip(move |cx| {
+                                                            .tooltip(move |window, cx| {
                                                                 let token_count =
                                                                     token_count.clone();
 
@@ -983,6 +1070,7 @@ impl PromptLibrary {
                                                                                 .0)
                                                                             .unwrap_or_default()
                                                                     ),
+                                                                    window,
                                                                     cx,
                                                                 )
                                                             })
@@ -1002,11 +1090,12 @@ impl PromptLibrary {
                                                             Icon::new(IconName::FileLock)
                                                                 .color(Color::Muted),
                                                         )
-                                                        .tooltip(move |cx| {
+                                                        .tooltip(move |window, cx| {
                                                             Tooltip::with_meta(
                                                                 "Built-in prompt",
                                                                 None,
                                                                 BUILT_IN_TOOLTIP_TEXT,
+                                                                window,
                                                                 cx,
                                                             )
                                                         })
@@ -1020,15 +1109,19 @@ impl PromptLibrary {
                                                     .style(ButtonStyle::Transparent)
                                                     .shape(IconButtonShape::Square)
                                                     .size(ButtonSize::Large)
-                                                    .tooltip(move |cx| {
+                                                    .tooltip(move |window, cx| {
                                                         Tooltip::for_action(
                                                             "Delete Prompt",
                                                             &DeletePrompt,
+                                                            window,
                                                             cx,
                                                         )
                                                     })
-                                                    .on_click(|_, cx| {
-                                                        cx.dispatch_action(Box::new(DeletePrompt));
+                                                    .on_click(|_, window, cx| {
+                                                        window.dispatch_action(
+                                                            Box::new(DeletePrompt),
+                                                            cx,
+                                                        );
                                                     })
                                                     .into_any_element()
                                                 })
@@ -1041,17 +1134,19 @@ impl PromptLibrary {
                                                     .style(ButtonStyle::Transparent)
                                                     .shape(IconButtonShape::Square)
                                                     .size(ButtonSize::Large)
-                                                    .tooltip(move |cx| {
+                                                    .tooltip(move |window, cx| {
                                                         Tooltip::for_action(
                                                             "Duplicate Prompt",
                                                             &DuplicatePrompt,
+                                                            window,
                                                             cx,
                                                         )
                                                     })
-                                                    .on_click(|_, cx| {
-                                                        cx.dispatch_action(Box::new(
-                                                            DuplicatePrompt,
-                                                        ));
+                                                    .on_click(|_, window, cx| {
+                                                        window.dispatch_action(
+                                                            Box::new(DuplicatePrompt),
+                                                            cx,
+                                                        );
                                                     }),
                                                 )
                                                 .child(
@@ -1069,20 +1164,18 @@ impl PromptLibrary {
                                                     })
                                                     .shape(IconButtonShape::Square)
                                                     .size(ButtonSize::Large)
-                                                    .tooltip(move |cx| {
-                                                        Tooltip::text(
-                                                            if prompt_metadata.default {
-                                                                "Remove from Default Prompt"
-                                                            } else {
-                                                                "Add to Default Prompt"
-                                                            },
+                                                    .tooltip(Tooltip::text(
+                                                        if prompt_metadata.default {
+                                                            "Remove from Default Prompt"
+                                                        } else {
+                                                            "Add to Default Prompt"
+                                                        },
+                                                    ))
+                                                    .on_click(|_, window, cx| {
+                                                        window.dispatch_action(
+                                                            Box::new(ToggleDefaultPrompt),
                                                             cx,
-                                                        )
-                                                    })
-                                                    .on_click(|_, cx| {
-                                                        cx.dispatch_action(Box::new(
-                                                            ToggleDefaultPrompt,
-                                                        ));
+                                                        );
                                                     }),
                                                 ),
                                         ),
@@ -1103,18 +1196,24 @@ impl PromptLibrary {
 }
 
 impl Render for PromptLibrary {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let ui_font = theme::setup_ui_font(cx);
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+        let ui_font = theme::setup_ui_font(window, cx);
         let theme = cx.theme().clone();
 
         h_flex()
             .id("prompt-manager")
             .key_context("PromptLibrary")
-            .on_action(cx.listener(|this, &NewPrompt, cx| this.new_prompt(cx)))
-            .on_action(cx.listener(|this, &DeletePrompt, cx| this.delete_active_prompt(cx)))
-            .on_action(cx.listener(|this, &DuplicatePrompt, cx| this.duplicate_active_prompt(cx)))
-            .on_action(cx.listener(|this, &ToggleDefaultPrompt, cx| {
-                this.toggle_default_for_active_prompt(cx)
+            .on_action(cx.listener(|this, &NewPrompt, window, cx| this.new_prompt(window, cx)))
+            .on_action(
+                cx.listener(|this, &DeletePrompt, window, cx| {
+                    this.delete_active_prompt(window, cx)
+                }),
+            )
+            .on_action(cx.listener(|this, &DuplicatePrompt, window, cx| {
+                this.duplicate_active_prompt(window, cx)
+            }))
+            .on_action(cx.listener(|this, &ToggleDefaultPrompt, window, cx| {
+                this.toggle_default_for_active_prompt(window, cx)
             }))
             .size_full()
             .overflow_hidden()
@@ -1156,10 +1255,13 @@ impl Render for PromptLibrary {
                                                 Button::new("create-prompt", "New Prompt")
                                                     .full_width()
                                                     .key_binding(KeyBinding::for_action(
-                                                        &NewPrompt, cx,
+                                                        &NewPrompt, window,
                                                     ))
-                                                    .on_click(|_, cx| {
-                                                        cx.dispatch_action(NewPrompt.boxed_clone())
+                                                    .on_click(|_, window, cx| {
+                                                        window.dispatch_action(
+                                                            NewPrompt.boxed_clone(),
+                                                            cx,
+                                                        )
                                                     }),
                                             ),
                                     )

crates/prompt_library/src/prompt_store.rs 🔗

@@ -4,7 +4,7 @@ use collections::HashMap;
 use futures::future::{self, BoxFuture, Shared};
 use futures::FutureExt as _;
 use fuzzy::StringMatchCandidate;
-use gpui::{AppContext, BackgroundExecutor, Global, ReadGlobal, SharedString, Task};
+use gpui::{App, BackgroundExecutor, Global, ReadGlobal, SharedString, Task};
 use heed::{
     types::{SerdeBincode, SerdeJson, Str},
     Database, RoTxn,
@@ -24,7 +24,7 @@ use uuid::Uuid;
 
 /// Init starts loading the PromptStore in the background and assigns
 /// a shared future to a global.
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     let db_path = paths::prompts_dir().join("prompts-library-db.0.mdb");
     let prompt_store_future = PromptStore::new(db_path, cx.background_executor().clone())
         .then(|result| future::ready(result.map(Arc::new).map_err(Arc::new)))
@@ -114,7 +114,7 @@ impl MetadataCache {
 }
 
 impl PromptStore {
-    pub fn global(cx: &AppContext) -> impl Future<Output = Result<Arc<Self>>> {
+    pub fn global(cx: &App) -> impl Future<Output = Result<Arc<Self>>> {
         let store = GlobalPromptStore::global(cx).0.clone();
         async move { store.await.map_err(|err| anyhow!(err)) }
     }

crates/prompt_library/src/prompts.rs 🔗

@@ -2,7 +2,7 @@ use anyhow::Result;
 use assets::Assets;
 use fs::Fs;
 use futures::StreamExt;
-use gpui::{AppContext, AssetSource};
+use gpui::{App, AssetSource};
 use handlebars::{Handlebars, RenderError};
 use language::{BufferSnapshot, LanguageName, Point};
 use parking_lot::Mutex;
@@ -48,7 +48,7 @@ pub struct ProjectSlashCommandPromptContext {
 pub struct PromptLoadingParams<'a> {
     pub fs: Arc<dyn Fs>,
     pub repo_path: Option<PathBuf>,
-    pub cx: &'a gpui::AppContext,
+    pub cx: &'a gpui::App,
 }
 
 pub struct PromptBuilder {
@@ -56,7 +56,7 @@ pub struct PromptBuilder {
 }
 
 impl PromptBuilder {
-    pub fn load(fs: Arc<dyn Fs>, stdout_is_a_pty: bool, cx: &mut AppContext) -> Arc<Self> {
+    pub fn load(fs: Arc<dyn Fs>, stdout_is_a_pty: bool, cx: &mut App) -> Arc<Self> {
         Self::new(Some(PromptLoadingParams {
             fs: fs.clone(),
             repo_path: stdout_is_a_pty

crates/recent_projects/src/disconnected_overlay.rs 🔗

@@ -1,13 +1,13 @@
 use std::path::PathBuf;
 
-use gpui::{ClickEvent, DismissEvent, EventEmitter, FocusHandle, FocusableView, Render, WeakView};
+use gpui::{ClickEvent, DismissEvent, EventEmitter, FocusHandle, Focusable, Render, WeakEntity};
 use project::project_settings::ProjectSettings;
 use remote::SshConnectionOptions;
 use settings::Settings;
 use ui::{
-    div, h_flex, rems, Button, ButtonCommon, ButtonStyle, Clickable, ElevationIndex, FluentBuilder,
-    Headline, HeadlineSize, IconName, IconPosition, InteractiveElement, IntoElement, Label, Modal,
-    ModalFooter, ModalHeader, ParentElement, Section, Styled, StyledExt, ViewContext,
+    div, h_flex, rems, Button, ButtonCommon, ButtonStyle, Clickable, Context, ElevationIndex,
+    FluentBuilder, Headline, HeadlineSize, IconName, IconPosition, InteractiveElement, IntoElement,
+    Label, Modal, ModalFooter, ModalHeader, ParentElement, Section, Styled, StyledExt, Window,
 };
 use workspace::{notifications::DetachAndPromptErr, ModalView, OpenOptions, Workspace};
 
@@ -19,20 +19,24 @@ enum Host {
 }
 
 pub struct DisconnectedOverlay {
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     host: Host,
     focus_handle: FocusHandle,
     finished: bool,
 }
 
 impl EventEmitter<DismissEvent> for DisconnectedOverlay {}
-impl FocusableView for DisconnectedOverlay {
-    fn focus_handle(&self, _cx: &gpui::AppContext) -> gpui::FocusHandle {
+impl Focusable for DisconnectedOverlay {
+    fn focus_handle(&self, _cx: &gpui::App) -> gpui::FocusHandle {
         self.focus_handle.clone()
     }
 }
 impl ModalView for DisconnectedOverlay {
-    fn on_before_dismiss(&mut self, _: &mut ViewContext<Self>) -> workspace::DismissDecision {
+    fn on_before_dismiss(
+        &mut self,
+        _window: &mut Window,
+        _: &mut Context<Self>,
+    ) -> workspace::DismissDecision {
         return workspace::DismissDecision::Dismiss(self.finished);
     }
     fn fade_out_background(&self) -> bool {
@@ -41,40 +45,52 @@ impl ModalView for DisconnectedOverlay {
 }
 
 impl DisconnectedOverlay {
-    pub fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
-        cx.subscribe(workspace.project(), |workspace, project, event, cx| {
-            if !matches!(
-                event,
-                project::Event::DisconnectedFromHost | project::Event::DisconnectedFromSshRemote
-            ) {
-                return;
-            }
-            let handle = cx.view().downgrade();
-
-            let ssh_connection_options = project.read(cx).ssh_connection_options(cx);
-            let host = if let Some(ssh_connection_options) = ssh_connection_options {
-                Host::SshRemoteProject(ssh_connection_options)
-            } else {
-                Host::RemoteProject
-            };
-
-            workspace.toggle_modal(cx, |cx| DisconnectedOverlay {
-                finished: false,
-                workspace: handle,
-                host,
-                focus_handle: cx.focus_handle(),
-            });
-        })
+    pub fn register(
+        workspace: &mut Workspace,
+        window: Option<&mut Window>,
+        cx: &mut Context<Workspace>,
+    ) {
+        let Some(window) = window else {
+            return;
+        };
+        cx.subscribe_in(
+            workspace.project(),
+            window,
+            |workspace, project, event, window, cx| {
+                if !matches!(
+                    event,
+                    project::Event::DisconnectedFromHost
+                        | project::Event::DisconnectedFromSshRemote
+                ) {
+                    return;
+                }
+                let handle = cx.model().downgrade();
+
+                let ssh_connection_options = project.read(cx).ssh_connection_options(cx);
+                let host = if let Some(ssh_connection_options) = ssh_connection_options {
+                    Host::SshRemoteProject(ssh_connection_options)
+                } else {
+                    Host::RemoteProject
+                };
+
+                workspace.toggle_modal(window, cx, |_, cx| DisconnectedOverlay {
+                    finished: false,
+                    workspace: handle,
+                    host,
+                    focus_handle: cx.focus_handle(),
+                });
+            },
+        )
         .detach();
     }
 
-    fn handle_reconnect(&mut self, _: &ClickEvent, cx: &mut ViewContext<Self>) {
+    fn handle_reconnect(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context<Self>) {
         self.finished = true;
         cx.emit(DismissEvent);
 
         match &self.host {
             Host::SshRemoteProject(ssh_connection_options) => {
-                self.reconnect_to_ssh_remote(ssh_connection_options.clone(), cx);
+                self.reconnect_to_ssh_remote(ssh_connection_options.clone(), window, cx);
             }
             _ => {}
         }
@@ -83,7 +99,8 @@ impl DisconnectedOverlay {
     fn reconnect_to_ssh_remote(
         &self,
         connection_options: SshConnectionOptions,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let Some(workspace) = self.workspace.upgrade() else {
             return;
@@ -93,7 +110,7 @@ impl DisconnectedOverlay {
             return;
         };
 
-        let Some(window) = cx.window_handle().downcast::<Workspace>() else {
+        let Some(window_handle) = window.window_handle().downcast::<Workspace>() else {
             return;
         };
 
@@ -101,13 +118,13 @@ impl DisconnectedOverlay {
 
         let paths = ssh_project.paths.iter().map(PathBuf::from).collect();
 
-        cx.spawn(move |_, mut cx| async move {
+        cx.spawn_in(window, move |_, mut cx| async move {
             open_ssh_project(
                 connection_options,
                 paths,
                 app_state,
                 OpenOptions {
-                    replace_window: Some(window),
+                    replace_window: Some(window_handle),
                     ..Default::default()
                 },
                 &mut cx,
@@ -115,17 +132,17 @@ impl DisconnectedOverlay {
             .await?;
             Ok(())
         })
-        .detach_and_prompt_err("Failed to reconnect", cx, |_, _| None);
+        .detach_and_prompt_err("Failed to reconnect", window, cx, |_, _, _| None);
     }
 
-    fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
+    fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {
         self.finished = true;
         cx.emit(DismissEvent)
     }
 }
 
 impl Render for DisconnectedOverlay {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let can_reconnect = matches!(self.host, Host::SshRemoteProject(_));
 
         let message = match &self.host {
@@ -171,8 +188,8 @@ impl Render for DisconnectedOverlay {
                                     Button::new("close-window", "Close Window")
                                         .style(ButtonStyle::Filled)
                                         .layer(ElevationIndex::ModalSurface)
-                                        .on_click(cx.listener(move |_, _, cx| {
-                                            cx.remove_window();
+                                        .on_click(cx.listener(move |_, _, window, _| {
+                                            window.remove_window();
                                         })),
                                 )
                                 .when(can_reconnect, |el| {

crates/recent_projects/src/recent_projects.rs 🔗

@@ -6,8 +6,8 @@ pub use ssh_connections::{is_connecting_over_ssh, open_ssh_project};
 use disconnected_overlay::DisconnectedOverlay;
 use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{
-    Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView,
-    Subscription, Task, View, ViewContext, WeakView,
+    Action, AnyElement, App, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable,
+    Subscription, Task, WeakEntity, Window,
 };
 use ordered_float::OrderedFloat;
 use picker::{
@@ -29,16 +29,15 @@ use workspace::{
 };
 use zed_actions::{OpenRecent, OpenRemote};
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     SshSettings::register(cx);
-    cx.observe_new_views(RecentProjects::register).detach();
-    cx.observe_new_views(RemoteServerProjects::register)
-        .detach();
-    cx.observe_new_views(DisconnectedOverlay::register).detach();
+    cx.observe_new(RecentProjects::register).detach();
+    cx.observe_new(RemoteServerProjects::register).detach();
+    cx.observe_new(DisconnectedOverlay::register).detach();
 }
 
 pub struct RecentProjects {
-    pub picker: View<Picker<RecentProjectsDelegate>>,
+    pub picker: Entity<Picker<RecentProjectsDelegate>>,
     rem_width: f32,
     _subscription: Subscription,
 }
@@ -46,28 +45,33 @@ pub struct RecentProjects {
 impl ModalView for RecentProjects {}
 
 impl RecentProjects {
-    fn new(delegate: RecentProjectsDelegate, rem_width: f32, cx: &mut ViewContext<Self>) -> Self {
-        let picker = cx.new_view(|cx| {
+    fn new(
+        delegate: RecentProjectsDelegate,
+        rem_width: f32,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Self {
+        let picker = cx.new(|cx| {
             // We want to use a list when we render paths, because the items can have different heights (multiple paths).
             if delegate.render_paths {
-                Picker::list(delegate, cx)
+                Picker::list(delegate, window, cx)
             } else {
-                Picker::uniform_list(delegate, cx)
+                Picker::uniform_list(delegate, window, cx)
             }
         });
         let _subscription = cx.subscribe(&picker, |_, _, _, cx| cx.emit(DismissEvent));
         // We do not want to block the UI on a potentially lengthy call to DB, so we're gonna swap
         // out workspace locations once the future runs to completion.
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let workspaces = WORKSPACE_DB
                 .recent_workspaces_on_disk()
                 .await
                 .log_err()
                 .unwrap_or_default();
-            this.update(&mut cx, move |this, cx| {
+            this.update_in(&mut cx, move |this, window, cx| {
                 this.picker.update(cx, move |picker, cx| {
                     picker.delegate.set_workspaces(workspaces);
-                    picker.update_matches(picker.query(cx), cx)
+                    picker.update_matches(picker.query(cx), window, cx)
                 })
             })
             .ok()
@@ -80,17 +84,21 @@ impl RecentProjects {
         }
     }
 
-    fn register(workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>) {
-        workspace.register_action(|workspace, open_recent: &OpenRecent, cx| {
+    fn register(
+        workspace: &mut Workspace,
+        _window: Option<&mut Window>,
+        _cx: &mut Context<Workspace>,
+    ) {
+        workspace.register_action(|workspace, open_recent: &OpenRecent, window, cx| {
             let Some(recent_projects) = workspace.active_modal::<Self>(cx) else {
-                Self::open(workspace, open_recent.create_new_window, cx);
+                Self::open(workspace, open_recent.create_new_window, window, cx);
                 return;
             };
 
             recent_projects.update(cx, |recent_projects, cx| {
                 recent_projects
                     .picker
-                    .update(cx, |picker, cx| picker.cycle_selection(cx))
+                    .update(cx, |picker, cx| picker.cycle_selection(window, cx))
             });
         });
     }
@@ -98,40 +106,41 @@ impl RecentProjects {
     pub fn open(
         workspace: &mut Workspace,
         create_new_window: bool,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
-        let weak = cx.view().downgrade();
-        workspace.toggle_modal(cx, |cx| {
+        let weak = cx.model().downgrade();
+        workspace.toggle_modal(window, cx, |window, cx| {
             let delegate = RecentProjectsDelegate::new(weak, create_new_window, true);
 
-            Self::new(delegate, 34., cx)
+            Self::new(delegate, 34., window, cx)
         })
     }
 }
 
 impl EventEmitter<DismissEvent> for RecentProjects {}
 
-impl FocusableView for RecentProjects {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for RecentProjects {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
 
 impl Render for RecentProjects {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         v_flex()
             .w(rems(self.rem_width))
             .child(self.picker.clone())
-            .on_mouse_down_out(cx.listener(|this, _, cx| {
+            .on_mouse_down_out(cx.listener(|this, _, window, cx| {
                 this.picker.update(cx, |this, cx| {
-                    this.cancel(&Default::default(), cx);
+                    this.cancel(&Default::default(), window, cx);
                 })
             }))
     }
 }
 
 pub struct RecentProjectsDelegate {
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     workspaces: Vec<(WorkspaceId, SerializedWorkspaceLocation)>,
     selected_match_index: usize,
     matches: Vec<StringMatch>,
@@ -143,7 +152,7 @@ pub struct RecentProjectsDelegate {
 }
 
 impl RecentProjectsDelegate {
-    fn new(workspace: WeakView<Workspace>, create_new_window: bool, render_paths: bool) -> Self {
+    fn new(workspace: WeakEntity<Workspace>, create_new_window: bool, render_paths: bool) -> Self {
         Self {
             workspace,
             workspaces: Vec::new(),
@@ -168,16 +177,16 @@ impl EventEmitter<DismissEvent> for RecentProjectsDelegate {}
 impl PickerDelegate for RecentProjectsDelegate {
     type ListItem = ListItem;
 
-    fn placeholder_text(&self, cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, window: &mut Window, _: &mut App) -> Arc<str> {
         let (create_window, reuse_window) = if self.create_new_window {
             (
-                cx.keystroke_text_for(&menu::Confirm),
-                cx.keystroke_text_for(&menu::SecondaryConfirm),
+                window.keystroke_text_for(&menu::Confirm),
+                window.keystroke_text_for(&menu::SecondaryConfirm),
             )
         } else {
             (
-                cx.keystroke_text_for(&menu::SecondaryConfirm),
-                cx.keystroke_text_for(&menu::Confirm),
+                window.keystroke_text_for(&menu::SecondaryConfirm),
+                window.keystroke_text_for(&menu::Confirm),
             )
         };
         Arc::from(format!(
@@ -193,14 +202,20 @@ impl PickerDelegate for RecentProjectsDelegate {
         self.selected_match_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, _cx: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
+    ) {
         self.selected_match_index = ix;
     }
 
     fn update_matches(
         &mut self,
         query: String,
-        cx: &mut ViewContext<Picker<Self>>,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> gpui::Task<()> {
         let query = query.trim_start();
         let smart_case = query.chars().any(|c| c.is_uppercase());
@@ -244,7 +259,7 @@ impl PickerDelegate for RecentProjectsDelegate {
         Task::ready(())
     }
 
-    fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
         if let Some((selected_match, workspace)) = self
             .matches
             .get(self.selected_index())
@@ -266,20 +281,22 @@ impl PickerDelegate for RecentProjectsDelegate {
                             SerializedWorkspaceLocation::Local(paths, _) => {
                                 let paths = paths.paths().to_vec();
                                 if replace_current_window {
-                                    cx.spawn(move |workspace, mut cx| async move {
+                                    cx.spawn_in(window, move |workspace, mut cx| async move {
                                         let continue_replacing = workspace
-                                            .update(&mut cx, |workspace, cx| {
+                                            .update_in(&mut cx, |workspace, window, cx| {
                                                 workspace.prepare_to_close(
                                                     CloseIntent::ReplaceWindow,
+                                                    window,
                                                     cx,
                                                 )
                                             })?
                                             .await?;
                                         if continue_replacing {
                                             workspace
-                                                .update(&mut cx, |workspace, cx| {
-                                                    workspace
-                                                        .open_workspace_for_paths(true, paths, cx)
+                                                .update_in(&mut cx, |workspace, window, cx| {
+                                                    workspace.open_workspace_for_paths(
+                                                        true, paths, window, cx,
+                                                    )
                                                 })?
                                                 .await
                                         } else {
@@ -287,14 +304,14 @@ impl PickerDelegate for RecentProjectsDelegate {
                                         }
                                     })
                                 } else {
-                                    workspace.open_workspace_for_paths(false, paths, cx)
+                                    workspace.open_workspace_for_paths(false, paths, window, cx)
                                 }
                             }
                             SerializedWorkspaceLocation::Ssh(ssh_project) => {
                                 let app_state = workspace.app_state().clone();
 
                                 let replace_window = if replace_current_window {
-                                    cx.window_handle().downcast::<Workspace>()
+                                    window.window_handle().downcast::<Workspace>()
                                 } else {
                                     None
                                 };
@@ -313,7 +330,7 @@ impl PickerDelegate for RecentProjectsDelegate {
 
                                 let paths = ssh_project.paths.iter().map(PathBuf::from).collect();
 
-                                cx.spawn(|_, mut cx| async move {
+                                cx.spawn_in(window, |_, mut cx| async move {
                                     open_ssh_project(
                                         connection_options,
                                         paths,
@@ -332,9 +349,9 @@ impl PickerDelegate for RecentProjectsDelegate {
         }
     }
 
-    fn dismissed(&mut self, _: &mut ViewContext<Picker<Self>>) {}
+    fn dismissed(&mut self, _window: &mut Window, _: &mut Context<Picker<Self>>) {}
 
-    fn no_matches_text(&self, _cx: &mut WindowContext) -> SharedString {
+    fn no_matches_text(&self, _window: &mut Window, _cx: &mut App) -> SharedString {
         if self.workspaces.is_empty() {
             "Recently opened projects will show up here".into()
         } else {
@@ -346,7 +363,8 @@ impl PickerDelegate for RecentProjectsDelegate {
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let hit = self.matches.get(ix)?;
 
@@ -398,7 +416,7 @@ impl PickerDelegate for RecentProjectsDelegate {
                             if !self.render_paths {
                                 highlighted.paths.clear();
                             }
-                            highlighted.render(cx)
+                            highlighted.render(window, cx)
                         }),
                 )
                 .map(|el| {
@@ -406,13 +424,13 @@ impl PickerDelegate for RecentProjectsDelegate {
                         .child(
                             IconButton::new("delete", IconName::Close)
                                 .icon_size(IconSize::Small)
-                                .on_click(cx.listener(move |this, _event, cx| {
+                                .on_click(cx.listener(move |this, _event, window, cx| {
                                     cx.stop_propagation();
-                                    cx.prevent_default();
+                                    window.prevent_default();
 
-                                    this.delegate.delete_recent_project(ix, cx)
+                                    this.delegate.delete_recent_project(ix, window, cx)
                                 }))
-                                .tooltip(|cx| Tooltip::text("Delete from Recent Projects...", cx)),
+                                .tooltip(Tooltip::text("Delete from Recent Projects...")),
                         )
                         .into_any_element();
 
@@ -422,9 +440,9 @@ impl PickerDelegate for RecentProjectsDelegate {
                         el.end_hover_slot::<AnyElement>(delete_button)
                     }
                 })
-                .tooltip(move |cx| {
+                .tooltip(move |_, cx| {
                     let tooltip_highlighted_location = highlighted_match.clone();
-                    cx.new_view(move |_| MatchTooltip {
+                    cx.new(|_| MatchTooltip {
                         highlighted_location: tooltip_highlighted_location,
                     })
                     .into()
@@ -432,7 +450,11 @@ impl PickerDelegate for RecentProjectsDelegate {
         )
     }
 
-    fn render_footer(&self, cx: &mut ViewContext<Picker<Self>>) -> Option<AnyElement> {
+    fn render_footer(
+        &self,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Option<AnyElement> {
         Some(
             h_flex()
                 .w_full()
@@ -443,13 +465,17 @@ impl PickerDelegate for RecentProjectsDelegate {
                 .border_color(cx.theme().colors().border_variant)
                 .child(
                     Button::new("remote", "Open Remote Folder")
-                        .key_binding(KeyBinding::for_action(&OpenRemote, cx))
-                        .on_click(|_, cx| cx.dispatch_action(OpenRemote.boxed_clone())),
+                        .key_binding(KeyBinding::for_action(&OpenRemote, window))
+                        .on_click(|_, window, cx| {
+                            window.dispatch_action(OpenRemote.boxed_clone(), cx)
+                        }),
                 )
                 .child(
                     Button::new("local", "Open Local Folder")
-                        .key_binding(KeyBinding::for_action(&workspace::Open, cx))
-                        .on_click(|_, cx| cx.dispatch_action(workspace::Open.boxed_clone())),
+                        .key_binding(KeyBinding::for_action(&workspace::Open, window))
+                        .on_click(|_, window, cx| {
+                            window.dispatch_action(workspace::Open.boxed_clone(), cx)
+                        }),
                 )
                 .into_any(),
         )
@@ -506,20 +532,27 @@ fn highlights_for_path(
     )
 }
 impl RecentProjectsDelegate {
-    fn delete_recent_project(&self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
+    fn delete_recent_project(
+        &self,
+        ix: usize,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) {
         if let Some(selected_match) = self.matches.get(ix) {
             let (workspace_id, _) = self.workspaces[selected_match.candidate_id];
-            cx.spawn(move |this, mut cx| async move {
+            cx.spawn_in(window, move |this, mut cx| async move {
                 let _ = WORKSPACE_DB.delete_workspace_by_id(workspace_id).await;
                 let workspaces = WORKSPACE_DB
                     .recent_workspaces_on_disk()
                     .await
                     .unwrap_or_default();
-                this.update(&mut cx, move |picker, cx| {
+                this.update_in(&mut cx, move |picker, window, cx| {
                     picker.delegate.set_workspaces(workspaces);
-                    picker.delegate.set_selected_index(ix.saturating_sub(1), cx);
+                    picker
+                        .delegate
+                        .set_selected_index(ix.saturating_sub(1), window, cx);
                     picker.delegate.reset_selected_match_index = false;
-                    picker.update_matches(picker.query(cx), cx)
+                    picker.update_matches(picker.query(cx), window, cx)
                 })
             })
             .detach();
@@ -529,7 +562,7 @@ impl RecentProjectsDelegate {
     fn is_current_workspace(
         &self,
         workspace_id: WorkspaceId,
-        cx: &mut ViewContext<Picker<Self>>,
+        cx: &mut Context<Picker<Self>>,
     ) -> bool {
         if let Some(workspace) = self.workspace.upgrade() {
             let workspace = workspace.read(cx);
@@ -546,8 +579,8 @@ struct MatchTooltip {
 }
 
 impl Render for MatchTooltip {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        tooltip_container(cx, |div, _| {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+        tooltip_container(window, cx, |div, _, _| {
             self.highlighted_location.render_paths_children(div)
         })
     }
@@ -602,7 +635,7 @@ mod tests {
 
         let workspace = cx.update(|cx| cx.windows()[0].downcast::<Workspace>().unwrap());
         workspace
-            .update(cx, |workspace, _| assert!(!workspace.is_edited()))
+            .update(cx, |workspace, _, _| assert!(!workspace.is_edited()))
             .unwrap();
 
         let editor = workspace
@@ -615,17 +648,17 @@ mod tests {
             })
             .unwrap();
         workspace
-            .update(cx, |_, cx| {
-                editor.update(cx, |editor, cx| editor.insert("EDIT", cx));
+            .update(cx, |_, window, cx| {
+                editor.update(cx, |editor, cx| editor.insert("EDIT", window, cx));
             })
             .unwrap();
         workspace
-            .update(cx, |workspace, _| assert!(workspace.is_edited(), "After inserting more text into the editor without saving, we should have a dirty project"))
+            .update(cx, |workspace, _, _| assert!(workspace.is_edited(), "After inserting more text into the editor without saving, we should have a dirty project"))
             .unwrap();
 
         let recent_projects_picker = open_recent_projects(&workspace, cx);
         workspace
-            .update(cx, |_, cx| {
+            .update(cx, |_, _, cx| {
                 recent_projects_picker.update(cx, |picker, cx| {
                     assert_eq!(picker.query(cx), "");
                     let delegate = &mut picker.delegate;
@@ -649,7 +682,7 @@ mod tests {
         );
         cx.dispatch_action(*workspace, menu::Confirm);
         workspace
-            .update(cx, |workspace, cx| {
+            .update(cx, |workspace, _, cx| {
                 assert!(
                     workspace.active_modal::<RecentProjects>(cx).is_none(),
                     "Should remove the modal after selecting new recent project"
@@ -667,7 +700,7 @@ mod tests {
             "Should have no pending prompt after cancelling"
         );
         workspace
-            .update(cx, |workspace, _| {
+            .update(cx, |workspace, _, _| {
                 assert!(
                     workspace.is_edited(),
                     "Should be in the same dirty project after cancelling"
@@ -679,7 +712,7 @@ mod tests {
     fn open_recent_projects(
         workspace: &WindowHandle<Workspace>,
         cx: &mut TestAppContext,
-    ) -> View<Picker<RecentProjectsDelegate>> {
+    ) -> Entity<Picker<RecentProjectsDelegate>> {
         cx.dispatch_action(
             (*workspace).into(),
             OpenRecent {
@@ -687,7 +720,7 @@ mod tests {
             },
         );
         workspace
-            .update(cx, |workspace, cx| {
+            .update(cx, |workspace, _, cx| {
                 workspace
                     .active_modal::<RecentProjects>(cx)
                     .unwrap()

crates/recent_projects/src/remote_servers.rs 🔗

@@ -10,10 +10,10 @@ use futures::FutureExt;
 use gpui::canvas;
 use gpui::ClipboardItem;
 use gpui::Task;
-use gpui::WeakView;
+use gpui::WeakEntity;
 use gpui::{
-    AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Model,
-    PromptLevel, ScrollHandle, View, ViewContext,
+    AnyElement, App, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable,
+    PromptLevel, ScrollHandle, Window,
 };
 use picker::Picker;
 use project::Project;
@@ -49,22 +49,22 @@ mod navigation_base {}
 pub struct RemoteServerProjects {
     mode: Mode,
     focus_handle: FocusHandle,
-    workspace: WeakView<Workspace>,
-    retained_connections: Vec<Model<SshRemoteClient>>,
+    workspace: WeakEntity<Workspace>,
+    retained_connections: Vec<Entity<SshRemoteClient>>,
 }
 
 struct CreateRemoteServer {
-    address_editor: View<Editor>,
+    address_editor: Entity<Editor>,
     address_error: Option<SharedString>,
-    ssh_prompt: Option<View<SshPrompt>>,
+    ssh_prompt: Option<Entity<SshPrompt>>,
     _creating: Option<Task<Option<()>>>,
 }
 
 impl CreateRemoteServer {
-    fn new(cx: &mut WindowContext) -> Self {
-        let address_editor = cx.new_view(Editor::single_line);
+    fn new(window: &mut Window, cx: &mut App) -> Self {
+        let address_editor = cx.new(|cx| Editor::single_line(window, cx));
         address_editor.update(cx, |this, cx| {
-            this.focus_handle(cx).focus(cx);
+            this.focus_handle(cx).focus(window);
         });
         Self {
             address_editor,
@@ -78,20 +78,20 @@ impl CreateRemoteServer {
 struct ProjectPicker {
     connection_string: SharedString,
     nickname: Option<SharedString>,
-    picker: View<Picker<OpenPathDelegate>>,
+    picker: Entity<Picker<OpenPathDelegate>>,
     _path_task: Shared<Task<Option<()>>>,
 }
 
 struct EditNicknameState {
     index: usize,
-    editor: View<Editor>,
+    editor: Entity<Editor>,
 }
 
 impl EditNicknameState {
-    fn new(index: usize, cx: &mut WindowContext) -> Self {
+    fn new(index: usize, window: &mut Window, cx: &mut App) -> Self {
         let this = Self {
             index,
-            editor: cx.new_view(Editor::single_line),
+            editor: cx.new(|cx| Editor::single_line(window, cx)),
         };
         let starting_text = SshSettings::get_global(cx)
             .ssh_connections()
@@ -101,16 +101,16 @@ impl EditNicknameState {
         this.editor.update(cx, |this, cx| {
             this.set_placeholder_text("Add a nickname for this server", cx);
             if let Some(starting_text) = starting_text {
-                this.set_text(starting_text, cx);
+                this.set_text(starting_text, window, cx);
             }
         });
-        this.editor.focus_handle(cx).focus(cx);
+        this.editor.focus_handle(cx).focus(window);
         this
     }
 }
 
-impl FocusableView for ProjectPicker {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for ProjectPicker {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
@@ -119,34 +119,36 @@ impl ProjectPicker {
     fn new(
         ix: usize,
         connection: SshConnectionOptions,
-        project: Model<Project>,
-        workspace: WeakView<Workspace>,
-        cx: &mut ViewContext<RemoteServerProjects>,
-    ) -> View<Self> {
+        project: Entity<Project>,
+        workspace: WeakEntity<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<RemoteServerProjects>,
+    ) -> Entity<Self> {
         let (tx, rx) = oneshot::channel();
         let lister = project::DirectoryLister::Project(project.clone());
         let query = lister.default_query(cx);
         let delegate = file_finder::OpenPathDelegate::new(tx, lister);
 
-        let picker = cx.new_view(|cx| {
-            let picker = Picker::uniform_list(delegate, cx)
+        let picker = cx.new(|cx| {
+            let picker = Picker::uniform_list(delegate, window, cx)
                 .width(rems(34.))
                 .modal(false);
-            picker.set_query(query, cx);
+            picker.set_query(query, window, cx);
             picker
         });
         let connection_string = connection.connection_string().into();
         let nickname = connection.nickname.clone().map(|nick| nick.into());
         let _path_task = cx
-            .spawn({
+            .spawn_in(window, {
                 let workspace = workspace.clone();
                 move |this, mut cx| async move {
                     let Ok(Some(paths)) = rx.await else {
                         workspace
-                            .update(&mut cx, |workspace, cx| {
-                                let weak = cx.view().downgrade();
-                                workspace
-                                    .toggle_modal(cx, |cx| RemoteServerProjects::new(cx, weak));
+                            .update_in(&mut cx, |workspace, window, cx| {
+                                let weak = cx.model().downgrade();
+                                workspace.toggle_modal(window, cx, |window, cx| {
+                                    RemoteServerProjects::new(window, cx, weak)
+                                });
                             })
                             .log_err()?;
                         return None;
@@ -156,11 +158,11 @@ impl ProjectPicker {
                         .update(&mut cx, |workspace, _| workspace.app_state().clone())
                         .ok()?;
                     let options = cx
-                        .update(|cx| (app_state.build_window_options)(None, cx))
+                        .update(|_, cx| (app_state.build_window_options)(None, cx))
                         .log_err()?;
 
-                    cx.open_window(options, |cx| {
-                        cx.activate_window();
+                    cx.open_window(options, |window, cx| {
+                        window.activate_window();
 
                         let fs = app_state.fs.clone();
                         update_settings_file::<SshSettings>(fs, cx, {
@@ -187,21 +189,25 @@ impl ProjectPicker {
                                 })
                             })
                             .collect::<Vec<_>>();
-                        cx.spawn(|_| async move {
-                            for task in tasks {
-                                task.await?;
-                            }
-                            Ok(())
-                        })
-                        .detach_and_prompt_err(
-                            "Failed to open path",
-                            cx,
-                            |_, _| None,
-                        );
+                        window
+                            .spawn(cx, |_| async move {
+                                for task in tasks {
+                                    task.await?;
+                                }
+                                Ok(())
+                            })
+                            .detach_and_prompt_err("Failed to open path", window, cx, |_, _, _| {
+                                None
+                            });
 
-                        cx.new_view(|cx| {
-                            let workspace =
-                                Workspace::new(None, project.clone(), app_state.clone(), cx);
+                        cx.new(|cx| {
+                            let workspace = Workspace::new(
+                                None,
+                                project.clone(),
+                                app_state.clone(),
+                                window,
+                                cx,
+                            );
 
                             workspace
                                 .client()
@@ -220,7 +226,7 @@ impl ProjectPicker {
                 }
             })
             .shared();
-        cx.new_view(|_| Self {
+        cx.new(|_| Self {
             _path_task,
             picker,
             connection_string,
@@ -230,7 +236,7 @@ impl ProjectPicker {
 }
 
 impl gpui::Render for ProjectPicker {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         v_flex()
             .child(
                 SshConnectionHeader {
@@ -238,7 +244,7 @@ impl gpui::Render for ProjectPicker {
                     paths: Default::default(),
                     nickname: self.nickname.clone(),
                 }
-                .render(cx),
+                .render(window, cx),
             )
             .child(
                 div()
@@ -264,7 +270,7 @@ struct DefaultState {
     servers: Vec<ProjectEntry>,
 }
 impl DefaultState {
-    fn new(cx: &WindowContext) -> Self {
+    fn new(cx: &mut App) -> Self {
         let handle = ScrollHandle::new();
         let scrollbar = ScrollbarState::new(handle.clone());
         let add_new_server = NavigableEntry::new(&handle, cx);
@@ -304,34 +310,42 @@ enum Mode {
     Default(DefaultState),
     ViewServerOptions(ViewServerOptionsState),
     EditNickname(EditNicknameState),
-    ProjectPicker(View<ProjectPicker>),
+    ProjectPicker(Entity<ProjectPicker>),
     CreateRemoteServer(CreateRemoteServer),
 }
 
 impl Mode {
-    fn default_mode(cx: &WindowContext) -> Self {
+    fn default_mode(cx: &mut App) -> Self {
         Self::Default(DefaultState::new(cx))
     }
 }
 impl RemoteServerProjects {
-    pub fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-        workspace.register_action(|workspace, _: &OpenRemote, cx| {
-            let handle = cx.view().downgrade();
-            workspace.toggle_modal(cx, |cx| Self::new(cx, handle))
+    pub fn register(
+        workspace: &mut Workspace,
+        _window: Option<&mut Window>,
+        _: &mut Context<Workspace>,
+    ) {
+        workspace.register_action(|workspace, _: &OpenRemote, window, cx| {
+            let handle = cx.model().downgrade();
+            workspace.toggle_modal(window, cx, |window, cx| Self::new(window, cx, handle))
         });
     }
 
-    pub fn open(workspace: View<Workspace>, cx: &mut WindowContext) {
+    pub fn open(workspace: Entity<Workspace>, window: &mut Window, cx: &mut App) {
         workspace.update(cx, |workspace, cx| {
-            let handle = cx.view().downgrade();
-            workspace.toggle_modal(cx, |cx| Self::new(cx, handle))
+            let handle = cx.model().downgrade();
+            workspace.toggle_modal(window, cx, |window, cx| Self::new(window, cx, handle))
         })
     }
 
-    pub fn new(cx: &mut ViewContext<Self>, workspace: WeakView<Workspace>) -> Self {
+    pub fn new(
+        window: &mut Window,
+        cx: &mut Context<Self>,
+        workspace: WeakEntity<Workspace>,
+    ) -> Self {
         let focus_handle = cx.focus_handle();
 
-        let mut base_style = cx.text_style();
+        let mut base_style = window.text_style();
         base_style.refine(&gpui::TextStyleRefinement {
             color: Some(cx.theme().colors().editor_foreground),
             ..Default::default()
@@ -348,16 +362,18 @@ impl RemoteServerProjects {
     pub fn project_picker(
         ix: usize,
         connection_options: remote::SshConnectionOptions,
-        project: Model<Project>,
-        cx: &mut ViewContext<Self>,
-        workspace: WeakView<Workspace>,
+        project: Entity<Project>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+        workspace: WeakEntity<Workspace>,
     ) -> Self {
-        let mut this = Self::new(cx, workspace.clone());
+        let mut this = Self::new(window, cx, workspace.clone());
         this.mode = Mode::ProjectPicker(ProjectPicker::new(
             ix,
             connection_options,
             project,
             workspace,
+            window,
             cx,
         ));
         cx.notify();
@@ -365,7 +381,12 @@ impl RemoteServerProjects {
         this
     }
 
-    fn create_ssh_server(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
+    fn create_ssh_server(
+        &mut self,
+        editor: Entity<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let input = get_text(&editor, cx);
         if input.is_empty() {
             return;
@@ -383,15 +404,16 @@ impl RemoteServerProjects {
                 return;
             }
         };
-        let ssh_prompt = cx.new_view(|cx| SshPrompt::new(&connection_options, cx));
+        let ssh_prompt = cx.new(|cx| SshPrompt::new(&connection_options, window, cx));
 
         let connection = connect_over_ssh(
             ConnectionIdentifier::setup(),
             connection_options.clone(),
             ssh_prompt.clone(),
+            window,
             cx,
         )
-        .prompt_err("Failed to connect", cx, |_, _| None);
+        .prompt_err("Failed to connect", window, cx, |_, _, _| None);
 
         let address_editor = editor.clone();
         let creating = cx.spawn(move |this, mut cx| async move {
@@ -442,14 +464,15 @@ impl RemoteServerProjects {
     fn view_server_options(
         &mut self,
         (server_index, connection): (usize, SshConnection),
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.mode = Mode::ViewServerOptions(ViewServerOptionsState {
             server_index,
             connection,
             entries: std::array::from_fn(|_| NavigableEntry::focusable(cx)),
         });
-        self.focus_handle(cx).focus(cx);
+        self.focus_handle(cx).focus(window);
         cx.notify();
     }
 
@@ -457,7 +480,8 @@ impl RemoteServerProjects {
         &mut self,
         ix: usize,
         ssh_connection: SshConnection,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let Some(workspace) = self.workspace.upgrade() else {
             return;
@@ -465,9 +489,9 @@ impl RemoteServerProjects {
 
         let connection_options = ssh_connection.into();
         workspace.update(cx, |_, cx| {
-            cx.defer(move |workspace, cx| {
-                workspace.toggle_modal(cx, |cx| {
-                    SshConnectionModal::new(&connection_options, Vec::new(), cx)
+            cx.defer_in(window, move |workspace, window, cx| {
+                workspace.toggle_modal(window, cx, |window, cx| {
+                    SshConnectionModal::new(&connection_options, Vec::new(), window, cx)
                 });
                 let prompt = workspace
                     .active_modal::<SshConnectionModal>(cx)
@@ -480,11 +504,12 @@ impl RemoteServerProjects {
                     ConnectionIdentifier::setup(),
                     connection_options.clone(),
                     prompt,
+                    window,
                     cx,
                 )
-                .prompt_err("Failed to connect", cx, |_, _| None);
+                .prompt_err("Failed to connect", window, cx, |_, _, _| None);
 
-                cx.spawn(move |workspace, mut cx| async move {
+                cx.spawn_in(window, move |workspace, mut cx| async move {
                     let session = connect.await;
 
                     workspace
@@ -497,19 +522,20 @@ impl RemoteServerProjects {
 
                     let Some(Some(session)) = session else {
                         workspace
-                            .update(&mut cx, |workspace, cx| {
-                                let weak = cx.view().downgrade();
-                                workspace
-                                    .toggle_modal(cx, |cx| RemoteServerProjects::new(cx, weak));
+                            .update_in(&mut cx, |workspace, window, cx| {
+                                let weak = cx.model().downgrade();
+                                workspace.toggle_modal(window, cx, |window, cx| {
+                                    RemoteServerProjects::new(window, cx, weak)
+                                });
                             })
                             .log_err();
                         return;
                     };
 
                     workspace
-                        .update(&mut cx, |workspace, cx| {
+                        .update_in(&mut cx, |workspace, window, cx| {
                             let app_state = workspace.app_state().clone();
-                            let weak = cx.view().downgrade();
+                            let weak = cx.model().downgrade();
                             let project = project::Project::ssh(
                                 session,
                                 app_state.client.clone(),
@@ -519,11 +545,12 @@ impl RemoteServerProjects {
                                 app_state.fs.clone(),
                                 cx,
                             );
-                            workspace.toggle_modal(cx, |cx| {
+                            workspace.toggle_modal(window, cx, |window, cx| {
                                 RemoteServerProjects::project_picker(
                                     ix,
                                     connection_options,
                                     project,
+                                    window,
                                     cx,
                                     weak,
                                 )
@@ -536,19 +563,19 @@ impl RemoteServerProjects {
         })
     }
 
-    fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
+    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
         match &self.mode {
             Mode::Default(_) | Mode::ViewServerOptions(_) => {}
             Mode::ProjectPicker(_) => {}
             Mode::CreateRemoteServer(state) => {
                 if let Some(prompt) = state.ssh_prompt.as_ref() {
                     prompt.update(cx, |prompt, cx| {
-                        prompt.confirm(cx);
+                        prompt.confirm(window, cx);
                     });
                     return;
                 }
 
-                self.create_ssh_server(state.address_editor.clone(), cx);
+                self.create_ssh_server(state.address_editor.clone(), window, cx);
             }
             Mode::EditNickname(state) => {
                 let text = Some(state.editor.read(cx).text(cx)).filter(|text| !text.is_empty());
@@ -561,19 +588,19 @@ impl RemoteServerProjects {
                     }
                 });
                 self.mode = Mode::default_mode(cx);
-                self.focus_handle.focus(cx);
+                self.focus_handle.focus(window);
             }
         }
     }
 
-    fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
+    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
         match &self.mode {
             Mode::Default(_) => cx.emit(DismissEvent),
             Mode::CreateRemoteServer(state) if state.ssh_prompt.is_some() => {
-                let new_state = CreateRemoteServer::new(cx);
+                let new_state = CreateRemoteServer::new(window, cx);
                 let old_prompt = state.address_editor.read(cx).text(cx);
                 new_state.address_editor.update(cx, |this, cx| {
-                    this.set_text(old_prompt, cx);
+                    this.set_text(old_prompt, window, cx);
                 });
 
                 self.mode = Mode::CreateRemoteServer(new_state);
@@ -581,7 +608,7 @@ impl RemoteServerProjects {
             }
             _ => {
                 self.mode = Mode::default_mode(cx);
-                self.focus_handle(cx).focus(cx);
+                self.focus_handle(cx).focus(window);
                 cx.notify();
             }
         }
@@ -591,7 +618,8 @@ impl RemoteServerProjects {
         &mut self,
         ix: usize,
         ssh_server: ProjectEntry,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> impl IntoElement {
         let (main_label, aux_label) = if let Some(nickname) = ssh_server.connection.nickname.clone()
         {
@@ -633,6 +661,7 @@ impl RemoteServerProjects {
                             &ssh_server,
                             pix,
                             p,
+                            window,
                             cx,
                         ))
                     }))
@@ -643,10 +672,11 @@ impl RemoteServerProjects {
                             .anchor_scroll(ssh_server.open_folder.scroll_anchor.clone())
                             .on_action(cx.listener({
                                 let ssh_connection = ssh_server.clone();
-                                move |this, _: &menu::Confirm, cx| {
+                                move |this, _: &menu::Confirm, window, cx| {
                                     this.create_ssh_project(
                                         ix,
                                         ssh_connection.connection.clone(),
+                                        window,
                                         cx,
                                     );
                                 }
@@ -654,7 +684,10 @@ impl RemoteServerProjects {
                             .child(
                                 ListItem::new(("new-remote-project", ix))
                                     .toggle_state(
-                                        ssh_server.open_folder.focus_handle.contains_focused(cx),
+                                        ssh_server
+                                            .open_folder
+                                            .focus_handle
+                                            .contains_focused(window, cx),
                                     )
                                     .inset(true)
                                     .spacing(ui::ListItemSpacing::Sparse)
@@ -662,10 +695,11 @@ impl RemoteServerProjects {
                                     .child(Label::new("Open Folder"))
                                     .on_click(cx.listener({
                                         let ssh_connection = ssh_server.clone();
-                                        move |this, _, cx| {
+                                        move |this, _, window, cx| {
                                             this.create_ssh_project(
                                                 ix,
                                                 ssh_connection.connection.clone(),
+                                                window,
                                                 cx,
                                             );
                                         }
@@ -679,9 +713,10 @@ impl RemoteServerProjects {
                             .anchor_scroll(ssh_server.configure.scroll_anchor.clone())
                             .on_action(cx.listener({
                                 let ssh_connection = ssh_server.clone();
-                                move |this, _: &menu::Confirm, cx| {
+                                move |this, _: &menu::Confirm, window, cx| {
                                     this.view_server_options(
                                         (ix, ssh_connection.connection.clone()),
+                                        window,
                                         cx,
                                     );
                                 }
@@ -689,7 +724,10 @@ impl RemoteServerProjects {
                             .child(
                                 ListItem::new(("server-options", ix))
                                     .toggle_state(
-                                        ssh_server.configure.focus_handle.contains_focused(cx),
+                                        ssh_server
+                                            .configure
+                                            .focus_handle
+                                            .contains_focused(window, cx),
                                     )
                                     .inset(true)
                                     .spacing(ui::ListItemSpacing::Sparse)
@@ -697,9 +735,10 @@ impl RemoteServerProjects {
                                     .child(Label::new("View Server Options"))
                                     .on_click(cx.listener({
                                         let ssh_connection = ssh_server.clone();
-                                        move |this, _, cx| {
+                                        move |this, _, window, cx| {
                                             this.view_server_options(
                                                 (ix, ssh_connection.connection.clone()),
+                                                window,
                                                 cx,
                                             );
                                         }
@@ -715,7 +754,8 @@ impl RemoteServerProjects {
         server: &ProjectEntry,
         ix: usize,
         (navigation, project): &(NavigableEntry, SshProject),
-        cx: &ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> impl IntoElement {
         let server = server.clone();
         let element_id_base = SharedString::from(format!("remote-project-{server_ix}"));
@@ -724,7 +764,7 @@ impl RemoteServerProjects {
 
         let callback = Arc::new({
             let project = project.clone();
-            move |this: &mut Self, cx: &mut ViewContext<Self>| {
+            move |this: &mut Self, window: &mut Window, cx: &mut Context<Self>| {
                 let Some(app_state) = this
                     .workspace
                     .update(cx, |workspace, _| workspace.app_state().clone())
@@ -735,7 +775,7 @@ impl RemoteServerProjects {
                 let project = project.clone();
                 let server = server.connection.clone();
                 cx.emit(DismissEvent);
-                cx.spawn(|_, mut cx| async move {
+                cx.spawn_in(window, |_, mut cx| async move {
                     let result = open_ssh_project(
                         server.into(),
                         project.paths.into_iter().map(PathBuf::from).collect(),
@@ -766,13 +806,13 @@ impl RemoteServerProjects {
             .anchor_scroll(navigation.scroll_anchor.clone())
             .on_action(cx.listener({
                 let callback = callback.clone();
-                move |this, _: &menu::Confirm, cx| {
-                    callback(this, cx);
+                move |this, _: &menu::Confirm, window, cx| {
+                    callback(this, window, cx);
                 }
             }))
             .child(
                 ListItem::new((element_id_base, ix))
-                    .toggle_state(navigation.focus_handle.contains_focused(cx))
+                    .toggle_state(navigation.focus_handle.contains_focused(window, cx))
                     .inset(true)
                     .spacing(ui::ListItemSpacing::Sparse)
                     .start_slot(
@@ -781,7 +821,7 @@ impl RemoteServerProjects {
                             .size(IconSize::Small),
                     )
                     .child(Label::new(project.paths.join(", ")))
-                    .on_click(cx.listener(move |this, _, cx| callback(this, cx)))
+                    .on_click(cx.listener(move |this, _, window, cx| callback(this, window, cx)))
                     .end_hover_slot::<AnyElement>(Some(
                         div()
                             .mr_2()
@@ -792,8 +832,8 @@ impl RemoteServerProjects {
                                     .icon_size(IconSize::Small)
                                     .shape(IconButtonShape::Square)
                                     .size(ButtonSize::Large)
-                                    .tooltip(|cx| Tooltip::text("Delete Remote Project", cx))
-                                    .on_click(cx.listener(move |this, _, cx| {
+                                    .tooltip(Tooltip::text("Delete Remote Project"))
+                                    .on_click(cx.listener(move |this, _, _, cx| {
                                         this.delete_ssh_project(server_ix, &project, cx)
                                     }))
                             })
@@ -804,8 +844,8 @@ impl RemoteServerProjects {
 
     fn update_settings_file(
         &mut self,
-        cx: &mut ViewContext<Self>,
-        f: impl FnOnce(&mut RemoteSettingsContent, &AppContext) + Send + Sync + 'static,
+        cx: &mut Context<Self>,
+        f: impl FnOnce(&mut RemoteSettingsContent, &App) + Send + Sync + 'static,
     ) {
         let Some(fs) = self
             .workspace
@@ -817,7 +857,7 @@ impl RemoteServerProjects {
         update_settings_file::<SshSettings>(fs, cx, move |setting, cx| f(setting, cx));
     }
 
-    fn delete_ssh_server(&mut self, server: usize, cx: &mut ViewContext<Self>) {
+    fn delete_ssh_server(&mut self, server: usize, cx: &mut Context<Self>) {
         self.update_settings_file(cx, move |setting, _| {
             if let Some(connections) = setting.ssh_connections.as_mut() {
                 connections.remove(server);
@@ -825,12 +865,7 @@ impl RemoteServerProjects {
         });
     }
 
-    fn delete_ssh_project(
-        &mut self,
-        server: usize,
-        project: &SshProject,
-        cx: &mut ViewContext<Self>,
-    ) {
+    fn delete_ssh_project(&mut self, server: usize, project: &SshProject, cx: &mut Context<Self>) {
         let project = project.clone();
         self.update_settings_file(cx, move |setting, _| {
             if let Some(server) = setting
@@ -846,7 +881,7 @@ impl RemoteServerProjects {
     fn add_ssh_server(
         &mut self,
         connection_options: remote::SshConnectionOptions,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.update_settings_file(cx, move |setting, _| {
             setting
@@ -867,7 +902,7 @@ impl RemoteServerProjects {
     fn render_create_remote_server(
         &self,
         state: &CreateRemoteServer,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> impl IntoElement {
         let ssh_prompt = state.ssh_prompt.clone();
 
@@ -927,7 +962,7 @@ impl RemoteServerProjects {
                                             .size(ButtonSize::None)
                                             .color(Color::Accent)
                                             .style(ButtonStyle::Transparent)
-                                            .on_click(|_, cx| {
+                                            .on_click(|_, _, cx| {
                                                 cx.open_url(
                                                     "https://zed.dev/docs/remote-development",
                                                 );
@@ -946,7 +981,8 @@ impl RemoteServerProjects {
             connection,
             entries,
         }: ViewServerOptionsState,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> impl IntoElement {
         let connection_string = connection.host.clone();
 
@@ -960,7 +996,7 @@ impl RemoteServerProjects {
                         paths: Default::default(),
                         nickname: connection.nickname.clone().map(|s| s.into()),
                     }
-                    .render(cx),
+                    .render(window, cx),
                 )
                 .child(
                     v_flex()
@@ -975,23 +1011,29 @@ impl RemoteServerProjects {
                             div()
                                 .id("ssh-options-add-nickname")
                                 .track_focus(&entries[0].focus_handle)
-                                .on_action(cx.listener(move |this, _: &menu::Confirm, cx| {
-                                    this.mode = Mode::EditNickname(EditNicknameState::new(
-                                        server_index,
-                                        cx,
-                                    ));
-                                    cx.notify();
-                                }))
+                                .on_action(cx.listener(
+                                    move |this, _: &menu::Confirm, window, cx| {
+                                        this.mode = Mode::EditNickname(EditNicknameState::new(
+                                            server_index,
+                                            window,
+                                            cx,
+                                        ));
+                                        cx.notify();
+                                    },
+                                ))
                                 .child(
                                     ListItem::new("add-nickname")
-                                        .toggle_state(entries[0].focus_handle.contains_focused(cx))
+                                        .toggle_state(
+                                            entries[0].focus_handle.contains_focused(window, cx),
+                                        )
                                         .inset(true)
                                         .spacing(ui::ListItemSpacing::Sparse)
                                         .start_slot(Icon::new(IconName::Pencil).color(Color::Muted))
                                         .child(Label::new(label))
-                                        .on_click(cx.listener(move |this, _, cx| {
+                                        .on_click(cx.listener(move |this, _, window, cx| {
                                             this.mode = Mode::EditNickname(EditNicknameState::new(
                                                 server_index,
+                                                window,
                                                 cx,
                                             ));
                                             cx.notify();
@@ -1001,9 +1043,9 @@ impl RemoteServerProjects {
                         .child({
                             let workspace = self.workspace.clone();
                             fn callback(
-                                workspace: WeakView<Workspace>,
+                                workspace: WeakEntity<Workspace>,
                                 connection_string: SharedString,
-                                cx: &mut WindowContext,
+                                cx: &mut App,
                             ) {
                                 cx.write_to_clipboard(ClipboardItem::new_string(
                                     connection_string.to_string(),
@@ -1037,13 +1079,15 @@ impl RemoteServerProjects {
                                 .on_action({
                                     let connection_string = connection_string.clone();
                                     let workspace = self.workspace.clone();
-                                    move |_: &menu::Confirm, cx| {
+                                    move |_: &menu::Confirm, _, cx| {
                                         callback(workspace.clone(), connection_string.clone(), cx);
                                     }
                                 })
                                 .child(
                                     ListItem::new("copy-server-address")
-                                        .toggle_state(entries[1].focus_handle.contains_focused(cx))
+                                        .toggle_state(
+                                            entries[1].focus_handle.contains_focused(window, cx),
+                                        )
                                         .inset(true)
                                         .spacing(ui::ListItemSpacing::Sparse)
                                         .start_slot(Icon::new(IconName::Copy).color(Color::Muted))
@@ -1054,7 +1098,7 @@ impl RemoteServerProjects {
                                         )
                                         .on_click({
                                             let connection_string = connection_string.clone();
-                                            move |_, cx| {
+                                            move |_, _, cx| {
                                                 callback(
                                                     workspace.clone(),
                                                     connection_string.clone(),
@@ -1066,19 +1110,21 @@ impl RemoteServerProjects {
                         })
                         .child({
                             fn remove_ssh_server(
-                                remote_servers: View<RemoteServerProjects>,
+                                remote_servers: Entity<RemoteServerProjects>,
                                 index: usize,
                                 connection_string: SharedString,
-                                cx: &mut WindowContext,
+                                window: &mut Window,
+                                cx: &mut App,
                             ) {
                                 let prompt_message =
                                     format!("Remove server `{}`?", connection_string);
 
-                                let confirmation = cx.prompt(
+                                let confirmation = window.prompt(
                                     PromptLevel::Warning,
                                     &prompt_message,
                                     None,
                                     &["Yes, remove it", "No, keep it"],
+                                    cx,
                                 );
 
                                 cx.spawn(|mut cx| async move {
@@ -1104,31 +1150,35 @@ impl RemoteServerProjects {
                                 .track_focus(&entries[2].focus_handle)
                                 .on_action(cx.listener({
                                     let connection_string = connection_string.clone();
-                                    move |_, _: &menu::Confirm, cx| {
+                                    move |_, _: &menu::Confirm, window, cx| {
                                         remove_ssh_server(
-                                            cx.view().clone(),
+                                            cx.model().clone(),
                                             server_index,
                                             connection_string.clone(),
+                                            window,
                                             cx,
                                         );
-                                        cx.focus_self();
+                                        cx.focus_self(window);
                                     }
                                 }))
                                 .child(
                                     ListItem::new("remove-server")
-                                        .toggle_state(entries[2].focus_handle.contains_focused(cx))
+                                        .toggle_state(
+                                            entries[2].focus_handle.contains_focused(window, cx),
+                                        )
                                         .inset(true)
                                         .spacing(ui::ListItemSpacing::Sparse)
                                         .start_slot(Icon::new(IconName::Trash).color(Color::Error))
                                         .child(Label::new("Remove Server").color(Color::Error))
-                                        .on_click(cx.listener(move |_, _, cx| {
+                                        .on_click(cx.listener(move |_, _, window, cx| {
                                             remove_ssh_server(
-                                                cx.view().clone(),
+                                                cx.model().clone(),
                                                 server_index,
                                                 connection_string.clone(),
+                                                window,
                                                 cx,
                                             );
-                                            cx.focus_self();
+                                            cx.focus_self(window);
                                         })),
                                 )
                         })
@@ -1137,23 +1187,25 @@ impl RemoteServerProjects {
                             div()
                                 .id("ssh-options-copy-server-address")
                                 .track_focus(&entries[3].focus_handle)
-                                .on_action(cx.listener(|this, _: &menu::Confirm, cx| {
+                                .on_action(cx.listener(|this, _: &menu::Confirm, window, cx| {
                                     this.mode = Mode::default_mode(cx);
-                                    cx.focus_self();
+                                    cx.focus_self(window);
                                     cx.notify();
                                 }))
                                 .child(
                                     ListItem::new("go-back")
-                                        .toggle_state(entries[3].focus_handle.contains_focused(cx))
+                                        .toggle_state(
+                                            entries[3].focus_handle.contains_focused(window, cx),
+                                        )
                                         .inset(true)
                                         .spacing(ui::ListItemSpacing::Sparse)
                                         .start_slot(
                                             Icon::new(IconName::ArrowLeft).color(Color::Muted),
                                         )
                                         .child(Label::new("Go Back"))
-                                        .on_click(cx.listener(|this, _, cx| {
+                                        .on_click(cx.listener(|this, _, window, cx| {
                                             this.mode = Mode::default_mode(cx);
-                                            cx.focus_self();
+                                            cx.focus_self(window);
                                             cx.notify()
                                         })),
                                 )
@@ -1165,13 +1217,14 @@ impl RemoteServerProjects {
             view = view.entry(entry);
         }
 
-        view.render(cx).into_any_element()
+        view.render(window, cx).into_any_element()
     }
 
     fn render_edit_nickname(
         &self,
         state: &EditNicknameState,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> impl IntoElement {
         let Some(connection) = SshSettings::get_global(cx)
             .ssh_connections()
@@ -1194,7 +1247,7 @@ impl RemoteServerProjects {
                     paths: Default::default(),
                     nickname,
                 }
-                .render(cx),
+                .render(window, cx),
             )
             .child(
                 h_flex()
@@ -1208,7 +1261,8 @@ impl RemoteServerProjects {
     fn render_default(
         &mut self,
         mut state: DefaultState,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> impl IntoElement {
         if SshSettings::get_global(cx)
             .ssh_connections
@@ -1226,27 +1280,32 @@ impl RemoteServerProjects {
                 state = new_state.clone();
             }
         }
-        let scroll_state = state.scrollbar.parent_view(cx.view());
+        let scroll_state = state.scrollbar.parent_model(&cx.model());
         let connect_button = div()
             .id("ssh-connect-new-server-container")
             .track_focus(&state.add_new_server.focus_handle)
             .anchor_scroll(state.add_new_server.scroll_anchor.clone())
             .child(
                 ListItem::new("register-remove-server-button")
-                    .toggle_state(state.add_new_server.focus_handle.contains_focused(cx))
+                    .toggle_state(
+                        state
+                            .add_new_server
+                            .focus_handle
+                            .contains_focused(window, cx),
+                    )
                     .inset(true)
                     .spacing(ui::ListItemSpacing::Sparse)
                     .start_slot(Icon::new(IconName::Plus).color(Color::Muted))
                     .child(Label::new("Connect New Server"))
-                    .on_click(cx.listener(|this, _, cx| {
-                        let state = CreateRemoteServer::new(cx);
+                    .on_click(cx.listener(|this, _, window, cx| {
+                        let state = CreateRemoteServer::new(window, cx);
                         this.mode = Mode::CreateRemoteServer(state);
 
                         cx.notify();
                     })),
             )
-            .on_action(cx.listener(|this, _: &menu::Confirm, cx| {
-                let state = CreateRemoteServer::new(cx);
+            .on_action(cx.listener(|this, _: &menu::Confirm, window, cx| {
+                let state = CreateRemoteServer::new(window, cx);
                 this.mode = Mode::CreateRemoteServer(state);
 
                 cx.notify();
@@ -1281,7 +1340,7 @@ impl RemoteServerProjects {
                                 .into_any_element(),
                         )
                         .children(state.servers.iter().enumerate().map(|(ix, connection)| {
-                            self.render_ssh_connection(ix, connection.clone(), cx)
+                            self.render_ssh_connection(ix, connection.clone(), window, cx)
                                 .into_any_element()
                         })),
                 )
@@ -1297,7 +1356,7 @@ impl RemoteServerProjects {
                 .entry(server.open_folder.clone())
                 .entry(server.configure.clone());
         }
-        let mut modal_section = modal_section.render(cx).into_any_element();
+        let mut modal_section = modal_section.render(window, cx).into_any_element();
 
         Modal::new("remote-projects", None)
             .header(
@@ -1313,16 +1372,17 @@ impl RemoteServerProjects {
                         .child(ListSeparator)
                         .child(
                             canvas(
-                                |bounds, cx| {
+                                |bounds, window, cx| {
                                     modal_section.prepaint_as_root(
                                         bounds.origin,
                                         bounds.size.into(),
+                                        window,
                                         cx,
                                     );
                                     modal_section
                                 },
-                                |_, mut modal_section, cx| {
-                                    modal_section.paint(cx);
+                                |_, mut modal_section, window, cx| {
+                                    modal_section.paint(window, cx);
                                 },
                             )
                             .size_full(),

crates/recent_projects/src/ssh_connections.rs 🔗

@@ -7,11 +7,10 @@ use editor::Editor;
 use extension_host::ExtensionStore;
 use futures::channel::oneshot;
 use gpui::{
-    percentage, Animation, AnimationExt, AnyWindowHandle, AsyncAppContext, DismissEvent,
-    EventEmitter, FocusableView, FontFeatures, ParentElement as _, PromptLevel, Render,
-    SemanticVersion, SharedString, Task, TextStyleRefinement, Transformation, View, WeakView,
+    percentage, Animation, AnimationExt, AnyWindowHandle, App, AsyncAppContext, DismissEvent,
+    Entity, EventEmitter, Focusable, FontFeatures, ParentElement as _, PromptLevel, Render,
+    SemanticVersion, SharedString, Task, TextStyleRefinement, Transformation, WeakEntity,
 };
-use gpui::{AppContext, Model};
 
 use language::CursorShape;
 use markdown::{Markdown, MarkdownStyle};
@@ -23,8 +22,8 @@ use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsSources};
 use theme::ThemeSettings;
 use ui::{
-    prelude::*, ActiveTheme, Color, Icon, IconName, IconSize, InteractiveElement, IntoElement,
-    Label, LabelCommon, Styled, ViewContext, VisualContext, WindowContext,
+    prelude::*, ActiveTheme, Color, Context, Icon, IconName, IconSize, InteractiveElement,
+    IntoElement, Label, LabelCommon, Styled, Window,
 };
 use workspace::{AppState, ModalView, Workspace};
 
@@ -118,7 +117,7 @@ impl Settings for SshSettings {
 
     type FileContent = RemoteSettingsContent;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
         sources.json_merge()
     }
 }
@@ -127,9 +126,9 @@ pub struct SshPrompt {
     connection_string: SharedString,
     nickname: Option<SharedString>,
     status_message: Option<SharedString>,
-    prompt: Option<(View<Markdown>, oneshot::Sender<Result<String>>)>,
+    prompt: Option<(Entity<Markdown>, oneshot::Sender<Result<String>>)>,
     cancellation: Option<oneshot::Sender<()>>,
-    editor: View<Editor>,
+    editor: Entity<Editor>,
 }
 
 impl Drop for SshPrompt {
@@ -141,7 +140,7 @@ impl Drop for SshPrompt {
 }
 
 pub struct SshConnectionModal {
-    pub(crate) prompt: View<SshPrompt>,
+    pub(crate) prompt: Entity<SshPrompt>,
     paths: Vec<PathBuf>,
     finished: bool,
 }
@@ -149,7 +148,8 @@ pub struct SshConnectionModal {
 impl SshPrompt {
     pub(crate) fn new(
         connection_options: &SshConnectionOptions,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let connection_string = connection_options.connection_string().into();
         let nickname = connection_options.nickname.clone().map(|s| s.into());
@@ -157,7 +157,7 @@ impl SshPrompt {
         Self {
             connection_string,
             nickname,
-            editor: cx.new_view(Editor::single_line),
+            editor: cx.new(|cx| Editor::single_line(window, cx)),
             status_message: None,
             cancellation: None,
             prompt: None,
@@ -172,11 +172,12 @@ impl SshPrompt {
         &mut self,
         prompt: String,
         tx: oneshot::Sender<Result<String>>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let theme = ThemeSettings::get_global(cx);
 
-        let mut text_style = cx.text_style();
+        let mut text_style = window.text_style();
         let refinement = TextStyleRefinement {
             font_family: Some(theme.buffer_font.family.clone()),
             font_features: Some(FontFeatures::disable_ligatures()),
@@ -201,33 +202,32 @@ impl SshPrompt {
             selection_background_color: cx.theme().players().local().selection,
             ..Default::default()
         };
-        let markdown = cx.new_view(|cx| Markdown::new_text(prompt, markdown_style, None, None, cx));
+        let markdown =
+            cx.new(|cx| Markdown::new_text(prompt, markdown_style, None, None, window, cx));
         self.prompt = Some((markdown, tx));
         self.status_message.take();
-        cx.focus_view(&self.editor);
+        window.focus(&self.editor.focus_handle(cx));
         cx.notify();
     }
 
-    pub fn set_status(&mut self, status: Option<String>, cx: &mut ViewContext<Self>) {
+    pub fn set_status(&mut self, status: Option<String>, cx: &mut Context<Self>) {
         self.status_message = status.map(|s| s.into());
         cx.notify();
     }
 
-    pub fn confirm(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn confirm(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if let Some((_, tx)) = self.prompt.take() {
             self.status_message = Some("Connecting".into());
             self.editor.update(cx, |editor, cx| {
                 tx.send(Ok(editor.text(cx))).ok();
-                editor.clear(cx);
+                editor.clear(window, cx);
             });
         }
     }
 }
 
 impl Render for SshPrompt {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let cx = cx.window_context();
-
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         v_flex()
             .key_context("PasswordPrompt")
             .py_2()
@@ -273,25 +273,27 @@ impl SshConnectionModal {
     pub(crate) fn new(
         connection_options: &SshConnectionOptions,
         paths: Vec<PathBuf>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         Self {
-            prompt: cx.new_view(|cx| SshPrompt::new(connection_options, cx)),
+            prompt: cx.new(|cx| SshPrompt::new(connection_options, window, cx)),
             finished: false,
             paths,
         }
     }
 
-    fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
-        self.prompt.update(cx, |prompt, cx| prompt.confirm(cx))
+    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
+        self.prompt
+            .update(cx, |prompt, cx| prompt.confirm(window, cx))
     }
 
-    pub fn finished(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn finished(&mut self, cx: &mut Context<Self>) {
         self.finished = true;
         cx.emit(DismissEvent);
     }
 
-    fn dismiss(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
+    fn dismiss(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {
         if let Some(tx) = self
             .prompt
             .update(cx, |prompt, _cx| prompt.cancellation.take())
@@ -309,7 +311,7 @@ pub(crate) struct SshConnectionHeader {
 }
 
 impl RenderOnce for SshConnectionHeader {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let theme = cx.theme();
 
         let mut header_color = theme.colors().text;
@@ -357,7 +359,7 @@ impl RenderOnce for SshConnectionHeader {
 }
 
 impl Render for SshConnectionModal {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl ui::IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement {
         let nickname = self.prompt.read(cx).nickname.clone();
         let connection_string = self.prompt.read(cx).connection_string.clone();
 
@@ -379,7 +381,7 @@ impl Render for SshConnectionModal {
                     connection_string,
                     nickname,
                 }
-                .render(cx),
+                .render(window, cx),
             )
             .child(
                 div()
@@ -393,8 +395,8 @@ impl Render for SshConnectionModal {
     }
 }
 
-impl FocusableView for SshConnectionModal {
-    fn focus_handle(&self, cx: &gpui::AppContext) -> gpui::FocusHandle {
+impl Focusable for SshConnectionModal {
+    fn focus_handle(&self, cx: &gpui::App) -> gpui::FocusHandle {
         self.prompt.read(cx).editor.focus_handle(cx)
     }
 }
@@ -402,7 +404,11 @@ impl FocusableView for SshConnectionModal {
 impl EventEmitter<DismissEvent> for SshConnectionModal {}
 
 impl ModalView for SshConnectionModal {
-    fn on_before_dismiss(&mut self, _: &mut ViewContext<Self>) -> workspace::DismissDecision {
+    fn on_before_dismiss(
+        &mut self,
+        _window: &mut Window,
+        _: &mut Context<Self>,
+    ) -> workspace::DismissDecision {
         return workspace::DismissDecision::Dismiss(self.finished);
     }
 
@@ -414,7 +420,7 @@ impl ModalView for SshConnectionModal {
 #[derive(Clone)]
 pub struct SshClientDelegate {
     window: AnyWindowHandle,
-    ui: WeakView<SshPrompt>,
+    ui: WeakEntity<SshPrompt>,
     known_password: Option<String>,
 }
 
@@ -430,9 +436,9 @@ impl remote::SshClientDelegate for SshClientDelegate {
             tx.send(Ok(password)).ok();
         } else {
             self.window
-                .update(cx, |_, cx| {
+                .update(cx, |_, window, cx| {
                     self.ui.update(cx, |modal, cx| {
-                        modal.set_prompt(prompt, tx, cx);
+                        modal.set_prompt(prompt, tx, window, cx);
                     })
                 })
                 .ok();
@@ -498,7 +504,7 @@ impl remote::SshClientDelegate for SshClientDelegate {
 impl SshClientDelegate {
     fn update_status(&self, status: Option<&str>, cx: &mut AsyncAppContext) {
         self.window
-            .update(cx, |_, cx| {
+            .update(cx, |_, _, cx| {
                 self.ui.update(cx, |modal, cx| {
                     modal.set_status(status.map(|s| s.to_string()), cx);
                 })
@@ -507,17 +513,18 @@ impl SshClientDelegate {
     }
 }
 
-pub fn is_connecting_over_ssh(workspace: &Workspace, cx: &AppContext) -> bool {
+pub fn is_connecting_over_ssh(workspace: &Workspace, cx: &App) -> bool {
     workspace.active_modal::<SshConnectionModal>(cx).is_some()
 }
 
 pub fn connect_over_ssh(
     unique_identifier: ConnectionIdentifier,
     connection_options: SshConnectionOptions,
-    ui: View<SshPrompt>,
-    cx: &mut WindowContext,
-) -> Task<Result<Option<Model<SshRemoteClient>>>> {
-    let window = cx.window_handle();
+    ui: Entity<SshPrompt>,
+    window: &mut Window,
+    cx: &mut App,
+) -> Task<Result<Option<Entity<SshRemoteClient>>>> {
+    let window = window.window_handle();
     let known_password = connection_options.password.clone();
     let (tx, rx) = oneshot::channel();
     ui.update(cx, |ui, _cx| ui.set_cancellation_tx(tx));
@@ -546,7 +553,7 @@ pub async fn open_ssh_project(
         window
     } else {
         let options = cx.update(|cx| (app_state.build_window_options)(None, cx))?;
-        cx.open_window(options, |cx| {
+        cx.open_window(options, |window, cx| {
             let project = project::Project::local(
                 app_state.client.clone(),
                 app_state.node_runtime.clone(),
@@ -556,7 +563,7 @@ pub async fn open_ssh_project(
                 None,
                 cx,
             );
-            cx.new_view(|cx| Workspace::new(None, project, app_state.clone(), cx))
+            cx.new(|cx| Workspace::new(None, project, app_state.clone(), window, cx))
         })?
     };
 
@@ -565,10 +572,10 @@ pub async fn open_ssh_project(
         let delegate = window.update(cx, {
             let connection_options = connection_options.clone();
             let paths = paths.clone();
-            move |workspace, cx| {
-                cx.activate_window();
-                workspace.toggle_modal(cx, |cx| {
-                    SshConnectionModal::new(&connection_options, paths, cx)
+            move |workspace, window, cx| {
+                window.activate_window();
+                workspace.toggle_modal(window, cx, |window, cx| {
+                    SshConnectionModal::new(&connection_options, paths, window, cx)
                 });
 
                 let ui = workspace
@@ -582,7 +589,7 @@ pub async fn open_ssh_project(
                 });
 
                 Some(Arc::new(SshClientDelegate {
-                    window: cx.window_handle(),
+                    window: window.window_handle(),
                     ui: ui.downgrade(),
                     known_password: connection_options.password.clone(),
                 }))
@@ -606,7 +613,7 @@ pub async fn open_ssh_project(
             .await;
 
         window
-            .update(cx, |workspace, cx| {
+            .update(cx, |workspace, _, cx| {
                 if let Some(ui) = workspace.active_modal::<SshConnectionModal>(cx) {
                     ui.update(cx, |modal, cx| modal.finished(cx))
                 }
@@ -616,12 +623,13 @@ pub async fn open_ssh_project(
         if let Err(e) = did_open_ssh_project {
             log::error!("Failed to open project: {:?}", e);
             let response = window
-                .update(cx, |_, cx| {
-                    cx.prompt(
+                .update(cx, |_, window, cx| {
+                    window.prompt(
                         PromptLevel::Critical,
                         "Failed to connect over SSH",
                         Some(&e.to_string()),
                         &["Retry", "Ok"],
+                        cx,
                     )
                 })?
                 .await;
@@ -632,7 +640,7 @@ pub async fn open_ssh_project(
         }
 
         window
-            .update(cx, |workspace, cx| {
+            .update(cx, |workspace, _, cx| {
                 if let Some(client) = workspace.project().read(cx).ssh_client().clone() {
                     ExtensionStore::global(cx)
                         .update(cx, |store, cx| store.register_ssh_client(client, cx));

crates/release_channel/src/lib.rs 🔗

@@ -4,7 +4,7 @@
 
 use std::{env, str::FromStr, sync::LazyLock};
 
-use gpui::{AppContext, Global, SemanticVersion};
+use gpui::{App, Global, SemanticVersion};
 
 /// stable | dev | nightly | preview
 pub static RELEASE_CHANNEL_NAME: LazyLock<String> = LazyLock::new(|| {
@@ -33,13 +33,13 @@ impl Global for GlobalAppCommitSha {}
 
 impl AppCommitSha {
     /// Returns the global [`AppCommitSha`], if one is set.
-    pub fn try_global(cx: &AppContext) -> Option<AppCommitSha> {
+    pub fn try_global(cx: &App) -> Option<AppCommitSha> {
         cx.try_global::<GlobalAppCommitSha>()
             .map(|sha| sha.0.clone())
     }
 
     /// Sets the global [`AppCommitSha`].
-    pub fn set_global(sha: AppCommitSha, cx: &mut AppContext) {
+    pub fn set_global(sha: AppCommitSha, cx: &mut App) {
         cx.set_global(GlobalAppCommitSha(sha))
     }
 }
@@ -67,7 +67,7 @@ impl AppVersion {
     }
 
     /// Returns the global version number.
-    pub fn global(cx: &AppContext) -> SemanticVersion {
+    pub fn global(cx: &App) -> SemanticVersion {
         if cx.has_global::<GlobalAppVersion>() {
             cx.global::<GlobalAppVersion>().0
         } else {
@@ -100,19 +100,19 @@ struct GlobalReleaseChannel(ReleaseChannel);
 impl Global for GlobalReleaseChannel {}
 
 /// Initializes the release channel.
-pub fn init(app_version: SemanticVersion, cx: &mut AppContext) {
+pub fn init(app_version: SemanticVersion, cx: &mut App) {
     cx.set_global(GlobalAppVersion(app_version));
     cx.set_global(GlobalReleaseChannel(*RELEASE_CHANNEL))
 }
 
 impl ReleaseChannel {
     /// Returns the global [`ReleaseChannel`].
-    pub fn global(cx: &AppContext) -> Self {
+    pub fn global(cx: &App) -> Self {
         cx.global::<GlobalReleaseChannel>().0
     }
 
     /// Returns the global [`ReleaseChannel`], if one is set.
-    pub fn try_global(cx: &AppContext) -> Option<Self> {
+    pub fn try_global(cx: &App) -> Option<Self> {
         cx.try_global::<GlobalReleaseChannel>()
             .map(|channel| channel.0)
     }

crates/remote/src/ssh_session.rs 🔗

@@ -17,8 +17,8 @@ use futures::{
     select, select_biased, AsyncReadExt as _, Future, FutureExt as _, StreamExt as _,
 };
 use gpui::{
-    AppContext, AsyncAppContext, BorrowAppContext, Context, EventEmitter, Global, Model,
-    ModelContext, SemanticVersion, Task, WeakModel,
+    App, AppContext, AsyncAppContext, BorrowAppContext, Context, Entity, EventEmitter, Global,
+    SemanticVersion, Task, WeakEntity,
 };
 use itertools::Itertools;
 use parking_lot::Mutex;
@@ -503,7 +503,7 @@ impl ConnectionIdentifier {
     // Must be less than about 100 characters
     //   https://unix.stackexchange.com/questions/367008/why-is-socket-path-length-limited-to-a-hundred-chars
     // So our strings should be at most 20 characters or so.
-    fn to_string(&self, cx: &AppContext) -> String {
+    fn to_string(&self, cx: &App) -> String {
         let identifier_prefix = match ReleaseChannel::global(cx) {
             ReleaseChannel::Stable => "".to_string(),
             release_channel => format!("{}-", release_channel.dev_name()),
@@ -523,8 +523,8 @@ impl SshRemoteClient {
         connection_options: SshConnectionOptions,
         cancellation: oneshot::Receiver<()>,
         delegate: Arc<dyn SshClientDelegate>,
-        cx: &mut AppContext,
-    ) -> Task<Result<Option<Model<Self>>>> {
+        cx: &mut App,
+    ) -> Task<Result<Option<Entity<Self>>>> {
         let unique_identifier = unique_identifier.to_string(cx);
         cx.spawn(|mut cx| async move {
             let success = Box::pin(async move {
@@ -534,7 +534,7 @@ impl SshRemoteClient {
 
                 let client =
                     cx.update(|cx| ChannelClient::new(incoming_rx, outgoing_tx, cx, "client"))?;
-                let this = cx.new_model(|_| Self {
+                let this = cx.new(|_| Self {
                     client: client.clone(),
                     unique_identifier: unique_identifier.clone(),
                     connection_options: connection_options.clone(),
@@ -629,7 +629,7 @@ impl SshRemoteClient {
         })
     }
 
-    fn reconnect(&mut self, cx: &mut ModelContext<Self>) -> Result<()> {
+    fn reconnect(&mut self, cx: &mut Context<Self>) -> Result<()> {
         let mut lock = self.state.lock();
 
         let can_reconnect = lock
@@ -811,7 +811,7 @@ impl SshRemoteClient {
     }
 
     fn heartbeat(
-        this: WeakModel<Self>,
+        this: WeakEntity<Self>,
         mut connection_activity_rx: mpsc::Receiver<()>,
         cx: &mut AsyncAppContext,
     ) -> Task<Result<()>> {
@@ -886,7 +886,7 @@ impl SshRemoteClient {
     fn handle_heartbeat_result(
         &mut self,
         missed_heartbeats: usize,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> ControlFlow<()> {
         let state = self.state.lock().take().unwrap();
         let next_state = if missed_heartbeats > 0 {
@@ -913,7 +913,7 @@ impl SshRemoteClient {
     }
 
     fn monitor(
-        this: WeakModel<Self>,
+        this: WeakEntity<Self>,
         io_task: Task<Result<i32>>,
         cx: &AsyncAppContext,
     ) -> Task<Result<()>> {
@@ -954,11 +954,7 @@ impl SshRemoteClient {
         self.state.lock().as_ref().map_or(false, check)
     }
 
-    fn try_set_state(
-        &self,
-        cx: &mut ModelContext<Self>,
-        map: impl FnOnce(&State) -> Option<State>,
-    ) {
+    fn try_set_state(&self, cx: &mut Context<Self>, map: impl FnOnce(&State) -> Option<State>) {
         let mut lock = self.state.lock();
         let new_state = lock.as_ref().and_then(map);
 
@@ -968,7 +964,7 @@ impl SshRemoteClient {
         }
     }
 
-    fn set_state(&self, state: State, cx: &mut ModelContext<Self>) {
+    fn set_state(&self, state: State, cx: &mut Context<Self>) {
         log::info!("setting state to '{}'", &state);
 
         let is_reconnect_exhausted = state.is_reconnect_exhausted();
@@ -981,7 +977,7 @@ impl SshRemoteClient {
         cx.notify();
     }
 
-    pub fn subscribe_to_entity<E: 'static>(&self, remote_id: u64, entity: &Model<E>) {
+    pub fn subscribe_to_entity<E: 'static>(&self, remote_id: u64, entity: &Entity<E>) {
         self.client.subscribe_to_entity(remote_id, entity);
     }
 
@@ -997,7 +993,7 @@ impl SshRemoteClient {
         &self,
         src_path: PathBuf,
         dest_path: PathBuf,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Result<()>> {
         let state = self.state.lock();
         let Some(connection) = state.as_ref().and_then(|state| state.ssh_connection()) else {
@@ -1031,7 +1027,7 @@ impl SshRemoteClient {
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn simulate_disconnect(&self, client_cx: &mut AppContext) -> Task<()> {
+    pub fn simulate_disconnect(&self, client_cx: &mut App) -> Task<()> {
         let opts = self.connection_options();
         client_cx.spawn(|cx| async move {
             let connection = cx
@@ -1095,7 +1091,7 @@ impl SshRemoteClient {
     pub async fn fake_client(
         opts: SshConnectionOptions,
         client_cx: &mut gpui::TestAppContext,
-    ) -> Model<Self> {
+    ) -> Entity<Self> {
         let (_tx, rx) = oneshot::channel();
         client_cx
             .update(|cx| {
@@ -1130,7 +1126,7 @@ impl ConnectionPool {
         &mut self,
         opts: SshConnectionOptions,
         delegate: &Arc<dyn SshClientDelegate>,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Shared<Task<Result<Arc<dyn RemoteConnection>, Arc<anyhow::Error>>>> {
         let connection = self.connections.get(&opts);
         match connection {
@@ -1210,12 +1206,8 @@ trait RemoteConnection: Send + Sync {
         delegate: Arc<dyn SshClientDelegate>,
         cx: &mut AsyncAppContext,
     ) -> Task<Result<i32>>;
-    fn upload_directory(
-        &self,
-        src_path: PathBuf,
-        dest_path: PathBuf,
-        cx: &AppContext,
-    ) -> Task<Result<()>>;
+    fn upload_directory(&self, src_path: PathBuf, dest_path: PathBuf, cx: &App)
+        -> Task<Result<()>>;
     async fn kill(&self) -> Result<()>;
     fn has_been_killed(&self) -> bool;
     fn ssh_args(&self) -> Vec<String>;
@@ -1259,7 +1251,7 @@ impl RemoteConnection for SshRemoteConnection {
         &self,
         src_path: PathBuf,
         dest_path: PathBuf,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Result<()>> {
         let mut command = util::command::new_smol_command("scp");
         let output = self
@@ -2071,7 +2063,7 @@ impl ChannelClient {
     pub fn new(
         incoming_rx: mpsc::UnboundedReceiver<Envelope>,
         outgoing_tx: mpsc::UnboundedSender<Envelope>,
-        cx: &AppContext,
+        cx: &App,
         name: &'static str,
     ) -> Arc<Self> {
         Arc::new_cyclic(|this| Self {
@@ -2199,7 +2191,7 @@ impl ChannelClient {
         *self.task.lock() = Self::start_handling_messages(Arc::downgrade(self), incoming_rx, cx);
     }
 
-    pub fn subscribe_to_entity<E: 'static>(&self, remote_id: u64, entity: &Model<E>) {
+    pub fn subscribe_to_entity<E: 'static>(&self, remote_id: u64, entity: &Entity<E>) {
         let id = (TypeId::of::<E>(), remote_id);
 
         let mut message_handlers = self.message_handlers.lock();
@@ -2373,7 +2365,7 @@ mod fake {
         },
         select_biased, FutureExt, SinkExt, StreamExt,
     };
-    use gpui::{AppContext, AsyncAppContext, SemanticVersion, Task, TestAppContext};
+    use gpui::{App, AsyncAppContext, SemanticVersion, Task, TestAppContext};
     use release_channel::ReleaseChannel;
     use rpc::proto::Envelope;
 
@@ -2421,7 +2413,7 @@ mod fake {
             &self,
             _src_path: PathBuf,
             _dest_path: PathBuf,
-            _cx: &AppContext,
+            _cx: &App,
         ) -> Task<Result<()>> {
             unreachable!()
         }

crates/remote_server/src/headless_project.rs 🔗

@@ -2,7 +2,7 @@ use anyhow::{anyhow, Result};
 use extension::ExtensionHostProxy;
 use extension_host::headless_host::HeadlessExtensionStore;
 use fs::Fs;
-use gpui::{AppContext, AsyncAppContext, Context as _, Model, ModelContext, PromptLevel};
+use gpui::{App, AppContext as _, AsyncAppContext, Context, Entity, PromptLevel};
 use http_client::HttpClient;
 use language::{proto::serialize_operation, Buffer, BufferEvent, LanguageRegistry};
 use node_runtime::NodeRuntime;
@@ -32,14 +32,14 @@ use worktree::Worktree;
 pub struct HeadlessProject {
     pub fs: Arc<dyn Fs>,
     pub session: AnyProtoClient,
-    pub worktree_store: Model<WorktreeStore>,
-    pub buffer_store: Model<BufferStore>,
-    pub lsp_store: Model<LspStore>,
-    pub task_store: Model<TaskStore>,
-    pub settings_observer: Model<SettingsObserver>,
+    pub worktree_store: Entity<WorktreeStore>,
+    pub buffer_store: Entity<BufferStore>,
+    pub lsp_store: Entity<LspStore>,
+    pub task_store: Entity<TaskStore>,
+    pub settings_observer: Entity<SettingsObserver>,
     pub next_entry_id: Arc<AtomicUsize>,
     pub languages: Arc<LanguageRegistry>,
-    pub extensions: Model<HeadlessExtensionStore>,
+    pub extensions: Entity<HeadlessExtensionStore>,
 }
 
 pub struct HeadlessAppState {
@@ -52,7 +52,7 @@ pub struct HeadlessAppState {
 }
 
 impl HeadlessProject {
-    pub fn init(cx: &mut AppContext) {
+    pub fn init(cx: &mut App) {
         settings::init(cx);
         language::init(cx);
         project::Project::init_settings(cx);
@@ -67,22 +67,22 @@ impl HeadlessProject {
             languages,
             extension_host_proxy: proxy,
         }: HeadlessAppState,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Self {
         language_extension::init(proxy.clone(), languages.clone());
         languages::init(languages.clone(), node_runtime.clone(), cx);
 
-        let worktree_store = cx.new_model(|cx| {
+        let worktree_store = cx.new(|cx| {
             let mut store = WorktreeStore::local(true, fs.clone());
             store.shared(SSH_PROJECT_ID, session.clone().into(), cx);
             store
         });
-        let buffer_store = cx.new_model(|cx| {
+        let buffer_store = cx.new(|cx| {
             let mut buffer_store = BufferStore::local(worktree_store.clone(), cx);
             buffer_store.shared(SSH_PROJECT_ID, session.clone().into(), cx);
             buffer_store
         });
-        let prettier_store = cx.new_model(|cx| {
+        let prettier_store = cx.new(|cx| {
             PrettierStore::new(
                 node_runtime.clone(),
                 fs.clone(),
@@ -92,7 +92,7 @@ impl HeadlessProject {
             )
         });
         let environment = project::ProjectEnvironment::new(&worktree_store, None, cx);
-        let toolchain_store = cx.new_model(|cx| {
+        let toolchain_store = cx.new(|cx| {
             ToolchainStore::local(
                 languages.clone(),
                 worktree_store.clone(),
@@ -101,7 +101,7 @@ impl HeadlessProject {
             )
         });
 
-        let task_store = cx.new_model(|cx| {
+        let task_store = cx.new(|cx| {
             let mut task_store = TaskStore::local(
                 fs.clone(),
                 buffer_store.downgrade(),
@@ -113,7 +113,7 @@ impl HeadlessProject {
             task_store.shared(SSH_PROJECT_ID, session.clone().into(), cx);
             task_store
         });
-        let settings_observer = cx.new_model(|cx| {
+        let settings_observer = cx.new(|cx| {
             let mut observer = SettingsObserver::new_local(
                 fs.clone(),
                 worktree_store.clone(),
@@ -124,7 +124,7 @@ impl HeadlessProject {
             observer
         });
 
-        let lsp_store = cx.new_model(|cx| {
+        let lsp_store = cx.new(|cx| {
             let mut lsp_store = LspStore::new_local(
                 buffer_store.clone(),
                 worktree_store.clone(),
@@ -166,7 +166,7 @@ impl HeadlessProject {
 
         session.subscribe_to_entity(SSH_PROJECT_ID, &worktree_store);
         session.subscribe_to_entity(SSH_PROJECT_ID, &buffer_store);
-        session.subscribe_to_entity(SSH_PROJECT_ID, &cx.handle());
+        session.subscribe_to_entity(SSH_PROJECT_ID, &cx.model());
         session.subscribe_to_entity(SSH_PROJECT_ID, &lsp_store);
         session.subscribe_to_entity(SSH_PROJECT_ID, &task_store);
         session.subscribe_to_entity(SSH_PROJECT_ID, &toolchain_store);
@@ -220,9 +220,9 @@ impl HeadlessProject {
 
     fn on_buffer_event(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         event: &BufferEvent,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match event {
             BufferEvent::Operation {
@@ -242,9 +242,9 @@ impl HeadlessProject {
 
     fn on_lsp_store_event(
         &mut self,
-        _lsp_store: Model<LspStore>,
+        _lsp_store: Entity<LspStore>,
         event: &LspStoreEvent,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match event {
             LspStoreEvent::LanguageServerUpdate {
@@ -306,7 +306,7 @@ impl HeadlessProject {
     }
 
     pub async fn handle_add_worktree(
-        this: Model<Self>,
+        this: Entity<Self>,
         message: TypedEnvelope<proto::AddWorktree>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::AddWorktreeResponse> {
@@ -379,7 +379,7 @@ impl HeadlessProject {
     }
 
     pub async fn handle_remove_worktree(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::RemoveWorktree>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::Ack> {
@@ -393,7 +393,7 @@ impl HeadlessProject {
     }
 
     pub async fn handle_open_buffer_by_path(
-        this: Model<Self>,
+        this: Entity<Self>,
         message: TypedEnvelope<proto::OpenBufferByPath>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::OpenBufferResponse> {
@@ -426,7 +426,7 @@ impl HeadlessProject {
     }
 
     pub async fn handle_open_new_buffer(
-        this: Model<Self>,
+        this: Entity<Self>,
         _message: TypedEnvelope<proto::OpenNewBuffer>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::OpenBufferResponse> {
@@ -452,7 +452,7 @@ impl HeadlessProject {
     }
 
     pub async fn handle_open_server_settings(
-        this: Model<Self>,
+        this: Entity<Self>,
         _: TypedEnvelope<proto::OpenServerSettings>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::OpenBufferResponse> {
@@ -505,7 +505,7 @@ impl HeadlessProject {
     }
 
     pub async fn handle_find_search_candidates(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::FindSearchCandidates>,
         mut cx: AsyncAppContext,
     ) -> Result<proto::FindSearchCandidatesResponse> {
@@ -541,7 +541,7 @@ impl HeadlessProject {
     }
 
     pub async fn handle_list_remote_directory(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::ListRemoteDirectory>,
         cx: AsyncAppContext,
     ) -> Result<proto::ListRemoteDirectoryResponse> {
@@ -559,7 +559,7 @@ impl HeadlessProject {
     }
 
     pub async fn handle_get_path_metadata(
-        this: Model<Self>,
+        this: Entity<Self>,
         envelope: TypedEnvelope<proto::GetPathMetadata>,
         cx: AsyncAppContext,
     ) -> Result<proto::GetPathMetadataResponse> {
@@ -577,7 +577,7 @@ impl HeadlessProject {
     }
 
     pub async fn handle_shutdown_remote_server(
-        _this: Model<Self>,
+        _this: Entity<Self>,
         _envelope: TypedEnvelope<proto::ShutdownRemoteServer>,
         cx: AsyncAppContext,
     ) -> Result<proto::Ack> {
@@ -595,7 +595,7 @@ impl HeadlessProject {
     }
 
     pub async fn handle_ping(
-        _this: Model<Self>,
+        _this: Entity<Self>,
         _envelope: TypedEnvelope<proto::Ping>,
         _cx: AsyncAppContext,
     ) -> Result<proto::Ack> {

crates/remote_server/src/remote_editing_tests.rs 🔗

@@ -3,7 +3,7 @@ use client::{Client, UserStore};
 use clock::FakeSystemClock;
 use extension::ExtensionHostProxy;
 use fs::{FakeFs, Fs};
-use gpui::{Context, Model, SemanticVersion, TestAppContext};
+use gpui::{AppContext as _, Entity, SemanticVersion, TestAppContext};
 use http_client::{BlockedHttpClient, FakeHttpClient};
 use language::{
     language_settings::{language_settings, AllLanguageSettings},
@@ -186,7 +186,7 @@ async fn test_remote_project_search(cx: &mut TestAppContext, server_cx: &mut Tes
 
     cx.run_until_parked();
 
-    async fn do_search(project: &Model<Project>, mut cx: TestAppContext) -> Model<Buffer> {
+    async fn do_search(project: &Entity<Project>, mut cx: TestAppContext) -> Entity<Buffer> {
         let receiver = project.update(&mut cx, |project, cx| {
             project.search(
                 SearchQuery::text(
@@ -1275,7 +1275,7 @@ pub async fn init_test(
     server_fs: &Arc<FakeFs>,
     cx: &mut TestAppContext,
     server_cx: &mut TestAppContext,
-) -> (Model<Project>, Model<HeadlessProject>) {
+) -> (Entity<Project>, Entity<HeadlessProject>) {
     let server_fs = server_fs.clone();
     cx.update(|cx| {
         release_channel::init(SemanticVersion::default(), cx);
@@ -1291,7 +1291,7 @@ pub async fn init_test(
     let languages = Arc::new(LanguageRegistry::new(cx.executor()));
     let proxy = Arc::new(ExtensionHostProxy::new());
     server_cx.update(HeadlessProject::init);
-    let headless = server_cx.new_model(|cx| {
+    let headless = server_cx.new(|cx| {
         client::init_settings(cx);
 
         HeadlessProject::new(
@@ -1324,7 +1324,7 @@ fn init_logger() {
     }
 }
 
-fn build_project(ssh: Model<SshRemoteClient>, cx: &mut TestAppContext) -> Model<Project> {
+fn build_project(ssh: Entity<SshRemoteClient>, cx: &mut TestAppContext) -> Entity<Project> {
     cx.update(|cx| {
         if !cx.has_global::<SettingsStore>() {
             let settings_store = SettingsStore::test(cx);
@@ -1341,7 +1341,7 @@ fn build_project(ssh: Model<SshRemoteClient>, cx: &mut TestAppContext) -> Model<
     });
 
     let node = NodeRuntime::unavailable();
-    let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
+    let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
     let languages = Arc::new(LanguageRegistry::test(cx.executor()));
     let fs = FakeFs::new(cx.executor());
 

crates/remote_server/src/unix.rs 🔗

@@ -1,6 +1,6 @@
 use crate::headless_project::HeadlessAppState;
 use crate::HeadlessProject;
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use chrono::Utc;
 use client::{telemetry, ProxySettings};
 use extension::ExtensionHostProxy;
@@ -8,7 +8,7 @@ use fs::{Fs, RealFs};
 use futures::channel::mpsc;
 use futures::{select, select_biased, AsyncRead, AsyncWrite, AsyncWriteExt, FutureExt, SinkExt};
 use git::GitHostingProviderRegistry;
-use gpui::{AppContext, Context as _, Model, ModelContext, SemanticVersion, UpdateGlobal as _};
+use gpui::{App, AppContext as _, Context, Entity, SemanticVersion, UpdateGlobal as _};
 use http_client::{read_proxy_from_env, Uri};
 use language::LanguageRegistry;
 use node_runtime::{NodeBinaryOptions, NodeRuntime};
@@ -189,7 +189,7 @@ fn init_panic_hook() {
     }));
 }
 
-fn handle_panic_requests(project: &Model<HeadlessProject>, client: &Arc<ChannelClient>) {
+fn handle_panic_requests(project: &Entity<HeadlessProject>, client: &Arc<ChannelClient>) {
     let client: AnyProtoClient = client.clone().into();
     client.add_request_handler(
         project.downgrade(),
@@ -250,7 +250,7 @@ impl ServerListeners {
 fn start_server(
     listeners: ServerListeners,
     log_rx: Receiver<Vec<u8>>,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) -> Arc<ChannelClient> {
     // This is the server idle timeout. If no connection comes in in this timeout, the server will shut down.
     const IDLE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10 * 60);
@@ -421,7 +421,7 @@ pub fn execute_run(
     let listeners = ServerListeners::new(stdin_socket, stdout_socket, stderr_socket)?;
 
     let git_hosting_provider_registry = Arc::new(GitHostingProviderRegistry::new());
-    gpui::App::headless().run(move |cx| {
+    gpui::Application::headless().run(move |cx| {
         settings::init(cx);
         let app_version = AppVersion::init(env!("ZED_PKG_VERSION"));
         release_channel::init(app_version, cx);
@@ -439,7 +439,7 @@ pub fn execute_run(
         extension::init(cx);
         let extension_host_proxy = ExtensionHostProxy::global(cx);
 
-        let project = cx.new_model(|cx| {
+        let project = cx.new(|cx| {
             let fs = Arc::new(RealFs::new(Default::default(), None));
             let node_settings_rx = initialize_settings(session.clone(), fs.clone(), cx);
 
@@ -740,7 +740,7 @@ async fn write_size_prefixed_buffer<S: AsyncWrite + Unpin>(
 fn initialize_settings(
     session: Arc<ChannelClient>,
     fs: Arc<dyn Fs>,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) -> async_watch::Receiver<Option<NodeBinaryOptions>> {
     let user_settings_file_rx = watch_config_file(
         &cx.background_executor(),
@@ -808,8 +808,8 @@ fn initialize_settings(
 
 pub fn handle_settings_file_changes(
     mut server_settings_file: mpsc::UnboundedReceiver<String>,
-    cx: &mut AppContext,
-    settings_changed: impl Fn(Option<anyhow::Error>, &mut AppContext) + 'static,
+    cx: &mut App,
+    settings_changed: impl Fn(Option<anyhow::Error>, &mut App) + 'static,
 ) {
     let server_settings_content = cx
         .background_executor()
@@ -828,7 +828,7 @@ pub fn handle_settings_file_changes(
                     log::error!("Failed to load server settings: {err}");
                 }
                 settings_changed(result.err(), cx);
-                cx.refresh();
+                cx.refresh_windows();
             });
             if result.is_err() {
                 break; // App dropped
@@ -838,7 +838,7 @@ pub fn handle_settings_file_changes(
     .detach();
 }
 
-fn read_proxy_settings(cx: &mut ModelContext<'_, HeadlessProject>) -> Option<Uri> {
+fn read_proxy_settings(cx: &mut Context<'_, HeadlessProject>) -> Option<Uri> {
     let proxy_str = ProxySettings::get_global(cx).proxy.to_owned();
     let proxy_url = proxy_str
         .as_ref()

crates/repl/src/components/kernel_list_item.rs 🔗

@@ -45,7 +45,7 @@ impl ParentElement for KernelListItem {
 }
 
 impl RenderOnce for KernelListItem {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         ListItem::new(self.kernel_specification.name())
             .selectable(false)
             .start_slot(

crates/repl/src/components/kernel_options.rs 🔗

@@ -16,7 +16,7 @@ use gpui::SharedString;
 use gpui::Task;
 use ui::{prelude::*, ListItem, PopoverMenu, PopoverMenuHandle, PopoverTrigger};
 
-type OnSelect = Box<dyn Fn(KernelSpecification, &mut WindowContext)>;
+type OnSelect = Box<dyn Fn(KernelSpecification, &mut Window, &mut App)>;
 
 #[derive(IntoElement)]
 pub struct KernelSelector<T: PopoverTrigger> {
@@ -84,16 +84,21 @@ impl PickerDelegate for KernelPickerDelegate {
         }
     }
 
-    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(&mut self, ix: usize, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.selected_kernelspec = self.filtered_kernels.get(ix).cloned();
         cx.notify();
     }
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Select a kernel...".into()
     }
 
-    fn update_matches(&mut self, query: String, _cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
+    fn update_matches(
+        &mut self,
+        query: String,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
+    ) -> Task<()> {
         let all_kernels = self.all_kernels.clone();
 
         if query.is_empty() {
@@ -113,20 +118,21 @@ impl PickerDelegate for KernelPickerDelegate {
         return Task::ready(());
     }
 
-    fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
         if let Some(kernelspec) = &self.selected_kernelspec {
-            (self.on_select)(kernelspec.clone(), cx.window_context());
+            (self.on_select)(kernelspec.clone(), window, cx);
             cx.emit(DismissEvent);
         }
     }
 
-    fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
+    fn dismissed(&mut self, _window: &mut Window, _cx: &mut Context<Picker<Self>>) {}
 
     fn render_match(
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let kernelspec = self.filtered_kernels.get(ix)?;
         let is_selected = self.selected_kernelspec.as_ref() == Some(kernelspec);
@@ -204,7 +210,11 @@ impl PickerDelegate for KernelPickerDelegate {
         )
     }
 
-    fn render_footer(&self, cx: &mut ViewContext<Picker<Self>>) -> Option<gpui::AnyElement> {
+    fn render_footer(
+        &self,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Option<gpui::AnyElement> {
         Some(
             h_flex()
                 .w_full()
@@ -218,7 +228,7 @@ impl PickerDelegate for KernelPickerDelegate {
                         .icon_size(IconSize::XSmall)
                         .icon_color(Color::Muted)
                         .icon_position(IconPosition::End)
-                        .on_click(move |_, cx| cx.open_url(KERNEL_DOCS_URL)),
+                        .on_click(move |_, _, cx| cx.open_url(KERNEL_DOCS_URL)),
                 )
                 .into_any(),
         )
@@ -226,7 +236,7 @@ impl PickerDelegate for KernelPickerDelegate {
 }
 
 impl<T: PopoverTrigger> RenderOnce for KernelSelector<T> {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
         let store = ReplStore::global(cx).read(cx);
 
         let all_kernels: Vec<KernelSpecification> = store
@@ -243,15 +253,15 @@ impl<T: PopoverTrigger> RenderOnce for KernelSelector<T> {
             selected_kernelspec,
         };
 
-        let picker_view = cx.new_view(|cx| {
-            let picker = Picker::uniform_list(delegate, cx)
+        let picker_view = cx.new(|cx| {
+            let picker = Picker::uniform_list(delegate, window, cx)
                 .width(rems(30.))
                 .max_height(Some(rems(20.).into()));
             picker
         });
 
         PopoverMenu::new("kernel-switcher")
-            .menu(move |_cx| Some(picker_view.clone()))
+            .menu(move |_window, _cx| Some(picker_view.clone()))
             .trigger(self.trigger)
             .attach(gpui::Corner::BottomLeft)
             .when_some(self.handle, |menu, handle| menu.with_handle(handle))

crates/repl/src/jupyter_settings.rs 🔗

@@ -1,7 +1,7 @@
 use std::collections::HashMap;
 
 use editor::EditorSettings;
-use gpui::AppContext;
+use gpui::App;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsSources};
@@ -12,7 +12,7 @@ pub struct JupyterSettings {
 }
 
 impl JupyterSettings {
-    pub fn enabled(cx: &AppContext) -> bool {
+    pub fn enabled(cx: &App) -> bool {
         // In order to avoid a circular dependency between `editor` and `repl` crates,
         // we put the `enable` flag on its settings.
         // This allows the editor to set up context for key bindings/actions.
@@ -43,7 +43,7 @@ impl Settings for JupyterSettings {
 
     fn load(
         sources: SettingsSources<Self::FileContent>,
-        _cx: &mut gpui::AppContext,
+        _cx: &mut gpui::App,
     ) -> anyhow::Result<Self>
     where
         Self: Sized,

crates/repl/src/kernels/mod.rs 🔗

@@ -6,7 +6,7 @@ use futures::{
     future::Shared,
     stream,
 };
-use gpui::{AppContext, Model, Task, WindowContext};
+use gpui::{App, Entity, Task, Window};
 use language::LanguageName;
 pub use native_kernel::*;
 
@@ -61,7 +61,7 @@ impl KernelSpecification {
         })
     }
 
-    pub fn icon(&self, cx: &AppContext) -> Icon {
+    pub fn icon(&self, cx: &App) -> Icon {
         let lang_name = match self {
             Self::Jupyter(spec) => spec.kernelspec.language.clone(),
             Self::PythonEnv(spec) => spec.kernelspec.language.clone(),
@@ -76,9 +76,9 @@ impl KernelSpecification {
 }
 
 pub fn python_env_kernel_specifications(
-    project: &Model<Project>,
+    project: &Entity<Project>,
     worktree_id: WorktreeId,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) -> impl Future<Output = Result<Vec<KernelSpecification>>> {
     let python_language = LanguageName::new("Python");
     let toolchains = project
@@ -148,7 +148,7 @@ pub trait RunningKernel: Send + Debug {
     fn set_execution_state(&mut self, state: ExecutionState);
     fn kernel_info(&self) -> Option<&KernelInfoReply>;
     fn set_kernel_info(&mut self, info: KernelInfoReply);
-    fn force_shutdown(&mut self, cx: &mut WindowContext) -> Task<anyhow::Result<()>>;
+    fn force_shutdown(&mut self, window: &mut Window, cx: &mut App) -> Task<anyhow::Result<()>>;
 }
 
 #[derive(Debug, Clone)]

crates/repl/src/kernels/native_kernel.rs 🔗

@@ -5,7 +5,7 @@ use futures::{
     stream::{SelectAll, StreamExt},
     AsyncBufReadExt as _, SinkExt as _,
 };
-use gpui::{EntityId, Task, View, WindowContext};
+use gpui::{App, Entity, EntityId, Task, Window};
 use jupyter_protocol::{
     connection_info::{ConnectionInfo, Transport},
     ExecutionState, JupyterKernelspec, JupyterMessage, JupyterMessageContent, KernelInfoReply,
@@ -114,10 +114,11 @@ impl NativeRunningKernel {
         working_directory: PathBuf,
         fs: Arc<dyn Fs>,
         // todo: convert to weak view
-        session: View<Session>,
-        cx: &mut WindowContext,
+        session: Entity<Session>,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<Box<dyn RunningKernel>>> {
-        cx.spawn(|cx| async move {
+        window.spawn(cx, |cx| async move {
             let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
             let ports = peek_ports(ip).await?;
 
@@ -179,8 +180,8 @@ impl NativeRunningKernel {
                 |mut cx| async move {
                     while let Some(message) = messages_rx.next().await {
                         session
-                            .update(&mut cx, |session, cx| {
-                                session.route(&message, cx);
+                            .update_in(&mut cx, |session, window, cx| {
+                                session.route(&message, window, cx);
                             })
                             .ok();
                     }
@@ -196,8 +197,8 @@ impl NativeRunningKernel {
                 |mut cx| async move {
                     while let Ok(message) = iopub_socket.read().await {
                         session
-                            .update(&mut cx, |session, cx| {
-                                session.route(&message, cx);
+                            .update_in(&mut cx, |session, window, cx| {
+                                session.route(&message, window, cx);
                             })
                             .ok();
                     }
@@ -347,7 +348,7 @@ impl RunningKernel for NativeRunningKernel {
         self.kernel_info = Some(info);
     }
 
-    fn force_shutdown(&mut self, _cx: &mut WindowContext) -> Task<anyhow::Result<()>> {
+    fn force_shutdown(&mut self, _window: &mut Window, _cx: &mut App) -> Task<anyhow::Result<()>> {
         self._process_status_task.take();
         self.request_tx.close_channel();
 

crates/repl/src/kernels/remote_kernels.rs 🔗

@@ -1,5 +1,5 @@
 use futures::{channel::mpsc, SinkExt as _};
-use gpui::{Task, View, WindowContext};
+use gpui::{App, Entity, Task, Window};
 use http_client::{AsyncBody, HttpClient, Request};
 use jupyter_protocol::{ExecutionState, JupyterKernelspec, JupyterMessage, KernelInfoReply};
 
@@ -137,8 +137,9 @@ impl RemoteRunningKernel {
     pub fn new(
         kernelspec: RemoteKernelSpecification,
         working_directory: std::path::PathBuf,
-        session: View<Session>,
-        cx: &mut WindowContext,
+        session: Entity<Session>,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<Box<dyn RunningKernel>>> {
         let remote_server = RemoteServer {
             base_url: kernelspec.url,
@@ -147,7 +148,7 @@ impl RemoteRunningKernel {
 
         let http_client = cx.http_client();
 
-        cx.spawn(|cx| async move {
+        window.spawn(cx, |cx| async move {
             let kernel_id = launch_remote_kernel(
                 &remote_server,
                 http_client.clone(),
@@ -205,8 +206,8 @@ impl RemoteRunningKernel {
                         match message {
                             Ok(message) => {
                                 session
-                                    .update(&mut cx, |session, cx| {
-                                        session.route(&message, cx);
+                                    .update_in(&mut cx, |session, window, cx| {
+                                        session.route(&message, window, cx);
                                     })
                                     .ok();
                             }
@@ -273,14 +274,14 @@ impl RunningKernel for RemoteRunningKernel {
         self.kernel_info = Some(info);
     }
 
-    fn force_shutdown(&mut self, cx: &mut WindowContext) -> Task<anyhow::Result<()>> {
+    fn force_shutdown(&mut self, window: &mut Window, cx: &mut App) -> Task<anyhow::Result<()>> {
         let url = self
             .remote_server
             .api_url(&format!("/kernels/{}", self.kernel_id));
         let token = self.remote_server.token.clone();
         let http_client = self.http_client.clone();
 
-        cx.spawn(|_| async move {
+        window.spawn(cx, |_| async move {
             let request = Request::builder()
                 .method("DELETE")
                 .uri(&url)

crates/repl/src/notebook/cell.rs 🔗

@@ -3,7 +3,7 @@ use std::sync::Arc;
 
 use editor::{Editor, EditorMode, MultiBuffer};
 use futures::future::Shared;
-use gpui::{prelude::*, AppContext, Hsla, Task, TextStyleRefinement, View};
+use gpui::{prelude::*, App, Entity, Hsla, Task, TextStyleRefinement};
 use language::{Buffer, Language, LanguageRegistry};
 use markdown_preview::{markdown_parser::parse_markdown, markdown_renderer::render_markdown_block};
 use nbformat::v4::{CellId, CellMetadata, CellType};
@@ -62,7 +62,10 @@ impl CellControl {
 }
 
 impl Clickable for CellControl {
-    fn on_click(self, handler: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static) -> Self {
+    fn on_click(
+        self,
+        handler: impl Fn(&gpui::ClickEvent, &mut Window, &mut App) + 'static,
+    ) -> Self {
         let button = self.button.on_click(handler);
         Self { button }
     }
@@ -75,28 +78,33 @@ impl Clickable for CellControl {
 /// A notebook cell
 #[derive(Clone)]
 pub enum Cell {
-    Code(View<CodeCell>),
-    Markdown(View<MarkdownCell>),
-    Raw(View<RawCell>),
+    Code(Entity<CodeCell>),
+    Markdown(Entity<MarkdownCell>),
+    Raw(Entity<RawCell>),
 }
 
-fn convert_outputs(outputs: &Vec<nbformat::v4::Output>, cx: &mut WindowContext) -> Vec<Output> {
+fn convert_outputs(
+    outputs: &Vec<nbformat::v4::Output>,
+    window: &mut Window,
+    cx: &mut App,
+) -> Vec<Output> {
     outputs
         .into_iter()
         .map(|output| match output {
             nbformat::v4::Output::Stream { text, .. } => Output::Stream {
-                content: cx.new_view(|cx| TerminalOutput::from(&text.0, cx)),
+                content: cx.new(|cx| TerminalOutput::from(&text.0, window, cx)),
             },
             nbformat::v4::Output::DisplayData(display_data) => {
-                Output::new(&display_data.data, None, cx)
+                Output::new(&display_data.data, None, window, cx)
             }
             nbformat::v4::Output::ExecuteResult(execute_result) => {
-                Output::new(&execute_result.data, None, cx)
+                Output::new(&execute_result.data, None, window, cx)
             }
             nbformat::v4::Output::Error(error) => Output::ErrorOutput(ErrorView {
                 ename: error.ename.clone(),
                 evalue: error.evalue.clone(),
-                traceback: cx.new_view(|cx| TerminalOutput::from(&error.traceback.join("\n"), cx)),
+                traceback: cx
+                    .new(|cx| TerminalOutput::from(&error.traceback.join("\n"), window, cx)),
             }),
         })
         .collect()
@@ -107,7 +115,8 @@ impl Cell {
         cell: &nbformat::v4::Cell,
         languages: &Arc<LanguageRegistry>,
         notebook_language: Shared<Task<Option<Arc<Language>>>>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Self {
         match cell {
             nbformat::v4::Cell::Markdown {
@@ -118,12 +127,12 @@ impl Cell {
             } => {
                 let source = source.join("");
 
-                let view = cx.new_view(|cx| {
+                let model = cx.new(|cx| {
                     let markdown_parsing_task = {
                         let languages = languages.clone();
                         let source = source.clone();
 
-                        cx.spawn(|this, mut cx| async move {
+                        cx.spawn_in(window, |this, mut cx| async move {
                             let parsed_markdown = cx
                                 .background_executor()
                                 .spawn(async move {
@@ -150,7 +159,7 @@ impl Cell {
                     }
                 });
 
-                Cell::Markdown(view)
+                Cell::Markdown(model)
             }
             nbformat::v4::Cell::Code {
                 id,
@@ -158,18 +167,19 @@ impl Cell {
                 execution_count,
                 source,
                 outputs,
-            } => Cell::Code(cx.new_view(|cx| {
+            } => Cell::Code(cx.new(|cx| {
                 let text = source.join("");
 
-                let buffer = cx.new_model(|cx| Buffer::local(text.clone(), cx));
-                let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
+                let buffer = cx.new(|cx| Buffer::local(text.clone(), cx));
+                let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
 
-                let editor_view = cx.new_view(|cx| {
+                let editor_view = cx.new(|cx| {
                     let mut editor = Editor::new(
                         EditorMode::AutoHeight { max_lines: 1024 },
                         multi_buffer,
                         None,
                         false,
+                        window,
                         cx,
                     );
 
@@ -183,7 +193,7 @@ impl Cell {
                         ..Default::default()
                     };
 
-                    editor.set_text(text, cx);
+                    editor.set_text(text, window, cx);
                     editor.set_show_gutter(false, cx);
                     editor.set_text_style_refinement(refinement);
 
@@ -192,7 +202,7 @@ impl Cell {
                 });
 
                 let buffer = buffer.clone();
-                let language_task = cx.spawn(|this, mut cx| async move {
+                let language_task = cx.spawn_in(window, |this, mut cx| async move {
                     let language = notebook_language.await;
 
                     buffer.update(&mut cx, |buffer, cx| {
@@ -206,7 +216,7 @@ impl Cell {
                     execution_count: *execution_count,
                     source: source.join(""),
                     editor: editor_view,
-                    outputs: convert_outputs(outputs, cx),
+                    outputs: convert_outputs(outputs, window, cx),
                     selected: false,
                     language_task,
                     cell_position: None,
@@ -216,7 +226,7 @@ impl Cell {
                 id,
                 metadata,
                 source,
-            } => Cell::Raw(cx.new_view(|_| RawCell {
+            } => Cell::Raw(cx.new(|_| RawCell {
                 id: id.clone(),
                 metadata: metadata.clone(),
                 source: source.join(""),
@@ -236,7 +246,7 @@ pub trait RenderableCell: Render {
     fn source(&self) -> &String;
     fn selected(&self) -> bool;
     fn set_selected(&mut self, selected: bool) -> &mut Self;
-    fn selected_bg_color(&self, cx: &ViewContext<Self>) -> Hsla {
+    fn selected_bg_color(&self, window: &mut Window, cx: &mut Context<Self>) -> Hsla {
         if self.selected() {
             let mut color = cx.theme().colors().icon_accent;
             color.fade_out(0.9);
@@ -246,14 +256,15 @@ pub trait RenderableCell: Render {
             cx.theme().colors().tab_bar_background
         }
     }
-    fn control(&self, _cx: &ViewContext<Self>) -> Option<CellControl> {
+    fn control(&self, _window: &mut Window, _cx: &mut Context<Self>) -> Option<CellControl> {
         None
     }
 
     fn cell_position_spacer(
         &self,
         is_first: bool,
-        cx: &ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<impl IntoElement> {
         let cell_position = self.cell_position();
 
@@ -266,7 +277,7 @@ pub trait RenderableCell: Render {
         }
     }
 
-    fn gutter(&self, cx: &ViewContext<Self>) -> impl IntoElement {
+    fn gutter(&self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let is_selected = self.selected();
 
         div()
@@ -289,7 +300,7 @@ pub trait RenderableCell: Render {
                             .when(!is_selected, |this| this.bg(cx.theme().colors().border)),
                     ),
             )
-            .when_some(self.control(cx), |this, control| {
+            .when_some(self.control(window, cx), |this, control| {
                 this.child(
                     div()
                         .absolute()
@@ -314,7 +325,7 @@ pub trait RenderableCell: Render {
 pub trait RunnableCell: RenderableCell {
     fn execution_count(&self) -> Option<i32>;
     fn set_execution_count(&mut self, count: i32) -> &mut Self;
-    fn run(&mut self, cx: &mut ViewContext<Self>) -> ();
+    fn run(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ();
 }
 
 pub struct MarkdownCell {
@@ -356,7 +367,7 @@ impl RenderableCell for MarkdownCell {
         self
     }
 
-    fn control(&self, _: &ViewContext<Self>) -> Option<CellControl> {
+    fn control(&self, _window: &mut Window, _: &mut Context<Self>) -> Option<CellControl> {
         None
     }
 
@@ -371,18 +382,18 @@ impl RenderableCell for MarkdownCell {
 }
 
 impl Render for MarkdownCell {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let Some(parsed) = self.parsed_markdown.as_ref() else {
             return div();
         };
 
         let mut markdown_render_context =
-            markdown_preview::markdown_renderer::RenderContext::new(None, cx);
+            markdown_preview::markdown_renderer::RenderContext::new(None, window, cx);
 
         v_flex()
             .size_full()
             // TODO: Move base cell render into trait impl so we don't have to repeat this
-            .children(self.cell_position_spacer(true, cx))
+            .children(self.cell_position_spacer(true, window, cx))
             .child(
                 h_flex()
                     .w_full()
@@ -390,8 +401,8 @@ impl Render for MarkdownCell {
                     .rounded_sm()
                     .items_start()
                     .gap(DynamicSpacing::Base08.rems(cx))
-                    .bg(self.selected_bg_color(cx))
-                    .child(self.gutter(cx))
+                    .bg(self.selected_bg_color(window, cx))
+                    .child(self.gutter(window, cx))
                     .child(
                         v_flex()
                             .size_full()
@@ -408,7 +419,7 @@ impl Render for MarkdownCell {
                     ),
             )
             // TODO: Move base cell render into trait impl so we don't have to repeat this
-            .children(self.cell_position_spacer(false, cx))
+            .children(self.cell_position_spacer(false, window, cx))
     }
 }
 
@@ -417,7 +428,7 @@ pub struct CodeCell {
     metadata: CellMetadata,
     execution_count: Option<i32>,
     source: String,
-    editor: View<editor::Editor>,
+    editor: Entity<editor::Editor>,
     outputs: Vec<Output>,
     selected: bool,
     cell_position: Option<CellPosition>,
@@ -425,7 +436,7 @@ pub struct CodeCell {
 }
 
 impl CodeCell {
-    pub fn is_dirty(&self, cx: &AppContext) -> bool {
+    pub fn is_dirty(&self, cx: &App) -> bool {
         self.editor.read(cx).buffer().read(cx).is_dirty(cx)
     }
     pub fn has_outputs(&self) -> bool {
@@ -444,7 +455,7 @@ impl CodeCell {
         }
     }
 
-    pub fn gutter_output(&self, cx: &ViewContext<Self>) -> impl IntoElement {
+    pub fn gutter_output(&self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let is_selected = self.selected();
 
         div()
@@ -505,12 +516,12 @@ impl RenderableCell for CodeCell {
         &self.source
     }
 
-    fn control(&self, cx: &ViewContext<Self>) -> Option<CellControl> {
+    fn control(&self, window: &mut Window, cx: &mut Context<Self>) -> Option<CellControl> {
         let cell_control = if self.has_outputs() {
             CellControl::new("rerun-cell", CellControlType::RerunCell)
         } else {
             CellControl::new("run-cell", CellControlType::RunCell)
-                .on_click(cx.listener(move |this, _, cx| this.run(cx)))
+                .on_click(cx.listener(move |this, _, window, cx| this.run(window, cx)))
         };
 
         Some(cell_control)
@@ -536,7 +547,7 @@ impl RenderableCell for CodeCell {
 }
 
 impl RunnableCell for CodeCell {
-    fn run(&mut self, cx: &mut ViewContext<Self>) {
+    fn run(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         println!("Running code cell: {}", self.id);
     }
 
@@ -552,11 +563,11 @@ impl RunnableCell for CodeCell {
 }
 
 impl Render for CodeCell {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         v_flex()
             .size_full()
             // TODO: Move base cell render into trait impl so we don't have to repeat this
-            .children(self.cell_position_spacer(true, cx))
+            .children(self.cell_position_spacer(true, window, cx))
             // Editor portion
             .child(
                 h_flex()
@@ -565,8 +576,8 @@ impl Render for CodeCell {
                     .rounded_sm()
                     .items_start()
                     .gap(DynamicSpacing::Base08.rems(cx))
-                    .bg(self.selected_bg_color(cx))
-                    .child(self.gutter(cx))
+                    .bg(self.selected_bg_color(window, cx))
+                    .child(self.gutter(window, cx))
                     .child(
                         div().py_1p5().w_full().child(
                             div()
@@ -591,8 +602,8 @@ impl Render for CodeCell {
                     .rounded_sm()
                     .items_start()
                     .gap(DynamicSpacing::Base08.rems(cx))
-                    .bg(self.selected_bg_color(cx))
-                    .child(self.gutter_output(cx))
+                    .bg(self.selected_bg_color(window, cx))
+                    .child(self.gutter_output(window, cx))
                     .child(
                         div().py_1p5().w_full().child(
                             div()
@@ -627,7 +638,7 @@ impl Render for CodeCell {
                                                 Some(content.clone().into_any_element())
                                             }
                                             Output::ErrorOutput(error_view) => {
-                                                error_view.render(cx)
+                                                error_view.render(window, cx)
                                             }
                                             Output::ClearOutputWaitMarker => None,
                                         };
@@ -648,7 +659,7 @@ impl Render for CodeCell {
                     ),
             )
             // TODO: Move base cell render into trait impl so we don't have to repeat this
-            .children(self.cell_position_spacer(false, cx))
+            .children(self.cell_position_spacer(false, window, cx))
     }
 }
 
@@ -699,11 +710,11 @@ impl RenderableCell for RawCell {
 }
 
 impl Render for RawCell {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         v_flex()
             .size_full()
             // TODO: Move base cell render into trait impl so we don't have to repeat this
-            .children(self.cell_position_spacer(true, cx))
+            .children(self.cell_position_spacer(true, window, cx))
             .child(
                 h_flex()
                     .w_full()
@@ -711,8 +722,8 @@ impl Render for RawCell {
                     .rounded_sm()
                     .items_start()
                     .gap(DynamicSpacing::Base08.rems(cx))
-                    .bg(self.selected_bg_color(cx))
-                    .child(self.gutter(cx))
+                    .bg(self.selected_bg_color(window, cx))
+                    .child(self.gutter(window, cx))
                     .child(
                         div()
                             .flex()
@@ -725,6 +736,6 @@ impl Render for RawCell {
                     ),
             )
             // TODO: Move base cell render into trait impl so we don't have to repeat this
-            .children(self.cell_position_spacer(false, cx))
+            .children(self.cell_position_spacer(false, window, cx))
     }
 }

crates/repl/src/notebook/notebook_ui.rs 🔗

@@ -9,8 +9,8 @@ use feature_flags::{FeatureFlagAppExt as _, NotebookFeatureFlag};
 use futures::future::Shared;
 use futures::FutureExt;
 use gpui::{
-    actions, list, prelude::*, AnyElement, AppContext, EventEmitter, FocusHandle, FocusableView,
-    ListScrollEvent, ListState, Model, Point, Task, View,
+    actions, list, prelude::*, AnyElement, App, Entity, EventEmitter, FocusHandle, Focusable,
+    ListScrollEvent, ListState, Point, Task,
 };
 use language::{Language, LanguageRegistry};
 use project::{Project, ProjectEntryId, ProjectPath};
@@ -46,7 +46,7 @@ pub(crate) const GUTTER_WIDTH: f32 = 19.0;
 pub(crate) const CODE_BLOCK_INSET: f32 = MEDIUM_SPACING_SIZE;
 pub(crate) const CONTROL_SIZE: f32 = 20.0;
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     if cx.has_flag::<NotebookFeatureFlag>() || std::env::var("LOCAL_NOTEBOOK_DEV").is_ok() {
         workspace::register_project_item::<NotebookEditor>(cx);
     }
@@ -66,10 +66,10 @@ pub fn init(cx: &mut AppContext) {
 
 pub struct NotebookEditor {
     languages: Arc<LanguageRegistry>,
-    project: Model<Project>,
+    project: Entity<Project>,
 
     focus_handle: FocusHandle,
-    notebook_item: Model<NotebookItem>,
+    notebook_item: Entity<NotebookItem>,
 
     remote_id: Option<ViewId>,
     cell_list: ListState,
@@ -81,9 +81,10 @@ pub struct NotebookEditor {
 
 impl NotebookEditor {
     pub fn new(
-        project: Model<Project>,
-        notebook_item: Model<NotebookItem>,
-        cx: &mut ViewContext<Self>,
+        project: Entity<Project>,
+        notebook_item: Entity<NotebookItem>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let focus_handle = cx.focus_handle();
 
@@ -91,7 +92,7 @@ impl NotebookEditor {
         let language_name = notebook_item.read(cx).language_name();
 
         let notebook_language = notebook_item.read(cx).notebook_language();
-        let notebook_language = cx.spawn(|_, _| notebook_language).shared();
+        let notebook_language = cx.spawn_in(window, |_, _| notebook_language).shared();
 
         let mut cell_order = vec![]; // Vec<CellId>
         let mut cell_map = HashMap::default(); // HashMap<CellId, Cell>
@@ -108,27 +109,32 @@ impl NotebookEditor {
             cell_order.push(cell_id.clone());
             cell_map.insert(
                 cell_id.clone(),
-                Cell::load(cell, &languages, notebook_language.clone(), cx),
+                Cell::load(cell, &languages, notebook_language.clone(), window, cx),
             );
         }
 
-        let view = cx.view().downgrade();
+        let notebook_handle = cx.model().downgrade();
         let cell_count = cell_order.len();
 
-        let this = cx.view();
+        let this = cx.model();
         let cell_list = ListState::new(
             cell_count,
             gpui::ListAlignment::Top,
             px(1000.),
-            move |ix, cx| {
-                view.upgrade()
+            move |ix, window, cx| {
+                notebook_handle
+                    .upgrade()
                     .and_then(|notebook_handle| {
                         notebook_handle.update(cx, |notebook, cx| {
                             notebook
                                 .cell_order
                                 .get(ix)
                                 .and_then(|cell_id| notebook.cell_map.get(cell_id))
-                                .map(|cell| notebook.render_cell(ix, cell, cx).into_any_element())
+                                .map(|cell| {
+                                    notebook
+                                        .render_cell(ix, cell, window, cx)
+                                        .into_any_element()
+                                })
                         })
                     })
                     .unwrap_or_else(|| div().into_any())
@@ -148,7 +154,7 @@ impl NotebookEditor {
         }
     }
 
-    fn has_outputs(&self, cx: &ViewContext<Self>) -> bool {
+    fn has_outputs(&self, window: &mut Window, cx: &mut Context<Self>) -> bool {
         self.cell_map.values().any(|cell| {
             if let Cell::Code(code_cell) = cell {
                 code_cell.read(cx).has_outputs()
@@ -158,7 +164,7 @@ impl NotebookEditor {
         })
     }
 
-    fn clear_outputs(&mut self, cx: &mut ViewContext<Self>) {
+    fn clear_outputs(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         for cell in self.cell_map.values() {
             if let Cell::Code(code_cell) = cell {
                 code_cell.update(cx, |cell, _cx| {
@@ -168,27 +174,27 @@ impl NotebookEditor {
         }
     }
 
-    fn run_cells(&mut self, cx: &mut ViewContext<Self>) {
+    fn run_cells(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         println!("Cells would all run here, if that was implemented!");
     }
 
-    fn open_notebook(&mut self, _: &OpenNotebook, _cx: &mut ViewContext<Self>) {
+    fn open_notebook(&mut self, _: &OpenNotebook, _window: &mut Window, _cx: &mut Context<Self>) {
         println!("Open notebook triggered");
     }
 
-    fn move_cell_up(&mut self, cx: &mut ViewContext<Self>) {
+    fn move_cell_up(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         println!("Move cell up triggered");
     }
 
-    fn move_cell_down(&mut self, cx: &mut ViewContext<Self>) {
+    fn move_cell_down(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         println!("Move cell down triggered");
     }
 
-    fn add_markdown_block(&mut self, cx: &mut ViewContext<Self>) {
+    fn add_markdown_block(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         println!("Add markdown block triggered");
     }
 
-    fn add_code_block(&mut self, cx: &mut ViewContext<Self>) {
+    fn add_code_block(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         println!("Add code block triggered");
     }
 
@@ -204,7 +210,8 @@ impl NotebookEditor {
         &mut self,
         index: usize,
         jump_to_index: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         // let previous_index = self.selected_cell_index;
         self.selected_cell_index = index;
@@ -213,11 +220,16 @@ impl NotebookEditor {
         // in the future we may have some `on_cell_change` event that we want to fire here
 
         if jump_to_index {
-            self.jump_to_cell(current_index, cx);
+            self.jump_to_cell(current_index, window, cx);
         }
     }
 
-    pub fn select_next(&mut self, _: &menu::SelectNext, cx: &mut ViewContext<Self>) {
+    pub fn select_next(
+        &mut self,
+        _: &menu::SelectNext,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let count = self.cell_count();
         if count > 0 {
             let index = self.selected_index();
@@ -226,42 +238,57 @@ impl NotebookEditor {
             } else {
                 index + 1
             };
-            self.set_selected_index(ix, true, cx);
+            self.set_selected_index(ix, true, window, cx);
             cx.notify();
         }
     }
 
-    pub fn select_previous(&mut self, _: &menu::SelectPrev, cx: &mut ViewContext<Self>) {
+    pub fn select_previous(
+        &mut self,
+        _: &menu::SelectPrev,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let count = self.cell_count();
         if count > 0 {
             let index = self.selected_index();
             let ix = if index == 0 { 0 } else { index - 1 };
-            self.set_selected_index(ix, true, cx);
+            self.set_selected_index(ix, true, window, cx);
             cx.notify();
         }
     }
 
-    pub fn select_first(&mut self, _: &menu::SelectFirst, cx: &mut ViewContext<Self>) {
+    pub fn select_first(
+        &mut self,
+        _: &menu::SelectFirst,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let count = self.cell_count();
         if count > 0 {
-            self.set_selected_index(0, true, cx);
+            self.set_selected_index(0, true, window, cx);
             cx.notify();
         }
     }
 
-    pub fn select_last(&mut self, _: &menu::SelectLast, cx: &mut ViewContext<Self>) {
+    pub fn select_last(
+        &mut self,
+        _: &menu::SelectLast,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let count = self.cell_count();
         if count > 0 {
-            self.set_selected_index(count - 1, true, cx);
+            self.set_selected_index(count - 1, true, window, cx);
             cx.notify();
         }
     }
 
-    fn jump_to_cell(&mut self, index: usize, _cx: &mut ViewContext<Self>) {
+    fn jump_to_cell(&mut self, index: usize, _window: &mut Window, _cx: &mut Context<Self>) {
         self.cell_list.scroll_to_reveal_item(index);
     }
 
-    fn button_group(cx: &ViewContext<Self>) -> Div {
+    fn button_group(window: &mut Window, cx: &mut Context<Self>) -> Div {
         v_flex()
             .gap(DynamicSpacing::Base04.rems(cx))
             .items_center()
@@ -277,14 +304,19 @@ impl NotebookEditor {
     fn render_notebook_control(
         id: impl Into<SharedString>,
         icon: IconName,
-        _cx: &ViewContext<Self>,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
     ) -> IconButton {
         let id: ElementId = ElementId::Name(id.into());
         IconButton::new(id, icon).width(px(CONTROL_SIZE).into())
     }
 
-    fn render_notebook_controls(&self, cx: &ViewContext<Self>) -> impl IntoElement {
-        let has_outputs = self.has_outputs(cx);
+    fn render_notebook_controls(
+        &self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> impl IntoElement {
+        let has_outputs = self.has_outputs(window, cx);
 
         v_flex()
             .max_w(px(CONTROL_SIZE + 4.0))
@@ -298,83 +330,107 @@ impl NotebookEditor {
                 v_flex()
                     .gap(DynamicSpacing::Base08.rems(cx))
                     .child(
-                        Self::button_group(cx)
+                        Self::button_group(window, cx)
                             .child(
-                                Self::render_notebook_control("run-all-cells", IconName::Play, cx)
-                                    .tooltip(move |cx| {
-                                        Tooltip::for_action("Execute all cells", &RunAll, cx)
-                                    })
-                                    .on_click(|_, cx| {
-                                        cx.dispatch_action(Box::new(RunAll));
-                                    }),
+                                Self::render_notebook_control(
+                                    "run-all-cells",
+                                    IconName::Play,
+                                    window,
+                                    cx,
+                                )
+                                .tooltip(move |window, cx| {
+                                    Tooltip::for_action("Execute all cells", &RunAll, window, cx)
+                                })
+                                .on_click(|_, window, cx| {
+                                    window.dispatch_action(Box::new(RunAll), cx);
+                                }),
                             )
                             .child(
                                 Self::render_notebook_control(
                                     "clear-all-outputs",
                                     IconName::ListX,
+                                    window,
                                     cx,
                                 )
                                 .disabled(!has_outputs)
-                                .tooltip(move |cx| {
-                                    Tooltip::for_action("Clear all outputs", &ClearOutputs, cx)
+                                .tooltip(move |window, cx| {
+                                    Tooltip::for_action(
+                                        "Clear all outputs",
+                                        &ClearOutputs,
+                                        window,
+                                        cx,
+                                    )
                                 })
-                                .on_click(|_, cx| {
-                                    cx.dispatch_action(Box::new(ClearOutputs));
+                                .on_click(|_, window, cx| {
+                                    window.dispatch_action(Box::new(ClearOutputs), cx);
                                 }),
                             ),
                     )
                     .child(
-                        Self::button_group(cx)
+                        Self::button_group(window, cx)
                             .child(
                                 Self::render_notebook_control(
                                     "move-cell-up",
                                     IconName::ArrowUp,
+                                    window,
                                     cx,
                                 )
-                                .tooltip(move |cx| {
-                                    Tooltip::for_action("Move cell up", &MoveCellUp, cx)
+                                .tooltip(move |window, cx| {
+                                    Tooltip::for_action("Move cell up", &MoveCellUp, window, cx)
                                 })
-                                .on_click(|_, cx| {
-                                    cx.dispatch_action(Box::new(MoveCellUp));
+                                .on_click(|_, window, cx| {
+                                    window.dispatch_action(Box::new(MoveCellUp), cx);
                                 }),
                             )
                             .child(
                                 Self::render_notebook_control(
                                     "move-cell-down",
                                     IconName::ArrowDown,
+                                    window,
                                     cx,
                                 )
-                                .tooltip(move |cx| {
-                                    Tooltip::for_action("Move cell down", &MoveCellDown, cx)
+                                .tooltip(move |window, cx| {
+                                    Tooltip::for_action("Move cell down", &MoveCellDown, window, cx)
                                 })
-                                .on_click(|_, cx| {
-                                    cx.dispatch_action(Box::new(MoveCellDown));
+                                .on_click(|_, window, cx| {
+                                    window.dispatch_action(Box::new(MoveCellDown), cx);
                                 }),
                             ),
                     )
                     .child(
-                        Self::button_group(cx)
+                        Self::button_group(window, cx)
                             .child(
                                 Self::render_notebook_control(
                                     "new-markdown-cell",
                                     IconName::Plus,
+                                    window,
                                     cx,
                                 )
-                                .tooltip(move |cx| {
-                                    Tooltip::for_action("Add markdown block", &AddMarkdownBlock, cx)
+                                .tooltip(move |window, cx| {
+                                    Tooltip::for_action(
+                                        "Add markdown block",
+                                        &AddMarkdownBlock,
+                                        window,
+                                        cx,
+                                    )
                                 })
-                                .on_click(|_, cx| {
-                                    cx.dispatch_action(Box::new(AddMarkdownBlock));
+                                .on_click(|_, window, cx| {
+                                    window.dispatch_action(Box::new(AddMarkdownBlock), cx);
                                 }),
                             )
                             .child(
-                                Self::render_notebook_control("new-code-cell", IconName::Code, cx)
-                                    .tooltip(move |cx| {
-                                        Tooltip::for_action("Add code block", &AddCodeBlock, cx)
-                                    })
-                                    .on_click(|_, cx| {
-                                        cx.dispatch_action(Box::new(AddCodeBlock));
-                                    }),
+                                Self::render_notebook_control(
+                                    "new-code-cell",
+                                    IconName::Code,
+                                    window,
+                                    cx,
+                                )
+                                .tooltip(move |window, cx| {
+                                    Tooltip::for_action("Add code block", &AddCodeBlock, window, cx)
+                                })
+                                .on_click(|_, window, cx| {
+                                    window.dispatch_action(Box::new(AddCodeBlock), cx);
+                                }),
                             ),
                     ),
             )
@@ -385,10 +441,11 @@ impl NotebookEditor {
                     .child(Self::render_notebook_control(
                         "more-menu",
                         IconName::Ellipsis,
+                        window,
                         cx,
                     ))
                     .child(
-                        Self::button_group(cx)
+                        Self::button_group(window, cx)
                             .child(IconButton::new("repl", IconName::ReplNeutral)),
                     ),
             )
@@ -406,7 +463,8 @@ impl NotebookEditor {
         &self,
         index: usize,
         cell: &Cell,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> impl IntoElement {
         let cell_position = self.cell_position(index);
 
@@ -439,17 +497,27 @@ impl NotebookEditor {
 }
 
 impl Render for NotebookEditor {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         div()
             .key_context("notebook")
             .track_focus(&self.focus_handle)
-            .on_action(cx.listener(|this, &OpenNotebook, cx| this.open_notebook(&OpenNotebook, cx)))
-            .on_action(cx.listener(|this, &ClearOutputs, cx| this.clear_outputs(cx)))
-            .on_action(cx.listener(|this, &RunAll, cx| this.run_cells(cx)))
-            .on_action(cx.listener(|this, &MoveCellUp, cx| this.move_cell_up(cx)))
-            .on_action(cx.listener(|this, &MoveCellDown, cx| this.move_cell_down(cx)))
-            .on_action(cx.listener(|this, &AddMarkdownBlock, cx| this.add_markdown_block(cx)))
-            .on_action(cx.listener(|this, &AddCodeBlock, cx| this.add_code_block(cx)))
+            .on_action(cx.listener(|this, &OpenNotebook, window, cx| {
+                this.open_notebook(&OpenNotebook, window, cx)
+            }))
+            .on_action(
+                cx.listener(|this, &ClearOutputs, window, cx| this.clear_outputs(window, cx)),
+            )
+            .on_action(cx.listener(|this, &RunAll, window, cx| this.run_cells(window, cx)))
+            .on_action(cx.listener(|this, &MoveCellUp, window, cx| this.move_cell_up(window, cx)))
+            .on_action(
+                cx.listener(|this, &MoveCellDown, window, cx| this.move_cell_down(window, cx)),
+            )
+            .on_action(cx.listener(|this, &AddMarkdownBlock, window, cx| {
+                this.add_markdown_block(window, cx)
+            }))
+            .on_action(
+                cx.listener(|this, &AddCodeBlock, window, cx| this.add_code_block(window, cx)),
+            )
             .on_action(cx.listener(Self::select_next))
             .on_action(cx.listener(Self::select_previous))
             .on_action(cx.listener(Self::select_first))
@@ -469,12 +537,12 @@ impl Render for NotebookEditor {
                     .overflow_y_scroll()
                     .child(list(self.cell_list.clone()).size_full()),
             )
-            .child(self.render_notebook_controls(cx))
+            .child(self.render_notebook_controls(window, cx))
     }
 }
 
-impl FocusableView for NotebookEditor {
-    fn focus_handle(&self, _: &AppContext) -> FocusHandle {
+impl Focusable for NotebookEditor {
+    fn focus_handle(&self, _: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -492,10 +560,10 @@ pub struct NotebookItem {
 
 impl project::ProjectItem for NotebookItem {
     fn try_open(
-        project: &Model<Project>,
+        project: &Entity<Project>,
         path: &ProjectPath,
-        cx: &mut AppContext,
-    ) -> Option<Task<gpui::Result<Model<Self>>>> {
+        cx: &mut App,
+    ) -> Option<Task<gpui::Result<Entity<Self>>>> {
         let path = path.clone();
         let project = project.clone();
         let fs = project.read(cx).fs().clone();
@@ -531,7 +599,7 @@ impl project::ProjectItem for NotebookItem {
                     .context("Entry not found")?
                     .id;
 
-                cx.new_model(|_| NotebookItem {
+                cx.new(|_| NotebookItem {
                     path: abs_path,
                     project_path: path,
                     languages,
@@ -544,11 +612,11 @@ impl project::ProjectItem for NotebookItem {
         }
     }
 
-    fn entry_id(&self, _: &AppContext) -> Option<ProjectEntryId> {
+    fn entry_id(&self, _: &App) -> Option<ProjectEntryId> {
         Some(self.id)
     }
 
-    fn project_path(&self, _: &AppContext) -> Option<ProjectPath> {
+    fn project_path(&self, _: &App) -> Option<ProjectPath> {
         Some(self.project_path.clone())
     }
 
@@ -607,7 +675,7 @@ impl EventEmitter<()> for NotebookEditor {}
 // impl EventEmitter<ToolbarItemEvent> for NotebookControls {}
 
 // impl Render for NotebookControls {
-//     fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+//     fn render(&mut self, window: &mut Window, cx: &mut ModelContext<Self>) -> impl IntoElement {
 //         div().child("notebook controls")
 //     }
 // }
@@ -616,7 +684,7 @@ impl EventEmitter<()> for NotebookEditor {}
 //     fn set_active_pane_item(
 //         &mut self,
 //         active_pane_item: Option<&dyn workspace::ItemHandle>,
-//         cx: &mut ViewContext<Self>,
+//         window: &mut Window, cx: &mut ModelContext<Self>,
 //     ) -> workspace::ToolbarItemLocation {
 //         cx.notify();
 //         self.active_item = None;
@@ -628,7 +696,7 @@ impl EventEmitter<()> for NotebookEditor {}
 //         ToolbarItemLocation::PrimaryLeft
 //     }
 
-//     fn pane_focus_update(&mut self, pane_focused: bool, _: &mut ViewContext<Self>) {
+//     fn pane_focus_update(&mut self, pane_focused: bool, _window: &mut Window, _cx: &mut ModelContext<Self>) {
 //         self.pane_focused = pane_focused;
 //     }
 // }
@@ -639,27 +707,28 @@ impl Item for NotebookEditor {
     fn clone_on_split(
         &self,
         _workspace_id: Option<workspace::WorkspaceId>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<gpui::View<Self>>
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Entity<Self>>
     where
         Self: Sized,
     {
-        Some(cx.new_view(|cx| Self::new(self.project.clone(), self.notebook_item.clone(), cx)))
+        Some(cx.new(|cx| Self::new(self.project.clone(), self.notebook_item.clone(), window, cx)))
     }
 
     fn for_each_project_item(
         &self,
-        cx: &AppContext,
+        cx: &App,
         f: &mut dyn FnMut(gpui::EntityId, &dyn project::ProjectItem),
     ) {
         f(self.notebook_item.entity_id(), self.notebook_item.read(cx))
     }
 
-    fn is_singleton(&self, _cx: &AppContext) -> bool {
+    fn is_singleton(&self, _cx: &App) -> bool {
         true
     }
 
-    fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement {
+    fn tab_content(&self, params: TabContentParams, window: &Window, cx: &App) -> AnyElement {
         let path = &self.notebook_item.read(cx).path;
         let title = path
             .file_name()
@@ -673,7 +742,7 @@ impl Item for NotebookEditor {
             .into_any_element()
     }
 
-    fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
+    fn tab_icon(&self, _window: &Window, _cx: &App) -> Option<Icon> {
         Some(IconName::Book.into())
     }
 
@@ -682,29 +751,35 @@ impl Item for NotebookEditor {
     }
 
     // TODO
-    fn pixel_position_of_cursor(&self, _: &AppContext) -> Option<Point<Pixels>> {
+    fn pixel_position_of_cursor(&self, _: &App) -> Option<Point<Pixels>> {
         None
     }
 
     // TODO
-    fn as_searchable(&self, _: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
+    fn as_searchable(&self, _: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {
         None
     }
 
-    fn set_nav_history(&mut self, _: workspace::ItemNavHistory, _: &mut ViewContext<Self>) {
+    fn set_nav_history(
+        &mut self,
+        _: workspace::ItemNavHistory,
+        _window: &mut Window,
+        _: &mut Context<Self>,
+    ) {
         // TODO
     }
 
     // TODO
-    fn can_save(&self, _cx: &AppContext) -> bool {
+    fn can_save(&self, _cx: &App) -> bool {
         false
     }
     // TODO
     fn save(
         &mut self,
         _format: bool,
-        _project: Model<Project>,
-        _cx: &mut ViewContext<Self>,
+        _project: Entity<Project>,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         unimplemented!("save() must be implemented if can_save() returns true")
     }
@@ -712,22 +787,24 @@ impl Item for NotebookEditor {
     // TODO
     fn save_as(
         &mut self,
-        _project: Model<Project>,
+        _project: Entity<Project>,
         _path: ProjectPath,
-        _cx: &mut ViewContext<Self>,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         unimplemented!("save_as() must be implemented if can_save() returns true")
     }
     // TODO
     fn reload(
         &mut self,
-        _project: Model<Project>,
-        _cx: &mut ViewContext<Self>,
+        _project: Entity<Project>,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         unimplemented!("reload() must be implemented if can_save() returns true")
     }
 
-    fn is_dirty(&self, cx: &AppContext) -> bool {
+    fn is_dirty(&self, cx: &App) -> bool {
         self.cell_map.values().any(|cell| {
             if let Cell::Code(code_cell) = cell {
                 code_cell.read(cx).is_dirty(cx)
@@ -745,13 +822,14 @@ impl ProjectItem for NotebookEditor {
     type Item = NotebookItem;
 
     fn for_project_item(
-        project: Model<Project>,
-        item: Model<Self::Item>,
-        cx: &mut ViewContext<Self>,
+        project: Entity<Project>,
+        item: Entity<Self::Item>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self
     where
         Self: Sized,
     {
-        Self::new(project, item, cx)
+        Self::new(project, item, window, cx)
     }
 }

crates/repl/src/outputs.rs 🔗

@@ -37,12 +37,12 @@ use std::time::Duration;
 
 use editor::{Editor, MultiBuffer};
 use gpui::{
-    percentage, Animation, AnimationExt, AnyElement, ClipboardItem, Model, Render, Transformation,
-    View, WeakView,
+    percentage, Animation, AnimationExt, AnyElement, ClipboardItem, Entity, Render, Transformation,
+    WeakEntity,
 };
 use language::Buffer;
 use runtimelib::{ExecutionState, JupyterMessageContent, MimeBundle, MimeType};
-use ui::{div, prelude::*, v_flex, IntoElement, Styled, Tooltip, ViewContext};
+use ui::{div, prelude::*, v_flex, Context, IntoElement, Styled, Tooltip, Window};
 
 mod image;
 use image::ImageView;
@@ -74,56 +74,56 @@ fn rank_mime_type(mimetype: &MimeType) -> usize {
 }
 
 pub(crate) trait OutputContent {
-    fn clipboard_content(&self, cx: &WindowContext) -> Option<ClipboardItem>;
-    fn has_clipboard_content(&self, _cx: &WindowContext) -> bool {
+    fn clipboard_content(&self, window: &Window, cx: &App) -> Option<ClipboardItem>;
+    fn has_clipboard_content(&self, _window: &Window, _cx: &App) -> bool {
         false
     }
-    fn has_buffer_content(&self, _cx: &WindowContext) -> bool {
+    fn has_buffer_content(&self, _window: &Window, _cx: &App) -> bool {
         false
     }
-    fn buffer_content(&mut self, _cx: &mut WindowContext) -> Option<Model<Buffer>> {
+    fn buffer_content(&mut self, _window: &mut Window, _cx: &mut App) -> Option<Entity<Buffer>> {
         None
     }
 }
 
-impl<V: OutputContent + 'static> OutputContent for View<V> {
-    fn clipboard_content(&self, cx: &WindowContext) -> Option<ClipboardItem> {
-        self.read(cx).clipboard_content(cx)
+impl<V: OutputContent + 'static> OutputContent for Entity<V> {
+    fn clipboard_content(&self, window: &Window, cx: &App) -> Option<ClipboardItem> {
+        self.read(cx).clipboard_content(window, cx)
     }
 
-    fn has_clipboard_content(&self, cx: &WindowContext) -> bool {
-        self.read(cx).has_clipboard_content(cx)
+    fn has_clipboard_content(&self, window: &Window, cx: &App) -> bool {
+        self.read(cx).has_clipboard_content(window, cx)
     }
 
-    fn has_buffer_content(&self, cx: &WindowContext) -> bool {
-        self.read(cx).has_buffer_content(cx)
+    fn has_buffer_content(&self, window: &Window, cx: &App) -> bool {
+        self.read(cx).has_buffer_content(window, cx)
     }
 
-    fn buffer_content(&mut self, cx: &mut WindowContext) -> Option<Model<Buffer>> {
-        self.update(cx, |item, cx| item.buffer_content(cx))
+    fn buffer_content(&mut self, window: &mut Window, cx: &mut App) -> Option<Entity<Buffer>> {
+        self.update(cx, |item, cx| item.buffer_content(window, cx))
     }
 }
 
 pub enum Output {
     Plain {
-        content: View<TerminalOutput>,
+        content: Entity<TerminalOutput>,
         display_id: Option<String>,
     },
     Stream {
-        content: View<TerminalOutput>,
+        content: Entity<TerminalOutput>,
     },
     Image {
-        content: View<ImageView>,
+        content: Entity<ImageView>,
         display_id: Option<String>,
     },
     ErrorOutput(ErrorView),
     Message(String),
     Table {
-        content: View<TableView>,
+        content: Entity<TableView>,
         display_id: Option<String>,
     },
     Markdown {
-        content: View<MarkdownView>,
+        content: Entity<MarkdownView>,
         display_id: Option<String>,
     },
     ClearOutputWaitMarker,
@@ -131,25 +131,26 @@ pub enum Output {
 
 impl Output {
     fn render_output_controls<V: OutputContent + 'static>(
-        v: View<V>,
-        workspace: WeakView<Workspace>,
-        cx: &mut ViewContext<ExecutionView>,
+        v: Entity<V>,
+        workspace: WeakEntity<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<ExecutionView>,
     ) -> Option<AnyElement> {
-        if !v.has_clipboard_content(cx) && !v.has_buffer_content(cx) {
+        if !v.has_clipboard_content(window, cx) && !v.has_buffer_content(window, cx) {
             return None;
         }
 
         Some(
             h_flex()
                 .pl_1()
-                .when(v.has_clipboard_content(cx), |el| {
+                .when(v.has_clipboard_content(window, cx), |el| {
                     let v = v.clone();
                     el.child(
                         IconButton::new(ElementId::Name("copy-output".into()), IconName::Copy)
                             .style(ButtonStyle::Transparent)
-                            .tooltip(move |cx| Tooltip::text("Copy Output", cx))
-                            .on_click(cx.listener(move |_, _, cx| {
-                                let clipboard_content = v.clipboard_content(cx);
+                            .tooltip(Tooltip::text("Copy Output"))
+                            .on_click(cx.listener(move |_, _, window, cx| {
+                                let clipboard_content = v.clipboard_content(window, cx);
 
                                 if let Some(clipboard_content) = clipboard_content.as_ref() {
                                     cx.write_to_clipboard(clipboard_content.clone());
@@ -157,7 +158,7 @@ impl Output {
                             })),
                     )
                 })
-                .when(v.has_buffer_content(cx), |el| {
+                .when(v.has_buffer_content(window, cx), |el| {
                     let v = v.clone();
                     el.child(
                         IconButton::new(
@@ -165,18 +166,18 @@ impl Output {
                             IconName::FileText,
                         )
                         .style(ButtonStyle::Transparent)
-                        .tooltip(move |cx| Tooltip::text("Open in Buffer", cx))
+                        .tooltip(Tooltip::text("Open in Buffer"))
                         .on_click(cx.listener({
                             let workspace = workspace.clone();
 
-                            move |_, _, cx| {
+                            move |_, _, window, cx| {
                                 let buffer_content =
-                                    v.update(cx, |item, cx| item.buffer_content(cx));
+                                    v.update(cx, |item, cx| item.buffer_content(window, cx));
 
                                 if let Some(buffer_content) = buffer_content.as_ref() {
                                     let buffer = buffer_content.clone();
-                                    let editor = Box::new(cx.new_view(|cx| {
-                                        let multibuffer = cx.new_model(|cx| {
+                                    let editor = Box::new(cx.new(|cx| {
+                                        let multibuffer = cx.new(|cx| {
                                             let mut multi_buffer =
                                                 MultiBuffer::singleton(buffer.clone(), cx);
 
@@ -184,12 +185,19 @@ impl Output {
                                             multi_buffer
                                         });
 
-                                        Editor::for_multibuffer(multibuffer, None, false, cx)
+                                        Editor::for_multibuffer(
+                                            multibuffer,
+                                            None,
+                                            false,
+                                            window,
+                                            cx,
+                                        )
                                     }));
                                     workspace
                                         .update(cx, |workspace, cx| {
-                                            workspace
-                                                .add_item_to_active_pane(editor, None, true, cx);
+                                            workspace.add_item_to_active_pane(
+                                                editor, None, true, window, cx,
+                                            );
                                         })
                                         .ok();
                                 }
@@ -204,8 +212,9 @@ impl Output {
     pub fn render(
         &self,
 
-        workspace: WeakView<Workspace>,
-        cx: &mut ViewContext<ExecutionView>,
+        workspace: WeakEntity<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<ExecutionView>,
     ) -> impl IntoElement {
         let content = match self {
             Self::Plain { content, .. } => Some(content.clone().into_any_element()),
@@ -214,7 +223,7 @@ impl Output {
             Self::Image { content, .. } => Some(content.clone().into_any_element()),
             Self::Message(message) => Some(div().child(message.clone()).into_any_element()),
             Self::Table { content, .. } => Some(content.clone().into_any_element()),
-            Self::ErrorOutput(error_view) => error_view.render(cx),
+            Self::ErrorOutput(error_view) => error_view.render(window, cx),
             Self::ClearOutputWaitMarker => None,
         };
 
@@ -224,23 +233,26 @@ impl Output {
             .child(div().flex_1().children(content))
             .children(match self {
                 Self::Plain { content, .. } => {
-                    Self::render_output_controls(content.clone(), workspace.clone(), cx)
+                    Self::render_output_controls(content.clone(), workspace.clone(), window, cx)
                 }
                 Self::Markdown { content, .. } => {
-                    Self::render_output_controls(content.clone(), workspace.clone(), cx)
+                    Self::render_output_controls(content.clone(), workspace.clone(), window, cx)
                 }
                 Self::Stream { content, .. } => {
-                    Self::render_output_controls(content.clone(), workspace.clone(), cx)
+                    Self::render_output_controls(content.clone(), workspace.clone(), window, cx)
                 }
                 Self::Image { content, .. } => {
-                    Self::render_output_controls(content.clone(), workspace.clone(), cx)
-                }
-                Self::ErrorOutput(err) => {
-                    Self::render_output_controls(err.traceback.clone(), workspace.clone(), cx)
+                    Self::render_output_controls(content.clone(), workspace.clone(), window, cx)
                 }
+                Self::ErrorOutput(err) => Self::render_output_controls(
+                    err.traceback.clone(),
+                    workspace.clone(),
+                    window,
+                    cx,
+                ),
                 Self::Message(_) => None,
                 Self::Table { content, .. } => {
-                    Self::render_output_controls(content.clone(), workspace.clone(), cx)
+                    Self::render_output_controls(content.clone(), workspace.clone(), window, cx)
                 }
                 Self::ClearOutputWaitMarker => None,
             })
@@ -259,28 +271,33 @@ impl Output {
         }
     }
 
-    pub fn new(data: &MimeBundle, display_id: Option<String>, cx: &mut WindowContext) -> Self {
+    pub fn new(
+        data: &MimeBundle,
+        display_id: Option<String>,
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Self {
         match data.richest(rank_mime_type) {
             Some(MimeType::Plain(text)) => Output::Plain {
-                content: cx.new_view(|cx| TerminalOutput::from(text, cx)),
+                content: cx.new(|cx| TerminalOutput::from(text, window, cx)),
                 display_id,
             },
             Some(MimeType::Markdown(text)) => {
-                let view = cx.new_view(|cx| MarkdownView::from(text.clone(), cx));
+                let content = cx.new(|cx| MarkdownView::from(text.clone(), cx));
                 Output::Markdown {
-                    content: view,
+                    content,
                     display_id,
                 }
             }
             Some(MimeType::Png(data)) | Some(MimeType::Jpeg(data)) => match ImageView::from(data) {
                 Ok(view) => Output::Image {
-                    content: cx.new_view(|_| view),
+                    content: cx.new(|_| view),
                     display_id,
                 },
                 Err(error) => Output::Message(format!("Failed to load image: {}", error)),
             },
             Some(MimeType::DataTable(data)) => Output::Table {
-                content: cx.new_view(|cx| TableView::new(data, cx)),
+                content: cx.new(|cx| TableView::new(data, window, cx)),
                 display_id,
             },
             // Any other media types are not supported
@@ -308,7 +325,7 @@ pub enum ExecutionStatus {
 /// sees as "the output" for a single execution.
 pub struct ExecutionView {
     #[allow(unused)]
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     pub outputs: Vec<Output>,
     pub status: ExecutionStatus,
 }
@@ -316,8 +333,8 @@ pub struct ExecutionView {
 impl ExecutionView {
     pub fn new(
         status: ExecutionStatus,
-        workspace: WeakView<Workspace>,
-        _cx: &mut ViewContext<Self>,
+        workspace: WeakEntity<Workspace>,
+        _cx: &mut Context<Self>,
     ) -> Self {
         Self {
             workspace,
@@ -327,21 +344,28 @@ impl ExecutionView {
     }
 
     /// Accept a Jupyter message belonging to this execution
-    pub fn push_message(&mut self, message: &JupyterMessageContent, cx: &mut ViewContext<Self>) {
+    pub fn push_message(
+        &mut self,
+        message: &JupyterMessageContent,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let output: Output = match message {
             JupyterMessageContent::ExecuteResult(result) => Output::new(
                 &result.data,
                 result.transient.as_ref().and_then(|t| t.display_id.clone()),
+                window,
                 cx,
             ),
             JupyterMessageContent::DisplayData(result) => Output::new(
                 &result.data,
                 result.transient.as_ref().and_then(|t| t.display_id.clone()),
+                window,
                 cx,
             ),
             JupyterMessageContent::StreamContent(result) => {
                 // Previous stream data will combine together, handling colors, carriage returns, etc
-                if let Some(new_terminal) = self.apply_terminal_text(&result.text, cx) {
+                if let Some(new_terminal) = self.apply_terminal_text(&result.text, window, cx) {
                     new_terminal
                 } else {
                     return;
@@ -349,7 +373,7 @@ impl ExecutionView {
             }
             JupyterMessageContent::ErrorOutput(result) => {
                 let terminal =
-                    cx.new_view(|cx| TerminalOutput::from(&result.traceback.join("\n"), cx));
+                    cx.new(|cx| TerminalOutput::from(&result.traceback.join("\n"), window, cx));
 
                 Output::ErrorOutput(ErrorView {
                     ename: result.ename.clone(),
@@ -360,7 +384,7 @@ impl ExecutionView {
             JupyterMessageContent::ExecuteReply(reply) => {
                 for payload in reply.payload.iter() {
                     if let runtimelib::Payload::Page { data, .. } = payload {
-                        let output = Output::new(data, None, cx);
+                        let output = Output::new(data, None, window, cx);
                         self.outputs.push(output);
                     }
                 }
@@ -408,14 +432,15 @@ impl ExecutionView {
         &mut self,
         data: &MimeBundle,
         display_id: &str,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let mut any = false;
 
         self.outputs.iter_mut().for_each(|output| {
             if let Some(other_display_id) = output.display_id().as_ref() {
                 if other_display_id == display_id {
-                    *output = Output::new(data, Some(display_id.to_owned()), cx);
+                    *output = Output::new(data, Some(display_id.to_owned()), window, cx);
                     any = true;
                 }
             }
@@ -426,7 +451,12 @@ impl ExecutionView {
         }
     }
 
-    fn apply_terminal_text(&mut self, text: &str, cx: &mut ViewContext<Self>) -> Option<Output> {
+    fn apply_terminal_text(
+        &mut self,
+        text: &str,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Output> {
         if let Some(last_output) = self.outputs.last_mut() {
             if let Output::Stream {
                 content: last_stream,
@@ -443,13 +473,13 @@ impl ExecutionView {
         }
 
         Some(Output::Stream {
-            content: cx.new_view(|cx| TerminalOutput::from(text, cx)),
+            content: cx.new(|cx| TerminalOutput::from(text, window, cx)),
         })
     }
 }
 
 impl Render for ExecutionView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let status = match &self.status {
             ExecutionStatus::ConnectingToKernel => Label::new("Connecting to kernel...")
                 .color(Color::Muted)
@@ -493,7 +523,7 @@ impl Render for ExecutionView {
 
         if self.outputs.is_empty() {
             return v_flex()
-                .min_h(cx.line_height())
+                .min_h(window.line_height())
                 .justify_center()
                 .child(status)
                 .into_any_element();
@@ -504,7 +534,7 @@ impl Render for ExecutionView {
             .children(
                 self.outputs
                     .iter()
-                    .map(|output| output.render(self.workspace.clone(), cx)),
+                    .map(|output| output.render(self.workspace.clone(), window, cx)),
             )
             .children(match self.status {
                 ExecutionStatus::Executing => vec![status],

crates/repl/src/outputs/image.rs 🔗

@@ -4,7 +4,7 @@ use base64::{
     engine::{DecodePaddingMode, GeneralPurpose, GeneralPurposeConfig},
     Engine as _,
 };
-use gpui::{img, ClipboardItem, Image, ImageFormat, Pixels, RenderImage, WindowContext};
+use gpui::{img, App, ClipboardItem, Image, ImageFormat, Pixels, RenderImage, Window};
 use std::sync::Arc;
 use ui::{div, prelude::*, IntoElement, Styled};
 
@@ -74,8 +74,8 @@ impl ImageView {
 }
 
 impl Render for ImageView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let line_height = cx.line_height();
+    fn render(&mut self, window: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
+        let line_height = window.line_height();
 
         let (height, width) = if self.height as f32 / line_height.0 == u8::MAX as f32 {
             let height = u8::MAX as f32 * line_height.0;
@@ -92,11 +92,11 @@ impl Render for ImageView {
 }
 
 impl OutputContent for ImageView {
-    fn clipboard_content(&self, _cx: &WindowContext) -> Option<ClipboardItem> {
+    fn clipboard_content(&self, _window: &Window, _cx: &App) -> Option<ClipboardItem> {
         Some(ClipboardItem::new_image(self.clipboard_image.as_ref()))
     }
 
-    fn has_clipboard_content(&self, _cx: &WindowContext) -> bool {
+    fn has_clipboard_content(&self, _window: &Window, _cx: &App) -> bool {
         true
     }
 }

crates/repl/src/outputs/markdown.rs 🔗

@@ -1,5 +1,5 @@
 use anyhow::Result;
-use gpui::{div, prelude::*, ClipboardItem, Model, Task, ViewContext, WindowContext};
+use gpui::{div, prelude::*, App, ClipboardItem, Context, Entity, Task, Window};
 use language::Buffer;
 use markdown_preview::{
     markdown_elements::ParsedMarkdown, markdown_parser::parse_markdown,
@@ -16,7 +16,7 @@ pub struct MarkdownView {
 }
 
 impl MarkdownView {
-    pub fn from(text: String, cx: &mut ViewContext<Self>) -> Self {
+    pub fn from(text: String, cx: &mut Context<Self>) -> Self {
         let task = cx.spawn(|markdown_view, mut cx| {
             let text = text.clone();
             let parsed = cx
@@ -43,20 +43,20 @@ impl MarkdownView {
 }
 
 impl OutputContent for MarkdownView {
-    fn clipboard_content(&self, _cx: &WindowContext) -> Option<ClipboardItem> {
+    fn clipboard_content(&self, _window: &Window, _cx: &App) -> Option<ClipboardItem> {
         Some(ClipboardItem::new_string(self.raw_text.clone()))
     }
 
-    fn has_clipboard_content(&self, _cx: &WindowContext) -> bool {
+    fn has_clipboard_content(&self, _window: &Window, _cx: &App) -> bool {
         true
     }
 
-    fn has_buffer_content(&self, _cx: &WindowContext) -> bool {
+    fn has_buffer_content(&self, _window: &Window, _cx: &App) -> bool {
         true
     }
 
-    fn buffer_content(&mut self, cx: &mut WindowContext) -> Option<Model<Buffer>> {
-        let buffer = cx.new_model(|cx| {
+    fn buffer_content(&mut self, _: &mut Window, cx: &mut App) -> Option<Entity<Buffer>> {
+        let buffer = cx.new(|cx| {
             // TODO: Bring in the language registry so we can set the language to markdown
             let mut buffer = Buffer::local(self.raw_text.clone(), cx)
                 .with_language(language::PLAIN_TEXT.clone(), cx);
@@ -68,13 +68,13 @@ impl OutputContent for MarkdownView {
 }
 
 impl Render for MarkdownView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let Some(parsed) = self.contents.as_ref() else {
             return div().into_any_element();
         };
 
         let mut markdown_render_context =
-            markdown_preview::markdown_renderer::RenderContext::new(None, cx);
+            markdown_preview::markdown_renderer::RenderContext::new(None, window, cx);
 
         v_flex()
             .gap_3()

crates/repl/src/outputs/plain.rs 🔗

@@ -22,7 +22,7 @@ use alacritty_terminal::{
     term::Config,
     vte::ansi::Processor,
 };
-use gpui::{canvas, size, ClipboardItem, FontStyle, Model, TextStyle, WhiteSpace};
+use gpui::{canvas, size, ClipboardItem, Entity, FontStyle, TextStyle, WhiteSpace};
 use language::Buffer;
 use settings::Settings as _;
 use terminal_view::terminal_element::TerminalElement;
@@ -45,7 +45,7 @@ use crate::outputs::OutputContent;
 /// supporting ANSI escape sequences for text formatting and colors.
 ///
 pub struct TerminalOutput {
-    full_buffer: Option<Model<Buffer>>,
+    full_buffer: Option<Entity<Buffer>>,
     /// ANSI escape sequence processor for parsing input text.
     parser: Processor,
     /// Alacritty terminal instance that manages the terminal state and content.
@@ -56,7 +56,7 @@ const DEFAULT_NUM_LINES: usize = 32;
 const DEFAULT_NUM_COLUMNS: usize = 128;
 
 /// Returns the default text style for the terminal output.
-pub fn text_style(cx: &mut WindowContext) -> TextStyle {
+pub fn text_style(window: &mut Window, cx: &mut App) -> TextStyle {
     let settings = ThemeSettings::get_global(cx).clone();
 
     let font_size = settings.buffer_font_size().into();
@@ -74,7 +74,7 @@ pub fn text_style(cx: &mut WindowContext) -> TextStyle {
         font_fallbacks,
         font_size,
         font_style: FontStyle::Normal,
-        line_height: cx.line_height().into(),
+        line_height: window.line_height().into(),
         background_color: Some(theme.colors().terminal_ansi_background),
         white_space: WhiteSpace::Normal,
         truncate: None,
@@ -88,13 +88,13 @@ pub fn text_style(cx: &mut WindowContext) -> TextStyle {
 }
 
 /// Returns the default terminal size for the terminal output.
-pub fn terminal_size(cx: &mut WindowContext) -> terminal::TerminalSize {
-    let text_style = text_style(cx);
-    let text_system = cx.text_system();
+pub fn terminal_size(window: &mut Window, cx: &mut App) -> terminal::TerminalSize {
+    let text_style = text_style(window, cx);
+    let text_system = window.text_system();
 
-    let line_height = cx.line_height();
+    let line_height = window.line_height();
 
-    let font_pixels = text_style.font_size.to_pixels(cx.rem_size());
+    let font_pixels = text_style.font_size.to_pixels(window.rem_size());
     let font_id = text_system.resolve_font(&text_style.font());
 
     let cell_width = text_system
@@ -107,7 +107,7 @@ pub fn terminal_size(cx: &mut WindowContext) -> terminal::TerminalSize {
 
     // Reversed math from terminal::TerminalSize to get pixel width according to terminal width
     let width = columns as f32 * cell_width;
-    let height = num_lines as f32 * cx.line_height();
+    let height = num_lines as f32 * window.line_height();
 
     terminal::TerminalSize {
         cell_width,
@@ -122,9 +122,12 @@ impl TerminalOutput {
     /// This method initializes a new terminal emulator with default configuration
     /// and sets up the necessary components for handling terminal events and rendering.
     ///
-    pub fn new(cx: &mut WindowContext) -> Self {
-        let term =
-            alacritty_terminal::Term::new(Config::default(), &terminal_size(cx), VoidListener);
+    pub fn new(window: &mut Window, cx: &mut App) -> Self {
+        let term = alacritty_terminal::Term::new(
+            Config::default(),
+            &terminal_size(window, cx),
+            VoidListener,
+        );
 
         Self {
             parser: Processor::new(),
@@ -145,8 +148,8 @@ impl TerminalOutput {
     /// # Returns
     ///
     /// A new instance of `TerminalOutput` containing the provided text.
-    pub fn from(text: &str, cx: &mut WindowContext) -> Self {
-        let mut output = Self::new(cx);
+    pub fn from(text: &str, window: &mut Window, cx: &mut App) -> Self {
+        let mut output = Self::new(window, cx);
         output.append_text(text, cx);
         output
     }
@@ -177,7 +180,7 @@ impl TerminalOutput {
     /// # Arguments
     ///
     /// * `text` - A string slice containing the text to be appended.
-    pub fn append_text(&mut self, text: &str, cx: &mut WindowContext) {
+    pub fn append_text(&mut self, text: &str, cx: &mut App) {
         for byte in text.as_bytes() {
             if *byte == b'\n' {
                 // Dirty (?) hack to move the cursor down
@@ -241,9 +244,9 @@ impl Render for TerminalOutput {
     /// Converts the current terminal state into a renderable GPUI element. It handles
     /// the layout of the terminal grid, calculates the dimensions of the output, and
     /// creates a canvas element that paints the terminal cells and background rectangles.
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let text_style = text_style(cx);
-        let text_system = cx.text_system();
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+        let text_style = text_style(window, cx);
+        let text_system = window.text_system();
 
         let grid = self
             .handler
@@ -253,14 +256,15 @@ impl Render for TerminalOutput {
                 point: ic.point,
                 cell: ic.cell.clone(),
             });
-        let (cells, rects) = TerminalElement::layout_grid(grid, &text_style, text_system, None, cx);
+        let (cells, rects) =
+            TerminalElement::layout_grid(grid, &text_style, text_system, None, window, cx);
 
         // lines are 0-indexed, so we must add 1 to get the number of lines
-        let text_line_height = text_style.line_height_in_pixels(cx.rem_size());
+        let text_line_height = text_style.line_height_in_pixels(window.rem_size());
         let num_lines = cells.iter().map(|c| c.point.line).max().unwrap_or(0) + 1;
         let height = num_lines as f32 * text_line_height;
 
-        let font_pixels = text_style.font_size.to_pixels(cx.rem_size());
+        let font_pixels = text_style.font_size.to_pixels(window.rem_size());
         let font_id = text_system.resolve_font(&text_style.font());
 
         let cell_width = text_system
@@ -270,9 +274,9 @@ impl Render for TerminalOutput {
 
         canvas(
             // prepaint
-            move |_bounds, _| {},
+            move |_bounds, _, _| {},
             // paint
-            move |bounds, _, cx| {
+            move |bounds, _, window, cx| {
                 for rect in rects {
                     rect.paint(
                         bounds.origin,
@@ -281,7 +285,7 @@ impl Render for TerminalOutput {
                             line_height: text_line_height,
                             size: bounds.size,
                         },
-                        cx,
+                        window,
                     );
                 }
 
@@ -294,6 +298,7 @@ impl Render for TerminalOutput {
                             size: bounds.size,
                         },
                         bounds,
+                        window,
                         cx,
                     );
                 }
@@ -305,24 +310,24 @@ impl Render for TerminalOutput {
 }
 
 impl OutputContent for TerminalOutput {
-    fn clipboard_content(&self, _cx: &WindowContext) -> Option<ClipboardItem> {
+    fn clipboard_content(&self, _window: &Window, _cx: &App) -> Option<ClipboardItem> {
         Some(ClipboardItem::new_string(self.full_text()))
     }
 
-    fn has_clipboard_content(&self, _cx: &WindowContext) -> bool {
+    fn has_clipboard_content(&self, _window: &Window, _cx: &App) -> bool {
         true
     }
 
-    fn has_buffer_content(&self, _cx: &WindowContext) -> bool {
+    fn has_buffer_content(&self, _window: &Window, _cx: &App) -> bool {
         true
     }
 
-    fn buffer_content(&mut self, cx: &mut WindowContext) -> Option<Model<Buffer>> {
+    fn buffer_content(&mut self, _: &mut Window, cx: &mut App) -> Option<Entity<Buffer>> {
         if self.full_buffer.as_ref().is_some() {
             return self.full_buffer.clone();
         }
 
-        let buffer = cx.new_model(|cx| {
+        let buffer = cx.new(|cx| {
             let mut buffer =
                 Buffer::local(self.full_text(), cx).with_language(language::PLAIN_TEXT.clone(), cx);
             buffer.set_capability(language::Capability::ReadOnly, cx);

crates/repl/src/outputs/table.rs 🔗

@@ -88,11 +88,11 @@ fn cell_content(row: &Value, field: &str) -> String {
 const TABLE_Y_PADDING_MULTIPLE: f32 = 0.5;
 
 impl TableView {
-    pub fn new(table: &TabularDataResource, cx: &mut WindowContext) -> Self {
+    pub fn new(table: &TabularDataResource, window: &mut Window, cx: &mut App) -> Self {
         let mut widths = Vec::with_capacity(table.schema.fields.len());
 
-        let text_system = cx.text_system();
-        let text_style = cx.text_style();
+        let text_system = window.text_system();
+        let text_style = window.text_style();
         let text_font = ThemeSettings::get_global(cx).buffer_font.clone();
         let font_size = ThemeSettings::get_global(cx).buffer_font_size;
         let mut runs = [TextRun {
@@ -119,7 +119,7 @@ impl TableView {
             for row in data {
                 let content = cell_content(row, &field.name);
                 runs[0].len = content.len();
-                let cell_width = cx
+                let cell_width = window
                     .text_system()
                     .layout_line(&content, font_size, &runs)
                     .map(|layout| layout.width)
@@ -189,11 +189,12 @@ impl TableView {
         schema: &TableSchema,
         is_header: bool,
         row: &Value,
-        cx: &WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> AnyElement {
         let theme = cx.theme();
 
-        let line_height = cx.line_height();
+        let line_height = window.line_height();
 
         let row_cells = schema
             .fields
@@ -248,7 +249,7 @@ impl TableView {
 }
 
 impl Render for TableView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let data = match &self.table.data {
             Some(data) => data,
             None => return div().into_any_element(),
@@ -258,11 +259,17 @@ impl Render for TableView {
         for field in &self.table.schema.fields {
             headings.insert(field.name.clone(), Value::String(field.name.clone()));
         }
-        let header = self.render_row(&self.table.schema, true, &Value::Object(headings), cx);
+        let header = self.render_row(
+            &self.table.schema,
+            true,
+            &Value::Object(headings),
+            window,
+            cx,
+        );
 
         let body = data
             .iter()
-            .map(|row| self.render_row(&self.table.schema, false, row, cx));
+            .map(|row| self.render_row(&self.table.schema, false, row, window, cx));
 
         v_flex()
             .id("table")
@@ -275,11 +282,11 @@ impl Render for TableView {
 }
 
 impl OutputContent for TableView {
-    fn clipboard_content(&self, _cx: &WindowContext) -> Option<ClipboardItem> {
+    fn clipboard_content(&self, _window: &Window, _cx: &App) -> Option<ClipboardItem> {
         Some(self.cached_clipboard_content.clone())
     }
 
-    fn has_clipboard_content(&self, _cx: &WindowContext) -> bool {
+    fn has_clipboard_content(&self, _window: &Window, _cx: &App) -> bool {
         true
     }
 }

crates/repl/src/outputs/user_error.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{AnyElement, FontWeight, View, WindowContext};
+use gpui::{AnyElement, App, Entity, FontWeight, Window};
 use ui::{h_flex, prelude::*, v_flex, Label};
 
 use crate::outputs::plain::TerminalOutput;
@@ -7,14 +7,14 @@ use crate::outputs::plain::TerminalOutput;
 pub struct ErrorView {
     pub ename: String,
     pub evalue: String,
-    pub traceback: View<TerminalOutput>,
+    pub traceback: Entity<TerminalOutput>,
 }
 
 impl ErrorView {
-    pub fn render(&self, cx: &mut WindowContext) -> Option<AnyElement> {
+    pub fn render(&self, window: &mut Window, cx: &mut App) -> Option<AnyElement> {
         let theme = cx.theme();
 
-        let padding = cx.line_height() / 2.;
+        let padding = window.line_height() / 2.;
 
         Some(
             v_flex()

crates/repl/src/repl.rs 🔗

@@ -11,7 +11,7 @@ mod session;
 use std::{sync::Arc, time::Duration};
 
 use async_dispatcher::{set_dispatcher, Dispatcher, Runnable};
-use gpui::{AppContext, PlatformDispatcher};
+use gpui::{App, PlatformDispatcher};
 use project::Fs;
 pub use runtimelib::ExecutionState;
 use settings::Settings as _;
@@ -27,7 +27,7 @@ pub use crate::session::Session;
 
 pub const KERNEL_DOCS_URL: &str = "https://zed.dev/docs/repl#changing-kernels";
 
-pub fn init(fs: Arc<dyn Fs>, cx: &mut AppContext) {
+pub fn init(fs: Arc<dyn Fs>, cx: &mut App) {
     set_dispatcher(zed_dispatcher(cx));
     JupyterSettings::register(cx);
     ::editor::init_settings(cx);
@@ -35,7 +35,7 @@ pub fn init(fs: Arc<dyn Fs>, cx: &mut AppContext) {
     ReplStore::init(fs, cx);
 }
 
-fn zed_dispatcher(cx: &mut AppContext) -> impl Dispatcher {
+fn zed_dispatcher(cx: &mut App) -> impl Dispatcher {
     struct ZedDispatcher {
         dispatcher: Arc<dyn PlatformDispatcher>,
     }

crates/repl/src/repl_editor.rs 🔗

@@ -3,9 +3,9 @@
 use std::ops::Range;
 use std::sync::Arc;
 
-use anyhow::{Context, Result};
+use anyhow::{Context as _, Result};
 use editor::Editor;
-use gpui::{prelude::*, Entity, View, WeakView, WindowContext};
+use gpui::{prelude::*, App, Entity, WeakEntity, Window};
 use language::{BufferSnapshot, Language, LanguageName, Point};
 use project::{ProjectItem as _, WorktreeId};
 
@@ -17,8 +17,9 @@ use crate::{
 
 pub fn assign_kernelspec(
     kernel_specification: KernelSpecification,
-    weak_editor: WeakView<Editor>,
-    cx: &mut WindowContext,
+    weak_editor: WeakEntity<Editor>,
+    window: &mut Window,
+    cx: &mut App,
 ) -> Result<()> {
     let store = ReplStore::global(cx);
     if !store.read(cx).is_enabled() {
@@ -38,12 +39,13 @@ pub fn assign_kernelspec(
         // Drop previous session, start new one
         session.update(cx, |session, cx| {
             session.clear_outputs(cx);
-            session.shutdown(cx);
+            session.shutdown(window, cx);
             cx.notify();
         });
     }
 
-    let session = cx.new_view(|cx| Session::new(weak_editor.clone(), fs, kernel_specification, cx));
+    let session =
+        cx.new(|cx| Session::new(weak_editor.clone(), fs, kernel_specification, window, cx));
 
     weak_editor
         .update(cx, |_editor, cx| {
@@ -70,7 +72,12 @@ pub fn assign_kernelspec(
     Ok(())
 }
 
-pub fn run(editor: WeakView<Editor>, move_down: bool, cx: &mut WindowContext) -> Result<()> {
+pub fn run(
+    editor: WeakEntity<Editor>,
+    move_down: bool,
+    window: &mut Window,
+    cx: &mut App,
+) -> Result<()> {
     let store = ReplStore::global(cx);
     if !store.read(cx).is_enabled() {
         return Ok(());
@@ -109,7 +116,8 @@ pub fn run(editor: WeakView<Editor>, move_down: bool, cx: &mut WindowContext) ->
             session
         } else {
             let weak_editor = editor.downgrade();
-            let session = cx.new_view(|cx| Session::new(weak_editor, fs, kernel_specification, cx));
+            let session =
+                cx.new(|cx| Session::new(weak_editor, fs, kernel_specification, window, cx));
 
             editor.update(cx, |_editor, cx| {
                 cx.notify();
@@ -148,7 +156,14 @@ pub fn run(editor: WeakView<Editor>, move_down: bool, cx: &mut WindowContext) ->
         }
 
         session.update(cx, |session, cx| {
-            session.execute(selected_text, anchor_range, next_cursor, move_down, cx);
+            session.execute(
+                selected_text,
+                anchor_range,
+                next_cursor,
+                move_down,
+                window,
+                cx,
+            );
         });
     }
 
@@ -157,16 +172,13 @@ pub fn run(editor: WeakView<Editor>, move_down: bool, cx: &mut WindowContext) ->
 
 #[allow(clippy::large_enum_variant)]
 pub enum SessionSupport {
-    ActiveSession(View<Session>),
+    ActiveSession(Entity<Session>),
     Inactive(KernelSpecification),
     RequiresSetup(LanguageName),
     Unsupported,
 }
 
-pub fn worktree_id_for_editor(
-    editor: WeakView<Editor>,
-    cx: &mut WindowContext,
-) -> Option<WorktreeId> {
+pub fn worktree_id_for_editor(editor: WeakEntity<Editor>, cx: &mut App) -> Option<WorktreeId> {
     editor.upgrade().and_then(|editor| {
         editor
             .read(cx)
@@ -179,7 +191,7 @@ pub fn worktree_id_for_editor(
     })
 }
 
-pub fn session(editor: WeakView<Editor>, cx: &mut WindowContext) -> SessionSupport {
+pub fn session(editor: WeakEntity<Editor>, cx: &mut App) -> SessionSupport {
     let store = ReplStore::global(cx);
     let entity_id = editor.entity_id();
 
@@ -213,7 +225,7 @@ pub fn session(editor: WeakView<Editor>, cx: &mut WindowContext) -> SessionSuppo
     }
 }
 
-pub fn clear_outputs(editor: WeakView<Editor>, cx: &mut WindowContext) {
+pub fn clear_outputs(editor: WeakEntity<Editor>, cx: &mut App) {
     let store = ReplStore::global(cx);
     let entity_id = editor.entity_id();
     let Some(session) = store.read(cx).get_session(entity_id).cloned() else {
@@ -225,7 +237,7 @@ pub fn clear_outputs(editor: WeakView<Editor>, cx: &mut WindowContext) {
     });
 }
 
-pub fn interrupt(editor: WeakView<Editor>, cx: &mut WindowContext) {
+pub fn interrupt(editor: WeakEntity<Editor>, cx: &mut App) {
     let store = ReplStore::global(cx);
     let entity_id = editor.entity_id();
     let Some(session) = store.read(cx).get_session(entity_id).cloned() else {
@@ -238,7 +250,7 @@ pub fn interrupt(editor: WeakView<Editor>, cx: &mut WindowContext) {
     });
 }
 
-pub fn shutdown(editor: WeakView<Editor>, cx: &mut WindowContext) {
+pub fn shutdown(editor: WeakEntity<Editor>, window: &mut Window, cx: &mut App) {
     let store = ReplStore::global(cx);
     let entity_id = editor.entity_id();
     let Some(session) = store.read(cx).get_session(entity_id).cloned() else {
@@ -246,12 +258,12 @@ pub fn shutdown(editor: WeakView<Editor>, cx: &mut WindowContext) {
     };
 
     session.update(cx, |session, cx| {
-        session.shutdown(cx);
+        session.shutdown(window, cx);
         cx.notify();
     });
 }
 
-pub fn restart(editor: WeakView<Editor>, cx: &mut WindowContext) {
+pub fn restart(editor: WeakEntity<Editor>, window: &mut Window, cx: &mut App) {
     let Some(editor) = editor.upgrade() else {
         return;
     };
@@ -267,16 +279,16 @@ pub fn restart(editor: WeakView<Editor>, cx: &mut WindowContext) {
     };
 
     session.update(cx, |session, cx| {
-        session.restart(cx);
+        session.restart(window, cx);
         cx.notify();
     });
 }
 
-pub fn setup_editor_session_actions(editor: &mut Editor, editor_handle: WeakView<Editor>) {
+pub fn setup_editor_session_actions(editor: &mut Editor, editor_handle: WeakEntity<Editor>) {
     editor
         .register_action({
             let editor_handle = editor_handle.clone();
-            move |_: &ClearOutputs, cx| {
+            move |_: &ClearOutputs, _, cx| {
                 if !JupyterSettings::enabled(cx) {
                     return;
                 }
@@ -289,7 +301,7 @@ pub fn setup_editor_session_actions(editor: &mut Editor, editor_handle: WeakView
     editor
         .register_action({
             let editor_handle = editor_handle.clone();
-            move |_: &Interrupt, cx| {
+            move |_: &Interrupt, _, cx| {
                 if !JupyterSettings::enabled(cx) {
                     return;
                 }
@@ -302,12 +314,12 @@ pub fn setup_editor_session_actions(editor: &mut Editor, editor_handle: WeakView
     editor
         .register_action({
             let editor_handle = editor_handle.clone();
-            move |_: &Shutdown, cx| {
+            move |_: &Shutdown, window, cx| {
                 if !JupyterSettings::enabled(cx) {
                     return;
                 }
 
-                crate::shutdown(editor_handle.clone(), cx);
+                crate::shutdown(editor_handle.clone(), window, cx);
             }
         })
         .detach();
@@ -315,12 +327,12 @@ pub fn setup_editor_session_actions(editor: &mut Editor, editor_handle: WeakView
     editor
         .register_action({
             let editor_handle = editor_handle.clone();
-            move |_: &Restart, cx| {
+            move |_: &Restart, window, cx| {
                 if !JupyterSettings::enabled(cx) {
                     return;
                 }
 
-                crate::restart(editor_handle.clone(), cx);
+                crate::restart(editor_handle.clone(), window, cx);
             }
         })
         .detach();
@@ -448,7 +460,7 @@ fn language_supported(language: &Arc<Language>) -> bool {
     }
 }
 
-fn get_language(editor: WeakView<Editor>, cx: &mut WindowContext) -> Option<Arc<Language>> {
+fn get_language(editor: WeakEntity<Editor>, cx: &mut App) -> Option<Arc<Language>> {
     editor
         .update(cx, |editor, cx| {
             let selection = editor.selections.newest::<usize>(cx);
@@ -462,12 +474,12 @@ fn get_language(editor: WeakView<Editor>, cx: &mut WindowContext) -> Option<Arc<
 #[cfg(test)]
 mod tests {
     use super::*;
-    use gpui::{AppContext, Context};
+    use gpui::App;
     use indoc::indoc;
     use language::{Buffer, Language, LanguageConfig, LanguageRegistry};
 
     #[gpui::test]
-    fn test_snippet_ranges(cx: &mut AppContext) {
+    fn test_snippet_ranges(cx: &mut App) {
         // Create a test language
         let test_language = Arc::new(Language::new(
             LanguageConfig {
@@ -478,7 +490,7 @@ mod tests {
             None,
         ));
 
-        let buffer = cx.new_model(|cx| {
+        let buffer = cx.new(|cx| {
             Buffer::local(
                 indoc! { r#"
                     print(1 + 1)
@@ -533,7 +545,7 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_jupytext_snippet_ranges(cx: &mut AppContext) {
+    fn test_jupytext_snippet_ranges(cx: &mut App) {
         // Create a test language
         let test_language = Arc::new(Language::new(
             LanguageConfig {
@@ -544,7 +556,7 @@ mod tests {
             None,
         ));
 
-        let buffer = cx.new_model(|cx| {
+        let buffer = cx.new(|cx| {
             Buffer::local(
                 indoc! { r#"
                     # Hello!
@@ -611,7 +623,7 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_markdown_code_blocks(cx: &mut AppContext) {
+    fn test_markdown_code_blocks(cx: &mut App) {
         let markdown = languages::language("markdown", tree_sitter_md::LANGUAGE.into());
         let typescript = languages::language(
             "typescript",
@@ -624,7 +636,7 @@ mod tests {
         language_registry.add(python.clone());
 
         // Two code blocks intersecting with selection
-        let buffer = cx.new_model(|cx| {
+        let buffer = cx.new(|cx| {
             let mut buffer = Buffer::local(
                 indoc! { r#"
                     Hey this is Markdown!
@@ -666,7 +678,7 @@ mod tests {
         );
 
         // Three code blocks intersecting with selection
-        let buffer = cx.new_model(|cx| {
+        let buffer = cx.new(|cx| {
             let mut buffer = Buffer::local(
                 indoc! { r#"
                     Hey this is Markdown!
@@ -712,7 +724,7 @@ mod tests {
         );
 
         // Python code block
-        let buffer = cx.new_model(|cx| {
+        let buffer = cx.new(|cx| {
             let mut buffer = Buffer::local(
                 indoc! { r#"
                     Hey this is Markdown!

crates/repl/src/repl_sessions_ui.rs 🔗

@@ -1,7 +1,7 @@
 use editor::Editor;
 use gpui::{
-    actions, prelude::*, AnyElement, AppContext, EventEmitter, FocusHandle, FocusableView,
-    Subscription, View,
+    actions, prelude::*, AnyElement, App, Entity, EventEmitter, FocusHandle, Focusable,
+    Subscription,
 };
 use project::ProjectItem as _;
 use ui::{prelude::*, ButtonLike, ElevationIndex, KeyBinding};
@@ -27,10 +27,10 @@ actions!(
     ]
 );
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(
-        |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
-            workspace.register_action(|workspace, _: &Sessions, cx| {
+pub fn init(cx: &mut App) {
+    cx.observe_new(
+        |workspace: &mut Workspace, _window, _cx: &mut Context<Workspace>| {
+            workspace.register_action(|workspace, _: &Sessions, window, cx| {
                 let existing = workspace
                     .active_pane()
                     .read(cx)
@@ -38,14 +38,20 @@ pub fn init(cx: &mut AppContext) {
                     .find_map(|item| item.downcast::<ReplSessionsPage>());
 
                 if let Some(existing) = existing {
-                    workspace.activate_item(&existing, true, true, cx);
+                    workspace.activate_item(&existing, true, true, window, cx);
                 } else {
-                    let repl_sessions_page = ReplSessionsPage::new(cx);
-                    workspace.add_item_to_active_pane(Box::new(repl_sessions_page), None, true, cx)
+                    let repl_sessions_page = ReplSessionsPage::new(window, cx);
+                    workspace.add_item_to_active_pane(
+                        Box::new(repl_sessions_page),
+                        None,
+                        true,
+                        window,
+                        cx,
+                    )
                 }
             });
 
-            workspace.register_action(|_workspace, _: &RefreshKernelspecs, cx| {
+            workspace.register_action(|_workspace, _: &RefreshKernelspecs, _, cx| {
                 let store = ReplStore::global(cx);
                 store.update(cx, |store, cx| {
                     store.refresh_kernelspecs(cx).detach();
@@ -55,74 +61,84 @@ pub fn init(cx: &mut AppContext) {
     )
     .detach();
 
-    cx.observe_new_views(move |editor: &mut Editor, cx: &mut ViewContext<Editor>| {
-        if !editor.use_modal_editing() || !editor.buffer().read(cx).is_singleton() {
-            return;
-        }
-
-        cx.defer(|editor, cx| {
-            let workspace = Workspace::for_window(cx);
-            let project = workspace.map(|workspace| workspace.read(cx).project().clone());
-
-            let is_local_project = project
-                .as_ref()
-                .map(|project| project.read(cx).is_local())
-                .unwrap_or(false);
+    cx.observe_new(
+        move |editor: &mut Editor, window, cx: &mut Context<Editor>| {
+            let Some(window) = window else {
+                return;
+            };
 
-            if !is_local_project {
+            if !editor.use_modal_editing() || !editor.buffer().read(cx).is_singleton() {
                 return;
             }
 
-            let buffer = editor.buffer().read(cx).as_singleton();
-
-            let language = buffer
-                .as_ref()
-                .and_then(|buffer| buffer.read(cx).language());
+            cx.defer_in(window, |editor, window, cx| {
+                let workspace = Workspace::for_window(window, cx);
+                let project = workspace.map(|workspace| workspace.read(cx).project().clone());
 
-            let project_path = buffer.and_then(|buffer| buffer.read(cx).project_path(cx));
+                let is_local_project = project
+                    .as_ref()
+                    .map(|project| project.read(cx).is_local())
+                    .unwrap_or(false);
 
-            let editor_handle = cx.view().downgrade();
+                if !is_local_project {
+                    return;
+                }
 
-            if let Some(language) = language {
-                if language.name() == "Python".into() {
-                    if let (Some(project_path), Some(project)) = (project_path, project) {
-                        let store = ReplStore::global(cx);
-                        store.update(cx, |store, cx| {
-                            store
-                                .refresh_python_kernelspecs(project_path.worktree_id, &project, cx)
-                                .detach_and_log_err(cx);
-                        });
+                let buffer = editor.buffer().read(cx).as_singleton();
+
+                let language = buffer
+                    .as_ref()
+                    .and_then(|buffer| buffer.read(cx).language());
+
+                let project_path = buffer.and_then(|buffer| buffer.read(cx).project_path(cx));
+
+                let editor_handle = cx.model().downgrade();
+
+                if let Some(language) = language {
+                    if language.name() == "Python".into() {
+                        if let (Some(project_path), Some(project)) = (project_path, project) {
+                            let store = ReplStore::global(cx);
+                            store.update(cx, |store, cx| {
+                                store
+                                    .refresh_python_kernelspecs(
+                                        project_path.worktree_id,
+                                        &project,
+                                        cx,
+                                    )
+                                    .detach_and_log_err(cx);
+                            });
+                        }
                     }
                 }
-            }
 
-            editor
-                .register_action({
-                    let editor_handle = editor_handle.clone();
-                    move |_: &Run, cx| {
-                        if !JupyterSettings::enabled(cx) {
-                            return;
-                        }
+                editor
+                    .register_action({
+                        let editor_handle = editor_handle.clone();
+                        move |_: &Run, window, cx| {
+                            if !JupyterSettings::enabled(cx) {
+                                return;
+                            }
 
-                        crate::run(editor_handle.clone(), true, cx).log_err();
-                    }
-                })
-                .detach();
-
-            editor
-                .register_action({
-                    let editor_handle = editor_handle.clone();
-                    move |_: &RunInPlace, cx| {
-                        if !JupyterSettings::enabled(cx) {
-                            return;
+                            crate::run(editor_handle.clone(), true, window, cx).log_err();
                         }
-
-                        crate::run(editor_handle.clone(), false, cx).log_err();
-                    }
-                })
-                .detach();
-        });
-    })
+                    })
+                    .detach();
+
+                editor
+                    .register_action({
+                        let editor_handle = editor_handle.clone();
+                        move |_: &RunInPlace, window, cx| {
+                            if !JupyterSettings::enabled(cx) {
+                                return;
+                            }
+
+                            crate::run(editor_handle.clone(), false, window, cx).log_err();
+                        }
+                    })
+                    .detach();
+            });
+        },
+    )
     .detach();
 }
 
@@ -132,13 +148,15 @@ pub struct ReplSessionsPage {
 }
 
 impl ReplSessionsPage {
-    pub fn new(cx: &mut ViewContext<Workspace>) -> View<Self> {
-        cx.new_view(|cx: &mut ViewContext<Self>| {
+    pub fn new(window: &mut Window, cx: &mut Context<Workspace>) -> Entity<Self> {
+        cx.new(|cx| {
             let focus_handle = cx.focus_handle();
 
             let subscriptions = vec![
-                cx.on_focus_in(&focus_handle, |_this, cx| cx.notify()),
-                cx.on_focus_out(&focus_handle, |_this, _event, cx| cx.notify()),
+                cx.on_focus_in(&focus_handle, window, |_this, _window, cx| cx.notify()),
+                cx.on_focus_out(&focus_handle, window, |_this, _event, _window, cx| {
+                    cx.notify()
+                }),
             ];
 
             Self {
@@ -151,8 +169,8 @@ impl ReplSessionsPage {
 
 impl EventEmitter<ItemEvent> for ReplSessionsPage {}
 
-impl FocusableView for ReplSessionsPage {
-    fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
+impl Focusable for ReplSessionsPage {
+    fn focus_handle(&self, _cx: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -160,7 +178,7 @@ impl FocusableView for ReplSessionsPage {
 impl Item for ReplSessionsPage {
     type Event = ItemEvent;
 
-    fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
+    fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option<SharedString> {
         Some("REPL Sessions".into())
     }
 
@@ -175,8 +193,9 @@ impl Item for ReplSessionsPage {
     fn clone_on_split(
         &self,
         _workspace_id: Option<WorkspaceId>,
-        _: &mut ViewContext<Self>,
-    ) -> Option<View<Self>> {
+        _window: &mut Window,
+        _: &mut Context<Self>,
+    ) -> Option<Entity<Self>> {
         None
     }
 
@@ -186,7 +205,7 @@ impl Item for ReplSessionsPage {
 }
 
 impl Render for ReplSessionsPage {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let store = ReplStore::global(cx);
 
         let (kernel_specifications, sessions) = store.update(cx, |store, _cx| {
@@ -214,7 +233,7 @@ impl Render for ReplSessionsPage {
                             .size(ButtonSize::Large)
                             .layer(ElevationIndex::ModalSurface)
                             .child(Label::new("Install Kernels"))
-                            .on_click(move |_, cx| {
+                            .on_click(move |_, _, cx| {
                                 cx.open_url(
                                     "https://zed.dev/docs/repl#language-specific-instructions",
                                 )
@@ -230,7 +249,7 @@ impl Render for ReplSessionsPage {
             return ReplSessionsContainer::new("No Jupyter Kernel Sessions").child(
                 v_flex()
                     .child(Label::new(instructions))
-                    .children(KeyBinding::for_action(&Run, cx)),
+                    .children(KeyBinding::for_action(&Run, window)),
             );
         }
 
@@ -260,7 +279,7 @@ impl ParentElement for ReplSessionsContainer {
 }
 
 impl RenderOnce for ReplSessionsContainer {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         v_flex()
             .p_4()
             .gap_2()

crates/repl/src/repl_store.rs 🔗

@@ -3,9 +3,7 @@ use std::sync::Arc;
 use anyhow::Result;
 use collections::HashMap;
 use command_palette_hooks::CommandPaletteFilter;
-use gpui::{
-    prelude::*, AppContext, EntityId, Global, Model, ModelContext, Subscription, Task, View,
-};
+use gpui::{prelude::*, App, Context, Entity, EntityId, Global, Subscription, Task};
 use jupyter_websocket_client::RemoteServer;
 use language::Language;
 use project::{Fs, Project, WorktreeId};
@@ -16,14 +14,14 @@ use crate::kernels::{
 };
 use crate::{JupyterSettings, KernelSpecification, Session};
 
-struct GlobalReplStore(Model<ReplStore>);
+struct GlobalReplStore(Entity<ReplStore>);
 
 impl Global for GlobalReplStore {}
 
 pub struct ReplStore {
     fs: Arc<dyn Fs>,
     enabled: bool,
-    sessions: HashMap<EntityId, View<Session>>,
+    sessions: HashMap<EntityId, Entity<Session>>,
     kernel_specifications: Vec<KernelSpecification>,
     selected_kernel_for_worktree: HashMap<WorktreeId, KernelSpecification>,
     kernel_specifications_for_worktree: HashMap<WorktreeId, Vec<KernelSpecification>>,
@@ -33,8 +31,8 @@ pub struct ReplStore {
 impl ReplStore {
     const NAMESPACE: &'static str = "repl";
 
-    pub(crate) fn init(fs: Arc<dyn Fs>, cx: &mut AppContext) {
-        let store = cx.new_model(move |cx| Self::new(fs, cx));
+    pub(crate) fn init(fs: Arc<dyn Fs>, cx: &mut App) {
+        let store = cx.new(move |cx| Self::new(fs, cx));
 
         store
             .update(cx, |store, cx| store.refresh_kernelspecs(cx))
@@ -43,11 +41,11 @@ impl ReplStore {
         cx.set_global(GlobalReplStore(store))
     }
 
-    pub fn global(cx: &AppContext) -> Model<Self> {
+    pub fn global(cx: &App) -> Entity<Self> {
         cx.global::<GlobalReplStore>().0.clone()
     }
 
-    pub fn new(fs: Arc<dyn Fs>, cx: &mut ModelContext<Self>) -> Self {
+    pub fn new(fs: Arc<dyn Fs>, cx: &mut Context<Self>) -> Self {
         let subscriptions = vec![cx.observe_global::<SettingsStore>(move |this, cx| {
             this.set_enabled(JupyterSettings::enabled(cx), cx);
         })];
@@ -88,11 +86,11 @@ impl ReplStore {
         self.kernel_specifications.iter()
     }
 
-    pub fn sessions(&self) -> impl Iterator<Item = &View<Session>> {
+    pub fn sessions(&self) -> impl Iterator<Item = &Entity<Session>> {
         self.sessions.values()
     }
 
-    fn set_enabled(&mut self, enabled: bool, cx: &mut ModelContext<Self>) {
+    fn set_enabled(&mut self, enabled: bool, cx: &mut Context<Self>) {
         if self.enabled == enabled {
             return;
         }
@@ -101,7 +99,7 @@ impl ReplStore {
         self.on_enabled_changed(cx);
     }
 
-    fn on_enabled_changed(&self, cx: &mut ModelContext<Self>) {
+    fn on_enabled_changed(&self, cx: &mut Context<Self>) {
         if !self.enabled {
             CommandPaletteFilter::update_global(cx, |filter, _cx| {
                 filter.hide_namespace(Self::NAMESPACE);
@@ -120,8 +118,8 @@ impl ReplStore {
     pub fn refresh_python_kernelspecs(
         &mut self,
         worktree_id: WorktreeId,
-        project: &Model<Project>,
-        cx: &mut ModelContext<Self>,
+        project: &Entity<Project>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let kernel_specifications = python_env_kernel_specifications(project, worktree_id, cx);
         cx.spawn(move |this, mut cx| async move {
@@ -139,7 +137,7 @@ impl ReplStore {
 
     fn get_remote_kernel_specifications(
         &self,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<Task<Result<Vec<KernelSpecification>>>> {
         match (
             std::env::var("JUPYTER_SERVER"),
@@ -161,7 +159,7 @@ impl ReplStore {
         }
     }
 
-    pub fn refresh_kernelspecs(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+    pub fn refresh_kernelspecs(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         let local_kernel_specifications = local_kernel_specifications(self.fs.clone());
 
         let remote_kernel_specifications = self.get_remote_kernel_specifications(cx);
@@ -201,7 +199,7 @@ impl ReplStore {
         &mut self,
         worktree_id: WorktreeId,
         kernelspec: KernelSpecification,
-        _cx: &mut ModelContext<Self>,
+        _cx: &mut Context<Self>,
     ) {
         self.selected_kernel_for_worktree
             .insert(worktree_id, kernelspec);
@@ -211,7 +209,7 @@ impl ReplStore {
         &self,
         worktree_id: WorktreeId,
         language_at_cursor: Option<Arc<Language>>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<KernelSpecification> {
         let selected_kernelspec = self.selected_kernel_for_worktree.get(&worktree_id).cloned();
 
@@ -226,7 +224,7 @@ impl ReplStore {
     fn kernelspec_legacy_by_lang_only(
         &self,
         language_at_cursor: Arc<Language>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<KernelSpecification> {
         let settings = JupyterSettings::get_global(cx);
         let selected_kernel = settings
@@ -270,11 +268,11 @@ impl ReplStore {
             .cloned()
     }
 
-    pub fn get_session(&self, entity_id: EntityId) -> Option<&View<Session>> {
+    pub fn get_session(&self, entity_id: EntityId) -> Option<&Entity<Session>> {
         self.sessions.get(&entity_id)
     }
 
-    pub fn insert_session(&mut self, entity_id: EntityId, session: View<Session>) {
+    pub fn insert_session(&mut self, entity_id: EntityId, session: Entity<Session>) {
         self.sessions.insert(entity_id, session);
     }
 

crates/repl/src/session.rs 🔗

@@ -17,7 +17,7 @@ use editor::{
 };
 use futures::FutureExt as _;
 use gpui::{
-    div, prelude::*, EventEmitter, Model, Render, Subscription, Task, View, ViewContext, WeakView,
+    div, prelude::*, Context, Entity, EventEmitter, Render, Subscription, Task, WeakEntity, Window,
 };
 use language::Point;
 use project::Fs;
@@ -32,7 +32,7 @@ use util::ResultExt as _;
 
 pub struct Session {
     fs: Arc<dyn Fs>,
-    editor: WeakView<Editor>,
+    editor: WeakEntity<Editor>,
     pub kernel: Kernel,
     blocks: HashMap<String, EditorBlock>,
     pub kernel_specification: KernelSpecification,
@@ -43,19 +43,19 @@ struct EditorBlock {
     code_range: Range<Anchor>,
     invalidation_anchor: Anchor,
     block_id: CustomBlockId,
-    execution_view: View<ExecutionView>,
+    execution_view: Entity<ExecutionView>,
 }
 
 type CloseBlockFn =
-    Arc<dyn for<'a> Fn(CustomBlockId, &'a mut WindowContext) + Send + Sync + 'static>;
+    Arc<dyn for<'a> Fn(CustomBlockId, &'a mut Window, &mut App) + Send + Sync + 'static>;
 
 impl EditorBlock {
     fn new(
-        editor: WeakView<Editor>,
+        editor: WeakEntity<Editor>,
         code_range: Range<Anchor>,
         status: ExecutionStatus,
         on_close: CloseBlockFn,
-        cx: &mut ViewContext<Session>,
+        cx: &mut Context<Session>,
     ) -> anyhow::Result<Self> {
         let editor = editor
             .upgrade()
@@ -65,8 +65,7 @@ impl EditorBlock {
             .workspace()
             .ok_or_else(|| anyhow::anyhow!("workspace dropped"))?;
 
-        let execution_view =
-            cx.new_view(|cx| ExecutionView::new(status, workspace.downgrade(), cx));
+        let execution_view = cx.new(|cx| ExecutionView::new(status, workspace.downgrade(), cx));
 
         let (block_id, invalidation_anchor) = editor.update(cx, |editor, cx| {
             let buffer = editor.buffer().clone();
@@ -108,26 +107,31 @@ impl EditorBlock {
         })
     }
 
-    fn handle_message(&mut self, message: &JupyterMessage, cx: &mut ViewContext<Session>) {
+    fn handle_message(
+        &mut self,
+        message: &JupyterMessage,
+        window: &mut Window,
+        cx: &mut Context<Session>,
+    ) {
         self.execution_view.update(cx, |execution_view, cx| {
-            execution_view.push_message(&message.content, cx);
+            execution_view.push_message(&message.content, window, cx);
         });
     }
 
     fn create_output_area_renderer(
-        execution_view: View<ExecutionView>,
+        execution_view: Entity<ExecutionView>,
         on_close: CloseBlockFn,
     ) -> RenderBlock {
         Arc::new(move |cx: &mut BlockContext| {
             let execution_view = execution_view.clone();
-            let text_style = crate::outputs::plain::text_style(cx);
+            let text_style = crate::outputs::plain::text_style(cx.window, cx.app);
 
             let gutter = cx.gutter_dimensions;
 
             let block_id = cx.block_id;
             let on_close = on_close.clone();
 
-            let rem_size = cx.rem_size();
+            let rem_size = cx.window.rem_size();
 
             let text_line_height = text_style.line_height_in_pixels(rem_size);
 
@@ -150,10 +154,10 @@ impl EditorBlock {
                         .icon_color(Color::Muted)
                         .size(ButtonSize::Compact)
                         .shape(IconButtonShape::Square)
-                        .tooltip(|cx| Tooltip::text("Close output area", cx))
-                        .on_click(move |_, cx| {
+                        .tooltip(Tooltip::text("Close output area"))
+                        .on_click(move |_, window, cx| {
                             if let BlockId::Custom(block_id) = block_id {
-                                (on_close)(block_id, cx)
+                                (on_close)(block_id, window, cx)
                             }
                         }),
                 );
@@ -190,10 +194,11 @@ impl EditorBlock {
 
 impl Session {
     pub fn new(
-        editor: WeakView<Editor>,
+        editor: WeakEntity<Editor>,
         fs: Arc<dyn Fs>,
         kernel_specification: KernelSpecification,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let subscription = match editor.upgrade() {
             Some(editor) => {
@@ -220,11 +225,11 @@ impl Session {
             _buffer_subscription: subscription,
         };
 
-        session.start_kernel(cx);
+        session.start_kernel(window, cx);
         session
     }
 
-    fn start_kernel(&mut self, cx: &mut ViewContext<Self>) {
+    fn start_kernel(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let kernel_language = self.kernel_specification.language();
         let entity_id = self.editor.entity_id();
         let working_directory = self
@@ -240,7 +245,7 @@ impl Session {
             repl_session_id = cx.entity_id().to_string(),
         );
 
-        let session_view = cx.view().clone();
+        let session_view = cx.model().clone();
 
         let kernel = match self.kernel_specification.clone() {
             KernelSpecification::Jupyter(kernel_specification)
@@ -250,12 +255,14 @@ impl Session {
                 working_directory,
                 self.fs.clone(),
                 session_view,
+                window,
                 cx,
             ),
             KernelSpecification::Remote(remote_kernel_specification) => RemoteRunningKernel::new(
                 remote_kernel_specification,
                 working_directory,
                 session_view,
+                window,
                 cx,
             ),
         };
@@ -285,7 +292,7 @@ impl Session {
         cx.notify();
     }
 
-    pub fn kernel_errored(&mut self, error_message: String, cx: &mut ViewContext<Self>) {
+    pub fn kernel_errored(&mut self, error_message: String, cx: &mut Context<Self>) {
         self.kernel(Kernel::ErroredLaunch(error_message.clone()), cx);
 
         self.blocks.values().for_each(|block| {
@@ -307,9 +314,9 @@ impl Session {
 
     fn on_buffer_event(
         &mut self,
-        buffer: Model<MultiBuffer>,
+        buffer: Entity<MultiBuffer>,
         event: &multi_buffer::Event,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if let multi_buffer::Event::Edited { .. } = event {
             let snapshot = buffer.read(cx).snapshot(cx);
@@ -336,7 +343,7 @@ impl Session {
         }
     }
 
-    fn send(&mut self, message: JupyterMessage, _cx: &mut ViewContext<Self>) -> anyhow::Result<()> {
+    fn send(&mut self, message: JupyterMessage, _cx: &mut Context<Self>) -> anyhow::Result<()> {
         if let Kernel::RunningKernel(kernel) = &mut self.kernel {
             kernel.request_tx().try_send(message).ok();
         }
@@ -344,7 +351,7 @@ impl Session {
         anyhow::Ok(())
     }
 
-    pub fn clear_outputs(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn clear_outputs(&mut self, cx: &mut Context<Self>) {
         let blocks_to_remove: HashSet<CustomBlockId> =
             self.blocks.values().map(|block| block.block_id).collect();
 
@@ -363,7 +370,8 @@ impl Session {
         anchor_range: Range<Anchor>,
         next_cell: Option<Anchor>,
         move_down: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let Some(editor) = self.editor.upgrade() else {
             return;
@@ -409,11 +417,11 @@ impl Session {
         };
 
         let parent_message_id = message.header.msg_id.clone();
-        let session_view = cx.view().downgrade();
+        let session_view = cx.model().downgrade();
         let weak_editor = self.editor.clone();
 
-        let on_close: CloseBlockFn =
-            Arc::new(move |block_id: CustomBlockId, cx: &mut WindowContext| {
+        let on_close: CloseBlockFn = Arc::new(
+            move |block_id: CustomBlockId, _: &mut Window, cx: &mut App| {
                 if let Some(session) = session_view.upgrade() {
                     session.update(cx, |session, cx| {
                         session.blocks.remove(&parent_message_id);
@@ -428,7 +436,8 @@ impl Session {
                         editor.remove_blocks(block_ids, None, cx);
                     });
                 }
-            });
+            },
+        );
 
         let Ok(editor_block) =
             EditorBlock::new(self.editor.clone(), anchor_range, status, on_close, cx)
@@ -468,14 +477,19 @@ impl Session {
 
         if move_down {
             editor.update(cx, move |editor, cx| {
-                editor.change_selections(Some(Autoscroll::top_relative(8)), cx, |selections| {
-                    selections.select_ranges([new_cursor_pos..new_cursor_pos]);
-                });
+                editor.change_selections(
+                    Some(Autoscroll::top_relative(8)),
+                    window,
+                    cx,
+                    |selections| {
+                        selections.select_ranges([new_cursor_pos..new_cursor_pos]);
+                    },
+                );
             });
         }
     }
 
-    pub fn route(&mut self, message: &JupyterMessage, cx: &mut ViewContext<Self>) {
+    pub fn route(&mut self, message: &JupyterMessage, window: &mut Window, cx: &mut Context<Self>) {
         let parent_message_id = match message.parent_header.as_ref() {
             Some(header) => &header.msg_id,
             None => return,
@@ -507,7 +521,7 @@ impl Session {
 
                 self.blocks.iter_mut().for_each(|(_, block)| {
                     block.execution_view.update(cx, |execution_view, cx| {
-                        execution_view.update_display_data(&update.data, &display_id, cx);
+                        execution_view.update_display_data(&update.data, &display_id, window, cx);
                     });
                 });
                 return;
@@ -516,11 +530,11 @@ impl Session {
         }
 
         if let Some(block) = self.blocks.get_mut(parent_message_id) {
-            block.handle_message(message, cx);
+            block.handle_message(message, window, cx);
         }
     }
 
-    pub fn interrupt(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn interrupt(&mut self, cx: &mut Context<Self>) {
         match &mut self.kernel {
             Kernel::RunningKernel(_kernel) => {
                 self.send(InterruptRequest {}.into(), cx).ok();
@@ -532,7 +546,7 @@ impl Session {
         }
     }
 
-    pub fn kernel(&mut self, kernel: Kernel, cx: &mut ViewContext<Self>) {
+    pub fn kernel(&mut self, kernel: Kernel, cx: &mut Context<Self>) {
         if let Kernel::Shutdown = kernel {
             cx.emit(SessionEvent::Shutdown(self.editor.clone()));
         }
@@ -550,14 +564,14 @@ impl Session {
         self.kernel = kernel;
     }
 
-    pub fn shutdown(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn shutdown(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let kernel = std::mem::replace(&mut self.kernel, Kernel::ShuttingDown);
 
         match kernel {
             Kernel::RunningKernel(mut kernel) => {
                 let mut request_tx = kernel.request_tx().clone();
 
-                let forced = kernel.force_shutdown(cx);
+                let forced = kernel.force_shutdown(window, cx);
 
                 cx.spawn(|this, mut cx| async move {
                     let message: JupyterMessage = ShutdownRequest { restart: false }.into();
@@ -584,7 +598,7 @@ impl Session {
         cx.notify();
     }
 
-    pub fn restart(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn restart(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let kernel = std::mem::replace(&mut self.kernel, Kernel::Restarting);
 
         match kernel {
@@ -594,9 +608,9 @@ impl Session {
             Kernel::RunningKernel(mut kernel) => {
                 let mut request_tx = kernel.request_tx().clone();
 
-                let forced = kernel.force_shutdown(cx);
+                let forced = kernel.force_shutdown(window, cx);
 
-                cx.spawn(|this, mut cx| async move {
+                cx.spawn_in(window, |this, mut cx| async move {
                     // Send shutdown request with restart flag
                     log::debug!("restarting kernel");
                     let message: JupyterMessage = ShutdownRequest { restart: true }.into();
@@ -609,10 +623,10 @@ impl Session {
                     forced.await.log_err();
 
                     // Start a new kernel
-                    this.update(&mut cx, |session, cx| {
+                    this.update_in(&mut cx, |session, window, cx| {
                         // TODO: Differentiate between restart and restart+clear-outputs
                         session.clear_outputs(cx);
-                        session.start_kernel(cx);
+                        session.start_kernel(window, cx);
                     })
                     .ok();
                 })
@@ -620,7 +634,7 @@ impl Session {
             }
             _ => {
                 self.clear_outputs(cx);
-                self.start_kernel(cx);
+                self.start_kernel(window, cx);
             }
         }
         cx.notify();
@@ -628,13 +642,13 @@ impl Session {
 }
 
 pub enum SessionEvent {
-    Shutdown(WeakView<Editor>),
+    Shutdown(WeakEntity<Editor>),
 }
 
 impl EventEmitter<SessionEvent> for Session {}
 
 impl Render for Session {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let (status_text, interrupt_button) = match &self.kernel {
             Kernel::RunningKernel(kernel) => (
                 kernel
@@ -644,7 +658,7 @@ impl Render for Session {
                 Some(
                     Button::new("interrupt", "Interrupt")
                         .style(ButtonStyle::Subtle)
-                        .on_click(cx.listener(move |session, _, cx| {
+                        .on_click(cx.listener(move |session, _, _, cx| {
                             session.interrupt(cx);
                         })),
                 ),
@@ -674,8 +688,8 @@ impl Render for Session {
                 Button::new("shutdown", "Shutdown")
                     .style(ButtonStyle::Subtle)
                     .disabled(self.kernel.is_shutting_down())
-                    .on_click(cx.listener(move |session, _, cx| {
-                        session.shutdown(cx);
+                    .on_click(cx.listener(move |session, _, window, cx| {
+                        session.shutdown(window, cx);
                     })),
             )
             .buttons(interrupt_button)

crates/reqwest_client/examples/client.rs 🔗

@@ -8,7 +8,7 @@ use reqwest_client::ReqwestClient;
 use smol::stream::StreamExt;
 
 fn main() {
-    let app = gpui::App::new();
+    let app = gpui::Application::new();
     app.run(|cx| {
         cx.spawn(|cx| async move {
             let client = ReqwestClient::new();

crates/rich_text/src/rich_text.rs 🔗

@@ -1,7 +1,7 @@
 use futures::FutureExt;
 use gpui::{
-    AnyElement, AnyView, ElementId, FontStyle, FontWeight, HighlightStyle, InteractiveText,
-    IntoElement, SharedString, StrikethroughStyle, StyledText, UnderlineStyle, WindowContext,
+    AnyElement, AnyView, App, ElementId, FontStyle, FontWeight, HighlightStyle, InteractiveText,
+    IntoElement, SharedString, StrikethroughStyle, StyledText, UnderlineStyle, Window,
 };
 use language::{HighlightId, Language, LanguageRegistry};
 use std::{ops::Range, sync::Arc};
@@ -40,7 +40,7 @@ pub struct RichText {
 
     pub custom_ranges: Vec<Range<usize>>,
     custom_ranges_tooltip_fn:
-        Option<Arc<dyn Fn(usize, Range<usize>, &mut WindowContext) -> Option<AnyView>>>,
+        Option<Arc<dyn Fn(usize, Range<usize>, &mut Window, &mut App) -> Option<AnyView>>>,
 }
 
 /// Allows one to specify extra links to the rendered markdown, which can be used
@@ -85,19 +85,19 @@ impl RichText {
 
     pub fn set_tooltip_builder_for_custom_ranges(
         &mut self,
-        f: impl Fn(usize, Range<usize>, &mut WindowContext) -> Option<AnyView> + 'static,
+        f: impl Fn(usize, Range<usize>, &mut Window, &mut App) -> Option<AnyView> + 'static,
     ) {
         self.custom_ranges_tooltip_fn = Some(Arc::new(f));
     }
 
-    pub fn element(&self, id: ElementId, cx: &mut WindowContext) -> AnyElement {
+    pub fn element(&self, id: ElementId, window: &mut Window, cx: &mut App) -> AnyElement {
         let theme = cx.theme();
         let code_background = theme.colors().surface_background;
 
         InteractiveText::new(
             id,
             StyledText::new(self.text.clone()).with_highlights(
-                &cx.text_style(),
+                &window.text_style(),
                 self.highlights.iter().map(|(range, highlight)| {
                     (
                         range.clone(),
@@ -143,7 +143,7 @@ impl RichText {
         )
         .on_click(self.link_ranges.clone(), {
             let link_urls = self.link_urls.clone();
-            move |ix, cx| {
+            move |ix, _, cx| {
                 let url = &link_urls[ix];
                 if url.starts_with("http") {
                     cx.open_url(url);
@@ -155,7 +155,7 @@ impl RichText {
             let link_urls = self.link_urls.clone();
             let custom_tooltip_ranges = self.custom_ranges.clone();
             let custom_tooltip_fn = self.custom_ranges_tooltip_fn.clone();
-            move |idx, cx| {
+            move |idx, window, cx| {
                 for (ix, range) in link_ranges.iter().enumerate() {
                     if range.contains(&idx) {
                         return Some(LinkPreview::new(&link_urls[ix], cx));
@@ -164,7 +164,7 @@ impl RichText {
                 for range in &custom_tooltip_ranges {
                     if range.contains(&idx) {
                         if let Some(f) = &custom_tooltip_fn {
-                            return f(idx, range.clone(), cx);
+                            return f(idx, range.clone(), window, cx);
                         }
                     }
                 }

crates/rpc/src/auth.rs 🔗

@@ -1,4 +1,4 @@
-use anyhow::{Context, Result};
+use anyhow::{Context as _, Result};
 use base64::prelude::*;
 use rand::{thread_rng, Rng as _};
 use rsa::pkcs1::{DecodeRsaPublicKey, EncodeRsaPublicKey};

crates/rpc/src/peer.rs 🔗

@@ -5,7 +5,7 @@ use super::{
     },
     Connection,
 };
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use collections::HashMap;
 use futures::{
     channel::{mpsc, oneshot},

crates/rpc/src/proto_client.rs 🔗

@@ -4,7 +4,7 @@ use futures::{
     future::{BoxFuture, LocalBoxFuture},
     Future, FutureExt as _,
 };
-use gpui::{AnyModel, AnyWeakModel, AsyncAppContext, Model};
+use gpui::{AnyEntity, AnyWeakEntity, AsyncAppContext, Entity};
 use proto::{
     error::ErrorExt as _, AnyTypedEnvelope, EntityMessage, Envelope, EnvelopedMessage,
     RequestMessage, TypedEnvelope,
@@ -53,7 +53,7 @@ pub struct ProtoMessageHandlerSet {
     pub entity_types_by_message_type: HashMap<TypeId, TypeId>,
     pub entities_by_type_and_remote_id: HashMap<(TypeId, u64), EntityMessageSubscriber>,
     pub entity_id_extractors: HashMap<TypeId, fn(&dyn AnyTypedEnvelope) -> u64>,
-    pub models_by_message_type: HashMap<TypeId, AnyWeakModel>,
+    pub models_by_message_type: HashMap<TypeId, AnyWeakEntity>,
     pub message_handlers: HashMap<TypeId, ProtoMessageHandler>,
 }
 
@@ -61,7 +61,7 @@ pub type ProtoMessageHandler = Arc<
     dyn Send
         + Sync
         + Fn(
-            AnyModel,
+            AnyEntity,
             Box<dyn AnyTypedEnvelope>,
             AnyProtoClient,
             AsyncAppContext,
@@ -79,7 +79,7 @@ impl ProtoMessageHandlerSet {
     fn add_message_handler(
         &mut self,
         message_type_id: TypeId,
-        model: gpui::AnyWeakModel,
+        model: gpui::AnyWeakEntity,
         handler: ProtoMessageHandler,
     ) {
         self.models_by_message_type.insert(message_type_id, model);
@@ -139,7 +139,7 @@ impl ProtoMessageHandlerSet {
 }
 
 pub enum EntityMessageSubscriber {
-    Entity { handle: AnyWeakModel },
+    Entity { handle: AnyWeakEntity },
     Pending(Vec<Box<dyn AnyTypedEnvelope>>),
 }
 
@@ -207,11 +207,11 @@ impl AnyProtoClient {
         self.0.send(envelope, T::NAME)
     }
 
-    pub fn add_request_handler<M, E, H, F>(&self, model: gpui::WeakModel<E>, handler: H)
+    pub fn add_request_handler<M, E, H, F>(&self, model: gpui::WeakEntity<E>, handler: H)
     where
         M: RequestMessage,
         E: 'static,
-        H: 'static + Sync + Fn(Model<E>, TypedEnvelope<M>, AsyncAppContext) -> F + Send + Sync,
+        H: 'static + Sync + Fn(Entity<E>, TypedEnvelope<M>, AsyncAppContext) -> F + Send + Sync,
         F: 'static + Future<Output = anyhow::Result<M::Response>>,
     {
         self.0.message_handler_set().lock().add_message_handler(
@@ -243,7 +243,7 @@ impl AnyProtoClient {
     where
         M: EnvelopedMessage + RequestMessage + EntityMessage,
         E: 'static,
-        H: 'static + Sync + Send + Fn(gpui::Model<E>, TypedEnvelope<M>, AsyncAppContext) -> F,
+        H: 'static + Sync + Send + Fn(gpui::Entity<E>, TypedEnvelope<M>, AsyncAppContext) -> F,
         F: 'static + Future<Output = anyhow::Result<M::Response>>,
     {
         let message_type_id = TypeId::of::<M>();
@@ -289,7 +289,7 @@ impl AnyProtoClient {
     where
         M: EnvelopedMessage + EntityMessage,
         E: 'static,
-        H: 'static + Sync + Send + Fn(gpui::Model<E>, TypedEnvelope<M>, AsyncAppContext) -> F,
+        H: 'static + Sync + Send + Fn(gpui::Entity<E>, TypedEnvelope<M>, AsyncAppContext) -> F,
         F: 'static + Future<Output = anyhow::Result<()>>,
     {
         let message_type_id = TypeId::of::<M>();

crates/search/src/buffer_search.rs 🔗

@@ -13,10 +13,9 @@ use editor::{
 };
 use futures::channel::oneshot;
 use gpui::{
-    actions, div, impl_actions, Action, AppContext, ClickEvent, EventEmitter, FocusHandle,
-    FocusableView, Hsla, InteractiveElement as _, IntoElement, KeyContext, ParentElement as _,
-    Render, ScrollHandle, Styled, Subscription, Task, TextStyle, View, ViewContext,
-    VisualContext as _, WindowContext,
+    actions, div, impl_actions, Action, App, ClickEvent, Context, Entity, EventEmitter,
+    FocusHandle, Focusable, Hsla, InteractiveElement as _, IntoElement, KeyContext,
+    ParentElement as _, Render, ScrollHandle, Styled, Subscription, Task, TextStyle, Window,
 };
 use project::{
     search::SearchQuery,
@@ -72,15 +71,15 @@ pub enum Event {
     UpdateLocation,
 }
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(|workspace: &mut Workspace, _| BufferSearchBar::register(workspace))
+pub fn init(cx: &mut App) {
+    cx.observe_new(|workspace: &mut Workspace, _, _| BufferSearchBar::register(workspace))
         .detach();
 }
 
 pub struct BufferSearchBar {
-    query_editor: View<Editor>,
+    query_editor: Entity<Editor>,
     query_editor_focused: bool,
-    replacement_editor: View<Editor>,
+    replacement_editor: Entity<Editor>,
     replacement_editor_focused: bool,
     active_searchable_item: Option<Box<dyn SearchableItemHandle>>,
     active_match_index: Option<usize>,
@@ -105,9 +104,10 @@ pub struct BufferSearchBar {
 impl BufferSearchBar {
     fn render_text_input(
         &self,
-        editor: &View<Editor>,
+        editor: &Entity<Editor>,
         color: Hsla,
-        cx: &ViewContext<Self>,
+
+        cx: &mut Context<Self>,
     ) -> impl IntoElement {
         let settings = ThemeSettings::get_global(cx);
         let text_style = TextStyle {
@@ -144,7 +144,7 @@ impl BufferSearchBar {
 impl EventEmitter<Event> for BufferSearchBar {}
 impl EventEmitter<workspace::ToolbarItemEvent> for BufferSearchBar {}
 impl Render for BufferSearchBar {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         if self.dismissed {
             return div().id("search_bar");
         }
@@ -152,14 +152,14 @@ impl Render for BufferSearchBar {
         let focus_handle = self.focus_handle(cx);
 
         let narrow_mode =
-            self.scroll_handle.bounds().size.width / cx.rem_size() < 340. / BASE_REM_SIZE_IN_PX;
+            self.scroll_handle.bounds().size.width / window.rem_size() < 340. / BASE_REM_SIZE_IN_PX;
         let hide_inline_icons = self.editor_needed_width
-            > self.editor_scroll_handle.bounds().size.width - cx.rem_size() * 6.;
+            > self.editor_scroll_handle.bounds().size.width - window.rem_size() * 6.;
 
         let supported_options = self.supported_options();
 
-        if self.query_editor.update(cx, |query_editor, cx| {
-            query_editor.placeholder_text(cx).is_none()
+        if self.query_editor.update(cx, |query_editor, _cx| {
+            query_editor.placeholder_text().is_none()
         }) {
             self.query_editor.update(cx, |editor, cx| {
                 editor.set_placeholder_text("Search…", cx);
@@ -192,7 +192,7 @@ impl Render for BufferSearchBar {
             })
             .unwrap_or_else(|| "0/0".to_string());
         let should_show_replace_input = self.replace_enabled && supported_options.replacement;
-        let in_replace = self.replacement_editor.focus_handle(cx).is_focused(cx);
+        let in_replace = self.replacement_editor.focus_handle(cx).is_focused(window);
 
         let mut key_context = KeyContext::new_with_defaults();
         key_context.add("BufferSearchBar");
@@ -205,7 +205,7 @@ impl Render for BufferSearchBar {
             cx.theme().colors().border
         };
 
-        let container_width = cx.viewport_size().width;
+        let container_width = window.viewport_size().width;
         let input_width = SearchInputWidth::calc_width(container_width);
 
         let input_base_styles = || {
@@ -236,8 +236,12 @@ impl Render for BufferSearchBar {
                                     self.render_search_option_button(
                                         SearchOptions::CASE_SENSITIVE,
                                         focus_handle.clone(),
-                                        cx.listener(|this, _, cx| {
-                                            this.toggle_case_sensitive(&ToggleCaseSensitive, cx)
+                                        cx.listener(|this, _, window, cx| {
+                                            this.toggle_case_sensitive(
+                                                &ToggleCaseSensitive,
+                                                window,
+                                                cx,
+                                            )
                                         }),
                                     )
                                 }))
@@ -245,8 +249,8 @@ impl Render for BufferSearchBar {
                                     self.render_search_option_button(
                                         SearchOptions::WHOLE_WORD,
                                         focus_handle.clone(),
-                                        cx.listener(|this, _, cx| {
-                                            this.toggle_whole_word(&ToggleWholeWord, cx)
+                                        cx.listener(|this, _, window, cx| {
+                                            this.toggle_whole_word(&ToggleWholeWord, window, cx)
                                         }),
                                     )
                                 }))
@@ -254,8 +258,8 @@ impl Render for BufferSearchBar {
                                     self.render_search_option_button(
                                         SearchOptions::REGEX,
                                         focus_handle.clone(),
-                                        cx.listener(|this, _, cx| {
-                                            this.toggle_regex(&ToggleRegex, cx)
+                                        cx.listener(|this, _, window, cx| {
+                                            this.toggle_regex(&ToggleRegex, window, cx)
                                         }),
                                     )
                                 })),
@@ -277,17 +281,18 @@ impl Render for BufferSearchBar {
                             .when(self.replace_enabled, |button| {
                                 button.style(ButtonStyle::Filled)
                             })
-                            .on_click(cx.listener(|this, _: &ClickEvent, cx| {
-                                this.toggle_replace(&ToggleReplace, cx);
+                            .on_click(cx.listener(|this, _: &ClickEvent, window, cx| {
+                                this.toggle_replace(&ToggleReplace, window, cx);
                             }))
                             .toggle_state(self.replace_enabled)
                             .tooltip({
                                 let focus_handle = focus_handle.clone();
-                                move |cx| {
+                                move |window, cx| {
                                     Tooltip::for_action_in(
                                         "Toggle Replace",
                                         &ToggleReplace,
                                         &focus_handle,
+                                        window,
                                         cx,
                                     )
                                 }
@@ -305,17 +310,18 @@ impl Render for BufferSearchBar {
                             .when(self.selection_search_enabled, |button| {
                                 button.style(ButtonStyle::Filled)
                             })
-                            .on_click(cx.listener(|this, _: &ClickEvent, cx| {
-                                this.toggle_selection(&ToggleSelection, cx);
+                            .on_click(cx.listener(|this, _: &ClickEvent, window, cx| {
+                                this.toggle_selection(&ToggleSelection, window, cx);
                             }))
                             .toggle_state(self.selection_search_enabled)
                             .tooltip({
                                 let focus_handle = focus_handle.clone();
-                                move |cx| {
+                                move |window, cx| {
                                     Tooltip::for_action_in(
                                         "Toggle Search Selection",
                                         &ToggleSelection,
                                         &focus_handle,
+                                        window,
                                         cx,
                                     )
                                 }
@@ -324,15 +330,18 @@ impl Render for BufferSearchBar {
                     })
                     .child(
                         IconButton::new("select-all", ui::IconName::SelectAll)
-                            .on_click(|_, cx| cx.dispatch_action(SelectAllMatches.boxed_clone()))
+                            .on_click(|_, window, cx| {
+                                window.dispatch_action(SelectAllMatches.boxed_clone(), cx)
+                            })
                             .shape(IconButtonShape::Square)
                             .tooltip({
                                 let focus_handle = focus_handle.clone();
-                                move |cx| {
+                                move |window, cx| {
                                     Tooltip::for_action_in(
                                         "Select All Matches",
                                         &SelectAllMatches,
                                         &focus_handle,
+                                        window,
                                         cx,
                                     )
                                 }
@@ -389,36 +398,38 @@ impl Render for BufferSearchBar {
                                 .shape(IconButtonShape::Square)
                                 .tooltip({
                                     let focus_handle = focus_handle.clone();
-                                    move |cx| {
+                                    move |window, cx| {
                                         Tooltip::for_action_in(
                                             "Replace Next Match",
                                             &ReplaceNext,
                                             &focus_handle,
+                                            window,
                                             cx,
                                         )
                                     }
                                 })
-                                .on_click(
-                                    cx.listener(|this, _, cx| this.replace_next(&ReplaceNext, cx)),
-                                ),
+                                .on_click(cx.listener(|this, _, window, cx| {
+                                    this.replace_next(&ReplaceNext, window, cx)
+                                })),
                         )
                         .child(
                             IconButton::new("search-replace-all", ui::IconName::ReplaceAll)
                                 .shape(IconButtonShape::Square)
                                 .tooltip({
                                     let focus_handle = focus_handle.clone();
-                                    move |cx| {
+                                    move |window, cx| {
                                         Tooltip::for_action_in(
                                             "Replace All Matches",
                                             &ReplaceAll,
                                             &focus_handle,
+                                            window,
                                             cx,
                                         )
                                     }
                                 })
-                                .on_click(
-                                    cx.listener(|this, _, cx| this.replace_all(&ReplaceAll, cx)),
-                                ),
+                                .on_click(cx.listener(|this, _, window, cx| {
+                                    this.replace_all(&ReplaceAll, window, cx)
+                                })),
                         ),
                 )
         });
@@ -464,11 +475,16 @@ impl Render for BufferSearchBar {
                             h_flex().absolute().right_0().child(
                                 IconButton::new(SharedString::from("Close"), IconName::Close)
                                     .shape(IconButtonShape::Square)
-                                    .tooltip(move |cx| {
-                                        Tooltip::for_action("Close Search Bar", &Dismiss, cx)
+                                    .tooltip(move |window, cx| {
+                                        Tooltip::for_action(
+                                            "Close Search Bar",
+                                            &Dismiss,
+                                            window,
+                                            cx,
+                                        )
                                     })
-                                    .on_click(cx.listener(|this, _: &ClickEvent, cx| {
-                                        this.dismiss(&Dismiss, cx)
+                                    .on_click(cx.listener(|this, _: &ClickEvent, window, cx| {
+                                        this.dismiss(&Dismiss, window, cx)
                                     })),
                             ),
                         )
@@ -478,8 +494,8 @@ impl Render for BufferSearchBar {
     }
 }
 
-impl FocusableView for BufferSearchBar {
-    fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
+impl Focusable for BufferSearchBar {
+    fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
         self.query_editor.focus_handle(cx)
     }
 }
@@ -488,7 +504,8 @@ impl ToolbarItemView for BufferSearchBar {
     fn set_active_pane_item(
         &mut self,
         item: Option<&dyn ItemHandle>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> ToolbarItemLocation {
         cx.notify();
         self.active_searchable_item_subscription.take();
@@ -499,22 +516,23 @@ impl ToolbarItemView for BufferSearchBar {
         if let Some(searchable_item_handle) =
             item.and_then(|item| item.to_searchable_item_handle(cx))
         {
-            let this = cx.view().downgrade();
+            let this = cx.model().downgrade();
 
             self.active_searchable_item_subscription =
                 Some(searchable_item_handle.subscribe_to_search_events(
+                    window,
                     cx,
-                    Box::new(move |search_event, cx| {
+                    Box::new(move |search_event, window, cx| {
                         if let Some(this) = this.upgrade() {
                             this.update(cx, |this, cx| {
-                                this.on_active_searchable_item_event(search_event, cx)
+                                this.on_active_searchable_item_event(search_event, window, cx)
                             });
                         }
                     }),
                 ));
 
             self.active_searchable_item = Some(searchable_item_handle);
-            drop(self.update_matches(true, cx));
+            drop(self.update_matches(true, window, cx));
             if !self.dismissed {
                 return ToolbarItemLocation::Secondary;
             }
@@ -525,66 +543,72 @@ impl ToolbarItemView for BufferSearchBar {
 
 impl BufferSearchBar {
     pub fn register(registrar: &mut impl SearchActionsRegistrar) {
-        registrar.register_handler(ForDeployed(|this, _: &FocusSearch, cx| {
-            this.query_editor.focus_handle(cx).focus(cx);
-            this.select_query(cx);
-        }));
-        registrar.register_handler(ForDeployed(|this, action: &ToggleCaseSensitive, cx| {
-            if this.supported_options().case {
-                this.toggle_case_sensitive(action, cx);
-            }
+        registrar.register_handler(ForDeployed(|this, _: &FocusSearch, window, cx| {
+            this.query_editor.focus_handle(cx).focus(window);
+            this.select_query(window, cx);
         }));
-        registrar.register_handler(ForDeployed(|this, action: &ToggleWholeWord, cx| {
+        registrar.register_handler(ForDeployed(
+            |this, action: &ToggleCaseSensitive, window, cx| {
+                if this.supported_options().case {
+                    this.toggle_case_sensitive(action, window, cx);
+                }
+            },
+        ));
+        registrar.register_handler(ForDeployed(|this, action: &ToggleWholeWord, window, cx| {
             if this.supported_options().word {
-                this.toggle_whole_word(action, cx);
+                this.toggle_whole_word(action, window, cx);
             }
         }));
-        registrar.register_handler(ForDeployed(|this, action: &ToggleRegex, cx| {
+        registrar.register_handler(ForDeployed(|this, action: &ToggleRegex, window, cx| {
             if this.supported_options().regex {
-                this.toggle_regex(action, cx);
+                this.toggle_regex(action, window, cx);
             }
         }));
-        registrar.register_handler(ForDeployed(|this, action: &ToggleSelection, cx| {
+        registrar.register_handler(ForDeployed(|this, action: &ToggleSelection, window, cx| {
             if this.supported_options().selection {
-                this.toggle_selection(action, cx);
+                this.toggle_selection(action, window, cx);
             }
         }));
-        registrar.register_handler(ForDeployed(|this, action: &ToggleReplace, cx| {
+        registrar.register_handler(ForDeployed(|this, action: &ToggleReplace, window, cx| {
             if this.supported_options().replacement {
-                this.toggle_replace(action, cx);
+                this.toggle_replace(action, window, cx);
             }
         }));
-        registrar.register_handler(WithResults(|this, action: &SelectNextMatch, cx| {
-            this.select_next_match(action, cx);
-        }));
-        registrar.register_handler(WithResults(|this, action: &SelectPrevMatch, cx| {
-            this.select_prev_match(action, cx);
+        registrar.register_handler(WithResults(|this, action: &SelectNextMatch, window, cx| {
+            this.select_next_match(action, window, cx);
         }));
-        registrar.register_handler(WithResults(|this, action: &SelectAllMatches, cx| {
-            this.select_all_matches(action, cx);
+        registrar.register_handler(WithResults(|this, action: &SelectPrevMatch, window, cx| {
+            this.select_prev_match(action, window, cx);
         }));
-        registrar.register_handler(ForDeployed(|this, _: &editor::actions::Cancel, cx| {
-            this.dismiss(&Dismiss, cx);
-        }));
-        registrar.register_handler(ForDeployed(|this, _: &Dismiss, cx| {
-            this.dismiss(&Dismiss, cx);
+        registrar.register_handler(WithResults(
+            |this, action: &SelectAllMatches, window, cx| {
+                this.select_all_matches(action, window, cx);
+            },
+        ));
+        registrar.register_handler(ForDeployed(
+            |this, _: &editor::actions::Cancel, window, cx| {
+                this.dismiss(&Dismiss, window, cx);
+            },
+        ));
+        registrar.register_handler(ForDeployed(|this, _: &Dismiss, window, cx| {
+            this.dismiss(&Dismiss, window, cx);
         }));
 
         // register deploy buffer search for both search bar states, since we want to focus into the search bar
         // when the deploy action is triggered in the buffer.
-        registrar.register_handler(ForDeployed(|this, deploy, cx| {
-            this.deploy(deploy, cx);
+        registrar.register_handler(ForDeployed(|this, deploy, window, cx| {
+            this.deploy(deploy, window, cx);
         }));
-        registrar.register_handler(ForDismissed(|this, deploy, cx| {
-            this.deploy(deploy, cx);
+        registrar.register_handler(ForDismissed(|this, deploy, window, cx| {
+            this.deploy(deploy, window, cx);
         }))
     }
 
-    pub fn new(cx: &mut ViewContext<Self>) -> Self {
-        let query_editor = cx.new_view(Editor::single_line);
-        cx.subscribe(&query_editor, Self::on_query_editor_event)
+    pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
+        let query_editor = cx.new(|cx| Editor::single_line(window, cx));
+        cx.subscribe_in(&query_editor, window, Self::on_query_editor_event)
             .detach();
-        let replacement_editor = cx.new_view(Editor::single_line);
+        let replacement_editor = cx.new(|cx| Editor::single_line(window, cx));
         cx.subscribe(&replacement_editor, Self::on_replacement_editor_event)
             .detach();
 
@@ -623,22 +647,22 @@ impl BufferSearchBar {
         self.dismissed
     }
 
-    pub fn dismiss(&mut self, _: &Dismiss, cx: &mut ViewContext<Self>) {
+    pub fn dismiss(&mut self, _: &Dismiss, window: &mut Window, cx: &mut Context<Self>) {
         self.dismissed = true;
         for searchable_item in self.searchable_items_with_matches.keys() {
             if let Some(searchable_item) =
                 WeakSearchableItemHandle::upgrade(searchable_item.as_ref(), cx)
             {
-                searchable_item.clear_matches(cx);
+                searchable_item.clear_matches(window, cx);
             }
         }
         if let Some(active_editor) = self.active_searchable_item.as_mut() {
             self.selection_search_enabled = false;
             self.replace_enabled = false;
-            active_editor.search_bar_visibility_changed(false, cx);
-            active_editor.toggle_filtered_search_ranges(false, cx);
-            let handle = active_editor.focus_handle(cx);
-            self.focus(&handle, cx);
+            active_editor.search_bar_visibility_changed(false, window, cx);
+            active_editor.toggle_filtered_search_ranges(false, window, cx);
+            let handle = active_editor.item_focus_handle(cx);
+            self.focus(&handle, window, cx);
         }
         cx.emit(Event::UpdateLocation);
         cx.emit(ToolbarItemEvent::ChangeLocation(
@@ -647,28 +671,32 @@ impl BufferSearchBar {
         cx.notify();
     }
 
-    pub fn deploy(&mut self, deploy: &Deploy, cx: &mut ViewContext<Self>) -> bool {
-        if self.show(cx) {
+    pub fn deploy(&mut self, deploy: &Deploy, window: &mut Window, cx: &mut Context<Self>) -> bool {
+        if self.show(window, cx) {
             if let Some(active_item) = self.active_searchable_item.as_mut() {
-                active_item.toggle_filtered_search_ranges(deploy.selection_search_enabled, cx);
+                active_item.toggle_filtered_search_ranges(
+                    deploy.selection_search_enabled,
+                    window,
+                    cx,
+                );
             }
-            self.search_suggested(cx);
-            self.smartcase(cx);
+            self.search_suggested(window, cx);
+            self.smartcase(window, cx);
             self.replace_enabled = deploy.replace_enabled;
             self.selection_search_enabled = deploy.selection_search_enabled;
             if deploy.focus {
                 let mut handle = self.query_editor.focus_handle(cx).clone();
                 let mut select_query = true;
-                if deploy.replace_enabled && handle.is_focused(cx) {
+                if deploy.replace_enabled && handle.is_focused(window) {
                     handle = self.replacement_editor.focus_handle(cx).clone();
                     select_query = false;
                 };
 
                 if select_query {
-                    self.select_query(cx);
+                    self.select_query(window, cx);
                 }
 
-                cx.focus(&handle);
+                window.focus(&handle);
             }
             return true;
         }
@@ -677,15 +705,15 @@ impl BufferSearchBar {
         false
     }
 
-    pub fn toggle(&mut self, action: &Deploy, cx: &mut ViewContext<Self>) {
+    pub fn toggle(&mut self, action: &Deploy, window: &mut Window, cx: &mut Context<Self>) {
         if self.is_dismissed() {
-            self.deploy(action, cx);
+            self.deploy(action, window, cx);
         } else {
-            self.dismiss(&Dismiss, cx);
+            self.dismiss(&Dismiss, window, cx);
         }
     }
 
-    pub fn show(&mut self, cx: &mut ViewContext<Self>) -> bool {
+    pub fn show(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
         let Some(handle) = self.active_searchable_item.as_ref() else {
             return false;
         };
@@ -698,7 +726,7 @@ impl BufferSearchBar {
         }
 
         self.dismissed = false;
-        handle.search_bar_visibility_changed(true, cx);
+        handle.search_bar_visibility_changed(true, window, cx);
         cx.notify();
         cx.emit(Event::UpdateLocation);
         cx.emit(ToolbarItemEvent::ChangeLocation(
@@ -714,53 +742,61 @@ impl BufferSearchBar {
             .unwrap_or_default()
     }
 
-    pub fn search_suggested(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn search_suggested(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let search = self
-            .query_suggestion(cx)
-            .map(|suggestion| self.search(&suggestion, Some(self.default_options), cx));
+            .query_suggestion(window, cx)
+            .map(|suggestion| self.search(&suggestion, Some(self.default_options), window, cx));
 
         if let Some(search) = search {
-            cx.spawn(|this, mut cx| async move {
+            cx.spawn_in(window, |this, mut cx| async move {
                 search.await?;
-                this.update(&mut cx, |this, cx| this.activate_current_match(cx))
+                this.update_in(&mut cx, |this, window, cx| {
+                    this.activate_current_match(window, cx)
+                })
             })
             .detach_and_log_err(cx);
         }
     }
 
-    pub fn activate_current_match(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn activate_current_match(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(match_ix) = self.active_match_index {
             if let Some(active_searchable_item) = self.active_searchable_item.as_ref() {
                 if let Some(matches) = self
                     .searchable_items_with_matches
                     .get(&active_searchable_item.downgrade())
                 {
-                    active_searchable_item.activate_match(match_ix, matches, cx)
+                    active_searchable_item.activate_match(match_ix, matches, window, cx)
                 }
             }
         }
     }
 
-    pub fn select_query(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn select_query(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.query_editor.update(cx, |query_editor, cx| {
-            query_editor.select_all(&Default::default(), cx);
+            query_editor.select_all(&Default::default(), window, cx);
         });
     }
 
-    pub fn query(&self, cx: &WindowContext) -> String {
+    pub fn query(&self, cx: &App) -> String {
         self.query_editor.read(cx).text(cx)
     }
-    pub fn replacement(&self, cx: &WindowContext) -> String {
+
+    pub fn replacement(&self, cx: &mut App) -> String {
         self.replacement_editor.read(cx).text(cx)
     }
-    pub fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<String> {
+
+    pub fn query_suggestion(
+        &mut self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<String> {
         self.active_searchable_item
             .as_ref()
-            .map(|searchable_item| searchable_item.query_suggestion(cx))
+            .map(|searchable_item| searchable_item.query_suggestion(window, cx))
             .filter(|suggestion| !suggestion.is_empty())
     }
 
-    pub fn set_replacement(&mut self, replacement: Option<&str>, cx: &mut ViewContext<Self>) {
+    pub fn set_replacement(&mut self, replacement: Option<&str>, cx: &mut Context<Self>) {
         if replacement.is_none() {
             self.replace_enabled = false;
             return;
@@ -781,7 +817,8 @@ impl BufferSearchBar {
         &mut self,
         query: &str,
         options: Option<SearchOptions>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> oneshot::Receiver<()> {
         let options = options.unwrap_or(self.default_options);
         let updated = query != self.query(cx) || self.search_options != options;
@@ -793,37 +830,38 @@ impl BufferSearchBar {
                 });
             });
             self.search_options = options;
-            self.clear_matches(cx);
+            self.clear_matches(window, cx);
             cx.notify();
         }
-        self.update_matches(!updated, cx)
+        self.update_matches(!updated, window, cx)
     }
 
     fn render_search_option_button(
         &self,
         option: SearchOptions,
         focus_handle: FocusHandle,
-        action: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
+        action: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
     ) -> impl IntoElement {
         let is_active = self.search_options.contains(option);
         option.as_button(is_active, focus_handle, action)
     }
 
-    pub fn focus_editor(&mut self, _: &FocusEditor, cx: &mut ViewContext<Self>) {
+    pub fn focus_editor(&mut self, _: &FocusEditor, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(active_editor) = self.active_searchable_item.as_ref() {
-            let handle = active_editor.focus_handle(cx);
-            cx.focus(&handle);
+            let handle = active_editor.item_focus_handle(cx);
+            window.focus(&handle);
         }
     }
 
     pub fn toggle_search_option(
         &mut self,
         search_option: SearchOptions,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.search_options.toggle(search_option);
         self.default_options = self.search_options;
-        drop(self.update_matches(false, cx));
+        drop(self.update_matches(false, window, cx));
         cx.notify();
     }
 
@@ -834,45 +872,63 @@ impl BufferSearchBar {
     pub fn enable_search_option(
         &mut self,
         search_option: SearchOptions,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         if !self.search_options.contains(search_option) {
-            self.toggle_search_option(search_option, cx)
+            self.toggle_search_option(search_option, window, cx)
         }
     }
 
-    pub fn set_search_options(
-        &mut self,
-        search_options: SearchOptions,
-        cx: &mut ViewContext<Self>,
-    ) {
+    pub fn set_search_options(&mut self, search_options: SearchOptions, cx: &mut Context<Self>) {
         self.search_options = search_options;
         cx.notify();
     }
 
-    fn select_next_match(&mut self, _: &SelectNextMatch, cx: &mut ViewContext<Self>) {
-        self.select_match(Direction::Next, 1, cx);
+    fn select_next_match(
+        &mut self,
+        _: &SelectNextMatch,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.select_match(Direction::Next, 1, window, cx);
     }
 
-    fn select_prev_match(&mut self, _: &SelectPrevMatch, cx: &mut ViewContext<Self>) {
-        self.select_match(Direction::Prev, 1, cx);
+    fn select_prev_match(
+        &mut self,
+        _: &SelectPrevMatch,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.select_match(Direction::Prev, 1, window, cx);
     }
 
-    fn select_all_matches(&mut self, _: &SelectAllMatches, cx: &mut ViewContext<Self>) {
+    fn select_all_matches(
+        &mut self,
+        _: &SelectAllMatches,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if !self.dismissed && self.active_match_index.is_some() {
             if let Some(searchable_item) = self.active_searchable_item.as_ref() {
                 if let Some(matches) = self
                     .searchable_items_with_matches
                     .get(&searchable_item.downgrade())
                 {
-                    searchable_item.select_matches(matches, cx);
-                    self.focus_editor(&FocusEditor, cx);
+                    searchable_item.select_matches(matches, window, cx);
+                    self.focus_editor(&FocusEditor, window, cx);
                 }
             }
         }
     }
 
-    pub fn select_match(&mut self, direction: Direction, count: usize, cx: &mut ViewContext<Self>) {
+    pub fn select_match(
+        &mut self,
+        direction: Direction,
+        count: usize,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(index) = self.active_match_index {
             if let Some(searchable_item) = self.active_searchable_item.as_ref() {
                 if let Some(matches) = self
@@ -885,20 +941,20 @@ impl BufferSearchBar {
                         && ((direction == Direction::Next && index + count >= matches.len())
                             || (direction == Direction::Prev && index < count))
                     {
-                        crate::show_no_more_matches(cx);
+                        crate::show_no_more_matches(window, cx);
                         return;
                     }
                     let new_match_index = searchable_item
-                        .match_index_for_direction(matches, index, direction, count, cx);
+                        .match_index_for_direction(matches, index, direction, count, window, cx);
 
-                    searchable_item.update_matches(matches, cx);
-                    searchable_item.activate_match(new_match_index, matches, cx);
+                    searchable_item.update_matches(matches, window, cx);
+                    searchable_item.activate_match(new_match_index, matches, window, cx);
                 }
             }
         }
     }
 
-    pub fn select_last_match(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn select_last_match(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(searchable_item) = self.active_searchable_item.as_ref() {
             if let Some(matches) = self
                 .searchable_items_with_matches
@@ -908,29 +964,30 @@ impl BufferSearchBar {
                     return;
                 }
                 let new_match_index = matches.len() - 1;
-                searchable_item.update_matches(matches, cx);
-                searchable_item.activate_match(new_match_index, matches, cx);
+                searchable_item.update_matches(matches, window, cx);
+                searchable_item.activate_match(new_match_index, matches, window, cx);
             }
         }
     }
 
     fn on_query_editor_event(
         &mut self,
-        editor: View<Editor>,
+        editor: &Entity<Editor>,
         event: &editor::EditorEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match event {
             editor::EditorEvent::Focused => self.query_editor_focused = true,
             editor::EditorEvent::Blurred => self.query_editor_focused = false,
             editor::EditorEvent::Edited { .. } => {
-                self.smartcase(cx);
-                self.clear_matches(cx);
-                let search = self.update_matches(false, cx);
+                self.smartcase(window, cx);
+                self.clear_matches(window, cx);
+                let search = self.update_matches(false, window, cx);
 
                 let width = editor.update(cx, |editor, cx| {
-                    let text_layout_details = editor.text_layout_details(cx);
-                    let snapshot = editor.snapshot(cx).display_snapshot;
+                    let text_layout_details = editor.text_layout_details(window);
+                    let snapshot = editor.snapshot(window, cx).display_snapshot;
 
                     snapshot.x_for_display_point(snapshot.max_point(), &text_layout_details)
                         - snapshot.x_for_display_point(DisplayPoint::zero(), &text_layout_details)
@@ -938,9 +995,11 @@ impl BufferSearchBar {
                 self.editor_needed_width = width;
                 cx.notify();
 
-                cx.spawn(|this, mut cx| async move {
+                cx.spawn_in(window, |this, mut cx| async move {
                     search.await?;
-                    this.update(&mut cx, |this, cx| this.activate_current_match(cx))
+                    this.update_in(&mut cx, |this, window, cx| {
+                        this.activate_current_match(window, cx)
+                    })
                 })
                 .detach_and_log_err(cx);
             }
@@ -950,9 +1009,9 @@ impl BufferSearchBar {
 
     fn on_replacement_editor_event(
         &mut self,
-        _: View<Editor>,
+        _: Entity<Editor>,
         event: &editor::EditorEvent,
-        _: &mut ViewContext<Self>,
+        _: &mut Context<Self>,
     ) {
         match event {
             editor::EditorEvent::Focused => self.replacement_editor_focused = true,
@@ -961,42 +1020,62 @@ impl BufferSearchBar {
         }
     }
 
-    fn on_active_searchable_item_event(&mut self, event: &SearchEvent, cx: &mut ViewContext<Self>) {
+    fn on_active_searchable_item_event(
+        &mut self,
+        event: &SearchEvent,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         match event {
             SearchEvent::MatchesInvalidated => {
-                drop(self.update_matches(false, cx));
+                drop(self.update_matches(false, window, cx));
             }
-            SearchEvent::ActiveMatchChanged => self.update_match_index(cx),
+            SearchEvent::ActiveMatchChanged => self.update_match_index(window, cx),
         }
     }
 
-    fn toggle_case_sensitive(&mut self, _: &ToggleCaseSensitive, cx: &mut ViewContext<Self>) {
-        self.toggle_search_option(SearchOptions::CASE_SENSITIVE, cx)
+    fn toggle_case_sensitive(
+        &mut self,
+        _: &ToggleCaseSensitive,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.toggle_search_option(SearchOptions::CASE_SENSITIVE, window, cx)
     }
 
-    fn toggle_whole_word(&mut self, _: &ToggleWholeWord, cx: &mut ViewContext<Self>) {
-        self.toggle_search_option(SearchOptions::WHOLE_WORD, cx)
+    fn toggle_whole_word(
+        &mut self,
+        _: &ToggleWholeWord,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.toggle_search_option(SearchOptions::WHOLE_WORD, window, cx)
     }
 
-    fn toggle_selection(&mut self, _: &ToggleSelection, cx: &mut ViewContext<Self>) {
+    fn toggle_selection(
+        &mut self,
+        _: &ToggleSelection,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(active_item) = self.active_searchable_item.as_mut() {
             self.selection_search_enabled = !self.selection_search_enabled;
-            active_item.toggle_filtered_search_ranges(self.selection_search_enabled, cx);
-            drop(self.update_matches(false, cx));
+            active_item.toggle_filtered_search_ranges(self.selection_search_enabled, window, cx);
+            drop(self.update_matches(false, window, cx));
             cx.notify();
         }
     }
 
-    fn toggle_regex(&mut self, _: &ToggleRegex, cx: &mut ViewContext<Self>) {
-        self.toggle_search_option(SearchOptions::REGEX, cx)
+    fn toggle_regex(&mut self, _: &ToggleRegex, window: &mut Window, cx: &mut Context<Self>) {
+        self.toggle_search_option(SearchOptions::REGEX, window, cx)
     }
 
-    fn clear_active_searchable_item_matches(&mut self, cx: &mut WindowContext) {
+    fn clear_active_searchable_item_matches(&mut self, window: &mut Window, cx: &mut App) {
         if let Some(active_searchable_item) = self.active_searchable_item.as_ref() {
             self.active_match_index = None;
             self.searchable_items_with_matches
                 .remove(&active_searchable_item.downgrade());
-            active_searchable_item.clear_matches(cx);
+            active_searchable_item.clear_matches(window, cx);
         }
     }
 
@@ -1004,7 +1083,7 @@ impl BufferSearchBar {
         self.active_match_index.is_some()
     }
 
-    fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
+    fn clear_matches(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let mut active_item_matches = None;
         for (searchable_item, matches) in self.searchable_items_with_matches.drain() {
             if let Some(searchable_item) =
@@ -1013,7 +1092,7 @@ impl BufferSearchBar {
                 if Some(&searchable_item) == self.active_searchable_item.as_ref() {
                     active_item_matches = Some((searchable_item.downgrade(), matches));
                 } else {
-                    searchable_item.clear_matches(cx);
+                    searchable_item.clear_matches(window, cx);
                 }
             }
         }
@@ -1025,7 +1104,8 @@ impl BufferSearchBar {
     fn update_matches(
         &mut self,
         reuse_existing_query: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> oneshot::Receiver<()> {
         let (done_tx, done_rx) = oneshot::channel();
         let query = self.query(cx);
@@ -1034,7 +1114,7 @@ impl BufferSearchBar {
         if let Some(active_searchable_item) = self.active_searchable_item.as_ref() {
             self.query_contains_error = false;
             if query.is_empty() {
-                self.clear_active_searchable_item_matches(cx);
+                self.clear_active_searchable_item_matches(window, cx);
                 let _ = done_tx.send(());
                 cx.notify();
             } else {
@@ -1056,7 +1136,7 @@ impl BufferSearchBar {
                             Ok(query) => query.with_replacement(self.replacement(cx)),
                             Err(_) => {
                                 self.query_contains_error = true;
-                                self.clear_active_searchable_item_matches(cx);
+                                self.clear_active_searchable_item_matches(window, cx);
                                 cx.notify();
                                 return done_rx;
                             }
@@ -1074,7 +1154,7 @@ impl BufferSearchBar {
                             Ok(query) => query.with_replacement(self.replacement(cx)),
                             Err(_) => {
                                 self.query_contains_error = true;
-                                self.clear_active_searchable_item_matches(cx);
+                                self.clear_active_searchable_item_matches(window, cx);
                                 cx.notify();
                                 return done_rx;
                             }
@@ -1086,20 +1166,20 @@ impl BufferSearchBar {
                 self.active_search = Some(query.clone());
                 let query_text = query.as_str().to_string();
 
-                let matches = active_searchable_item.find_matches(query, cx);
+                let matches = active_searchable_item.find_matches(query, window, cx);
 
                 let active_searchable_item = active_searchable_item.downgrade();
-                self.pending_search = Some(cx.spawn(|this, mut cx| async move {
+                self.pending_search = Some(cx.spawn_in(window, |this, mut cx| async move {
                     let matches = matches.await;
 
-                    this.update(&mut cx, |this, cx| {
+                    this.update_in(&mut cx, |this, window, cx| {
                         if let Some(active_searchable_item) =
                             WeakSearchableItemHandle::upgrade(active_searchable_item.as_ref(), cx)
                         {
                             this.searchable_items_with_matches
                                 .insert(active_searchable_item.downgrade(), matches);
 
-                            this.update_match_index(cx);
+                            this.update_match_index(window, cx);
                             this.search_history
                                 .add(&mut this.search_history_cursor, query_text);
                             if !this.dismissed {
@@ -1108,9 +1188,9 @@ impl BufferSearchBar {
                                     .get(&active_searchable_item.downgrade())
                                     .unwrap();
                                 if matches.is_empty() {
-                                    active_searchable_item.clear_matches(cx);
+                                    active_searchable_item.clear_matches(window, cx);
                                 } else {
-                                    active_searchable_item.update_matches(matches, cx);
+                                    active_searchable_item.update_matches(matches, window, cx);
                                 }
                                 let _ = done_tx.send(());
                             }
@@ -1124,7 +1204,7 @@ impl BufferSearchBar {
         done_rx
     }
 
-    pub fn update_match_index(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn update_match_index(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let new_index = self
             .active_searchable_item
             .as_ref()

crates/search/src/buffer_search/registrar.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{div, Action, Div, InteractiveElement, View, ViewContext};
+use gpui::{div, Action, Context, Div, Entity, InteractiveElement, Window};
 use workspace::Workspace;
 
 use crate::BufferSearchBar;
@@ -8,20 +8,21 @@ pub trait SearchActionsRegistrar {
     fn register_handler<A: Action>(&mut self, callback: impl ActionExecutor<A>);
 }
 
-type SearchBarActionCallback<A> = fn(&mut BufferSearchBar, &A, &mut ViewContext<BufferSearchBar>);
+type SearchBarActionCallback<A> =
+    fn(&mut BufferSearchBar, &A, &mut Window, &mut Context<BufferSearchBar>);
 
 type GetSearchBar<T> =
-    for<'a, 'b> fn(&'a T, &'a mut ViewContext<'b, T>) -> Option<View<BufferSearchBar>>;
+    for<'a, 'b> fn(&'a T, &'a mut Window, &mut Context<'b, T>) -> Option<Entity<BufferSearchBar>>;
 
 /// Registers search actions on a div that can be taken out.
 pub struct DivRegistrar<'a, 'b, T: 'static> {
     div: Option<Div>,
-    cx: &'a mut ViewContext<'b, T>,
+    cx: &'a mut Context<'b, T>,
     search_getter: GetSearchBar<T>,
 }
 
 impl<'a, 'b, T: 'static> DivRegistrar<'a, 'b, T> {
-    pub fn new(search_getter: GetSearchBar<T>, cx: &'a mut ViewContext<'b, T>) -> Self {
+    pub fn new(search_getter: GetSearchBar<T>, cx: &'a mut Context<'b, T>) -> Self {
         Self {
             div: Some(div()),
             cx,
@@ -39,12 +40,12 @@ impl<T: 'static> SearchActionsRegistrar for DivRegistrar<'_, '_, T> {
     fn register_handler<A: Action>(&mut self, callback: impl ActionExecutor<A>) {
         let getter = self.search_getter;
         self.div = self.div.take().map(|div| {
-            div.on_action(self.cx.listener(move |this, action, cx| {
-                let should_notify = (getter)(this, cx)
+            div.on_action(self.cx.listener(move |this, action, window, cx| {
+                let should_notify = (getter)(this, window, cx)
                     .clone()
                     .map(|search_bar| {
                         search_bar.update(cx, |search_bar, cx| {
-                            callback.execute(search_bar, action, cx)
+                            callback.execute(search_bar, action, window, cx)
                         })
                     })
                     .unwrap_or(false);
@@ -61,8 +62,8 @@ impl<T: 'static> SearchActionsRegistrar for DivRegistrar<'_, '_, T> {
 /// Register actions for an active pane.
 impl SearchActionsRegistrar for Workspace {
     fn register_handler<A: Action>(&mut self, callback: impl ActionExecutor<A>) {
-        self.register_action(move |workspace, action: &A, cx| {
-            if workspace.has_active_modal(cx) {
+        self.register_action(move |workspace, action: &A, window, cx| {
+            if workspace.has_active_modal(window, cx) {
                 cx.propagate();
                 return;
             }
@@ -73,7 +74,7 @@ impl SearchActionsRegistrar for Workspace {
                 this.toolbar().update(cx, move |this, cx| {
                     if let Some(search_bar) = this.item_of_type::<BufferSearchBar>() {
                         let should_notify = search_bar.update(cx, move |search_bar, cx| {
-                            callback.execute(search_bar, action, cx)
+                            callback.execute(search_bar, action, window, cx)
                         });
                         if should_notify {
                             cx.notify();
@@ -94,7 +95,8 @@ pub trait ActionExecutor<A: Action>: 'static + Clone {
         &self,
         search_bar: &mut BufferSearchBar,
         action: &A,
-        cx: &mut ViewContext<BufferSearchBar>,
+        window: &mut Window,
+        cx: &mut Context<BufferSearchBar>,
     ) -> DidHandleAction;
 }
 
@@ -111,10 +113,11 @@ impl<A: Action> ActionExecutor<A> for ForDismissed<A> {
         &self,
         search_bar: &mut BufferSearchBar,
         action: &A,
-        cx: &mut ViewContext<BufferSearchBar>,
+        window: &mut Window,
+        cx: &mut Context<BufferSearchBar>,
     ) -> DidHandleAction {
         if search_bar.is_dismissed() {
-            self.0(search_bar, action, cx);
+            self.0(search_bar, action, window, cx);
             true
         } else {
             false
@@ -135,12 +138,13 @@ impl<A: Action> ActionExecutor<A> for ForDeployed<A> {
         &self,
         search_bar: &mut BufferSearchBar,
         action: &A,
-        cx: &mut ViewContext<BufferSearchBar>,
+        window: &mut Window,
+        cx: &mut Context<BufferSearchBar>,
     ) -> DidHandleAction {
         if search_bar.is_dismissed() || search_bar.active_searchable_item.is_none() {
             false
         } else {
-            self.0(search_bar, action, cx);
+            self.0(search_bar, action, window, cx);
             true
         }
     }
@@ -160,10 +164,11 @@ impl<A: Action> ActionExecutor<A> for WithResults<A> {
         &self,
         search_bar: &mut BufferSearchBar,
         action: &A,
-        cx: &mut ViewContext<BufferSearchBar>,
+        window: &mut Window,
+        cx: &mut Context<BufferSearchBar>,
     ) -> DidHandleAction {
         if search_bar.active_match_index.is_some() {
-            self.0(search_bar, action, cx);
+            self.0(search_bar, action, window, cx);
             true
         } else {
             false

crates/search/src/project_search.rs 🔗

@@ -10,11 +10,10 @@ use editor::{
 };
 use futures::StreamExt;
 use gpui::{
-    actions, div, Action, AnyElement, AnyView, AppContext, Axis, Context as _, EntityId,
-    EventEmitter, FocusHandle, FocusableView, Global, Hsla, InteractiveElement, IntoElement,
-    KeyContext, Model, ModelContext, ParentElement, Point, Render, SharedString, Styled,
-    Subscription, Task, TextStyle, UpdateGlobal, View, ViewContext, VisualContext, WeakModel,
-    WeakView, WindowContext,
+    actions, div, Action, AnyElement, AnyView, App, Axis, Context, Entity, EntityId, EventEmitter,
+    FocusHandle, Focusable, Global, Hsla, InteractiveElement, IntoElement, KeyContext,
+    ParentElement, Point, Render, SharedString, Styled, Subscription, Task, TextStyle,
+    UpdateGlobal, WeakEntity, Window,
 };
 use language::Buffer;
 use menu::Confirm;
@@ -50,67 +49,76 @@ actions!(
 );
 
 #[derive(Default)]
-struct ActiveSettings(HashMap<WeakModel<Project>, ProjectSearchSettings>);
+struct ActiveSettings(HashMap<WeakEntity<Project>, ProjectSearchSettings>);
 
 impl Global for ActiveSettings {}
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     cx.set_global(ActiveSettings::default());
-    cx.observe_new_views(|workspace: &mut Workspace, _cx| {
-        register_workspace_action(workspace, move |search_bar, _: &Deploy, cx| {
-            search_bar.focus_search(cx);
+    cx.observe_new(|workspace: &mut Workspace, _window, _cx| {
+        register_workspace_action(workspace, move |search_bar, _: &Deploy, window, cx| {
+            search_bar.focus_search(window, cx);
         });
-        register_workspace_action(workspace, move |search_bar, _: &FocusSearch, cx| {
-            search_bar.focus_search(cx);
+        register_workspace_action(workspace, move |search_bar, _: &FocusSearch, window, cx| {
+            search_bar.focus_search(window, cx);
         });
-        register_workspace_action(workspace, move |search_bar, _: &ToggleFilters, cx| {
-            search_bar.toggle_filters(cx);
-        });
-        register_workspace_action(workspace, move |search_bar, _: &ToggleCaseSensitive, cx| {
-            search_bar.toggle_search_option(SearchOptions::CASE_SENSITIVE, cx);
-        });
-        register_workspace_action(workspace, move |search_bar, _: &ToggleWholeWord, cx| {
+        register_workspace_action(
+            workspace,
+            move |search_bar, _: &ToggleFilters, window, cx| {
+                search_bar.toggle_filters(window, cx);
+            },
+        );
+        register_workspace_action(
+            workspace,
+            move |search_bar, _: &ToggleCaseSensitive, _, cx| {
+                search_bar.toggle_search_option(SearchOptions::CASE_SENSITIVE, cx);
+            },
+        );
+        register_workspace_action(workspace, move |search_bar, _: &ToggleWholeWord, _, cx| {
             search_bar.toggle_search_option(SearchOptions::WHOLE_WORD, cx);
         });
-        register_workspace_action(workspace, move |search_bar, _: &ToggleRegex, cx| {
+        register_workspace_action(workspace, move |search_bar, _: &ToggleRegex, _, cx| {
             search_bar.toggle_search_option(SearchOptions::REGEX, cx);
         });
-        register_workspace_action(workspace, move |search_bar, action: &ToggleReplace, cx| {
-            search_bar.toggle_replace(action, cx)
-        });
         register_workspace_action(
             workspace,
-            move |search_bar, action: &SelectPrevMatch, cx| {
-                search_bar.select_prev_match(action, cx)
+            move |search_bar, action: &ToggleReplace, window, cx| {
+                search_bar.toggle_replace(action, window, cx)
+            },
+        );
+        register_workspace_action(
+            workspace,
+            move |search_bar, action: &SelectPrevMatch, window, cx| {
+                search_bar.select_prev_match(action, window, cx)
             },
         );
         register_workspace_action(
             workspace,
-            move |search_bar, action: &SelectNextMatch, cx| {
-                search_bar.select_next_match(action, cx)
+            move |search_bar, action: &SelectNextMatch, window, cx| {
+                search_bar.select_next_match(action, window, cx)
             },
         );
 
         // Only handle search_in_new if there is a search present
-        register_workspace_action_for_present_search(workspace, |workspace, action, cx| {
-            ProjectSearchView::search_in_new(workspace, action, cx)
+        register_workspace_action_for_present_search(workspace, |workspace, action, window, cx| {
+            ProjectSearchView::search_in_new(workspace, action, window, cx)
         });
 
         // Both on present and dismissed search, we need to unconditionally handle those actions to focus from the editor.
-        workspace.register_action(move |workspace, action: &DeploySearch, cx| {
-            if workspace.has_active_modal(cx) {
+        workspace.register_action(move |workspace, action: &DeploySearch, window, cx| {
+            if workspace.has_active_modal(window, cx) {
                 cx.propagate();
                 return;
             }
-            ProjectSearchView::deploy_search(workspace, action, cx);
+            ProjectSearchView::deploy_search(workspace, action, window, cx);
             cx.notify();
         });
-        workspace.register_action(move |workspace, action: &NewSearch, cx| {
-            if workspace.has_active_modal(cx) {
+        workspace.register_action(move |workspace, action: &NewSearch, window, cx| {
+            if workspace.has_active_modal(window, cx) {
                 cx.propagate();
                 return;
             }
-            ProjectSearchView::new_search(workspace, action, cx);
+            ProjectSearchView::new_search(workspace, action, window, cx);
             cx.notify();
         });
     })
@@ -122,8 +130,8 @@ fn is_contains_uppercase(str: &str) -> bool {
 }
 
 pub struct ProjectSearch {
-    project: Model<Project>,
-    excerpts: Model<MultiBuffer>,
+    project: Entity<Project>,
+    excerpts: Entity<MultiBuffer>,
     pending_search: Option<Task<Option<()>>>,
     match_ranges: Vec<Range<Anchor>>,
     active_query: Option<SearchQuery>,
@@ -144,18 +152,18 @@ enum InputPanel {
 }
 
 pub struct ProjectSearchView {
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     focus_handle: FocusHandle,
-    model: Model<ProjectSearch>,
-    query_editor: View<Editor>,
-    replacement_editor: View<Editor>,
-    results_editor: View<Editor>,
+    model: Entity<ProjectSearch>,
+    query_editor: Entity<Editor>,
+    replacement_editor: Entity<Editor>,
+    results_editor: Entity<Editor>,
     search_options: SearchOptions,
     panels_with_errors: HashSet<InputPanel>,
     active_match_index: Option<usize>,
     search_id: usize,
-    included_files_editor: View<Editor>,
-    excluded_files_editor: View<Editor>,
+    included_files_editor: Entity<Editor>,
+    excluded_files_editor: Entity<Editor>,
     filters_enabled: bool,
     replace_enabled: bool,
     included_opened_only: bool,
@@ -169,17 +177,17 @@ pub struct ProjectSearchSettings {
 }
 
 pub struct ProjectSearchBar {
-    active_project_search: Option<View<ProjectSearchView>>,
+    active_project_search: Option<Entity<ProjectSearchView>>,
     subscription: Option<Subscription>,
 }
 
 impl ProjectSearch {
-    pub fn new(project: Model<Project>, cx: &mut ModelContext<Self>) -> Self {
+    pub fn new(project: Entity<Project>, cx: &mut Context<Self>) -> Self {
         let capability = project.read(cx).capability();
 
         Self {
             project,
-            excerpts: cx.new_model(|_| MultiBuffer::new(capability)),
+            excerpts: cx.new(|_| MultiBuffer::new(capability)),
             pending_search: Default::default(),
             match_ranges: Default::default(),
             active_query: None,
@@ -193,12 +201,12 @@ impl ProjectSearch {
         }
     }
 
-    fn clone(&self, cx: &mut ModelContext<Self>) -> Model<Self> {
-        cx.new_model(|cx| Self {
+    fn clone(&self, cx: &mut Context<Self>) -> Entity<Self> {
+        cx.new(|cx| Self {
             project: self.project.clone(),
             excerpts: self
                 .excerpts
-                .update(cx, |excerpts, cx| cx.new_model(|cx| excerpts.clone(cx))),
+                .update(cx, |excerpts, cx| cx.new(|cx| excerpts.clone(cx))),
             pending_search: Default::default(),
             match_ranges: self.match_ranges.clone(),
             active_query: self.active_query.clone(),
@@ -226,7 +234,7 @@ impl ProjectSearch {
         }
     }
 
-    fn search(&mut self, query: SearchQuery, cx: &mut ModelContext<Self>) {
+    fn search(&mut self, query: SearchQuery, cx: &mut Context<Self>) {
         let search = self.project.update(cx, |project, cx| {
             project
                 .search_history_mut(SearchInputKind::Query)
@@ -321,7 +329,7 @@ pub enum ViewEvent {
 impl EventEmitter<ViewEvent> for ProjectSearchView {}
 
 impl Render for ProjectSearchView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         if self.has_matches() {
             div()
                 .flex_1()
@@ -356,7 +364,7 @@ impl Render for ProjectSearchView {
                     None
                 }
             } else {
-                Some(self.landing_text_minor(cx).into_any_element())
+                Some(self.landing_text_minor(window).into_any_element())
             };
 
             let page_content = page_content.map(|text| div().child(text));
@@ -381,15 +389,15 @@ impl Render for ProjectSearchView {
     }
 }
 
-impl FocusableView for ProjectSearchView {
-    fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
+impl Focusable for ProjectSearchView {
+    fn focus_handle(&self, _: &App) -> gpui::FocusHandle {
         self.focus_handle.clone()
     }
 }
 
 impl Item for ProjectSearchView {
     type Event = ViewEvent;
-    fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString> {
+    fn tab_tooltip_text(&self, cx: &App) -> Option<SharedString> {
         let query_text = self.query_editor.read(cx).text(cx);
 
         query_text
@@ -402,8 +410,8 @@ impl Item for ProjectSearchView {
     fn act_as_type<'a>(
         &'a self,
         type_id: TypeId,
-        self_handle: &'a View<Self>,
-        _: &'a AppContext,
+        self_handle: &'a Entity<Self>,
+        _: &'a App,
     ) -> Option<AnyView> {
         if type_id == TypeId::of::<Self>() {
             Some(self_handle.clone().into())
@@ -414,16 +422,16 @@ impl Item for ProjectSearchView {
         }
     }
 
-    fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
+    fn deactivated(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.results_editor
-            .update(cx, |editor, cx| editor.deactivated(cx));
+            .update(cx, |editor, cx| editor.deactivated(window, cx));
     }
 
-    fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
+    fn tab_icon(&self, _window: &Window, _cx: &App) -> Option<Icon> {
         Some(Icon::new(IconName::MagnifyingGlass))
     }
 
-    fn tab_content_text(&self, cx: &WindowContext) -> Option<SharedString> {
+    fn tab_content_text(&self, _: &Window, cx: &App) -> Option<SharedString> {
         let last_query: Option<SharedString> = self
             .model
             .read(cx)
@@ -447,82 +455,102 @@ impl Item for ProjectSearchView {
 
     fn for_each_project_item(
         &self,
-        cx: &AppContext,
+        cx: &App,
         f: &mut dyn FnMut(EntityId, &dyn project::ProjectItem),
     ) {
         self.results_editor.for_each_project_item(cx, f)
     }
 
-    fn is_singleton(&self, _: &AppContext) -> bool {
+    fn is_singleton(&self, _: &App) -> bool {
         false
     }
 
-    fn can_save(&self, _: &AppContext) -> bool {
+    fn can_save(&self, _: &App) -> bool {
         true
     }
 
-    fn is_dirty(&self, cx: &AppContext) -> bool {
+    fn is_dirty(&self, cx: &App) -> bool {
         self.results_editor.read(cx).is_dirty(cx)
     }
 
-    fn has_conflict(&self, cx: &AppContext) -> bool {
+    fn has_conflict(&self, cx: &App) -> bool {
         self.results_editor.read(cx).has_conflict(cx)
     }
 
     fn save(
         &mut self,
         format: bool,
-        project: Model<Project>,
-        cx: &mut ViewContext<Self>,
+        project: Entity<Project>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<anyhow::Result<()>> {
         self.results_editor
-            .update(cx, |editor, cx| editor.save(format, project, cx))
+            .update(cx, |editor, cx| editor.save(format, project, window, cx))
     }
 
     fn save_as(
         &mut self,
-        _: Model<Project>,
+        _: Entity<Project>,
         _: ProjectPath,
-        _: &mut ViewContext<Self>,
+        _window: &mut Window,
+        _: &mut Context<Self>,
     ) -> Task<anyhow::Result<()>> {
         unreachable!("save_as should not have been called")
     }
 
     fn reload(
         &mut self,
-        project: Model<Project>,
-        cx: &mut ViewContext<Self>,
+        project: Entity<Project>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<anyhow::Result<()>> {
         self.results_editor
-            .update(cx, |editor, cx| editor.reload(project, cx))
+            .update(cx, |editor, cx| editor.reload(project, window, cx))
     }
 
     fn clone_on_split(
         &self,
         _workspace_id: Option<WorkspaceId>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<View<Self>>
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Entity<Self>>
     where
         Self: Sized,
     {
         let model = self.model.update(cx, |model, cx| model.clone(cx));
-        Some(cx.new_view(|cx| Self::new(self.workspace.clone(), model, cx, None)))
+        Some(cx.new(|cx| Self::new(self.workspace.clone(), model, window, cx, None)))
     }
 
-    fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
-        self.results_editor
-            .update(cx, |editor, cx| editor.added_to_workspace(workspace, cx));
+    fn added_to_workspace(
+        &mut self,
+        workspace: &mut Workspace,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.results_editor.update(cx, |editor, cx| {
+            editor.added_to_workspace(workspace, window, cx)
+        });
     }
 
-    fn set_nav_history(&mut self, nav_history: ItemNavHistory, cx: &mut ViewContext<Self>) {
+    fn set_nav_history(
+        &mut self,
+        nav_history: ItemNavHistory,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.results_editor.update(cx, |editor, _| {
             editor.set_nav_history(Some(nav_history));
         });
     }
 
-    fn navigate(&mut self, data: Box<dyn Any>, cx: &mut ViewContext<Self>) -> bool {
+    fn navigate(
+        &mut self,
+        data: Box<dyn Any>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> bool {
         self.results_editor
-            .update(cx, |editor, cx| editor.navigate(data, cx))
+            .update(cx, |editor, cx| editor.navigate(data, window, cx))
     }
 
     fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
@@ -539,7 +567,7 @@ impl Item for ProjectSearchView {
         }
     }
 
-    fn breadcrumb_location(&self, _: &AppContext) -> ToolbarItemLocation {
+    fn breadcrumb_location(&self, _: &App) -> ToolbarItemLocation {
         if self.has_matches() {
             ToolbarItemLocation::Secondary
         } else {
@@ -547,17 +575,17 @@ impl Item for ProjectSearchView {
         }
     }
 
-    fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
+    fn breadcrumbs(&self, theme: &theme::Theme, cx: &App) -> Option<Vec<BreadcrumbText>> {
         self.results_editor.breadcrumbs(theme, cx)
     }
 }
 
 impl ProjectSearchView {
-    pub fn get_matches(&self, cx: &AppContext) -> Vec<Range<Anchor>> {
+    pub fn get_matches(&self, cx: &App) -> Vec<Range<Anchor>> {
         self.model.read(cx).match_ranges.clone()
     }
 
-    fn toggle_filters(&mut self, cx: &mut ViewContext<Self>) {
+    fn toggle_filters(&mut self, cx: &mut Context<Self>) {
         self.filters_enabled = !self.filters_enabled;
         ActiveSettings::update_global(cx, |settings, cx| {
             settings.0.insert(
@@ -574,7 +602,7 @@ impl ProjectSearchView {
         }
     }
 
-    fn toggle_search_option(&mut self, option: SearchOptions, cx: &mut ViewContext<Self>) {
+    fn toggle_search_option(&mut self, option: SearchOptions, cx: &mut Context<Self>) {
         self.search_options.toggle(option);
         ActiveSettings::update_global(cx, |settings, cx| {
             settings.0.insert(
@@ -584,11 +612,11 @@ impl ProjectSearchView {
         });
     }
 
-    fn toggle_opened_only(&mut self, _cx: &mut ViewContext<Self>) {
+    fn toggle_opened_only(&mut self, _window: &mut Window, _cx: &mut Context<Self>) {
         self.included_opened_only = !self.included_opened_only;
     }
 
-    fn replace_next(&mut self, _: &ReplaceNext, cx: &mut ViewContext<Self>) {
+    fn replace_next(&mut self, _: &ReplaceNext, window: &mut Window, cx: &mut Context<Self>) {
         if self.model.read(cx).match_ranges.is_empty() {
             return;
         }
@@ -603,15 +631,15 @@ impl ProjectSearchView {
             // TODO: Do we need the clone here?
             let mat = self.model.read(cx).match_ranges[active_index].clone();
             self.results_editor.update(cx, |editor, cx| {
-                editor.replace(&mat, &query, cx);
+                editor.replace(&mat, &query, window, cx);
             });
-            self.select_match(Direction::Next, cx)
+            self.select_match(Direction::Next, window, cx)
         }
     }
-    pub fn replacement(&self, cx: &AppContext) -> String {
+    pub fn replacement(&self, cx: &App) -> String {
         self.replacement_editor.read(cx).text(cx)
     }
-    fn replace_all(&mut self, _: &ReplaceAll, cx: &mut ViewContext<Self>) {
+    fn replace_all(&mut self, _: &ReplaceAll, window: &mut Window, cx: &mut Context<Self>) {
         if self.active_match_index.is_none() {
             return;
         }
@@ -629,7 +657,7 @@ impl ProjectSearchView {
         }
 
         self.results_editor.update(cx, |editor, cx| {
-            editor.replace_all(&mut match_ranges.iter(), &query, cx);
+            editor.replace_all(&mut match_ranges.iter(), &query, window, cx);
         });
 
         self.model.update(cx, |model, _cx| {
@@ -638,9 +666,10 @@ impl ProjectSearchView {
     }
 
     pub fn new(
-        workspace: WeakView<Workspace>,
-        model: Model<ProjectSearch>,
-        cx: &mut ViewContext<Self>,
+        workspace: WeakEntity<Workspace>,
+        model: Entity<ProjectSearch>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
         settings: Option<ProjectSearchSettings>,
     ) -> Self {
         let project;
@@ -668,12 +697,14 @@ impl ProjectSearchView {
                 options = SearchOptions::from_query(active_query);
             }
         }
-        subscriptions.push(cx.observe(&model, |this, _, cx| this.model_changed(cx)));
+        subscriptions.push(cx.observe_in(&model, window, |this, _, window, cx| {
+            this.model_changed(window, cx)
+        }));
 
-        let query_editor = cx.new_view(|cx| {
-            let mut editor = Editor::single_line(cx);
+        let query_editor = cx.new(|cx| {
+            let mut editor = Editor::single_line(window, cx);
             editor.set_placeholder_text("Search all files…", cx);
-            editor.set_text(query_text, cx);
+            editor.set_text(query_text, window, cx);
             editor
         });
         // Subscribe to query_editor in order to reraise editor events for workspace item activation purposes
@@ -693,16 +724,17 @@ impl ProjectSearchView {
                 cx.emit(ViewEvent::EditorEvent(event.clone()))
             }),
         );
-        let replacement_editor = cx.new_view(|cx| {
-            let mut editor = Editor::single_line(cx);
+        let replacement_editor = cx.new(|cx| {
+            let mut editor = Editor::single_line(window, cx);
             editor.set_placeholder_text("Replace in project…", cx);
             if let Some(text) = replacement_text {
-                editor.set_text(text, cx);
+                editor.set_text(text, window, cx);
             }
             editor
         });
-        let results_editor = cx.new_view(|cx| {
-            let mut editor = Editor::for_multibuffer(excerpts, Some(project.clone()), true, cx);
+        let results_editor = cx.new(|cx| {
+            let mut editor =
+                Editor::for_multibuffer(excerpts, Some(project.clone()), true, window, cx);
             editor.set_searchable(false);
             editor
         });
@@ -718,8 +750,8 @@ impl ProjectSearchView {
             }),
         );
 
-        let included_files_editor = cx.new_view(|cx| {
-            let mut editor = Editor::single_line(cx);
+        let included_files_editor = cx.new(|cx| {
+            let mut editor = Editor::single_line(window, cx);
             editor.set_placeholder_text("Include: crates/**/*.toml", cx);
 
             editor
@@ -731,8 +763,8 @@ impl ProjectSearchView {
             }),
         );
 
-        let excluded_files_editor = cx.new_view(|cx| {
-            let mut editor = Editor::single_line(cx);
+        let excluded_files_editor = cx.new(|cx| {
+            let mut editor = Editor::single_line(window, cx);
             editor.set_placeholder_text("Exclude: vendor/*, *.lock", cx);
 
             editor
@@ -745,12 +777,12 @@ impl ProjectSearchView {
         );
 
         let focus_handle = cx.focus_handle();
-        subscriptions.push(cx.on_focus_in(&focus_handle, |this, cx| {
-            if this.focus_handle.is_focused(cx) {
+        subscriptions.push(cx.on_focus_in(&focus_handle, window, |this, window, cx| {
+            if this.focus_handle.is_focused(window) {
                 if this.has_matches() {
-                    this.results_editor.focus_handle(cx).focus(cx);
+                    this.results_editor.focus_handle(cx).focus(window);
                 } else {
-                    this.query_editor.focus_handle(cx).focus(cx);
+                    this.query_editor.focus_handle(cx).focus(window);
                 }
             }
         }));
@@ -774,30 +806,31 @@ impl ProjectSearchView {
             included_opened_only: false,
             _subscriptions: subscriptions,
         };
-        this.model_changed(cx);
+        this.model_changed(window, cx);
         this
     }
 
     pub fn new_search_in_directory(
         workspace: &mut Workspace,
         dir_path: &Path,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
         let Some(filter_str) = dir_path.to_str() else {
             return;
         };
 
-        let weak_workspace = cx.view().downgrade();
+        let weak_workspace = cx.model().downgrade();
 
-        let model = cx.new_model(|cx| ProjectSearch::new(workspace.project().clone(), cx));
-        let search = cx.new_view(|cx| ProjectSearchView::new(weak_workspace, model, cx, None));
-        workspace.add_item_to_active_pane(Box::new(search.clone()), None, true, cx);
+        let model = cx.new(|cx| ProjectSearch::new(workspace.project().clone(), cx));
+        let search = cx.new(|cx| ProjectSearchView::new(weak_workspace, model, window, cx, None));
+        workspace.add_item_to_active_pane(Box::new(search.clone()), None, true, window, cx);
         search.update(cx, |search, cx| {
             search
                 .included_files_editor
-                .update(cx, |editor, cx| editor.set_text(filter_str, cx));
+                .update(cx, |editor, cx| editor.set_text(filter_str, window, cx));
             search.filters_enabled = true;
-            search.focus_query_editor(cx)
+            search.focus_query_editor(window, cx)
         });
     }
 
@@ -806,7 +839,8 @@ impl ProjectSearchView {
     pub fn deploy_search(
         workspace: &mut Workspace,
         action: &workspace::DeploySearch,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
         let existing = workspace
             .active_pane()
@@ -814,10 +848,15 @@ impl ProjectSearchView {
             .items()
             .find_map(|item| item.downcast::<ProjectSearchView>());
 
-        Self::existing_or_new_search(workspace, existing, action, cx);
+        Self::existing_or_new_search(workspace, existing, action, window, cx);
     }
 
-    fn search_in_new(workspace: &mut Workspace, _: &SearchInNew, cx: &mut ViewContext<Workspace>) {
+    fn search_in_new(
+        workspace: &mut Workspace,
+        _: &SearchInNew,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) {
         if let Some(search_view) = workspace
             .active_item(cx)
             .and_then(|item| item.downcast::<ProjectSearchView>())
@@ -827,7 +866,7 @@ impl ProjectSearchView {
                 if new_query.is_some() {
                     if let Some(old_query) = search_view.model.read(cx).active_query.clone() {
                         search_view.query_editor.update(cx, |editor, cx| {
-                            editor.set_text(old_query.as_str(), cx);
+                            editor.set_text(old_query.as_str(), window, cx);
                         });
                         search_view.search_options = SearchOptions::from_query(&old_query);
                     }
@@ -835,18 +874,21 @@ impl ProjectSearchView {
                 new_query
             });
             if let Some(new_query) = new_query {
-                let model = cx.new_model(|cx| {
+                let model = cx.new(|cx| {
                     let mut model = ProjectSearch::new(workspace.project().clone(), cx);
                     model.search(new_query, cx);
                     model
                 });
-                let weak_workspace = cx.view().downgrade();
+                let weak_workspace = cx.model().downgrade();
                 workspace.add_item_to_active_pane(
                     Box::new(
-                        cx.new_view(|cx| ProjectSearchView::new(weak_workspace, model, cx, None)),
+                        cx.new(|cx| {
+                            ProjectSearchView::new(weak_workspace, model, window, cx, None)
+                        }),
                     ),
                     None,
                     true,
+                    window,
                     cx,
                 );
             }
@@ -857,16 +899,18 @@ impl ProjectSearchView {
     fn new_search(
         workspace: &mut Workspace,
         _: &workspace::NewSearch,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
-        Self::existing_or_new_search(workspace, None, &DeploySearch::find(), cx)
+        Self::existing_or_new_search(workspace, None, &DeploySearch::find(), window, cx)
     }
 
     fn existing_or_new_search(
         workspace: &mut Workspace,
-        existing: Option<View<ProjectSearchView>>,
+        existing: Option<Entity<ProjectSearchView>>,
         action: &workspace::DeploySearch,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
         let query = workspace.active_item(cx).and_then(|item| {
             if let Some(buffer_search_query) = buffer_search_query(workspace, item.as_ref(), cx) {
@@ -874,7 +918,7 @@ impl ProjectSearchView {
             }
 
             let editor = item.act_as::<Editor>(cx)?;
-            let query = editor.query_suggestion(cx);
+            let query = editor.query_suggestion(window, cx);
             if query.is_empty() {
                 None
             } else {
@@ -883,7 +927,7 @@ impl ProjectSearchView {
         });
 
         let search = if let Some(existing) = existing {
-            workspace.activate_item(&existing, true, true, cx);
+            workspace.activate_item(&existing, true, true, window, cx);
             existing
         } else {
             let settings = cx
@@ -893,36 +937,43 @@ impl ProjectSearchView {
 
             let settings = settings.cloned();
 
-            let weak_workspace = cx.view().downgrade();
+            let weak_workspace = cx.model().downgrade();
 
-            let model = cx.new_model(|cx| ProjectSearch::new(workspace.project().clone(), cx));
-            let view =
-                cx.new_view(|cx| ProjectSearchView::new(weak_workspace, model, cx, settings));
+            let project_search = cx.new(|cx| ProjectSearch::new(workspace.project().clone(), cx));
+            let project_search_view = cx.new(|cx| {
+                ProjectSearchView::new(weak_workspace, project_search, window, cx, settings)
+            });
 
-            workspace.add_item_to_active_pane(Box::new(view.clone()), None, true, cx);
-            view
+            workspace.add_item_to_active_pane(
+                Box::new(project_search_view.clone()),
+                None,
+                true,
+                window,
+                cx,
+            );
+            project_search_view
         };
 
         search.update(cx, |search, cx| {
             search.replace_enabled = action.replace_enabled;
             if let Some(query) = query {
-                search.set_query(&query, cx);
+                search.set_query(&query, window, cx);
             }
-            search.focus_query_editor(cx)
+            search.focus_query_editor(window, cx)
         });
     }
 
-    fn search(&mut self, cx: &mut ViewContext<Self>) {
+    fn search(&mut self, cx: &mut Context<Self>) {
         if let Some(query) = self.build_search_query(cx) {
             self.model.update(cx, |model, cx| model.search(query, cx));
         }
     }
 
-    pub fn search_query_text(&self, cx: &WindowContext) -> String {
+    pub fn search_query_text(&self, cx: &App) -> String {
         self.query_editor.read(cx).text(cx)
     }
 
-    fn build_search_query(&mut self, cx: &mut ViewContext<Self>) -> Option<SearchQuery> {
+    fn build_search_query(&mut self, cx: &mut Context<Self>) -> Option<SearchQuery> {
         // Do not bail early in this function, as we want to fill out `self.panels_with_errors`.
         let text = self.query_editor.read(cx).text(cx);
         let open_buffers = if self.included_opened_only {
@@ -1030,7 +1081,7 @@ impl ProjectSearchView {
         query
     }
 
-    fn open_buffers(&self, cx: &mut ViewContext<Self>) -> Vec<Model<Buffer>> {
+    fn open_buffers(&self, cx: &mut Context<Self>) -> Vec<Entity<Buffer>> {
         let mut buffers = Vec::new();
         self.workspace
             .update(cx, |workspace, cx| {
@@ -1054,7 +1105,7 @@ impl ProjectSearchView {
         Ok(PathMatcher::new(&queries)?)
     }
 
-    fn select_match(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
+    fn select_match(&mut self, direction: Direction, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(index) = self.active_match_index {
             let match_ranges = self.model.read(cx).match_ranges.clone();
 
@@ -1062,35 +1113,35 @@ impl ProjectSearchView {
                 && ((direction == Direction::Next && index + 1 >= match_ranges.len())
                     || (direction == Direction::Prev && index == 0))
             {
-                crate::show_no_more_matches(cx);
+                crate::show_no_more_matches(window, cx);
                 return;
             }
 
             let new_index = self.results_editor.update(cx, |editor, cx| {
-                editor.match_index_for_direction(&match_ranges, index, direction, 1, cx)
+                editor.match_index_for_direction(&match_ranges, index, direction, 1, window, cx)
             });
 
             let range_to_select = match_ranges[new_index].clone();
             self.results_editor.update(cx, |editor, cx| {
                 let range_to_select = editor.range_for_match(&range_to_select);
                 editor.unfold_ranges(&[range_to_select.clone()], false, true, cx);
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.select_ranges([range_to_select])
                 });
             });
         }
     }
 
-    fn focus_query_editor(&mut self, cx: &mut ViewContext<Self>) {
+    fn focus_query_editor(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.query_editor.update(cx, |query_editor, cx| {
-            query_editor.select_all(&SelectAll, cx);
+            query_editor.select_all(&SelectAll, window, cx);
         });
         let editor_handle = self.query_editor.focus_handle(cx);
-        cx.focus(&editor_handle);
+        window.focus(&editor_handle);
     }
 
-    fn set_query(&mut self, query: &str, cx: &mut ViewContext<Self>) {
-        self.set_search_editor(SearchInputKind::Query, query, cx);
+    fn set_query(&mut self, query: &str, window: &mut Window, cx: &mut Context<Self>) {
+        self.set_search_editor(SearchInputKind::Query, query, window, cx);
         if EditorSettings::get_global(cx).use_smartcase_search
             && !query.is_empty()
             && self.search_options.contains(SearchOptions::CASE_SENSITIVE)
@@ -1100,26 +1151,34 @@ impl ProjectSearchView {
         }
     }
 
-    fn set_search_editor(&mut self, kind: SearchInputKind, text: &str, cx: &mut ViewContext<Self>) {
+    fn set_search_editor(
+        &mut self,
+        kind: SearchInputKind,
+        text: &str,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let editor = match kind {
             SearchInputKind::Query => &self.query_editor,
             SearchInputKind::Include => &self.included_files_editor,
 
             SearchInputKind::Exclude => &self.excluded_files_editor,
         };
-        editor.update(cx, |included_editor, cx| included_editor.set_text(text, cx));
+        editor.update(cx, |included_editor, cx| {
+            included_editor.set_text(text, window, cx)
+        });
     }
 
-    fn focus_results_editor(&mut self, cx: &mut ViewContext<Self>) {
+    fn focus_results_editor(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.query_editor.update(cx, |query_editor, cx| {
             let cursor = query_editor.selections.newest_anchor().head();
-            query_editor.change_selections(None, cx, |s| s.select_ranges([cursor..cursor]));
+            query_editor.change_selections(None, window, cx, |s| s.select_ranges([cursor..cursor]));
         });
         let results_handle = self.results_editor.focus_handle(cx);
-        cx.focus(&results_handle);
+        window.focus(&results_handle);
     }
 
-    fn model_changed(&mut self, cx: &mut ViewContext<Self>) {
+    fn model_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let match_ranges = self.model.read(cx).match_ranges.clone();
         if match_ranges.is_empty() {
             self.active_match_index = None;
@@ -1133,10 +1192,10 @@ impl ProjectSearchView {
                     let range_to_select = match_ranges
                         .first()
                         .map(|range| editor.range_for_match(range));
-                    editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                    editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                         s.select_ranges(range_to_select)
                     });
-                    editor.scroll(Point::default(), Some(Axis::Vertical), cx);
+                    editor.scroll(Point::default(), Some(Axis::Vertical), window, cx);
                 }
                 editor.highlight_background::<Self>(
                     &match_ranges,
@@ -1144,8 +1203,8 @@ impl ProjectSearchView {
                     cx,
                 );
             });
-            if is_new_search && self.query_editor.focus_handle(cx).is_focused(cx) {
-                self.focus_results_editor(cx);
+            if is_new_search && self.query_editor.focus_handle(cx).is_focused(window) {
+                self.focus_results_editor(window, cx);
             }
         }
 
@@ -1153,7 +1212,7 @@ impl ProjectSearchView {
         cx.notify();
     }
 
-    fn update_match_index(&mut self, cx: &mut ViewContext<Self>) {
+    fn update_match_index(&mut self, cx: &mut Context<Self>) {
         let results_editor = self.results_editor.read(cx);
         let new_index = active_match_index(
             &self.model.read(cx).match_ranges,
@@ -1170,7 +1229,7 @@ impl ProjectSearchView {
         self.active_match_index.is_some()
     }
 
-    fn landing_text_minor(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn landing_text_minor(&self, window: &mut Window) -> impl IntoElement {
         let focus_handle = self.focus_handle.clone();
         v_flex()
             .gap_1()
@@ -1184,24 +1243,42 @@ impl ProjectSearchView {
                     .icon(IconName::Filter)
                     .icon_position(IconPosition::Start)
                     .icon_size(IconSize::Small)
-                    .key_binding(KeyBinding::for_action_in(&ToggleFilters, &focus_handle, cx))
-                    .on_click(|_event, cx| cx.dispatch_action(ToggleFilters.boxed_clone())),
+                    .key_binding(KeyBinding::for_action_in(
+                        &ToggleFilters,
+                        &focus_handle,
+                        window,
+                    ))
+                    .on_click(|_event, window, cx| {
+                        window.dispatch_action(ToggleFilters.boxed_clone(), cx)
+                    }),
             )
             .child(
                 Button::new("find-replace", "Find and replace")
                     .icon(IconName::Replace)
                     .icon_position(IconPosition::Start)
                     .icon_size(IconSize::Small)
-                    .key_binding(KeyBinding::for_action_in(&ToggleReplace, &focus_handle, cx))
-                    .on_click(|_event, cx| cx.dispatch_action(ToggleReplace.boxed_clone())),
+                    .key_binding(KeyBinding::for_action_in(
+                        &ToggleReplace,
+                        &focus_handle,
+                        window,
+                    ))
+                    .on_click(|_event, window, cx| {
+                        window.dispatch_action(ToggleReplace.boxed_clone(), cx)
+                    }),
             )
             .child(
                 Button::new("regex", "Match with regex")
                     .icon(IconName::Regex)
                     .icon_position(IconPosition::Start)
                     .icon_size(IconSize::Small)
-                    .key_binding(KeyBinding::for_action_in(&ToggleRegex, &focus_handle, cx))
-                    .on_click(|_event, cx| cx.dispatch_action(ToggleRegex.boxed_clone())),
+                    .key_binding(KeyBinding::for_action_in(
+                        &ToggleRegex,
+                        &focus_handle,
+                        window,
+                    ))
+                    .on_click(|_event, window, cx| {
+                        window.dispatch_action(ToggleRegex.boxed_clone(), cx)
+                    }),
             )
             .child(
                 Button::new("match-case", "Match case")
@@ -1211,9 +1288,11 @@ impl ProjectSearchView {
                     .key_binding(KeyBinding::for_action_in(
                         &ToggleCaseSensitive,
                         &focus_handle,
-                        cx,
+                        window,
                     ))
-                    .on_click(|_event, cx| cx.dispatch_action(ToggleCaseSensitive.boxed_clone())),
+                    .on_click(|_event, window, cx| {
+                        window.dispatch_action(ToggleCaseSensitive.boxed_clone(), cx)
+                    }),
             )
             .child(
                 Button::new("match-whole-words", "Match whole words")
@@ -1223,13 +1302,15 @@ impl ProjectSearchView {
                     .key_binding(KeyBinding::for_action_in(
                         &ToggleWholeWord,
                         &focus_handle,
-                        cx,
+                        window,
                     ))
-                    .on_click(|_event, cx| cx.dispatch_action(ToggleWholeWord.boxed_clone())),
+                    .on_click(|_event, window, cx| {
+                        window.dispatch_action(ToggleWholeWord.boxed_clone(), cx)
+                    }),
             )
     }
 
-    fn border_color_for(&self, panel: InputPanel, cx: &WindowContext) -> Hsla {
+    fn border_color_for(&self, panel: InputPanel, cx: &App) -> Hsla {
         if self.panels_with_errors.contains(&panel) {
             Color::Error.color(cx)
         } else {

crates/search/src/search.rs 🔗

@@ -1,7 +1,7 @@
 use bitflags::bitflags;
 pub use buffer_search::BufferSearchBar;
 use editor::SearchSettings;
-use gpui::{actions, Action, AppContext, FocusHandle, IntoElement};
+use gpui::{actions, Action, App, FocusHandle, IntoElement};
 use project::search::SearchQuery;
 pub use project_search::ProjectSearchView;
 use ui::{prelude::*, Tooltip};
@@ -13,7 +13,7 @@ pub mod buffer_search;
 pub mod project_search;
 pub(crate) mod search_bar;
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     menu::init();
     buffer_search::init(cx);
     project_search::init(cx);
@@ -107,7 +107,7 @@ impl SearchOptions {
         &self,
         active: bool,
         focus_handle: FocusHandle,
-        action: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
+        action: impl Fn(&gpui::ClickEvent, &mut Window, &mut App) + 'static,
     ) -> impl IntoElement {
         IconButton::new(self.label(), self.icon())
             .on_click(action)
@@ -117,20 +117,20 @@ impl SearchOptions {
             .tooltip({
                 let action = self.to_toggle_action();
                 let label = self.label();
-                move |cx| Tooltip::for_action_in(label, &*action, &focus_handle, cx)
+                move |window, cx| Tooltip::for_action_in(label, &*action, &focus_handle, window, cx)
             })
     }
 }
 
-pub(crate) fn show_no_more_matches(cx: &mut WindowContext) {
-    cx.defer(|cx| {
+pub(crate) fn show_no_more_matches(window: &mut Window, cx: &mut App) {
+    window.defer(cx, |window, cx| {
         struct NotifType();
         let notification_id = NotificationId::unique::<NotifType>();
-        let Some(workspace) = cx.window_handle().downcast::<Workspace>() else {
+        let Some(workspace) = window.window_handle().downcast::<Workspace>() else {
             return;
         };
         workspace
-            .update(cx, |workspace, cx| {
+            .update(cx, |workspace, _, cx| {
                 workspace.show_toast(
                     Toast::new(notification_id.clone(), "No more matches").autohide(),
                     cx,

crates/search/src/search_bar.rs 🔗

@@ -14,7 +14,7 @@ pub(super) fn render_nav_button(
         icon,
     )
     .shape(IconButtonShape::Square)
-    .on_click(|_, cx| cx.dispatch_action(action.boxed_clone()))
-    .tooltip(move |cx| Tooltip::for_action_in(tooltip, action, &focus_handle, cx))
+    .on_click(|_, window, cx| window.dispatch_action(action.boxed_clone(), cx))
+    .tooltip(move |window, cx| Tooltip::for_action_in(tooltip, action, &focus_handle, window, cx))
     .disabled(!active)
 }

crates/semantic_index/examples/index.rs 🔗

@@ -1,6 +1,6 @@
 use client::Client;
 use futures::channel::oneshot;
-use gpui::App;
+use gpui::Application;
 use http_client::HttpClientWithUrl;
 use language::language_settings::AllLanguageSettings;
 use project::Project;
@@ -16,7 +16,7 @@ fn main() {
 
     use clock::FakeSystemClock;
 
-    App::new().run(|cx| {
+    Application::new().run(|cx| {
         let store = SettingsStore::test(cx);
         cx.set_global(store);
         language::init(cx);

crates/semantic_index/src/embedding/cloud.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{Embedding, EmbeddingProvider, TextToEmbed};
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use client::{proto, Client};
 use collections::HashMap;
 use futures::{future::BoxFuture, FutureExt};

crates/semantic_index/src/embedding_index.rs 🔗

@@ -10,7 +10,7 @@ use fs::Fs;
 use fs::MTime;
 use futures::{stream::StreamExt, FutureExt as _};
 use futures_batch::ChunksTimeoutStreamExt;
-use gpui::{AppContext, Model, Task};
+use gpui::{App, Entity, Task};
 use heed::types::{SerdeBincode, Str};
 use language::LanguageRegistry;
 use log;
@@ -22,7 +22,7 @@ use util::ResultExt;
 use worktree::Snapshot;
 
 pub struct EmbeddingIndex {
-    worktree: Model<Worktree>,
+    worktree: Entity<Worktree>,
     db_connection: heed::Env,
     db: heed::Database<Str, SerdeBincode<EmbeddedFile>>,
     fs: Arc<dyn Fs>,
@@ -33,7 +33,7 @@ pub struct EmbeddingIndex {
 
 impl EmbeddingIndex {
     pub fn new(
-        worktree: Model<Worktree>,
+        worktree: Entity<Worktree>,
         fs: Arc<dyn Fs>,
         db_connection: heed::Env,
         embedding_db: heed::Database<Str, SerdeBincode<EmbeddedFile>>,
@@ -56,10 +56,7 @@ impl EmbeddingIndex {
         &self.db
     }
 
-    pub fn index_entries_changed_on_disk(
-        &self,
-        cx: &AppContext,
-    ) -> impl Future<Output = Result<()>> {
+    pub fn index_entries_changed_on_disk(&self, cx: &App) -> impl Future<Output = Result<()>> {
         if !cx.is_staff() {
             return async move { Ok(()) }.boxed();
         }
@@ -80,7 +77,7 @@ impl EmbeddingIndex {
     pub fn index_updated_entries(
         &self,
         updated_entries: UpdatedEntriesSet,
-        cx: &AppContext,
+        cx: &App,
     ) -> impl Future<Output = Result<()>> {
         if !cx.is_staff() {
             return async move { Ok(()) }.boxed();
@@ -99,7 +96,7 @@ impl EmbeddingIndex {
         .boxed()
     }
 
-    fn scan_entries(&self, worktree: Snapshot, cx: &AppContext) -> ScanEntries {
+    fn scan_entries(&self, worktree: Snapshot, cx: &App) -> ScanEntries {
         let (updated_entries_tx, updated_entries_rx) = channel::bounded(512);
         let (deleted_entry_ranges_tx, deleted_entry_ranges_rx) = channel::bounded(128);
         let db_connection = self.db_connection.clone();
@@ -183,7 +180,7 @@ impl EmbeddingIndex {
         &self,
         worktree: Snapshot,
         updated_entries: UpdatedEntriesSet,
-        cx: &AppContext,
+        cx: &App,
     ) -> ScanEntries {
         let (updated_entries_tx, updated_entries_rx) = channel::bounded(512);
         let (deleted_entry_ranges_tx, deleted_entry_ranges_rx) = channel::bounded(128);
@@ -227,7 +224,7 @@ impl EmbeddingIndex {
         &self,
         worktree_abs_path: Arc<Path>,
         entries: channel::Receiver<(Entry, IndexingEntryHandle)>,
-        cx: &AppContext,
+        cx: &App,
     ) -> ChunkFiles {
         let language_registry = self.language_registry.clone();
         let fs = self.fs.clone();
@@ -277,7 +274,7 @@ impl EmbeddingIndex {
     pub fn embed_files(
         embedding_provider: Arc<dyn EmbeddingProvider>,
         chunked_files: channel::Receiver<ChunkedFile>,
-        cx: &AppContext,
+        cx: &App,
     ) -> EmbedFiles {
         let embedding_provider = embedding_provider.clone();
         let (embedded_files_tx, embedded_files_rx) = channel::bounded(512);
@@ -359,7 +356,7 @@ impl EmbeddingIndex {
         &self,
         deleted_entry_ranges: channel::Receiver<(Bound<String>, Bound<String>)>,
         embedded_files: channel::Receiver<(EmbeddedFile, IndexingEntryHandle)>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Result<()>> {
         let db_connection = self.db_connection.clone();
         let db = self.db;
@@ -397,7 +394,7 @@ impl EmbeddingIndex {
         })
     }
 
-    pub fn paths(&self, cx: &AppContext) -> Task<Result<Vec<Arc<Path>>>> {
+    pub fn paths(&self, cx: &App) -> Task<Result<Vec<Arc<Path>>>> {
         let connection = self.db_connection.clone();
         let db = self.db;
         cx.background_executor().spawn(async move {
@@ -413,11 +410,7 @@ impl EmbeddingIndex {
         })
     }
 
-    pub fn chunks_for_path(
-        &self,
-        path: Arc<Path>,
-        cx: &AppContext,
-    ) -> Task<Result<Vec<EmbeddedChunk>>> {
+    pub fn chunks_for_path(&self, path: Arc<Path>, cx: &App) -> Task<Result<Vec<EmbeddedChunk>>> {
         let connection = self.db_connection.clone();
         let db = self.db;
         cx.background_executor().spawn(async move {

crates/semantic_index/src/project_index.rs 🔗

@@ -3,13 +3,11 @@ use crate::{
     summary_index::FileSummary,
     worktree_index::{WorktreeIndex, WorktreeIndexHandle},
 };
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use collections::HashMap;
 use fs::Fs;
 use futures::FutureExt;
-use gpui::{
-    AppContext, Entity, EntityId, EventEmitter, Model, ModelContext, Subscription, Task, WeakModel,
-};
+use gpui::{App, Context, Entity, EntityId, EventEmitter, Subscription, Task, WeakEntity};
 use language::LanguageRegistry;
 use log;
 use project::{Project, Worktree, WorktreeId};
@@ -27,7 +25,7 @@ use util::ResultExt;
 
 #[derive(Debug)]
 pub struct SearchResult {
-    pub worktree: Model<Worktree>,
+    pub worktree: Entity<Worktree>,
     pub path: Arc<Path>,
     pub range: Range<usize>,
     pub score: f32,
@@ -60,7 +58,7 @@ pub enum Status {
 
 pub struct ProjectIndex {
     db_connection: heed::Env,
-    project: WeakModel<Project>,
+    project: WeakEntity<Project>,
     worktree_indices: HashMap<EntityId, WorktreeIndexHandle>,
     language_registry: Arc<LanguageRegistry>,
     fs: Arc<dyn Fs>,
@@ -73,10 +71,10 @@ pub struct ProjectIndex {
 
 impl ProjectIndex {
     pub fn new(
-        project: Model<Project>,
+        project: Entity<Project>,
         db_connection: heed::Env,
         embedding_provider: Arc<dyn EmbeddingProvider>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Self {
         let language_registry = project.read(cx).languages().clone();
         let fs = project.read(cx).fs().clone();
@@ -110,7 +108,7 @@ impl ProjectIndex {
         self.last_status
     }
 
-    pub fn project(&self) -> WeakModel<Project> {
+    pub fn project(&self) -> WeakEntity<Project> {
         self.project.clone()
     }
 
@@ -120,9 +118,9 @@ impl ProjectIndex {
 
     fn handle_project_event(
         &mut self,
-        _: Model<Project>,
+        _: Entity<Project>,
         event: &project::Event,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match event {
             project::Event::WorktreeAdded(_) | project::Event::WorktreeRemoved(_) => {
@@ -132,7 +130,7 @@ impl ProjectIndex {
         }
     }
 
-    fn update_worktree_indices(&mut self, cx: &mut ModelContext<Self>) {
+    fn update_worktree_indices(&mut self, cx: &mut Context<Self>) {
         let Some(project) = self.project.upgrade() else {
             return;
         };
@@ -198,7 +196,7 @@ impl ProjectIndex {
         self.update_status(cx);
     }
 
-    fn update_status(&mut self, cx: &mut ModelContext<Self>) {
+    fn update_status(&mut self, cx: &mut Context<Self>) {
         let mut indexing_count = 0;
         let mut any_loading = false;
 
@@ -232,7 +230,7 @@ impl ProjectIndex {
         &self,
         queries: Vec<String>,
         limit: usize,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Result<Vec<SearchResult>>> {
         let (chunks_tx, chunks_rx) = channel::bounded(1024);
         let mut worktree_scan_tasks = Vec::new();
@@ -372,7 +370,7 @@ impl ProjectIndex {
     }
 
     #[cfg(test)]
-    pub fn path_count(&self, cx: &AppContext) -> Result<u64> {
+    pub fn path_count(&self, cx: &App) -> Result<u64> {
         let mut result = 0;
         for worktree_index in self.worktree_indices.values() {
             if let WorktreeIndexHandle::Loaded { index, .. } = worktree_index {
@@ -385,8 +383,8 @@ impl ProjectIndex {
     pub(crate) fn worktree_index(
         &self,
         worktree_id: WorktreeId,
-        cx: &AppContext,
-    ) -> Option<Model<WorktreeIndex>> {
+        cx: &App,
+    ) -> Option<Entity<WorktreeIndex>> {
         for index in self.worktree_indices.values() {
             if let WorktreeIndexHandle::Loaded { index, .. } = index {
                 if index.read(cx).worktree().read(cx).id() == worktree_id {
@@ -397,7 +395,7 @@ impl ProjectIndex {
         None
     }
 
-    pub(crate) fn worktree_indices(&self, cx: &AppContext) -> Vec<Model<WorktreeIndex>> {
+    pub(crate) fn worktree_indices(&self, cx: &App) -> Vec<Entity<WorktreeIndex>> {
         let mut result = self
             .worktree_indices
             .values()
@@ -413,7 +411,7 @@ impl ProjectIndex {
         result
     }
 
-    pub fn all_summaries(&self, cx: &AppContext) -> Task<Result<Vec<FileSummary>>> {
+    pub fn all_summaries(&self, cx: &App) -> Task<Result<Vec<FileSummary>>> {
         let (summaries_tx, summaries_rx) = channel::bounded(1024);
         let mut worktree_scan_tasks = Vec::new();
         for worktree_index in self.worktree_indices.values() {
@@ -503,7 +501,7 @@ impl ProjectIndex {
     }
 
     /// Empty out the backlogs of all the worktrees in the project
-    pub fn flush_summary_backlogs(&self, cx: &AppContext) -> impl Future<Output = ()> {
+    pub fn flush_summary_backlogs(&self, cx: &App) -> impl Future<Output = ()> {
         let flush_start = std::time::Instant::now();
 
         futures::future::join_all(self.worktree_indices.values().map(|worktree_index| {
@@ -540,7 +538,7 @@ impl ProjectIndex {
         })
     }
 
-    pub fn remaining_summaries(&self, cx: &mut ModelContext<Self>) -> usize {
+    pub fn remaining_summaries(&self, cx: &mut Context<Self>) -> usize {
         self.worktree_indices(cx)
             .iter()
             .map(|index| index.read(cx).summary_index().backlog_len())

crates/semantic_index/src/project_index_debug_view.rs 🔗

@@ -1,8 +1,8 @@
 use crate::ProjectIndex;
 use gpui::{
-    canvas, div, list, uniform_list, AnyElement, AppContext, CursorStyle, EventEmitter,
-    FocusHandle, FocusableView, IntoElement, ListOffset, ListState, Model, MouseMoveEvent, Render,
-    UniformListScrollHandle, View,
+    canvas, div, list, uniform_list, AnyElement, App, CursorStyle, Entity, EventEmitter,
+    FocusHandle, Focusable, IntoElement, ListOffset, ListState, MouseMoveEvent, Render,
+    UniformListScrollHandle,
 };
 use project::WorktreeId;
 use settings::Settings;
@@ -12,7 +12,7 @@ use ui::prelude::*;
 use workspace::item::Item;
 
 pub struct ProjectIndexDebugView {
-    index: Model<ProjectIndex>,
+    index: Entity<ProjectIndex>,
     rows: Vec<Row>,
     selected_path: Option<PathState>,
     hovered_row_ix: Option<usize>,
@@ -33,23 +33,25 @@ enum Row {
 }
 
 impl ProjectIndexDebugView {
-    pub fn new(index: Model<ProjectIndex>, cx: &mut ViewContext<Self>) -> Self {
+    pub fn new(index: Entity<ProjectIndex>, window: &mut Window, cx: &mut Context<Self>) -> Self {
         let mut this = Self {
             rows: Vec::new(),
             list_scroll_handle: UniformListScrollHandle::new(),
             selected_path: None,
             hovered_row_ix: None,
             focus_handle: cx.focus_handle(),
-            _subscription: cx.subscribe(&index, |this, _, _, cx| this.update_rows(cx)),
+            _subscription: cx.subscribe_in(&index, window, |this, _, _, window, cx| {
+                this.update_rows(window, cx)
+            }),
             index,
         };
-        this.update_rows(cx);
+        this.update_rows(window, cx);
         this
     }
 
-    fn update_rows(&mut self, cx: &mut ViewContext<Self>) {
+    fn update_rows(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let worktree_indices = self.index.read(cx).worktree_indices(cx);
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let mut rows = Vec::new();
 
             for index in worktree_indices {
@@ -83,7 +85,8 @@ impl ProjectIndexDebugView {
         &mut self,
         worktree_id: WorktreeId,
         file_path: Arc<Path>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<()> {
         let project_index = self.index.read(cx);
         let fs = project_index.fs().clone();
@@ -93,7 +96,7 @@ impl ProjectIndexDebugView {
             .embedding_index()
             .chunks_for_path(file_path.clone(), cx);
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let chunks = chunks.await?;
             let content = fs.load(&root_path.join(&file_path)).await?;
             let chunks = chunks
@@ -112,14 +115,14 @@ impl ProjectIndexDebugView {
                 .collect::<Vec<_>>();
 
             this.update(&mut cx, |this, cx| {
-                let view = cx.view().downgrade();
+                let view = cx.model().downgrade();
                 this.selected_path = Some(PathState {
                     path: file_path,
                     list_state: ListState::new(
                         chunks.len(),
                         gpui::ListAlignment::Top,
                         px(100.),
-                        move |ix, cx| {
+                        move |ix, _, cx| {
                             if let Some(view) = view.upgrade() {
                                 view.update(cx, |view, cx| view.render_chunk(ix, cx))
                             } else {
@@ -136,7 +139,7 @@ impl ProjectIndexDebugView {
         None
     }
 
-    fn render_chunk(&mut self, ix: usize, cx: &mut ViewContext<Self>) -> AnyElement {
+    fn render_chunk(&mut self, ix: usize, cx: &mut Context<Self>) -> AnyElement {
         let buffer_font = ThemeSettings::get_global(cx).buffer_font.clone();
         let Some(state) = &self.selected_path else {
             return div().into_any();
@@ -163,16 +166,16 @@ impl ProjectIndexDebugView {
                             .child(
                                 Button::new(("prev", ix), "prev")
                                     .disabled(ix == 0)
-                                    .on_click(cx.listener(move |this, _, _| {
+                                    .on_click(cx.listener(move |this, _, _, _| {
                                         this.scroll_to_chunk(ix.saturating_sub(1))
                                     })),
                             )
                             .child(
                                 Button::new(("next", ix), "next")
                                     .disabled(ix + 1 == state.chunks.len())
-                                    .on_click(
-                                        cx.listener(move |this, _, _| this.scroll_to_chunk(ix + 1)),
-                                    ),
+                                    .on_click(cx.listener(move |this, _, _, _| {
+                                        this.scroll_to_chunk(ix + 1)
+                                    })),
                             ),
                     ),
             )
@@ -196,7 +199,7 @@ impl ProjectIndexDebugView {
 }
 
 impl Render for ProjectIndexDebugView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         if let Some(selected_path) = self.selected_path.as_ref() {
             v_flex()
                 .child(
@@ -211,7 +214,7 @@ impl Render for ProjectIndexDebugView {
                         .border_b_1()
                         .border_color(cx.theme().colors().border)
                         .cursor(CursorStyle::PointingHand)
-                        .on_click(cx.listener(|this, _, cx| {
+                        .on_click(cx.listener(|this, _, _, cx| {
                             this.selected_path.take();
                             cx.notify();
                         })),
@@ -221,10 +224,10 @@ impl Render for ProjectIndexDebugView {
                 .into_any_element()
         } else {
             let mut list = uniform_list(
-                cx.view().clone(),
+                cx.model().clone(),
                 "ProjectIndexDebugView",
                 self.rows.len(),
-                move |this, range, cx| {
+                move |this, range, _, cx| {
                     this.rows[range]
                         .iter()
                         .enumerate()
@@ -236,18 +239,25 @@ impl Render for ProjectIndexDebugView {
                                 .id(ix)
                                 .pl_8()
                                 .child(Label::new(file_path.to_string_lossy().to_string()))
-                                .on_mouse_move(cx.listener(move |this, _: &MouseMoveEvent, cx| {
-                                    if this.hovered_row_ix != Some(ix) {
-                                        this.hovered_row_ix = Some(ix);
-                                        cx.notify();
-                                    }
-                                }))
+                                .on_mouse_move(cx.listener(
+                                    move |this, _: &MouseMoveEvent, _, cx| {
+                                        if this.hovered_row_ix != Some(ix) {
+                                            this.hovered_row_ix = Some(ix);
+                                            cx.notify();
+                                        }
+                                    },
+                                ))
                                 .cursor(CursorStyle::PointingHand)
                                 .on_click(cx.listener({
                                     let worktree_id = *worktree_id;
                                     let file_path = file_path.clone();
-                                    move |this, _, cx| {
-                                        this.handle_path_click(worktree_id, file_path.clone(), cx);
+                                    move |this, _, window, cx| {
+                                        this.handle_path_click(
+                                            worktree_id,
+                                            file_path.clone(),
+                                            window,
+                                            cx,
+                                        );
                                     }
                                 })),
                         })
@@ -260,12 +270,12 @@ impl Render for ProjectIndexDebugView {
             .into_any_element();
 
             canvas(
-                move |bounds, cx| {
-                    list.prepaint_as_root(bounds.origin, bounds.size.into(), cx);
+                move |bounds, window, cx| {
+                    list.prepaint_as_root(bounds.origin, bounds.size.into(), window, cx);
                     list
                 },
-                |_, mut list, cx| {
-                    list.paint(cx);
+                |_, mut list, window, cx| {
+                    list.paint(window, cx);
                 },
             )
             .size_full()
@@ -279,24 +289,25 @@ impl EventEmitter<()> for ProjectIndexDebugView {}
 impl Item for ProjectIndexDebugView {
     type Event = ();
 
-    fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
+    fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option<SharedString> {
         Some("Project Index (Debug)".into())
     }
 
     fn clone_on_split(
         &self,
         _: Option<workspace::WorkspaceId>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<View<Self>>
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Entity<Self>>
     where
         Self: Sized,
     {
-        Some(cx.new_view(|cx| Self::new(self.index.clone(), cx)))
+        Some(cx.new(|cx| Self::new(self.index.clone(), window, cx)))
     }
 }
 
-impl FocusableView for ProjectIndexDebugView {
-    fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
+impl Focusable for ProjectIndexDebugView {
+    fn focus_handle(&self, _: &App) -> gpui::FocusHandle {
         self.focus_handle.clone()
     }
 }

crates/semantic_index/src/semantic_index.rs 🔗

@@ -11,7 +11,9 @@ mod worktree_index;
 use anyhow::{Context as _, Result};
 use collections::HashMap;
 use fs::Fs;
-use gpui::{AppContext, AsyncAppContext, BorrowAppContext, Context, Global, Model, WeakModel};
+use gpui::{
+    App, AppContext as _, AsyncAppContext, BorrowAppContext, Context, Entity, Global, WeakEntity,
+};
 use language::LineEnding;
 use project::{Project, Worktree};
 use std::{
@@ -19,7 +21,6 @@ use std::{
     path::{Path, PathBuf},
     sync::Arc,
 };
-use ui::ViewContext;
 use util::ResultExt as _;
 use workspace::Workspace;
 
@@ -31,7 +32,7 @@ pub use summary_index::FileSummary;
 pub struct SemanticDb {
     embedding_provider: Arc<dyn EmbeddingProvider>,
     db_connection: Option<heed::Env>,
-    project_indices: HashMap<WeakModel<Project>, Model<ProjectIndex>>,
+    project_indices: HashMap<WeakEntity<Project>, Entity<ProjectIndex>>,
 }
 
 impl Global for SemanticDb {}
@@ -57,8 +58,8 @@ impl SemanticDb {
             .context("opening database connection")?;
 
         cx.update(|cx| {
-            cx.observe_new_views(
-                |workspace: &mut Workspace, cx: &mut ViewContext<Workspace>| {
+            cx.observe_new(
+                |workspace: &mut Workspace, _window, cx: &mut Context<Workspace>| {
                     let project = workspace.project().clone();
 
                     if cx.has_global::<SemanticDb>() {
@@ -108,7 +109,7 @@ impl SemanticDb {
                 .then_with(|| a.range.start.cmp(&b.range.start))
         });
 
-        let mut last_loaded_file: Option<(Model<Worktree>, Arc<Path>, PathBuf, String)> = None;
+        let mut last_loaded_file: Option<(Entity<Worktree>, Arc<Path>, PathBuf, String)> = None;
         let mut loaded_results = Vec::<LoadedSearchResult>::new();
         for result in results {
             let full_path;
@@ -208,16 +209,16 @@ impl SemanticDb {
 
     pub fn project_index(
         &mut self,
-        project: Model<Project>,
-        _cx: &mut AppContext,
-    ) -> Option<Model<ProjectIndex>> {
+        project: Entity<Project>,
+        _cx: &mut App,
+    ) -> Option<Entity<ProjectIndex>> {
         self.project_indices.get(&project.downgrade()).cloned()
     }
 
     pub fn remaining_summaries(
         &self,
-        project: &WeakModel<Project>,
-        cx: &mut AppContext,
+        project: &WeakEntity<Project>,
+        cx: &mut App,
     ) -> Option<usize> {
         self.project_indices.get(project).map(|project_index| {
             project_index.update(cx, |project_index, cx| {
@@ -228,10 +229,10 @@ impl SemanticDb {
 
     pub fn create_project_index(
         &mut self,
-        project: Model<Project>,
-        cx: &mut AppContext,
-    ) -> Model<ProjectIndex> {
-        let project_index = cx.new_model(|cx| {
+        project: Entity<Project>,
+        cx: &mut App,
+    ) -> Entity<ProjectIndex> {
+        let project_index = cx.new(|cx| {
             ProjectIndex::new(
                 project.clone(),
                 self.db_connection.clone().unwrap(),

crates/semantic_index/src/summary_index.rs 🔗

@@ -3,7 +3,7 @@ use arrayvec::ArrayString;
 use fs::{Fs, MTime};
 use futures::{stream::StreamExt, TryFutureExt};
 use futures_batch::ChunksTimeoutStreamExt;
-use gpui::{AppContext, Model, Task};
+use gpui::{App, Entity, Task};
 use heed::{
     types::{SerdeBincode, Str},
     RoTxn,
@@ -79,7 +79,7 @@ struct SummarizeFiles {
 }
 
 pub struct SummaryIndex {
-    worktree: Model<Worktree>,
+    worktree: Entity<Worktree>,
     fs: Arc<dyn Fs>,
     db_connection: heed::Env,
     file_digest_db: heed::Database<Str, SerdeBincode<FileDigest>>, // Key: file path. Val: BLAKE3 digest of its contents.
@@ -100,7 +100,7 @@ struct MightNeedSummaryFiles {
 
 impl SummaryIndex {
     pub fn new(
-        worktree: Model<Worktree>,
+        worktree: Entity<Worktree>,
         fs: Arc<dyn Fs>,
         db_connection: heed::Env,
         file_digest_db: heed::Database<Str, SerdeBincode<FileDigest>>,
@@ -129,7 +129,7 @@ impl SummaryIndex {
     pub fn index_entries_changed_on_disk(
         &self,
         is_auto_available: bool,
-        cx: &AppContext,
+        cx: &App,
     ) -> impl Future<Output = Result<()>> {
         let start = Instant::now();
         let backlogged;
@@ -192,7 +192,7 @@ impl SummaryIndex {
         &mut self,
         updated_entries: UpdatedEntriesSet,
         is_auto_available: bool,
-        cx: &AppContext,
+        cx: &App,
     ) -> impl Future<Output = Result<()>> {
         let start = Instant::now();
         let backlogged;
@@ -249,7 +249,7 @@ impl SummaryIndex {
     fn check_summary_cache(
         &self,
         might_need_summary: channel::Receiver<UnsummarizedFile>,
-        cx: &AppContext,
+        cx: &App,
     ) -> NeedsSummary {
         let db_connection = self.db_connection.clone();
         let db = self.summary_db;
@@ -286,7 +286,7 @@ impl SummaryIndex {
         }
     }
 
-    fn scan_entries(&self, worktree: Snapshot, cx: &AppContext) -> Backlogged {
+    fn scan_entries(&self, worktree: Snapshot, cx: &App) -> Backlogged {
         let (tx, rx) = channel::bounded(512);
         let db_connection = self.db_connection.clone();
         let digest_db = self.file_digest_db;
@@ -360,7 +360,7 @@ impl SummaryIndex {
         &self,
         worktree: Snapshot,
         updated_entries: UpdatedEntriesSet,
-        cx: &AppContext,
+        cx: &App,
     ) -> Backlogged {
         log::info!("Scanning for updated entries that might need summarization...");
         let (tx, rx) = channel::bounded(512);
@@ -418,7 +418,7 @@ impl SummaryIndex {
         &self,
         paths: channel::Receiver<Vec<(Arc<Path>, Option<MTime>)>>,
         worktree_abs_path: Arc<Path>,
-        cx: &AppContext,
+        cx: &App,
     ) -> MightNeedSummaryFiles {
         let fs = self.fs.clone();
         let (rx, tx) = channel::bounded(2048);
@@ -487,7 +487,7 @@ impl SummaryIndex {
     fn summarize_files(
         &self,
         unsummarized_files: channel::Receiver<UnsummarizedFile>,
-        cx: &AppContext,
+        cx: &App,
     ) -> SummarizeFiles {
         let (summarized_tx, summarized_rx) = channel::bounded(512);
         let task = cx.spawn(|cx| async move {
@@ -528,11 +528,7 @@ impl SummaryIndex {
         }
     }
 
-    fn summarize_code(
-        code: &str,
-        path: &Path,
-        cx: &AppContext,
-    ) -> impl Future<Output = Result<String>> {
+    fn summarize_code(code: &str, path: &Path, cx: &App) -> impl Future<Output = Result<String>> {
         let start = Instant::now();
         let (summary_model_id, use_cache): (LanguageModelId, bool) = (
             "Qwen/Qwen2-7B-Instruct".to_string().into(), // TODO read this from the user's settings.
@@ -603,7 +599,7 @@ impl SummaryIndex {
     fn persist_summaries(
         &self,
         summaries: channel::Receiver<SummarizedFile>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Task<Result<()>> {
         let db_connection = self.db_connection.clone();
         let digest_db = self.file_digest_db;
@@ -643,7 +639,7 @@ impl SummaryIndex {
     pub(crate) fn flush_backlog(
         &self,
         worktree_abs_path: Arc<Path>,
-        cx: &AppContext,
+        cx: &App,
     ) -> impl Future<Output = Result<()>> {
         let start = Instant::now();
         let backlogged = {

crates/semantic_index/src/worktree_index.rs 🔗

@@ -7,7 +7,7 @@ use feature_flags::{AutoCommand, FeatureFlagAppExt};
 use fs::Fs;
 use futures::future::Shared;
 use gpui::{
-    AppContext, AsyncAppContext, Context, Model, ModelContext, Subscription, Task, WeakModel,
+    App, AppContext as _, AsyncAppContext, Context, Entity, Subscription, Task, WeakEntity,
 };
 use language::LanguageRegistry;
 use log;
@@ -19,15 +19,15 @@ use util::ResultExt;
 #[derive(Clone)]
 pub enum WorktreeIndexHandle {
     Loading {
-        index: Shared<Task<Result<Model<WorktreeIndex>, Arc<anyhow::Error>>>>,
+        index: Shared<Task<Result<Entity<WorktreeIndex>, Arc<anyhow::Error>>>>,
     },
     Loaded {
-        index: Model<WorktreeIndex>,
+        index: Entity<WorktreeIndex>,
     },
 }
 
 pub struct WorktreeIndex {
-    worktree: Model<Worktree>,
+    worktree: Entity<Worktree>,
     db_connection: heed::Env,
     embedding_index: EmbeddingIndex,
     summary_index: SummaryIndex,
@@ -38,14 +38,14 @@ pub struct WorktreeIndex {
 
 impl WorktreeIndex {
     pub fn load(
-        worktree: Model<Worktree>,
+        worktree: Entity<Worktree>,
         db_connection: heed::Env,
         language_registry: Arc<LanguageRegistry>,
         fs: Arc<dyn Fs>,
         status_tx: channel::Sender<()>,
         embedding_provider: Arc<dyn EmbeddingProvider>,
-        cx: &mut AppContext,
-    ) -> Task<Result<Model<Self>>> {
+        cx: &mut App,
+    ) -> Task<Result<Entity<Self>>> {
         let worktree_for_index = worktree.clone();
         let worktree_for_summary = worktree.clone();
         let worktree_abs_path = worktree.read(cx).abs_path();
@@ -106,7 +106,7 @@ impl WorktreeIndex {
                 })
                 .await?;
 
-            cx.new_model(|cx| {
+            cx.new(|cx| {
                 Self::new(
                     worktree,
                     db_connection,
@@ -121,12 +121,12 @@ impl WorktreeIndex {
 
     #[allow(clippy::too_many_arguments)]
     pub fn new(
-        worktree: Model<Worktree>,
+        worktree: Entity<Worktree>,
         db_connection: heed::Env,
         embedding_index: EmbeddingIndex,
         summary_index: SummaryIndex,
         entry_ids_being_indexed: Arc<IndexingEntrySet>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Self {
         let (updated_entries_tx, updated_entries_rx) = channel::unbounded();
         let _subscription = cx.subscribe(&worktree, move |_this, _worktree, event, _cx| {
@@ -151,7 +151,7 @@ impl WorktreeIndex {
         self.entry_ids_being_indexed.as_ref()
     }
 
-    pub fn worktree(&self) -> &Model<Worktree> {
+    pub fn worktree(&self) -> &Entity<Worktree> {
         &self.worktree
     }
 
@@ -168,7 +168,7 @@ impl WorktreeIndex {
     }
 
     async fn index_entries(
-        this: WeakModel<Self>,
+        this: WeakEntity<Self>,
         updated_entries: channel::Receiver<UpdatedEntriesSet>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -206,7 +206,7 @@ impl WorktreeIndex {
 
     #[cfg(test)]
     pub fn path_count(&self) -> Result<u64> {
-        use anyhow::Context;
+        use anyhow::Context as _;
 
         let txn = self
             .db_connection

crates/session/src/session.rs 🔗

@@ -1,7 +1,7 @@
 use std::time::Duration;
 
 use db::kvp::KEY_VALUE_STORE;
-use gpui::{AnyWindowHandle, ModelContext, Subscription, Task, WindowId};
+use gpui::{AnyWindowHandle, Context, Subscription, Task, WindowId};
 use util::ResultExt;
 use uuid::Uuid;
 
@@ -64,7 +64,7 @@ pub struct AppSession {
 }
 
 impl AppSession {
-    pub fn new(session: Session, cx: &ModelContext<Self>) -> Self {
+    pub fn new(session: Session, cx: &Context<Self>) -> Self {
         let _subscriptions = vec![cx.on_app_quit(Self::app_will_quit)];
 
         let _serialization_task = Some(cx.spawn(|_, cx| async move {
@@ -86,7 +86,7 @@ impl AppSession {
         }
     }
 
-    fn app_will_quit(&mut self, cx: &mut ModelContext<Self>) -> Task<()> {
+    fn app_will_quit(&mut self, cx: &mut Context<Self>) -> Task<()> {
         if let Some(windows) = cx.window_stack() {
             cx.background_executor().spawn(store_window_stack(windows))
         } else {

crates/settings/src/editable_setting_control.rs 🔗

@@ -1,5 +1,5 @@
 use fs::Fs;
-use gpui::{AppContext, RenderOnce, SharedString};
+use gpui::{App, RenderOnce, SharedString};
 
 use crate::{update_settings_file, Settings};
 
@@ -15,7 +15,7 @@ pub trait EditableSettingControl: RenderOnce {
     fn name(&self) -> SharedString;
 
     /// Reads the setting value from the settings.
-    fn read(cx: &AppContext) -> Self::Value;
+    fn read(cx: &App) -> Self::Value;
 
     /// Applies the given setting file to the settings file contents.
     ///
@@ -23,11 +23,11 @@ pub trait EditableSettingControl: RenderOnce {
     fn apply(
         settings: &mut <Self::Settings as Settings>::FileContent,
         value: Self::Value,
-        cx: &AppContext,
+        cx: &App,
     );
 
     /// Writes the given setting value to the settings files.
-    fn write(value: Self::Value, cx: &AppContext) {
+    fn write(value: Self::Value, cx: &App) {
         let fs = <dyn Fs>::global(cx);
 
         update_settings_file::<Self::Settings>(fs, cx, move |settings, cx| {

crates/settings/src/keymap_file.rs 🔗

@@ -4,8 +4,8 @@ use crate::{settings_store::parse_json_with_comments, SettingsAssets};
 use anyhow::anyhow;
 use collections::{HashMap, IndexMap};
 use gpui::{
-    Action, ActionBuildError, AppContext, InvalidKeystrokeError, KeyBinding,
-    KeyBindingContextPredicate, NoAction, SharedString, KEYSTROKE_PARSE_EXPECTED_MESSAGE,
+    Action, ActionBuildError, App, InvalidKeystrokeError, KeyBinding, KeyBindingContextPredicate,
+    NoAction, SharedString, KEYSTROKE_PARSE_EXPECTED_MESSAGE,
 };
 use schemars::{
     gen::{SchemaGenerator, SchemaSettings},
@@ -129,7 +129,7 @@ impl KeymapFile {
         parse_json_with_comments::<Self>(content)
     }
 
-    pub fn load_asset(asset_path: &str, cx: &AppContext) -> anyhow::Result<Vec<KeyBinding>> {
+    pub fn load_asset(asset_path: &str, cx: &App) -> anyhow::Result<Vec<KeyBinding>> {
         match Self::load(asset_str::<SettingsAssets>(asset_path).as_ref(), cx) {
             KeymapFileLoadResult::Success { key_bindings, .. } => Ok(key_bindings),
             KeymapFileLoadResult::SomeFailedToLoad { error_message, .. } => Err(anyhow!(
@@ -144,7 +144,7 @@ impl KeymapFile {
     #[cfg(feature = "test-support")]
     pub fn load_asset_allow_partial_failure(
         asset_path: &str,
-        cx: &AppContext,
+        cx: &App,
     ) -> anyhow::Result<Vec<KeyBinding>> {
         match Self::load(asset_str::<SettingsAssets>(asset_path).as_ref(), cx) {
             KeymapFileLoadResult::SomeFailedToLoad {
@@ -162,7 +162,7 @@ impl KeymapFile {
     }
 
     #[cfg(feature = "test-support")]
-    pub fn load_panic_on_failure(content: &str, cx: &AppContext) -> Vec<KeyBinding> {
+    pub fn load_panic_on_failure(content: &str, cx: &App) -> Vec<KeyBinding> {
         match Self::load(content, cx) {
             KeymapFileLoadResult::Success { key_bindings } => key_bindings,
             KeymapFileLoadResult::SomeFailedToLoad { error_message, .. } => {
@@ -174,7 +174,7 @@ impl KeymapFile {
         }
     }
 
-    pub fn load(content: &str, cx: &AppContext) -> KeymapFileLoadResult {
+    pub fn load(content: &str, cx: &App) -> KeymapFileLoadResult {
         let key_equivalents = crate::key_equivalents::get_key_equivalents(&cx.keyboard_layout());
 
         if content.is_empty() {
@@ -294,7 +294,7 @@ impl KeymapFile {
         action: &KeymapAction,
         context: Option<Rc<KeyBindingContextPredicate>>,
         key_equivalents: Option<&HashMap<char, char>>,
-        cx: &AppContext,
+        cx: &App,
     ) -> std::result::Result<KeyBinding, String> {
         let (build_result, action_input_string) = match &action.0 {
             Value::Array(items) => {
@@ -367,7 +367,7 @@ impl KeymapFile {
         }
     }
 
-    pub fn generate_json_schema_for_registered_actions(cx: &mut AppContext) -> Value {
+    pub fn generate_json_schema_for_registered_actions(cx: &mut App) -> Value {
         let mut generator = SchemaSettings::draft07()
             .with(|settings| settings.option_add_null_type = false)
             .into_generator();

crates/settings/src/settings.rs 🔗

@@ -5,7 +5,7 @@ mod keymap_file;
 mod settings_file;
 mod settings_store;
 
-use gpui::AppContext;
+use gpui::App;
 use rust_embed::RustEmbed;
 use std::{borrow::Cow, fmt, str};
 use util::asset_str;
@@ -60,7 +60,7 @@ impl fmt::Display for WorktreeId {
 #[exclude = "*.DS_Store"]
 pub struct SettingsAssets;
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     let mut settings = SettingsStore::new(cx);
     settings
         .set_default_settings(&default_settings(), cx)

crates/settings/src/settings_file.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{settings_store::SettingsStore, Settings};
 use fs::Fs;
 use futures::{channel::mpsc, StreamExt};
-use gpui::{AppContext, BackgroundExecutor, ReadGlobal, UpdateGlobal};
+use gpui::{App, BackgroundExecutor, ReadGlobal, UpdateGlobal};
 use std::{path::PathBuf, sync::Arc, time::Duration};
 use util::ResultExt;
 
@@ -65,8 +65,8 @@ pub fn watch_config_file(
 
 pub fn handle_settings_file_changes(
     mut user_settings_file_rx: mpsc::UnboundedReceiver<String>,
-    cx: &mut AppContext,
-    settings_changed: impl Fn(Option<anyhow::Error>, &mut AppContext) + 'static,
+    cx: &mut App,
+    settings_changed: impl Fn(Option<anyhow::Error>, &mut App) + 'static,
 ) {
     let user_settings_content = cx
         .background_executor()
@@ -85,7 +85,7 @@ pub fn handle_settings_file_changes(
                     log::error!("Failed to load user settings: {err}");
                 }
                 settings_changed(result.err(), cx);
-                cx.refresh();
+                cx.refresh_windows();
             });
             if result.is_err() {
                 break; // App dropped
@@ -97,8 +97,8 @@ pub fn handle_settings_file_changes(
 
 pub fn update_settings_file<T: Settings>(
     fs: Arc<dyn Fs>,
-    cx: &AppContext,
-    update: impl 'static + Send + FnOnce(&mut T::FileContent, &AppContext),
+    cx: &App,
+    update: impl 'static + Send + FnOnce(&mut T::FileContent, &App),
 ) {
     SettingsStore::global(cx).update_settings_file::<T>(fs, update);
 }

crates/settings/src/settings_store.rs 🔗

@@ -1,9 +1,9 @@
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use collections::{btree_map, hash_map, BTreeMap, HashMap};
 use ec4rs::{ConfigParser, PropertiesSource, Section};
 use fs::Fs;
 use futures::{channel::mpsc, future::LocalBoxFuture, FutureExt, StreamExt};
-use gpui::{AppContext, AsyncAppContext, BorrowAppContext, Global, Task, UpdateGlobal};
+use gpui::{App, AsyncAppContext, BorrowAppContext, Global, Task, UpdateGlobal};
 use paths::{local_settings_file_relative_path, EDITORCONFIG_NAME};
 use schemars::{gen::SchemaGenerator, schema::RootSchema, JsonSchema};
 use serde::{de::DeserializeOwned, Deserialize as _, Serialize};
@@ -45,14 +45,14 @@ pub trait Settings: 'static + Send + Sync {
 
     /// The logic for combining together values from one or more JSON files into the
     /// final value for this setting.
-    fn load(sources: SettingsSources<Self::FileContent>, cx: &mut AppContext) -> Result<Self>
+    fn load(sources: SettingsSources<Self::FileContent>, cx: &mut App) -> Result<Self>
     where
         Self: Sized;
 
     fn json_schema(
         generator: &mut SchemaGenerator,
         _: &SettingsJsonSchemaParams,
-        _: &AppContext,
+        _: &App,
     ) -> RootSchema {
         generator.root_schema_for::<Self::FileContent>()
     }
@@ -62,7 +62,7 @@ pub trait Settings: 'static + Send + Sync {
     }
 
     #[track_caller]
-    fn register(cx: &mut AppContext)
+    fn register(cx: &mut App)
     where
         Self: Sized,
     {
@@ -72,7 +72,7 @@ pub trait Settings: 'static + Send + Sync {
     }
 
     #[track_caller]
-    fn get<'a>(path: Option<SettingsLocation>, cx: &'a AppContext) -> &'a Self
+    fn get<'a>(path: Option<SettingsLocation>, cx: &'a App) -> &'a Self
     where
         Self: Sized,
     {
@@ -80,7 +80,7 @@ pub trait Settings: 'static + Send + Sync {
     }
 
     #[track_caller]
-    fn get_global(cx: &AppContext) -> &Self
+    fn get_global(cx: &App) -> &Self
     where
         Self: Sized,
     {
@@ -96,7 +96,7 @@ pub trait Settings: 'static + Send + Sync {
     }
 
     #[track_caller]
-    fn override_global(settings: Self, cx: &mut AppContext)
+    fn override_global(settings: Self, cx: &mut App)
     where
         Self: Sized,
     {
@@ -225,7 +225,7 @@ trait AnySettingValue: 'static + Send + Sync {
     fn load_setting(
         &self,
         sources: SettingsSources<DeserializedSetting>,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Result<Box<dyn Any>>;
     fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any;
     fn set_global_value(&mut self, value: Box<dyn Any>);
@@ -234,14 +234,14 @@ trait AnySettingValue: 'static + Send + Sync {
         &self,
         generator: &mut SchemaGenerator,
         _: &SettingsJsonSchemaParams,
-        cx: &AppContext,
+        cx: &App,
     ) -> RootSchema;
 }
 
 struct DeserializedSetting(Box<dyn Any>);
 
 impl SettingsStore {
-    pub fn new(cx: &AppContext) -> Self {
+    pub fn new(cx: &App) -> Self {
         let (setting_file_updates_tx, mut setting_file_updates_rx) = mpsc::unbounded();
         Self {
             setting_values: Default::default(),
@@ -269,7 +269,7 @@ impl SettingsStore {
     }
 
     /// Add a new type of setting to the store.
-    pub fn register_setting<T: Settings>(&mut self, cx: &mut AppContext) {
+    pub fn register_setting<T: Settings>(&mut self, cx: &mut App) {
         let setting_type_id = TypeId::of::<T>();
         let entry = self.setting_values.entry(setting_type_id);
 
@@ -363,7 +363,7 @@ impl SettingsStore {
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn test(cx: &mut AppContext) -> Self {
+    pub fn test(cx: &mut App) -> Self {
         let mut this = Self::new(cx);
         this.set_default_settings(&crate::test_settings(), cx)
             .unwrap();
@@ -378,7 +378,7 @@ impl SettingsStore {
     #[cfg(any(test, feature = "test-support"))]
     pub fn update_user_settings<T: Settings>(
         &mut self,
-        cx: &mut AppContext,
+        cx: &mut App,
         update: impl FnOnce(&mut T::FileContent),
     ) {
         let old_text = serde_json::to_string(&self.raw_user_settings).unwrap();
@@ -403,7 +403,7 @@ impl SettingsStore {
     pub fn update_settings_file<T: Settings>(
         &self,
         fs: Arc<dyn Fs>,
-        update: impl 'static + Send + FnOnce(&mut T::FileContent, &AppContext),
+        update: impl 'static + Send + FnOnce(&mut T::FileContent, &App),
     ) {
         self.setting_file_updates_tx
             .unbounded_send(Box::new(move |cx: AsyncAppContext| {
@@ -531,7 +531,7 @@ impl SettingsStore {
     pub fn set_default_settings(
         &mut self,
         default_settings_content: &str,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Result<()> {
         let settings: serde_json::Value = parse_json_with_comments(default_settings_content)?;
         if settings.is_object() {
@@ -544,11 +544,7 @@ impl SettingsStore {
     }
 
     /// Sets the user settings via a JSON string.
-    pub fn set_user_settings(
-        &mut self,
-        user_settings_content: &str,
-        cx: &mut AppContext,
-    ) -> Result<()> {
+    pub fn set_user_settings(&mut self, user_settings_content: &str, cx: &mut App) -> Result<()> {
         let settings: serde_json::Value = if user_settings_content.is_empty() {
             parse_json_with_comments("{}")?
         } else {
@@ -564,7 +560,7 @@ impl SettingsStore {
     pub fn set_server_settings(
         &mut self,
         server_settings_content: &str,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Result<()> {
         let settings: Option<serde_json::Value> = if server_settings_content.is_empty() {
             None
@@ -591,7 +587,7 @@ impl SettingsStore {
         directory_path: Arc<Path>,
         kind: LocalSettingsKind,
         settings_content: Option<&str>,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> std::result::Result<(), InvalidSettingsError> {
         let mut zed_settings_changed = false;
         match (
@@ -683,11 +679,7 @@ impl SettingsStore {
         Ok(())
     }
 
-    pub fn set_extension_settings<T: Serialize>(
-        &mut self,
-        content: T,
-        cx: &mut AppContext,
-    ) -> Result<()> {
+    pub fn set_extension_settings<T: Serialize>(&mut self, content: T, cx: &mut App) -> Result<()> {
         let settings: serde_json::Value = serde_json::to_value(content)?;
         anyhow::ensure!(settings.is_object(), "settings must be an object");
         self.raw_extension_settings = settings;
@@ -696,7 +688,7 @@ impl SettingsStore {
     }
 
     /// Add or remove a set of local settings via a JSON string.
-    pub fn clear_local_settings(&mut self, root_id: WorktreeId, cx: &mut AppContext) -> Result<()> {
+    pub fn clear_local_settings(&mut self, root_id: WorktreeId, cx: &mut App) -> Result<()> {
         self.raw_local_settings
             .retain(|(worktree_id, _), _| worktree_id != &root_id);
         self.recompute_values(Some((root_id, "".as_ref())), cx)?;
@@ -738,7 +730,7 @@ impl SettingsStore {
     pub fn json_schema(
         &self,
         schema_params: &SettingsJsonSchemaParams,
-        cx: &AppContext,
+        cx: &App,
     ) -> serde_json::Value {
         use schemars::{
             gen::SchemaSettings,
@@ -845,7 +837,7 @@ impl SettingsStore {
     fn recompute_values(
         &mut self,
         changed_local_path: Option<(WorktreeId, &Path)>,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> std::result::Result<(), InvalidSettingsError> {
         // Reload the global and local values for every setting.
         let mut project_settings_stack = Vec::<DeserializedSetting>::new();
@@ -1054,7 +1046,7 @@ impl<T: Settings> AnySettingValue for SettingValue<T> {
     fn load_setting(
         &self,
         values: SettingsSources<DeserializedSetting>,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Result<Box<dyn Any>> {
         Ok(Box::new(T::load(
             SettingsSources {
@@ -1127,7 +1119,7 @@ impl<T: Settings> AnySettingValue for SettingValue<T> {
         &self,
         generator: &mut SchemaGenerator,
         params: &SettingsJsonSchemaParams,
-        cx: &AppContext,
+        cx: &App,
     ) -> RootSchema {
         T::json_schema(generator, params, cx)
     }
@@ -1352,7 +1344,7 @@ mod tests {
     use unindent::Unindent;
 
     #[gpui::test]
-    fn test_settings_store_basic(cx: &mut AppContext) {
+    fn test_settings_store_basic(cx: &mut App) {
         let mut store = SettingsStore::new(cx);
         store.register_setting::<UserSettings>(cx);
         store.register_setting::<TurboSetting>(cx);
@@ -1484,7 +1476,7 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_setting_store_assign_json_before_register(cx: &mut AppContext) {
+    fn test_setting_store_assign_json_before_register(cx: &mut App) {
         let mut store = SettingsStore::new(cx);
         store
             .set_default_settings(
@@ -1527,7 +1519,7 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_setting_store_update(cx: &mut AppContext) {
+    fn test_setting_store_update(cx: &mut App) {
         let mut store = SettingsStore::new(cx);
         store.register_setting::<MultiKeySettings>(cx);
         store.register_setting::<UserSettings>(cx);
@@ -1651,7 +1643,7 @@ mod tests {
         old_json: String,
         update: fn(&mut T::FileContent),
         expected_new_json: String,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) {
         store.set_user_settings(&old_json, cx).ok();
         let edits = store.edits_for_update::<T>(&old_json, update);
@@ -1680,7 +1672,7 @@ mod tests {
         const KEY: Option<&'static str> = Some("user");
         type FileContent = UserSettingsJson;
 
-        fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+        fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
             sources.json_merge()
         }
     }
@@ -1692,7 +1684,7 @@ mod tests {
         const KEY: Option<&'static str> = Some("turbo");
         type FileContent = Option<bool>;
 
-        fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+        fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
             sources.json_merge()
         }
     }
@@ -1716,7 +1708,7 @@ mod tests {
 
         type FileContent = MultiKeySettingsJson;
 
-        fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+        fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
             sources.json_merge()
         }
     }
@@ -1745,7 +1737,7 @@ mod tests {
 
         type FileContent = JournalSettingsJson;
 
-        fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+        fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
             sources.json_merge()
         }
     }
@@ -1767,7 +1759,7 @@ mod tests {
 
         type FileContent = Self;
 
-        fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+        fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
             sources.json_merge()
         }
     }

crates/settings_ui/src/appearance_settings_controls.rs 🔗

@@ -1,6 +1,6 @@
 use std::sync::Arc;
 
-use gpui::{AppContext, FontFeatures, FontWeight};
+use gpui::{App, FontFeatures, FontWeight};
 use settings::{EditableSettingControl, Settings};
 use theme::{FontFamilyCache, SystemAppearance, ThemeMode, ThemeRegistry, ThemeSettings};
 use ui::{
@@ -18,7 +18,7 @@ impl AppearanceSettingsControls {
 }
 
 impl RenderOnce for AppearanceSettingsControls {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         SettingsContainer::new()
             .child(
                 SettingsGroup::new("Theme").child(
@@ -55,7 +55,7 @@ impl EditableSettingControl for ThemeControl {
         "Theme".into()
     }
 
-    fn read(cx: &AppContext) -> Self::Value {
+    fn read(cx: &App) -> Self::Value {
         let settings = ThemeSettings::get_global(cx);
         let appearance = SystemAppearance::global(cx);
         settings
@@ -68,7 +68,7 @@ impl EditableSettingControl for ThemeControl {
     fn apply(
         settings: &mut <Self::Settings as Settings>::FileContent,
         value: Self::Value,
-        cx: &AppContext,
+        cx: &App,
     ) {
         let appearance = SystemAppearance::global(cx);
         settings.set_theme(value, appearance.0);
@@ -76,24 +76,24 @@ impl EditableSettingControl for ThemeControl {
 }
 
 impl RenderOnce for ThemeControl {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
         let value = Self::read(cx);
 
         DropdownMenu::new(
             "theme",
             value.clone(),
-            ContextMenu::build(cx, |mut menu, cx| {
+            ContextMenu::build(window, cx, |mut menu, _, cx| {
                 let theme_registry = ThemeRegistry::global(cx);
 
                 for theme in theme_registry.list_names() {
                     menu = menu.custom_entry(
                         {
                             let theme = theme.clone();
-                            move |_cx| Label::new(theme.clone()).into_any_element()
+                            move |_window, _cx| Label::new(theme.clone()).into_any_element()
                         },
                         {
                             let theme = theme.clone();
-                            move |cx| {
+                            move |_window, cx| {
                                 Self::write(theme.to_string(), cx);
                             }
                         },
@@ -118,7 +118,7 @@ impl EditableSettingControl for ThemeModeControl {
         "Theme Mode".into()
     }
 
-    fn read(cx: &AppContext) -> Self::Value {
+    fn read(cx: &App) -> Self::Value {
         let settings = ThemeSettings::get_global(cx);
         settings
             .theme_selection
@@ -130,14 +130,14 @@ impl EditableSettingControl for ThemeModeControl {
     fn apply(
         settings: &mut <Self::Settings as Settings>::FileContent,
         value: Self::Value,
-        _cx: &AppContext,
+        _cx: &App,
     ) {
         settings.set_mode(value);
     }
 }
 
 impl RenderOnce for ThemeModeControl {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let value = Self::read(cx);
 
         h_flex()
@@ -146,7 +146,7 @@ impl RenderOnce for ThemeModeControl {
                     .style(ButtonStyle::Filled)
                     .size(ButtonSize::Large)
                     .toggle_state(value == ThemeMode::Light)
-                    .on_click(|_, cx| Self::write(ThemeMode::Light, cx))
+                    .on_click(|_, _, cx| Self::write(ThemeMode::Light, cx))
                     .first(),
             )
             .child(
@@ -154,7 +154,7 @@ impl RenderOnce for ThemeModeControl {
                     .style(ButtonStyle::Filled)
                     .size(ButtonSize::Large)
                     .toggle_state(value == ThemeMode::System)
-                    .on_click(|_, cx| Self::write(ThemeMode::System, cx))
+                    .on_click(|_, _, cx| Self::write(ThemeMode::System, cx))
                     .middle(),
             )
             .child(
@@ -162,7 +162,7 @@ impl RenderOnce for ThemeModeControl {
                     .style(ButtonStyle::Filled)
                     .size(ButtonSize::Large)
                     .toggle_state(value == ThemeMode::Dark)
-                    .on_click(|_, cx| Self::write(ThemeMode::Dark, cx))
+                    .on_click(|_, _, cx| Self::write(ThemeMode::Dark, cx))
                     .last(),
             )
     }
@@ -179,7 +179,7 @@ impl EditableSettingControl for UiFontFamilyControl {
         "UI Font Family".into()
     }
 
-    fn read(cx: &AppContext) -> Self::Value {
+    fn read(cx: &App) -> Self::Value {
         let settings = ThemeSettings::get_global(cx);
         settings.ui_font.family.clone()
     }
@@ -187,14 +187,14 @@ impl EditableSettingControl for UiFontFamilyControl {
     fn apply(
         settings: &mut <Self::Settings as Settings>::FileContent,
         value: Self::Value,
-        _cx: &AppContext,
+        _cx: &App,
     ) {
         settings.ui_font_family = Some(value.to_string());
     }
 }
 
 impl RenderOnce for UiFontFamilyControl {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
         let value = Self::read(cx);
 
         h_flex()
@@ -203,18 +203,18 @@ impl RenderOnce for UiFontFamilyControl {
             .child(DropdownMenu::new(
                 "ui-font-family",
                 value.clone(),
-                ContextMenu::build(cx, |mut menu, cx| {
+                ContextMenu::build(window, cx, |mut menu, _, cx| {
                     let font_family_cache = FontFamilyCache::global(cx);
 
                     for font_name in font_family_cache.list_font_families(cx) {
                         menu = menu.custom_entry(
                             {
                                 let font_name = font_name.clone();
-                                move |_cx| Label::new(font_name.clone()).into_any_element()
+                                move |_window, _cx| Label::new(font_name.clone()).into_any_element()
                             },
                             {
                                 let font_name = font_name.clone();
-                                move |cx| {
+                                move |_window, cx| {
                                     Self::write(font_name.clone(), cx);
                                 }
                             },
@@ -238,7 +238,7 @@ impl EditableSettingControl for UiFontSizeControl {
         "UI Font Size".into()
     }
 
-    fn read(cx: &AppContext) -> Self::Value {
+    fn read(cx: &App) -> Self::Value {
         let settings = ThemeSettings::get_global(cx);
         settings.ui_font_size
     }
@@ -246,14 +246,14 @@ impl EditableSettingControl for UiFontSizeControl {
     fn apply(
         settings: &mut <Self::Settings as Settings>::FileContent,
         value: Self::Value,
-        _cx: &AppContext,
+        _cx: &App,
     ) {
         settings.ui_font_size = Some(value.into());
     }
 }
 
 impl RenderOnce for UiFontSizeControl {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let value = Self::read(cx);
 
         h_flex()
@@ -262,10 +262,10 @@ impl RenderOnce for UiFontSizeControl {
             .child(NumericStepper::new(
                 "ui-font-size",
                 value.to_string(),
-                move |_, cx| {
+                move |_, _, cx| {
                     Self::write(value - px(1.), cx);
                 },
-                move |_, cx| {
+                move |_, _, cx| {
                     Self::write(value + px(1.), cx);
                 },
             ))
@@ -283,7 +283,7 @@ impl EditableSettingControl for UiFontWeightControl {
         "UI Font Weight".into()
     }
 
-    fn read(cx: &AppContext) -> Self::Value {
+    fn read(cx: &App) -> Self::Value {
         let settings = ThemeSettings::get_global(cx);
         settings.ui_font.weight
     }
@@ -291,14 +291,14 @@ impl EditableSettingControl for UiFontWeightControl {
     fn apply(
         settings: &mut <Self::Settings as Settings>::FileContent,
         value: Self::Value,
-        _cx: &AppContext,
+        _cx: &App,
     ) {
         settings.ui_font_weight = Some(value.0);
     }
 }
 
 impl RenderOnce for UiFontWeightControl {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
         let value = Self::read(cx);
 
         h_flex()
@@ -307,12 +307,12 @@ impl RenderOnce for UiFontWeightControl {
             .child(DropdownMenu::new(
                 "ui-font-weight",
                 value.0.to_string(),
-                ContextMenu::build(cx, |mut menu, _cx| {
+                ContextMenu::build(window, cx, |mut menu, _window, _cx| {
                     for weight in FontWeight::ALL {
                         menu = menu.custom_entry(
-                            move |_cx| Label::new(weight.0.to_string()).into_any_element(),
+                            move |_window, _cx| Label::new(weight.0.to_string()).into_any_element(),
                             {
-                                move |cx| {
+                                move |_window, cx| {
                                     Self::write(weight, cx);
                                 }
                             },
@@ -336,7 +336,7 @@ impl EditableSettingControl for UiFontLigaturesControl {
         "UI Font Ligatures".into()
     }
 
-    fn read(cx: &AppContext) -> Self::Value {
+    fn read(cx: &App) -> Self::Value {
         let settings = ThemeSettings::get_global(cx);
         settings.ui_font.features.is_calt_enabled().unwrap_or(true)
     }
@@ -344,7 +344,7 @@ impl EditableSettingControl for UiFontLigaturesControl {
     fn apply(
         settings: &mut <Self::Settings as Settings>::FileContent,
         value: Self::Value,
-        _cx: &AppContext,
+        _cx: &App,
     ) {
         let value = if value { 1 } else { 0 };
 
@@ -365,14 +365,14 @@ impl EditableSettingControl for UiFontLigaturesControl {
 }
 
 impl RenderOnce for UiFontLigaturesControl {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let value = Self::read(cx);
 
         CheckboxWithLabel::new(
             "ui-font-ligatures",
             Label::new(self.name()),
             value.into(),
-            |selection, cx| {
+            |selection, _, cx| {
                 Self::write(
                     match selection {
                         ToggleState::Selected => true,

crates/settings_ui/src/settings_ui.rs 🔗

@@ -5,7 +5,7 @@ use std::any::TypeId;
 use command_palette_hooks::CommandPaletteFilter;
 use editor::EditorSettingsControls;
 use feature_flags::{FeatureFlag, FeatureFlagViewExt};
-use gpui::{actions, AppContext, EventEmitter, FocusHandle, FocusableView, View};
+use gpui::{actions, App, Entity, EventEmitter, FocusHandle, Focusable};
 use ui::prelude::*;
 use workspace::item::{Item, ItemEvent};
 use workspace::Workspace;
@@ -20,9 +20,13 @@ impl FeatureFlag for SettingsUiFeatureFlag {
 
 actions!(zed, [OpenSettingsEditor]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(|workspace: &mut Workspace, cx| {
-        workspace.register_action(|workspace, _: &OpenSettingsEditor, cx| {
+pub fn init(cx: &mut App) {
+    cx.observe_new(|workspace: &mut Workspace, window, cx| {
+        let Some(window) = window else {
+            return;
+        };
+
+        workspace.register_action(|workspace, _: &OpenSettingsEditor, window, cx| {
             let existing = workspace
                 .active_pane()
                 .read(cx)
@@ -30,10 +34,10 @@ pub fn init(cx: &mut AppContext) {
                 .find_map(|item| item.downcast::<SettingsPage>());
 
             if let Some(existing) = existing {
-                workspace.activate_item(&existing, true, true, cx);
+                workspace.activate_item(&existing, true, true, window, cx);
             } else {
                 let settings_page = SettingsPage::new(workspace, cx);
-                workspace.add_item_to_active_pane(Box::new(settings_page), None, true, cx)
+                workspace.add_item_to_active_pane(Box::new(settings_page), None, true, window, cx)
             }
         });
 
@@ -43,17 +47,20 @@ pub fn init(cx: &mut AppContext) {
             filter.hide_action_types(&settings_ui_actions);
         });
 
-        cx.observe_flag::<SettingsUiFeatureFlag, _>(move |is_enabled, _view, cx| {
-            if is_enabled {
-                CommandPaletteFilter::update_global(cx, |filter, _cx| {
-                    filter.show_action_types(settings_ui_actions.iter());
-                });
-            } else {
-                CommandPaletteFilter::update_global(cx, |filter, _cx| {
-                    filter.hide_action_types(&settings_ui_actions);
-                });
-            }
-        })
+        cx.observe_flag::<SettingsUiFeatureFlag, _>(
+            window,
+            move |is_enabled, _workspace, _, cx| {
+                if is_enabled {
+                    CommandPaletteFilter::update_global(cx, |filter, _cx| {
+                        filter.show_action_types(settings_ui_actions.iter());
+                    });
+                } else {
+                    CommandPaletteFilter::update_global(cx, |filter, _cx| {
+                        filter.hide_action_types(&settings_ui_actions);
+                    });
+                }
+            },
+        )
         .detach();
     })
     .detach();
@@ -64,8 +71,8 @@ pub struct SettingsPage {
 }
 
 impl SettingsPage {
-    pub fn new(_workspace: &Workspace, cx: &mut ViewContext<Workspace>) -> View<Self> {
-        cx.new_view(|cx| Self {
+    pub fn new(_workspace: &Workspace, cx: &mut Context<Workspace>) -> Entity<Self> {
+        cx.new(|cx| Self {
             focus_handle: cx.focus_handle(),
         })
     }
@@ -73,8 +80,8 @@ impl SettingsPage {
 
 impl EventEmitter<ItemEvent> for SettingsPage {}
 
-impl FocusableView for SettingsPage {
-    fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
+impl Focusable for SettingsPage {
+    fn focus_handle(&self, _cx: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -82,11 +89,11 @@ impl FocusableView for SettingsPage {
 impl Item for SettingsPage {
     type Event = ItemEvent;
 
-    fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
+    fn tab_icon(&self, _window: &Window, _cx: &App) -> Option<Icon> {
         Some(Icon::new(IconName::Settings))
     }
 
-    fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
+    fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option<SharedString> {
         Some("Settings".into())
     }
 
@@ -100,7 +107,7 @@ impl Item for SettingsPage {
 }
 
 impl Render for SettingsPage {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         v_flex()
             .p_4()
             .size_full()

crates/snippet/src/snippet.rs 🔗

@@ -1,4 +1,4 @@
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use smallvec::SmallVec;
 use std::{collections::BTreeMap, ops::Range};
 

crates/snippet_provider/src/extension_snippet.rs 🔗

@@ -3,11 +3,11 @@ use std::sync::Arc;
 
 use anyhow::Result;
 use extension::{ExtensionHostProxy, ExtensionSnippetProxy};
-use gpui::AppContext;
+use gpui::App;
 
 use crate::SnippetRegistry;
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     let proxy = ExtensionHostProxy::default_global(cx);
     proxy.register_snippet_proxy(SnippetRegistryProxy {
         snippet_registry: SnippetRegistry::global(cx),

crates/snippet_provider/src/lib.rs 🔗

@@ -13,11 +13,11 @@ use collections::{BTreeMap, BTreeSet, HashMap};
 use format::VSSnippetsFile;
 use fs::Fs;
 use futures::stream::StreamExt;
-use gpui::{AppContext, AsyncAppContext, Context, Model, ModelContext, Task, WeakModel};
+use gpui::{App, AppContext as _, AsyncAppContext, Context, Entity, Task, WeakEntity};
 pub use registry::*;
 use util::ResultExt;
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     SnippetRegistry::init_global(cx);
     extension_snippet::init(cx);
 }
@@ -62,7 +62,7 @@ pub struct Snippet {
 }
 
 async fn process_updates(
-    this: WeakModel<SnippetProvider>,
+    this: WeakEntity<SnippetProvider>,
     entries: Vec<PathBuf>,
     mut cx: AsyncAppContext,
 ) -> Result<()> {
@@ -112,7 +112,7 @@ async fn process_updates(
 }
 
 async fn initial_scan(
-    this: WeakModel<SnippetProvider>,
+    this: WeakEntity<SnippetProvider>,
     path: Arc<Path>,
     mut cx: AsyncAppContext,
 ) -> Result<()> {
@@ -136,12 +136,12 @@ pub struct SnippetProvider {
 }
 
 // Watches global snippet directory, is created just once and reused across multiple projects
-struct GlobalSnippetWatcher(Model<SnippetProvider>);
+struct GlobalSnippetWatcher(Entity<SnippetProvider>);
 
 impl GlobalSnippetWatcher {
-    fn new(fs: Arc<dyn Fs>, cx: &mut AppContext) -> Self {
+    fn new(fs: Arc<dyn Fs>, cx: &mut App) -> Self {
         let global_snippets_dir = paths::config_dir().join("snippets");
-        let provider = cx.new_model(|_cx| SnippetProvider {
+        let provider = cx.new(|_cx| SnippetProvider {
             fs,
             snippets: Default::default(),
             watch_tasks: vec![],
@@ -156,12 +156,8 @@ impl GlobalSnippetWatcher {
 impl gpui::Global for GlobalSnippetWatcher {}
 
 impl SnippetProvider {
-    pub fn new(
-        fs: Arc<dyn Fs>,
-        dirs_to_watch: BTreeSet<PathBuf>,
-        cx: &mut AppContext,
-    ) -> Model<Self> {
-        cx.new_model(move |cx| {
+    pub fn new(fs: Arc<dyn Fs>, dirs_to_watch: BTreeSet<PathBuf>, cx: &mut App) -> Entity<Self> {
+        cx.new(move |cx| {
             if !cx.has_global::<GlobalSnippetWatcher>() {
                 let global_watcher = GlobalSnippetWatcher::new(fs.clone(), cx);
                 cx.set_global(global_watcher);
@@ -181,7 +177,7 @@ impl SnippetProvider {
     }
 
     /// Add directory to be watched for content changes
-    fn watch_directory(&mut self, path: &Path, cx: &ModelContext<Self>) {
+    fn watch_directory(&mut self, path: &Path, cx: &Context<Self>) {
         let path: Arc<Path> = Arc::from(path);
 
         self.watch_tasks.push(cx.spawn(|this, mut cx| async move {
@@ -206,7 +202,7 @@ impl SnippetProvider {
     fn lookup_snippets<'a, const LOOKUP_GLOBALS: bool>(
         &'a self,
         language: &'a SnippetKind,
-        cx: &AppContext,
+        cx: &App,
     ) -> Vec<Arc<Snippet>> {
         let mut user_snippets: Vec<_> = self
             .snippets
@@ -237,7 +233,7 @@ impl SnippetProvider {
         user_snippets
     }
 
-    pub fn snippets_for(&self, language: SnippetKind, cx: &AppContext) -> Vec<Arc<Snippet>> {
+    pub fn snippets_for(&self, language: SnippetKind, cx: &App) -> Vec<Arc<Snippet>> {
         let mut requested_snippets = self.lookup_snippets::<true>(&language, cx);
 
         if language.is_some() {

crates/snippet_provider/src/registry.rs 🔗

@@ -2,7 +2,7 @@ use std::{path::Path, sync::Arc};
 
 use anyhow::Result;
 use collections::HashMap;
-use gpui::{AppContext, Global, ReadGlobal, UpdateGlobal};
+use gpui::{App, Global, ReadGlobal, UpdateGlobal};
 use parking_lot::RwLock;
 
 use crate::{file_stem_to_key, Snippet, SnippetKind};
@@ -17,16 +17,16 @@ pub struct SnippetRegistry {
 }
 
 impl SnippetRegistry {
-    pub fn global(cx: &AppContext) -> Arc<Self> {
+    pub fn global(cx: &App) -> Arc<Self> {
         GlobalSnippetRegistry::global(cx).0.clone()
     }
 
-    pub fn try_global(cx: &AppContext) -> Option<Arc<Self>> {
+    pub fn try_global(cx: &App) -> Option<Arc<Self>> {
         cx.try_global::<GlobalSnippetRegistry>()
             .map(|registry| registry.0.clone())
     }
 
-    pub fn init_global(cx: &mut AppContext) {
+    pub fn init_global(cx: &mut App) {
         GlobalSnippetRegistry::set_global(cx, GlobalSnippetRegistry(Arc::new(Self::new())))
     }
 

crates/snippets_ui/src/snippets_ui.rs 🔗

@@ -1,23 +1,23 @@
 use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
 use gpui::{
-    actions, AppContext, DismissEvent, EventEmitter, FocusableView, ParentElement, Render, Styled,
-    View, ViewContext, VisualContext, WeakView,
+    actions, App, Context, DismissEvent, Entity, EventEmitter, Focusable, ParentElement, Render,
+    Styled, WeakEntity, Window,
 };
 use language::LanguageRegistry;
 use paths::config_dir;
 use picker::{Picker, PickerDelegate};
 use std::{borrow::Borrow, fs, sync::Arc};
-use ui::{prelude::*, HighlightedLabel, ListItem, ListItemSpacing, WindowContext};
+use ui::{prelude::*, HighlightedLabel, ListItem, ListItemSpacing};
 use util::ResultExt;
 use workspace::{notifications::NotifyResultExt, ModalView, Workspace};
 
 actions!(snippets, [ConfigureSnippets, OpenFolder]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(register).detach();
+pub fn init(cx: &mut App) {
+    cx.observe_new(register).detach();
 }
 
-fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+fn register(workspace: &mut Workspace, _window: Option<&mut Window>, _: &mut Context<Workspace>) {
     workspace.register_action(configure_snippets);
     workspace.register_action(open_folder);
 }
@@ -25,35 +25,42 @@ fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
 fn configure_snippets(
     workspace: &mut Workspace,
     _: &ConfigureSnippets,
-    cx: &mut ViewContext<Workspace>,
+    window: &mut Window,
+    cx: &mut Context<Workspace>,
 ) {
     let language_registry = workspace.app_state().languages.clone();
     let workspace_handle = workspace.weak_handle();
 
-    workspace.toggle_modal(cx, move |cx| {
-        ScopeSelector::new(language_registry, workspace_handle, cx)
+    workspace.toggle_modal(window, cx, move |window, cx| {
+        ScopeSelector::new(language_registry, workspace_handle, window, cx)
     });
 }
 
-fn open_folder(workspace: &mut Workspace, _: &OpenFolder, cx: &mut ViewContext<Workspace>) {
+fn open_folder(
+    workspace: &mut Workspace,
+    _: &OpenFolder,
+    _: &mut Window,
+    cx: &mut Context<Workspace>,
+) {
     fs::create_dir_all(config_dir().join("snippets")).notify_err(workspace, cx);
     cx.open_with_system(config_dir().join("snippets").borrow());
 }
 
 pub struct ScopeSelector {
-    picker: View<Picker<ScopeSelectorDelegate>>,
+    picker: Entity<Picker<ScopeSelectorDelegate>>,
 }
 
 impl ScopeSelector {
     fn new(
         language_registry: Arc<LanguageRegistry>,
-        workspace: WeakView<Workspace>,
-        cx: &mut ViewContext<Self>,
+        workspace: WeakEntity<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let delegate =
-            ScopeSelectorDelegate::new(workspace, cx.view().downgrade(), language_registry);
+            ScopeSelectorDelegate::new(workspace, cx.model().downgrade(), language_registry);
 
-        let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
+        let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
 
         Self { picker }
     }
@@ -63,21 +70,21 @@ impl ModalView for ScopeSelector {}
 
 impl EventEmitter<DismissEvent> for ScopeSelector {}
 
-impl FocusableView for ScopeSelector {
-    fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
+impl Focusable for ScopeSelector {
+    fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
 
 impl Render for ScopeSelector {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         v_flex().w(rems(34.)).child(self.picker.clone())
     }
 }
 
 pub struct ScopeSelectorDelegate {
-    workspace: WeakView<Workspace>,
-    scope_selector: WeakView<ScopeSelector>,
+    workspace: WeakEntity<Workspace>,
+    scope_selector: WeakEntity<ScopeSelector>,
     language_registry: Arc<LanguageRegistry>,
     candidates: Vec<StringMatchCandidate>,
     matches: Vec<StringMatch>,
@@ -86,8 +93,8 @@ pub struct ScopeSelectorDelegate {
 
 impl ScopeSelectorDelegate {
     fn new(
-        workspace: WeakView<Workspace>,
-        scope_selector: WeakView<ScopeSelector>,
+        workspace: WeakEntity<Workspace>,
+        scope_selector: WeakEntity<ScopeSelector>,
         language_registry: Arc<LanguageRegistry>,
     ) -> Self {
         let candidates = Vec::from(["Global".to_string()]).into_iter();
@@ -113,7 +120,7 @@ impl ScopeSelectorDelegate {
 impl PickerDelegate for ScopeSelectorDelegate {
     type ListItem = ListItem;
 
-    fn placeholder_text(&self, _: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _: &mut App) -> Arc<str> {
         "Select snippet scope...".into()
     }
 
@@ -121,23 +128,24 @@ impl PickerDelegate for ScopeSelectorDelegate {
         self.matches.len()
     }
 
-    fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
         if let Some(mat) = self.matches.get(self.selected_index) {
             let scope_name = self.candidates[mat.candidate_id].string.clone();
             let language = self.language_registry.language_for_name(&scope_name);
 
             if let Some(workspace) = self.workspace.upgrade() {
-                cx.spawn(|_, mut cx| async move {
+                cx.spawn_in(window, |_, mut cx| async move {
                     let scope = match scope_name.as_str() {
                         "Global" => "snippets".to_string(),
                         _ => language.await?.lsp_id(),
                     };
 
-                    workspace.update(&mut cx, |workspace, cx| {
+                    workspace.update_in(&mut cx, |workspace, window, cx| {
                         workspace
                             .open_abs_path(
                                 config_dir().join("snippets").join(scope + ".json"),
                                 false,
+                                window,
                                 cx,
                             )
                             .detach();
@@ -146,10 +154,10 @@ impl PickerDelegate for ScopeSelectorDelegate {
                 .detach_and_log_err(cx);
             };
         }
-        self.dismissed(cx);
+        self.dismissed(window, cx);
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+    fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.scope_selector
             .update(cx, |_, cx| cx.emit(DismissEvent))
             .log_err();
@@ -159,18 +167,24 @@ impl PickerDelegate for ScopeSelectorDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        _window: &mut Window,
+        _: &mut Context<Picker<Self>>,
+    ) {
         self.selected_index = ix;
     }
 
     fn update_matches(
         &mut self,
         query: String,
-        cx: &mut ViewContext<Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> gpui::Task<()> {
         let background = cx.background_executor().clone();
         let candidates = self.candidates.clone();
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let matches = if query.is_empty() {
                 candidates
                     .into_iter()
@@ -210,7 +224,8 @@ impl PickerDelegate for ScopeSelectorDelegate {
         &self,
         ix: usize,
         selected: bool,
-        _: &mut ViewContext<Picker<Self>>,
+        _window: &mut Window,
+        _: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let mat = &self.matches[ix];
         let label = mat.string.clone();

crates/sqlez/src/bindable.rs 🔗

@@ -3,7 +3,7 @@ use std::{
     sync::Arc,
 };
 
-use anyhow::{Context, Result};
+use anyhow::{Context as _, Result};
 use util::paths::PathExt;
 
 use crate::statement::{SqlType, Statement};

crates/sqlez/src/migrations.rs 🔗

@@ -6,7 +6,7 @@
 
 use std::ffi::CString;
 
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use indoc::{formatdoc, indoc};
 use libsqlite3_sys::sqlite3_exec;
 

crates/story/src/story.rs 🔗

@@ -1,6 +1,6 @@
 use gpui::{
-    div, prelude::*, px, rems, AnyElement, DefaultColor, DefaultColors, Div, SharedString,
-    WindowContext,
+    div, prelude::*, px, rems, AnyElement, App, DefaultColor, DefaultColors, Div, SharedString,
+    Window,
 };
 use itertools::Itertools;
 use smallvec::SmallVec;
@@ -135,7 +135,7 @@ impl StoryItem {
 }
 
 impl RenderOnce for StoryItem {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         let colors = DefaultColors::light();
 
         div()
@@ -205,7 +205,7 @@ impl StorySection {
 }
 
 impl RenderOnce for StorySection {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         let children: SmallVec<[AnyElement; 2]> = SmallVec::from_iter(Itertools::intersperse_with(
             self.children.into_iter(),
             || Story::divider().into_any_element(),

crates/storybook/src/stories/auto_height_editor.rs 🔗

@@ -1,23 +1,23 @@
 use editor::Editor;
 use gpui::{
-    div, white, IntoElement, KeyBinding, ParentElement, Render, Styled, View, ViewContext,
-    VisualContext, WindowContext,
+    div, white, App, AppContext as _, Context, Entity, IntoElement, KeyBinding, ParentElement,
+    Render, Styled, Window,
 };
 
 pub struct AutoHeightEditorStory {
-    editor: View<Editor>,
+    editor: Entity<Editor>,
 }
 
 impl AutoHeightEditorStory {
-    pub fn new(cx: &mut WindowContext) -> View<Self> {
+    pub fn new(window: &mut Window, cx: &mut App) -> gpui::Entity<Self> {
         cx.bind_keys([KeyBinding::new(
             "enter",
             editor::actions::Newline,
             Some("Editor"),
         )]);
-        cx.new_view(|cx| Self {
-            editor: cx.new_view(|cx| {
-                let mut editor = Editor::auto_height(3, cx);
+        cx.new(|cx| Self {
+            editor: cx.new(|cx| {
+                let mut editor = Editor::auto_height(3, window, cx);
                 editor.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
                 editor
             }),
@@ -26,7 +26,7 @@ impl AutoHeightEditorStory {
 }
 
 impl Render for AutoHeightEditorStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         div()
             .size_full()
             .bg(white())

crates/storybook/src/stories/cursor.rs 🔗

@@ -5,7 +5,7 @@ use ui::prelude::*;
 pub struct CursorStory;
 
 impl Render for CursorStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         let all_cursors: [(&str, Box<dyn Fn(Stateful<Div>) -> Stateful<Div>>); 19] = [
             (
                 "cursor_default",

crates/storybook/src/stories/default_colors.rs 🔗

@@ -1,6 +1,6 @@
 use gpui::{
-    colors, div, prelude::*, DefaultColor, DefaultThemeAppearance, Hsla, Render, View, ViewContext,
-    WindowContext,
+    colors, div, prelude::*, App, Context, DefaultColor, DefaultThemeAppearance, Entity, Hsla,
+    Render, Window,
 };
 use story::Story;
 use strum::IntoEnumIterator;
@@ -9,13 +9,13 @@ use ui::{h_flex, ActiveTheme};
 pub struct DefaultColorsStory;
 
 impl DefaultColorsStory {
-    pub fn view(cx: &mut WindowContext) -> View<Self> {
-        cx.new_view(|_cx| Self)
+    pub fn model(cx: &mut App) -> Entity<Self> {
+        cx.new(|_| Self)
     }
 }
 
 impl Render for DefaultColorsStory {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let appearances = [DefaultThemeAppearance::Light, DefaultThemeAppearance::Dark];
 
         Story::container()

crates/storybook/src/stories/focus.rs 🔗

@@ -1,5 +1,5 @@
 use gpui::{
-    actions, div, prelude::*, FocusHandle, KeyBinding, Render, Subscription, View, WindowContext,
+    actions, div, prelude::*, App, Entity, FocusHandle, KeyBinding, Render, Subscription, Window,
 };
 use ui::prelude::*;
 
@@ -13,34 +13,34 @@ pub struct FocusStory {
 }
 
 impl FocusStory {
-    pub fn view(cx: &mut WindowContext) -> View<Self> {
+    pub fn model(window: &mut Window, cx: &mut App) -> Entity<Self> {
         cx.bind_keys([
             KeyBinding::new("cmd-a", ActionA, Some("parent")),
             KeyBinding::new("cmd-a", ActionB, Some("child-1")),
             KeyBinding::new("cmd-c", ActionC, None),
         ]);
 
-        cx.new_view(move |cx| {
+        cx.new(|cx| {
             let parent_focus = cx.focus_handle();
             let child_1_focus = cx.focus_handle();
             let child_2_focus = cx.focus_handle();
             let _focus_subscriptions = vec![
-                cx.on_focus(&parent_focus, |_, _| {
+                cx.on_focus(&parent_focus, window, |_, _, _| {
                     println!("Parent focused");
                 }),
-                cx.on_blur(&parent_focus, |_, _| {
+                cx.on_blur(&parent_focus, window, |_, _, _| {
                     println!("Parent blurred");
                 }),
-                cx.on_focus(&child_1_focus, |_, _| {
+                cx.on_focus(&child_1_focus, window, |_, _, _| {
                     println!("Child 1 focused");
                 }),
-                cx.on_blur(&child_1_focus, |_, _| {
+                cx.on_blur(&child_1_focus, window, |_, _, _| {
                     println!("Child 1 blurred");
                 }),
-                cx.on_focus(&child_2_focus, |_, _| {
+                cx.on_focus(&child_2_focus, window, |_, _, _| {
                     println!("Child 2 focused");
                 }),
-                cx.on_blur(&child_2_focus, |_, _| {
+                cx.on_blur(&child_2_focus, window, |_, _, _| {
                     println!("Child 2 blurred");
                 }),
             ];
@@ -56,7 +56,7 @@ impl FocusStory {
 }
 
 impl Render for FocusStory {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let theme = cx.theme();
         let color_1 = theme.status().created;
         let color_2 = theme.status().modified;
@@ -70,14 +70,14 @@ impl Render for FocusStory {
             .active(|style| style.bg(color_7))
             .track_focus(&self.parent_focus)
             .key_context("parent")
-            .on_action(cx.listener(|_, _action: &ActionA, _cx| {
+            .on_action(cx.listener(|_, _action: &ActionA, _window, _cx| {
                 println!("Action A dispatched on parent");
             }))
-            .on_action(cx.listener(|_, _action: &ActionB, _cx| {
+            .on_action(cx.listener(|_, _action: &ActionB, _window, _cx| {
                 println!("Action B dispatched on parent");
             }))
-            .on_key_down(cx.listener(|_, event, _| println!("Key down on parent {:?}", event)))
-            .on_key_up(cx.listener(|_, event, _| println!("Key up on parent {:?}", event)))
+            .on_key_down(cx.listener(|_, event, _, _| println!("Key down on parent {:?}", event)))
+            .on_key_up(cx.listener(|_, event, _, _| println!("Key up on parent {:?}", event)))
             .size_full()
             .bg(color_1)
             .focus(|style| style.bg(color_2))
@@ -85,7 +85,7 @@ impl Render for FocusStory {
                 div()
                     .track_focus(&self.child_1_focus)
                     .key_context("child-1")
-                    .on_action(cx.listener(|_, _action: &ActionB, _cx| {
+                    .on_action(cx.listener(|_, _action: &ActionB, _window, _cx| {
                         println!("Action B dispatched on child 1 during");
                     }))
                     .w_full()
@@ -94,25 +94,29 @@ impl Render for FocusStory {
                     .focus(|style| style.bg(color_5))
                     .in_focus(|style| style.bg(color_6))
                     .on_key_down(
-                        cx.listener(|_, event, _| println!("Key down on child 1 {:?}", event)),
+                        cx.listener(|_, event, _, _| println!("Key down on child 1 {:?}", event)),
+                    )
+                    .on_key_up(
+                        cx.listener(|_, event, _, _| println!("Key up on child 1 {:?}", event)),
                     )
-                    .on_key_up(cx.listener(|_, event, _| println!("Key up on child 1 {:?}", event)))
                     .child("Child 1"),
             )
             .child(
                 div()
                     .track_focus(&self.child_2_focus)
                     .key_context("child-2")
-                    .on_action(cx.listener(|_, _action: &ActionC, _cx| {
+                    .on_action(cx.listener(|_, _action: &ActionC, _window, _cx| {
                         println!("Action C dispatched on child 2");
                     }))
                     .w_full()
                     .h_6()
                     .bg(color_4)
                     .on_key_down(
-                        cx.listener(|_, event, _| println!("Key down on child 2 {:?}", event)),
+                        cx.listener(|_, event, _, _| println!("Key down on child 2 {:?}", event)),
+                    )
+                    .on_key_up(
+                        cx.listener(|_, event, _, _| println!("Key up on child 2 {:?}", event)),
                     )
-                    .on_key_up(cx.listener(|_, event, _| println!("Key up on child 2 {:?}", event)))
                     .child("Child 2"),
             )
     }

crates/storybook/src/stories/indent_guides.rs 🔗

@@ -2,7 +2,6 @@ use std::fmt::format;
 
 use gpui::{
     colors, div, prelude::*, uniform_list, DefaultColor, DefaultThemeAppearance, Hsla, Render,
-    View, ViewContext, WindowContext,
 };
 use story::Story;
 use strum::IntoEnumIterator;
@@ -17,7 +16,7 @@ pub struct IndentGuidesStory {
 }
 
 impl IndentGuidesStory {
-    pub fn view(cx: &mut WindowContext) -> View<Self> {
+    pub fn model(window: &mut Window, cx: &mut AppContext) -> Model<Self> {
         let mut depths = Vec::new();
         depths.push(0);
         depths.push(1);
@@ -29,18 +28,18 @@ impl IndentGuidesStory {
         depths.push(1);
         depths.push(0);
 
-        cx.new_view(|_cx| Self { depths })
+        cx.new(|_cx| Self { depths })
     }
 }
 
 impl Render for IndentGuidesStory {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut ModelContext<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title("Indent guides"))
             .child(
                 v_flex().size_full().child(
                     uniform_list(
-                        cx.view().clone(),
+                        cx.model().clone(),
                         "some-list",
                         self.depths.len(),
                         |this, range, cx| {
@@ -61,7 +60,7 @@ impl Render for IndentGuidesStory {
                     )
                     .with_sizing_behavior(gpui::ListSizingBehavior::Infer)
                     .with_decoration(ui::indent_guides(
-                        cx.view().clone(),
+                        cx.model().clone(),
                         px(16.),
                         ui::IndentGuideColors {
                             default: Color::Info.color(cx),

crates/storybook/src/stories/kitchen_sink.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{prelude::*, Render, View};
+use gpui::{prelude::*, Entity, Render};
 use story::Story;
 use strum::IntoEnumIterator;
 use ui::prelude::*;
@@ -8,15 +8,15 @@ use crate::story_selector::ComponentStory;
 pub struct KitchenSinkStory;
 
 impl KitchenSinkStory {
-    pub fn view(cx: &mut WindowContext) -> View<Self> {
-        cx.new_view(|_cx| Self)
+    pub fn model(cx: &mut App) -> Entity<Self> {
+        cx.new(|_| Self)
     }
 }
 
 impl Render for KitchenSinkStory {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let component_stories = ComponentStory::iter()
-            .map(|selector| selector.story(cx))
+            .map(|selector| selector.story(window, cx))
             .collect::<Vec<_>>();
 
         Story::container()

crates/storybook/src/stories/overflow_scroll.rs 🔗

@@ -6,7 +6,7 @@ use ui::prelude::*;
 pub struct OverflowScrollStory;
 
 impl Render for OverflowScrollStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title("Overflow Scroll"))
             .child(Story::label("`overflow_x_scroll`"))

crates/storybook/src/stories/picker.rs 🔗

@@ -1,12 +1,12 @@
 use fuzzy::StringMatchCandidate;
-use gpui::{div, prelude::*, KeyBinding, Render, SharedString, Styled, Task, View, WindowContext};
+use gpui::{div, prelude::*, App, Entity, KeyBinding, Render, SharedString, Styled, Task, Window};
 use picker::{Picker, PickerDelegate};
 use std::sync::Arc;
 use ui::{prelude::*, ListItemSpacing};
 use ui::{Label, ListItem};
 
 pub struct PickerStory {
-    picker: View<Picker<Delegate>>,
+    picker: Entity<Picker<Delegate>>,
 }
 
 struct Delegate {
@@ -37,7 +37,7 @@ impl PickerDelegate for Delegate {
         self.candidates.len()
     }
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Test".into()
     }
 
@@ -45,7 +45,8 @@ impl PickerDelegate for Delegate {
         &self,
         ix: usize,
         selected: bool,
-        _cx: &mut ViewContext<Picker<Self>>,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let candidate_ix = self.matches.get(ix)?;
         // TASK: Make StringMatchCandidate::string a SharedString
@@ -64,12 +65,12 @@ impl PickerDelegate for Delegate {
         self.selected_ix
     }
 
-    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(&mut self, ix: usize, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.selected_ix = ix;
         cx.notify();
     }
 
-    fn confirm(&mut self, secondary: bool, _cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, secondary: bool, _window: &mut Window, _cx: &mut Context<Picker<Self>>) {
         let candidate_ix = self.matches[self.selected_ix];
         let candidate = self.candidates[candidate_ix].string.clone();
 
@@ -80,11 +81,16 @@ impl PickerDelegate for Delegate {
         }
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+    fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         cx.quit();
     }
 
-    fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
+    fn update_matches(
+        &mut self,
+        query: String,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Task<()> {
         let candidates = self.candidates.clone();
         self.matches = cx
             .background_executor()
@@ -105,8 +111,8 @@ impl PickerDelegate for Delegate {
 }
 
 impl PickerStory {
-    pub fn new(cx: &mut WindowContext) -> View<Self> {
-        cx.new_view(|cx| {
+    pub fn new(window: &mut Window, cx: &mut App) -> Entity<Self> {
+        cx.new(|cx| {
             cx.bind_keys([
                 KeyBinding::new("up", menu::SelectPrev, Some("picker")),
                 KeyBinding::new("pageup", menu::SelectFirst, Some("picker")),
@@ -126,7 +132,7 @@ impl PickerStory {
             ]);
 
             PickerStory {
-                picker: cx.new_view(|cx| {
+                picker: cx.new(|cx| {
                     let mut delegate = Delegate::new(&[
                         "Baguette (France)",
                         "Baklava (Turkey)",
@@ -178,10 +184,10 @@ impl PickerStory {
                         "Tzatziki (Greece)",
                         "Wiener Schnitzel (Austria)",
                     ]);
-                    delegate.update_matches("".into(), cx).detach();
+                    delegate.update_matches("".into(), window, cx).detach();
 
-                    let picker = Picker::uniform_list(delegate, cx);
-                    picker.focus(cx);
+                    let picker = Picker::uniform_list(delegate, window, cx);
+                    picker.focus(window, cx);
                     picker
                 }),
             }
@@ -190,7 +196,7 @@ impl PickerStory {
 }
 
 impl Render for PickerStory {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         div()
             .bg(cx.theme().styles.colors.background)
             .size_full()

crates/storybook/src/stories/scroll.rs 🔗

@@ -1,17 +1,17 @@
-use gpui::{div, prelude::*, px, Render, SharedString, Styled, View, WindowContext};
+use gpui::{div, prelude::*, px, App, Entity, Render, SharedString, Styled, Window};
 use ui::prelude::*;
 use ui::Tooltip;
 
 pub struct ScrollStory;
 
 impl ScrollStory {
-    pub fn view(cx: &mut WindowContext) -> View<ScrollStory> {
-        cx.new_view(|_cx| ScrollStory)
+    pub fn model(cx: &mut App) -> Entity<ScrollStory> {
+        cx.new(|_| ScrollStory)
     }
 }
 
 impl Render for ScrollStory {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let theme = cx.theme();
         let color_1 = theme.status().created;
         let color_2 = theme.status().modified;
@@ -35,8 +35,8 @@ impl Render for ScrollStory {
                             color_2
                         };
                         div()
-                            .id(id)
-                            .tooltip(move |cx| Tooltip::text(format!("{}, {}", row, column), cx))
+                            .id(id.clone())
+                            .tooltip(Tooltip::text(id))
                             .bg(bg)
                             .size(px(100_f32))
                             .when(row >= 5 && column >= 5, |d| {

crates/storybook/src/stories/text.rs 🔗

@@ -1,6 +1,6 @@
 use gpui::{
-    div, green, red, HighlightStyle, InteractiveText, IntoElement, ParentElement, Render, Styled,
-    StyledText, View, ViewContext, VisualContext, WindowContext,
+    div, green, red, App, AppContext as _, Context, Entity, HighlightStyle, InteractiveText,
+    IntoElement, ParentElement, Render, Styled, StyledText, Window,
 };
 use indoc::indoc;
 use story::*;
@@ -8,13 +8,13 @@ use story::*;
 pub struct TextStory;
 
 impl TextStory {
-    pub fn view(cx: &mut WindowContext) -> View<Self> {
-        cx.new_view(|_cx| Self)
+    pub fn model(cx: &mut App) -> Entity<Self> {
+        cx.new(|_| Self)
     }
 }
 
 impl Render for TextStory {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title("Text"))
             .children(vec![
@@ -82,7 +82,7 @@ impl Render for TextStory {
                             InteractiveText::new(
                                 "interactive",
                                 StyledText::new("Hello world, how is it going?").with_highlights(
-                                    &cx.text_style(),
+                                    &window.text_style(),
                                     [
                                         (
                                             6..11,
@@ -94,14 +94,14 @@ impl Render for TextStory {
                                     ],
                                 ),
                             )
-                            .on_click(vec![2..4, 1..3, 7..9], |range_ix, _cx| {
+                            .on_click(vec![2..4, 1..3, 7..9], |range_ix, _, _cx| {
                                 println!("Clicked range {range_ix}");
                             }),
                         )
                         .usage(indoc! {r##"
                             InteractiveText::new(
                                 "interactive",
-                                StyledText::new("Hello world, how is it going?").with_highlights(&cx.text_style(), [
+                                StyledText::new("Hello world, how is it going?").with_highlights(&window.text_style(), [
                                     (6..11, HighlightStyle {
                                         background_color: Some(green()),
                                         ..Default::default()

crates/storybook/src/stories/viewport_units.rs 🔗

@@ -6,23 +6,23 @@ use ui::prelude::*;
 pub struct ViewportUnitsStory;
 
 impl Render for ViewportUnitsStory {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
         Story::container().child(
             div()
                 .flex()
                 .flex_row()
                 .child(
                     div()
-                        .w(vw(0.5, cx))
-                        .h(vh(0.8, cx))
+                        .w(vw(0.5, window))
+                        .h(vh(0.8, window))
                         .bg(gpui::red())
                         .text_color(gpui::white())
                         .child("50vw, 80vh"),
                 )
                 .child(
                     div()
-                        .w(vw(0.25, cx))
-                        .h(vh(0.33, cx))
+                        .w(vw(0.25, window))
+                        .h(vh(0.33, window))
                         .bg(gpui::green())
                         .text_color(gpui::white())
                         .child("25vw, 33vh"),

crates/storybook/src/stories/with_rem_size.rs 🔗

@@ -6,7 +6,7 @@ use ui::{prelude::*, utils::WithRemSize};
 pub struct WithRemSizeStory;
 
 impl Render for WithRemSizeStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         Story::container().child(
             Example::new(16., gpui::red())
                 .child(
@@ -47,7 +47,7 @@ impl ParentElement for Example {
 }
 
 impl RenderOnce for Example {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         WithRemSize::new(self.rem_size).child(
             v_flex()
                 .gap_2()

crates/storybook/src/story_selector.rs 🔗

@@ -5,7 +5,7 @@ use crate::stories::*;
 use anyhow::anyhow;
 use clap::builder::PossibleValue;
 use clap::ValueEnum;
-use gpui::{AnyView, VisualContext};
+use gpui::AnyView;
 use strum::{EnumIter, EnumString, IntoEnumIterator};
 use ui::prelude::*;
 
@@ -43,40 +43,40 @@ pub enum ComponentStory {
 }
 
 impl ComponentStory {
-    pub fn story(&self, cx: &mut WindowContext) -> AnyView {
+    pub fn story(&self, window: &mut Window, cx: &mut App) -> AnyView {
         match self {
             Self::ApplicationMenu => cx
-                .new_view(|cx| title_bar::ApplicationMenuStory::new(cx))
+                .new(|cx| title_bar::ApplicationMenuStory::new(window, cx))
                 .into(),
-            Self::AutoHeightEditor => AutoHeightEditorStory::new(cx).into(),
-            Self::Avatar => cx.new_view(|_| ui::AvatarStory).into(),
-            Self::Button => cx.new_view(|_| ui::ButtonStory).into(),
+            Self::AutoHeightEditor => AutoHeightEditorStory::new(window, cx).into(),
+            Self::Avatar => cx.new(|_| ui::AvatarStory).into(),
+            Self::Button => cx.new(|_| ui::ButtonStory).into(),
             Self::CollabNotification => cx
-                .new_view(|_| collab_ui::notifications::CollabNotificationStory)
+                .new(|_| collab_ui::notifications::CollabNotificationStory)
                 .into(),
-            Self::ContextMenu => cx.new_view(|_| ui::ContextMenuStory).into(),
-            Self::Cursor => cx.new_view(|_| crate::stories::CursorStory).into(),
-            Self::DefaultColors => DefaultColorsStory::view(cx).into(),
-            Self::Disclosure => cx.new_view(|_| ui::DisclosureStory).into(),
-            Self::Focus => FocusStory::view(cx).into(),
-            Self::Icon => cx.new_view(|_| ui::IconStory).into(),
-            Self::IconButton => cx.new_view(|_| ui::IconButtonStory).into(),
-            Self::Keybinding => cx.new_view(|_| ui::KeybindingStory).into(),
-            Self::Label => cx.new_view(|_| ui::LabelStory).into(),
-            Self::List => cx.new_view(|_| ui::ListStory).into(),
-            Self::ListHeader => cx.new_view(|_| ui::ListHeaderStory).into(),
-            Self::ListItem => cx.new_view(|_| ui::ListItemStory).into(),
-            Self::OverflowScroll => cx.new_view(|_| crate::stories::OverflowScrollStory).into(),
-            Self::Picker => PickerStory::new(cx).into(),
-            Self::Scroll => ScrollStory::view(cx).into(),
-            Self::Tab => cx.new_view(|_| ui::TabStory).into(),
-            Self::TabBar => cx.new_view(|_| ui::TabBarStory).into(),
-            Self::Text => TextStory::view(cx).into(),
-            Self::ToggleButton => cx.new_view(|_| ui::ToggleButtonStory).into(),
-            Self::ToolStrip => cx.new_view(|_| ui::ToolStripStory).into(),
-            Self::ViewportUnits => cx.new_view(|_| crate::stories::ViewportUnitsStory).into(),
-            Self::WithRemSize => cx.new_view(|_| crate::stories::WithRemSizeStory).into(),
-            Self::Vector => cx.new_view(|_| ui::VectorStory).into(),
+            Self::ContextMenu => cx.new(|_| ui::ContextMenuStory).into(),
+            Self::Cursor => cx.new(|_| crate::stories::CursorStory).into(),
+            Self::DefaultColors => DefaultColorsStory::model(cx).into(),
+            Self::Disclosure => cx.new(|_| ui::DisclosureStory).into(),
+            Self::Focus => FocusStory::model(window, cx).into(),
+            Self::Icon => cx.new(|_| ui::IconStory).into(),
+            Self::IconButton => cx.new(|_| ui::IconButtonStory).into(),
+            Self::Keybinding => cx.new(|_| ui::KeybindingStory).into(),
+            Self::Label => cx.new(|_| ui::LabelStory).into(),
+            Self::List => cx.new(|_| ui::ListStory).into(),
+            Self::ListHeader => cx.new(|_| ui::ListHeaderStory).into(),
+            Self::ListItem => cx.new(|_| ui::ListItemStory).into(),
+            Self::OverflowScroll => cx.new(|_| crate::stories::OverflowScrollStory).into(),
+            Self::Picker => PickerStory::new(window, cx).into(),
+            Self::Scroll => ScrollStory::model(cx).into(),
+            Self::Tab => cx.new(|_| ui::TabStory).into(),
+            Self::TabBar => cx.new(|_| ui::TabBarStory).into(),
+            Self::Text => TextStory::model(cx).into(),
+            Self::ToggleButton => cx.new(|_| ui::ToggleButtonStory).into(),
+            Self::ToolStrip => cx.new(|_| ui::ToolStripStory).into(),
+            Self::ViewportUnits => cx.new(|_| crate::stories::ViewportUnitsStory).into(),
+            Self::WithRemSize => cx.new(|_| crate::stories::WithRemSizeStory).into(),
+            Self::Vector => cx.new(|_| ui::VectorStory).into(),
         }
     }
 }
@@ -91,7 +91,7 @@ impl FromStr for StorySelector {
     type Err = anyhow::Error;
 
     fn from_str(raw_story_name: &str) -> std::result::Result<Self, Self::Err> {
-        use anyhow::Context;
+        use anyhow::Context as _;
 
         let story = raw_story_name.to_ascii_lowercase();
 
@@ -111,10 +111,10 @@ impl FromStr for StorySelector {
 }
 
 impl StorySelector {
-    pub fn story(&self, cx: &mut WindowContext) -> AnyView {
+    pub fn story(&self, window: &mut Window, cx: &mut App) -> AnyView {
         match self {
-            Self::Component(component_story) => component_story.story(cx),
-            Self::KitchenSink => KitchenSinkStory::view(cx).into(),
+            Self::Component(component_story) => component_story.story(window, cx),
+            Self::KitchenSink => KitchenSinkStory::model(cx).into(),
         }
     }
 }

crates/storybook/src/storybook.rs 🔗

@@ -9,8 +9,7 @@ use std::sync::Arc;
 use clap::Parser;
 use dialoguer::FuzzySelect;
 use gpui::{
-    div, px, size, AnyView, AppContext, Bounds, Render, ViewContext, VisualContext, WindowBounds,
-    WindowOptions,
+    div, px, size, AnyView, App, Bounds, Context, Render, Window, WindowBounds, WindowOptions,
 };
 use log::LevelFilter;
 use project::Project;
@@ -65,7 +64,7 @@ fn main() {
     });
     let theme_name = args.theme.unwrap_or("One Dark".to_string());
 
-    gpui::App::new().with_assets(Assets).run(move |cx| {
+    gpui::Application::new().with_assets(Assets).run(move |cx| {
         load_embedded_fonts(cx).unwrap();
 
         let http_client = ReqwestClient::user_agent("zed_storybook").unwrap();
@@ -95,10 +94,10 @@ fn main() {
                 window_bounds: Some(WindowBounds::Windowed(bounds)),
                 ..Default::default()
             },
-            move |cx| {
-                theme::setup_ui_font(cx);
+            move |window, cx| {
+                theme::setup_ui_font(window, cx);
 
-                cx.new_view(|cx| StoryWrapper::new(selector.story(cx)))
+                cx.new(|cx| StoryWrapper::new(selector.story(window, cx)))
             },
         );
 
@@ -118,7 +117,7 @@ impl StoryWrapper {
 }
 
 impl Render for StoryWrapper {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         div()
             .flex()
             .flex_col()
@@ -128,7 +127,7 @@ impl Render for StoryWrapper {
     }
 }
 
-fn load_embedded_fonts(cx: &AppContext) -> gpui::Result<()> {
+fn load_embedded_fonts(cx: &App) -> gpui::Result<()> {
     let font_paths = cx.asset_source().list("fonts")?;
     let mut embedded_fonts = Vec::new();
     for font_path in font_paths {
@@ -144,15 +143,15 @@ fn load_embedded_fonts(cx: &AppContext) -> gpui::Result<()> {
     cx.text_system().add_fonts(embedded_fonts)
 }
 
-fn load_storybook_keymap(cx: &mut AppContext) {
+fn load_storybook_keymap(cx: &mut App) {
     cx.bind_keys(KeymapFile::load_asset("keymaps/storybook.json", cx).unwrap());
 }
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     cx.on_action(quit);
 }
 
-fn quit(_: &Quit, cx: &mut AppContext) {
+fn quit(_: &Quit, cx: &mut App) {
     cx.spawn(|cx| async move {
         cx.update(|cx| cx.quit())?;
         anyhow::Ok(())

crates/supermaven/src/supermaven.rs 🔗

@@ -9,9 +9,7 @@ use client::{proto, Client};
 use collections::BTreeMap;
 
 use futures::{channel::mpsc, io::BufReader, AsyncBufReadExt, StreamExt};
-use gpui::{
-    actions, AppContext, AsyncAppContext, EntityId, Global, Model, ModelContext, Task, WeakModel,
-};
+use gpui::{actions, App, AsyncAppContext, Context, Entity, EntityId, Global, Task, WeakEntity};
 use language::{
     language_settings::all_language_settings, Anchor, Buffer, BufferSnapshot, ToOffset,
 };
@@ -29,8 +27,8 @@ use util::ResultExt;
 
 actions!(supermaven, [SignOut]);
 
-pub fn init(client: Arc<Client>, cx: &mut AppContext) {
-    let supermaven = cx.new_model(|_| Supermaven::Starting);
+pub fn init(client: Arc<Client>, cx: &mut App) {
+    let supermaven = cx.new(|_| Supermaven::Starting);
     Supermaven::set_global(supermaven.clone(), cx);
 
     let mut provider = all_language_settings(None, cx).inline_completions.provider;
@@ -73,21 +71,21 @@ pub enum AccountStatus {
 }
 
 #[derive(Clone)]
-struct SupermavenGlobal(Model<Supermaven>);
+struct SupermavenGlobal(Entity<Supermaven>);
 
 impl Global for SupermavenGlobal {}
 
 impl Supermaven {
-    pub fn global(cx: &AppContext) -> Option<Model<Self>> {
+    pub fn global(cx: &App) -> Option<Entity<Self>> {
         cx.try_global::<SupermavenGlobal>()
             .map(|model| model.0.clone())
     }
 
-    pub fn set_global(supermaven: Model<Self>, cx: &mut AppContext) {
+    pub fn set_global(supermaven: Entity<Self>, cx: &mut App) {
         cx.set_global(SupermavenGlobal(supermaven));
     }
 
-    pub fn start(&mut self, client: Arc<Client>, cx: &mut ModelContext<Self>) {
+    pub fn start(&mut self, client: Arc<Client>, cx: &mut Context<Self>) {
         if let Self::Starting = self {
             cx.spawn(|this, mut cx| async move {
                 let binary_path =
@@ -115,9 +113,9 @@ impl Supermaven {
 
     pub fn complete(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         cursor_position: Anchor,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<SupermavenCompletion> {
         if let Self::Spawned(agent) = self {
             let buffer_id = buffer.entity_id();
@@ -179,9 +177,9 @@ impl Supermaven {
 
     pub fn completion(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         cursor_position: Anchor,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<&str> {
         if let Self::Spawned(agent) = self {
             find_relevant_completion(
@@ -267,7 +265,7 @@ impl SupermavenAgent {
     fn new(
         binary_path: PathBuf,
         client: Arc<Client>,
-        cx: &mut ModelContext<Supermaven>,
+        cx: &mut Context<Supermaven>,
     ) -> Result<Self> {
         let mut process = util::command::new_smol_command(&binary_path)
             .arg("stdio")
@@ -342,7 +340,7 @@ impl SupermavenAgent {
     }
 
     async fn handle_incoming_messages(
-        this: WeakModel<Supermaven>,
+        this: WeakEntity<Supermaven>,
         stdout: ChildStdout,
         mut cx: AsyncAppContext,
     ) -> Result<()> {

crates/supermaven/src/supermaven_completion_provider.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{Supermaven, SupermavenCompletionStateId};
 use anyhow::Result;
 use futures::StreamExt as _;
-use gpui::{AppContext, EntityId, Model, ModelContext, Task};
+use gpui::{App, Context, Entity, EntityId, Task};
 use inline_completion::{Direction, InlineCompletion, InlineCompletionProvider};
 use language::{language_settings::all_language_settings, Anchor, Buffer, BufferSnapshot};
 use std::{
@@ -15,7 +15,7 @@ use unicode_segmentation::UnicodeSegmentation;
 pub const DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
 
 pub struct SupermavenCompletionProvider {
-    supermaven: Model<Supermaven>,
+    supermaven: Entity<Supermaven>,
     buffer_id: Option<EntityId>,
     completion_id: Option<SupermavenCompletionStateId>,
     file_extension: Option<String>,
@@ -23,7 +23,7 @@ pub struct SupermavenCompletionProvider {
 }
 
 impl SupermavenCompletionProvider {
-    pub fn new(supermaven: Model<Supermaven>) -> Self {
+    pub fn new(supermaven: Entity<Supermaven>) -> Self {
         Self {
             supermaven,
             buffer_id: None,
@@ -113,7 +113,7 @@ impl InlineCompletionProvider for SupermavenCompletionProvider {
         false
     }
 
-    fn is_enabled(&self, buffer: &Model<Buffer>, cursor_position: Anchor, cx: &AppContext) -> bool {
+    fn is_enabled(&self, buffer: &Entity<Buffer>, cursor_position: Anchor, cx: &App) -> bool {
         if !self.supermaven.read(cx).is_enabled() {
             return false;
         }
@@ -131,10 +131,10 @@ impl InlineCompletionProvider for SupermavenCompletionProvider {
 
     fn refresh(
         &mut self,
-        buffer_handle: Model<Buffer>,
+        buffer_handle: Entity<Buffer>,
         cursor_position: Anchor,
         debounce: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let Some(mut completion) = self.supermaven.update(cx, |supermaven, cx| {
             supermaven.complete(&buffer_handle, cursor_position, cx)
@@ -169,28 +169,28 @@ impl InlineCompletionProvider for SupermavenCompletionProvider {
 
     fn cycle(
         &mut self,
-        _buffer: Model<Buffer>,
+        _buffer: Entity<Buffer>,
         _cursor_position: Anchor,
         _direction: Direction,
-        _cx: &mut ModelContext<Self>,
+        _cx: &mut Context<Self>,
     ) {
     }
 
-    fn accept(&mut self, _cx: &mut ModelContext<Self>) {
+    fn accept(&mut self, _cx: &mut Context<Self>) {
         self.pending_refresh = None;
         self.completion_id = None;
     }
 
-    fn discard(&mut self, _cx: &mut ModelContext<Self>) {
+    fn discard(&mut self, _cx: &mut Context<Self>) {
         self.pending_refresh = None;
         self.completion_id = None;
     }
 
     fn suggest(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         cursor_position: Anchor,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<InlineCompletion> {
         let completion_text = self
             .supermaven

crates/supermaven_api/src/supermaven_api.rs 🔗

@@ -1,4 +1,4 @@
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use futures::io::BufReader;
 use futures::AsyncReadExt;
 use http_client::{AsyncBody, HttpClient, Request as HttpRequest};

crates/tab_switcher/src/tab_switcher.rs 🔗

@@ -4,9 +4,9 @@ mod tab_switcher_tests;
 use collections::HashMap;
 use editor::items::entry_git_aware_label_color;
 use gpui::{
-    actions, impl_actions, rems, Action, AnyElement, AppContext, DismissEvent, EntityId,
-    EventEmitter, FocusHandle, FocusableView, Model, Modifiers, ModifiersChangedEvent, MouseButton,
-    MouseUpEvent, ParentElement, Render, Styled, Task, View, ViewContext, VisualContext, WeakView,
+    actions, impl_actions, rems, Action, AnyElement, App, Context, DismissEvent, Entity, EntityId,
+    EventEmitter, FocusHandle, Focusable, Modifiers, ModifiersChangedEvent, MouseButton,
+    MouseUpEvent, ParentElement, Render, Styled, Task, WeakEntity, Window,
 };
 use picker::{Picker, PickerDelegate};
 use project::Project;
@@ -34,33 +34,42 @@ impl_actions!(tab_switcher, [Toggle]);
 actions!(tab_switcher, [CloseSelectedItem]);
 
 pub struct TabSwitcher {
-    picker: View<Picker<TabSwitcherDelegate>>,
+    picker: Entity<Picker<TabSwitcherDelegate>>,
     init_modifiers: Option<Modifiers>,
 }
 
 impl ModalView for TabSwitcher {}
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(TabSwitcher::register).detach();
+pub fn init(cx: &mut App) {
+    cx.observe_new(TabSwitcher::register).detach();
 }
 
 impl TabSwitcher {
-    fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-        workspace.register_action(|workspace, action: &Toggle, cx| {
+    fn register(
+        workspace: &mut Workspace,
+        _window: Option<&mut Window>,
+        _: &mut Context<Workspace>,
+    ) {
+        workspace.register_action(|workspace, action: &Toggle, window, cx| {
             let Some(tab_switcher) = workspace.active_modal::<Self>(cx) else {
-                Self::open(action, workspace, cx);
+                Self::open(action, workspace, window, cx);
                 return;
             };
 
             tab_switcher.update(cx, |tab_switcher, cx| {
                 tab_switcher
                     .picker
-                    .update(cx, |picker, cx| picker.cycle_selection(cx))
+                    .update(cx, |picker, cx| picker.cycle_selection(window, cx))
             });
         });
     }
 
-    fn open(action: &Toggle, workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
+    fn open(
+        action: &Toggle,
+        workspace: &mut Workspace,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) {
         let mut weak_pane = workspace.active_pane().downgrade();
         for dock in [
             workspace.left_dock(),
@@ -70,7 +79,7 @@ impl TabSwitcher {
             dock.update(cx, |this, cx| {
                 let Some(panel) = this
                     .active_panel()
-                    .filter(|panel| panel.focus_handle(cx).contains_focused(cx))
+                    .filter(|panel| panel.panel_focus_handle(cx).contains_focused(window, cx))
                 else {
                     return;
                 };
@@ -81,24 +90,31 @@ impl TabSwitcher {
         }
 
         let project = workspace.project().clone();
-        workspace.toggle_modal(cx, |cx| {
-            let delegate =
-                TabSwitcherDelegate::new(project, action, cx.view().downgrade(), weak_pane, cx);
-            TabSwitcher::new(delegate, cx)
+        workspace.toggle_modal(window, cx, |window, cx| {
+            let delegate = TabSwitcherDelegate::new(
+                project,
+                action,
+                cx.model().downgrade(),
+                weak_pane,
+                window,
+                cx,
+            );
+            TabSwitcher::new(delegate, window, cx)
         });
     }
 
-    fn new(delegate: TabSwitcherDelegate, cx: &mut ViewContext<Self>) -> Self {
+    fn new(delegate: TabSwitcherDelegate, window: &mut Window, cx: &mut Context<Self>) -> Self {
         Self {
-            picker: cx.new_view(|cx| Picker::nonsearchable_uniform_list(delegate, cx)),
-            init_modifiers: cx.modifiers().modified().then_some(cx.modifiers()),
+            picker: cx.new(|cx| Picker::nonsearchable_uniform_list(delegate, window, cx)),
+            init_modifiers: window.modifiers().modified().then_some(window.modifiers()),
         }
     }
 
     fn handle_modifiers_changed(
         &mut self,
         event: &ModifiersChangedEvent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let Some(init_modifiers) = self.init_modifiers else {
             return;
@@ -108,30 +124,35 @@ impl TabSwitcher {
             if self.picker.read(cx).delegate.matches.is_empty() {
                 cx.emit(DismissEvent)
             } else {
-                cx.dispatch_action(menu::Confirm.boxed_clone());
+                window.dispatch_action(menu::Confirm.boxed_clone(), cx);
             }
         }
     }
 
-    fn handle_close_selected_item(&mut self, _: &CloseSelectedItem, cx: &mut ViewContext<Self>) {
+    fn handle_close_selected_item(
+        &mut self,
+        _: &CloseSelectedItem,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.picker.update(cx, |picker, cx| {
             picker
                 .delegate
-                .close_item_at(picker.delegate.selected_index(), cx)
+                .close_item_at(picker.delegate.selected_index(), window, cx)
         });
     }
 }
 
 impl EventEmitter<DismissEvent> for TabSwitcher {}
 
-impl FocusableView for TabSwitcher {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for TabSwitcher {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
 
 impl Render for TabSwitcher {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         v_flex()
             .key_context("TabSwitcher")
             .w(rems(PANEL_WIDTH_REMS))
@@ -150,22 +171,23 @@ struct TabMatch {
 
 pub struct TabSwitcherDelegate {
     select_last: bool,
-    tab_switcher: WeakView<TabSwitcher>,
+    tab_switcher: WeakEntity<TabSwitcher>,
     selected_index: usize,
-    pane: WeakView<Pane>,
-    project: Model<Project>,
+    pane: WeakEntity<Pane>,
+    project: Entity<Project>,
     matches: Vec<TabMatch>,
 }
 
 impl TabSwitcherDelegate {
     fn new(
-        project: Model<Project>,
+        project: Entity<Project>,
         action: &Toggle,
-        tab_switcher: WeakView<TabSwitcher>,
-        pane: WeakView<Pane>,
-        cx: &mut ViewContext<TabSwitcher>,
+        tab_switcher: WeakEntity<TabSwitcher>,
+        pane: WeakEntity<Pane>,
+        window: &mut Window,
+        cx: &mut Context<TabSwitcher>,
     ) -> Self {
-        Self::subscribe_to_updates(&pane, cx);
+        Self::subscribe_to_updates(&pane, window, cx);
         Self {
             select_last: action.select_last,
             tab_switcher,
@@ -176,16 +198,20 @@ impl TabSwitcherDelegate {
         }
     }
 
-    fn subscribe_to_updates(pane: &WeakView<Pane>, cx: &mut ViewContext<TabSwitcher>) {
+    fn subscribe_to_updates(
+        pane: &WeakEntity<Pane>,
+        window: &mut Window,
+        cx: &mut Context<TabSwitcher>,
+    ) {
         let Some(pane) = pane.upgrade() else {
             return;
         };
-        cx.subscribe(&pane, |tab_switcher, _, event, cx| {
+        cx.subscribe_in(&pane, window, |tab_switcher, _, event, window, cx| {
             match event {
                 PaneEvent::AddItem { .. }
                 | PaneEvent::RemovedItem { .. }
                 | PaneEvent::Remove { .. } => tab_switcher.picker.update(cx, |picker, cx| {
-                    picker.delegate.update_matches(cx);
+                    picker.delegate.update_matches(window, cx);
                     cx.notify();
                 }),
                 _ => {}
@@ -194,7 +220,7 @@ impl TabSwitcherDelegate {
         .detach();
     }
 
-    fn update_matches(&mut self, cx: &mut WindowContext) {
+    fn update_matches(&mut self, _window: &mut Window, cx: &mut App) {
         let selected_item_id = self.selected_item_id();
         self.matches.clear();
         let Some(pane) = self.pane.upgrade() else {
@@ -272,7 +298,12 @@ impl TabSwitcherDelegate {
         0
     }
 
-    fn close_item_at(&mut self, ix: usize, cx: &mut ViewContext<Picker<TabSwitcherDelegate>>) {
+    fn close_item_at(
+        &mut self,
+        ix: usize,
+        window: &mut Window,
+        cx: &mut Context<Picker<TabSwitcherDelegate>>,
+    ) {
         let Some(tab_match) = self.matches.get(ix) else {
             return;
         };
@@ -280,7 +311,7 @@ impl TabSwitcherDelegate {
             return;
         };
         pane.update(cx, |pane, cx| {
-            pane.close_item_by_id(tab_match.item.item_id(), SaveIntent::Close, cx)
+            pane.close_item_by_id(tab_match.item.item_id(), SaveIntent::Close, window, cx)
                 .detach_and_log_err(cx);
         });
     }
@@ -289,11 +320,11 @@ impl TabSwitcherDelegate {
 impl PickerDelegate for TabSwitcherDelegate {
     type ListItem = ListItem;
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         Arc::default()
     }
 
-    fn no_matches_text(&self, _cx: &mut WindowContext) -> SharedString {
+    fn no_matches_text(&self, _window: &mut Window, _cx: &mut App) -> SharedString {
         "No tabs".into()
     }
 
@@ -305,7 +336,7 @@ impl PickerDelegate for TabSwitcherDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(&mut self, ix: usize, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.selected_index = ix;
         cx.notify();
     }
@@ -317,13 +348,19 @@ impl PickerDelegate for TabSwitcherDelegate {
     fn update_matches(
         &mut self,
         _raw_query: String,
-        cx: &mut ViewContext<Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Task<()> {
-        self.update_matches(cx);
+        self.update_matches(window, cx);
         Task::ready(())
     }
 
-    fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<TabSwitcherDelegate>>) {
+    fn confirm(
+        &mut self,
+        _secondary: bool,
+        window: &mut Window,
+        cx: &mut Context<Picker<TabSwitcherDelegate>>,
+    ) {
         let Some(pane) = self.pane.upgrade() else {
             return;
         };
@@ -331,11 +368,11 @@ impl PickerDelegate for TabSwitcherDelegate {
             return;
         };
         pane.update(cx, |pane, cx| {
-            pane.activate_item(selected_match.item_index, true, true, cx);
+            pane.activate_item(selected_match.item_index, true, true, window, cx);
         });
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<TabSwitcherDelegate>>) {
+    fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<TabSwitcherDelegate>>) {
         self.tab_switcher
             .update(cx, |_, cx| cx.emit(DismissEvent))
             .log_err();
@@ -345,7 +382,8 @@ impl PickerDelegate for TabSwitcherDelegate {
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let tab_match = self
             .matches
@@ -357,9 +395,9 @@ impl PickerDelegate for TabSwitcherDelegate {
             selected: true,
             preview: tab_match.preview,
         };
-        let label = tab_match.item.tab_content(params, cx);
+        let label = tab_match.item.tab_content(params, window, cx);
 
-        let icon = tab_match.item.tab_icon(cx).map(|icon| {
+        let icon = tab_match.item.tab_icon(window, cx).map(|icon| {
             let git_status_color = ItemSettings::get_global(cx)
                 .git_status
                 .then(|| {
@@ -403,16 +441,16 @@ impl PickerDelegate for TabSwitcherDelegate {
             // See the same handler in Picker for more details.
             .on_mouse_up(
                 MouseButton::Right,
-                cx.listener(move |picker, _: &MouseUpEvent, cx| {
+                cx.listener(move |picker, _: &MouseUpEvent, window, cx| {
                     cx.stop_propagation();
-                    picker.delegate.close_item_at(ix, cx);
+                    picker.delegate.close_item_at(ix, window, cx);
                 }),
             )
             .child(
                 IconButton::new("close_tab", IconName::Close)
                     .icon_size(IconSize::Small)
                     .icon_color(indicator_color)
-                    .tooltip(|cx| Tooltip::text("Close", cx)),
+                    .tooltip(Tooltip::text("Close")),
             )
             .into_any_element();
 

crates/tab_switcher/src/tab_switcher_tests.rs 🔗

@@ -35,7 +35,8 @@ async fn test_open_with_prev_tab_selected_and_cycle_on_toggle_action(
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+    let (workspace, cx) =
+        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
     let tab_1 = open_buffer("1.txt", &workspace, cx).await;
     let tab_2 = open_buffer("2.txt", &workspace, cx).await;
@@ -90,7 +91,8 @@ async fn test_open_with_last_tab_selected(cx: &mut gpui::TestAppContext) {
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+    let (workspace, cx) =
+        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
     let tab_1 = open_buffer("1.txt", &workspace, cx).await;
     let tab_2 = open_buffer("2.txt", &workspace, cx).await;
@@ -123,7 +125,8 @@ async fn test_open_item_on_modifiers_release(cx: &mut gpui::TestAppContext) {
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+    let (workspace, cx) =
+        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
     let tab_1 = open_buffer("1.txt", &workspace, cx).await;
     let tab_2 = open_buffer("2.txt", &workspace, cx).await;
@@ -150,7 +153,8 @@ async fn test_open_on_empty_pane(cx: &mut gpui::TestAppContext) {
     app_state.fs.as_fake().insert_tree("/root", json!({})).await;
 
     let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+    let (workspace, cx) =
+        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
     cx.simulate_modifiers_change(Modifiers::control());
     let tab_switcher = open_tab_switcher(false, &workspace, cx);
@@ -172,7 +176,8 @@ async fn test_open_with_single_item(cx: &mut gpui::TestAppContext) {
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+    let (workspace, cx) =
+        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
     let tab = open_buffer("1.txt", &workspace, cx).await;
 
@@ -199,7 +204,8 @@ async fn test_close_selected_item(cx: &mut gpui::TestAppContext) {
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+    let (workspace, cx) =
+        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
     let tab_1 = open_buffer("1.txt", &workspace, cx).await;
     let tab_2 = open_buffer("2.txt", &workspace, cx).await;
@@ -245,7 +251,8 @@ async fn test_close_preserves_selected_position(cx: &mut gpui::TestAppContext) {
         .await;
 
     let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-    let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+    let (workspace, cx) =
+        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
     let tab_1 = open_buffer("1.txt", &workspace, cx).await;
     let tab_2 = open_buffer("2.txt", &workspace, cx).await;
@@ -291,18 +298,18 @@ fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {
 #[track_caller]
 fn open_tab_switcher(
     select_last: bool,
-    workspace: &View<Workspace>,
+    workspace: &Entity<Workspace>,
     cx: &mut VisualTestContext,
-) -> View<Picker<TabSwitcherDelegate>> {
+) -> Entity<Picker<TabSwitcherDelegate>> {
     cx.dispatch_action(Toggle { select_last });
     get_active_tab_switcher(workspace, cx)
 }
 
 #[track_caller]
 fn get_active_tab_switcher(
-    workspace: &View<Workspace>,
+    workspace: &Entity<Workspace>,
     cx: &mut VisualTestContext,
-) -> View<Picker<TabSwitcherDelegate>> {
+) -> Entity<Picker<TabSwitcherDelegate>> {
     workspace.update(cx, |workspace, cx| {
         workspace
             .active_modal::<TabSwitcher>(cx)
@@ -315,7 +322,7 @@ fn get_active_tab_switcher(
 
 async fn open_buffer(
     file_path: &str,
-    workspace: &View<Workspace>,
+    workspace: &Entity<Workspace>,
     cx: &mut gpui::VisualTestContext,
 ) -> Box<dyn ItemHandle> {
     let project = workspace.update(cx, |workspace, _| workspace.project().clone());
@@ -328,8 +335,8 @@ async fn open_buffer(
         path: Arc::from(Path::new(file_path)),
     };
     workspace
-        .update(cx, move |workspace, cx| {
-            workspace.open_path(project_path, None, true, cx)
+        .update_in(cx, move |workspace, window, cx| {
+            workspace.open_path(project_path, None, true, window, cx)
         })
         .await
         .unwrap()
@@ -364,7 +371,7 @@ fn assert_match_at_position(
 }
 
 #[track_caller]
-fn assert_tab_switcher_is_closed(workspace: View<Workspace>, cx: &mut VisualTestContext) {
+fn assert_tab_switcher_is_closed(workspace: Entity<Workspace>, cx: &mut VisualTestContext) {
     workspace.update(cx, |workspace, cx| {
         assert!(
             workspace.active_modal::<TabSwitcher>(cx).is_none(),

crates/task/src/static_source.rs 🔗

@@ -3,7 +3,7 @@
 use std::sync::Arc;
 
 use futures::{channel::mpsc::UnboundedSender, StreamExt};
-use gpui::AppContext;
+use gpui::App;
 use parking_lot::RwLock;
 use serde::Deserialize;
 use util::ResultExt;
@@ -27,7 +27,7 @@ impl<T: PartialEq + 'static + Sync> TrackedFile<T> {
     pub fn new(
         mut tracker: UnboundedReceiver<String>,
         notification_outlet: UnboundedSender<()>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Self
     where
         T: for<'a> Deserialize<'a> + Default + Send,
@@ -69,7 +69,7 @@ impl<T: PartialEq + 'static + Sync> TrackedFile<T> {
     pub fn new_convertible<U: for<'a> Deserialize<'a> + TryInto<T, Error = anyhow::Error>>(
         mut tracker: UnboundedReceiver<String>,
         notification_outlet: UnboundedSender<()>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Self
     where
         T: Default + Send,

crates/tasks_ui/Cargo.toml 🔗

@@ -8,6 +8,9 @@ license = "GPL-3.0-or-later"
 [lints]
 workspace = true
 
+[lib]
+path = "src/tasks_ui.rs"
+
 [dependencies]
 anyhow.workspace = true
 editor.workspace = true

crates/tasks_ui/src/modal.rs 🔗

@@ -3,9 +3,9 @@ use std::sync::Arc;
 use crate::active_item_selection_properties;
 use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{
-    rems, Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusableView,
-    InteractiveElement, Model, ParentElement, Render, SharedString, Styled, Subscription, Task,
-    View, ViewContext, VisualContext, WeakView,
+    rems, Action, AnyElement, App, AppContext as _, Context, DismissEvent, Entity, EventEmitter,
+    Focusable, InteractiveElement, ParentElement, Render, SharedString, Styled, Subscription, Task,
+    WeakEntity, Window,
 };
 use picker::{highlighted_match_with_paths::HighlightedText, Picker, PickerDelegate};
 use project::{task_store::TaskStore, TaskSourceKind};
@@ -14,7 +14,6 @@ use ui::{
     div, h_flex, v_flex, ActiveTheme, Button, ButtonCommon, ButtonSize, Clickable, Color,
     FluentBuilder as _, Icon, IconButton, IconButtonShape, IconName, IconSize, IntoElement,
     KeyBinding, LabelSize, ListItem, ListItemSpacing, RenderOnce, Toggleable, Tooltip,
-    WindowContext,
 };
 use util::ResultExt;
 use workspace::{tasks::schedule_resolved_task, ModalView, Workspace};
@@ -22,14 +21,14 @@ pub use zed_actions::{Rerun, Spawn};
 
 /// A modal used to spawn new tasks.
 pub(crate) struct TasksModalDelegate {
-    task_store: Model<TaskStore>,
+    task_store: Entity<TaskStore>,
     candidates: Option<Vec<(TaskSourceKind, ResolvedTask)>>,
     task_overrides: Option<TaskOverrides>,
     last_used_candidate_index: Option<usize>,
     divider_index: Option<usize>,
     matches: Vec<StringMatch>,
     selected_index: usize,
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     prompt: String,
     task_context: TaskContext,
     placeholder_text: Arc<str>,
@@ -44,10 +43,10 @@ pub(crate) struct TaskOverrides {
 
 impl TasksModalDelegate {
     fn new(
-        task_store: Model<TaskStore>,
+        task_store: Entity<TaskStore>,
         task_context: TaskContext,
         task_overrides: Option<TaskOverrides>,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
     ) -> Self {
         let placeholder_text = if let Some(TaskOverrides {
             reveal_target: Some(RevealTarget::Center),
@@ -96,7 +95,7 @@ impl TasksModalDelegate {
         ))
     }
 
-    fn delete_previously_used(&mut self, ix: usize, cx: &mut AppContext) {
+    fn delete_previously_used(&mut self, ix: usize, cx: &mut App) {
         let Some(candidates) = self.candidates.as_mut() else {
             return;
         };
@@ -116,21 +115,23 @@ impl TasksModalDelegate {
 }
 
 pub(crate) struct TasksModal {
-    picker: View<Picker<TasksModalDelegate>>,
+    picker: Entity<Picker<TasksModalDelegate>>,
     _subscription: Subscription,
 }
 
 impl TasksModal {
     pub(crate) fn new(
-        task_store: Model<TaskStore>,
+        task_store: Entity<TaskStore>,
         task_context: TaskContext,
         task_overrides: Option<TaskOverrides>,
-        workspace: WeakView<Workspace>,
-        cx: &mut ViewContext<Self>,
+        workspace: WeakEntity<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
-        let picker = cx.new_view(|cx| {
+        let picker = cx.new(|cx| {
             Picker::uniform_list(
                 TasksModalDelegate::new(task_store, task_context, task_overrides, workspace),
+                window,
                 cx,
             )
         });
@@ -145,7 +146,11 @@ impl TasksModal {
 }
 
 impl Render for TasksModal {
-    fn render(&mut self, _: &mut ViewContext<Self>) -> impl gpui::prelude::IntoElement {
+    fn render(
+        &mut self,
+        _window: &mut Window,
+        _: &mut Context<Self>,
+    ) -> impl gpui::prelude::IntoElement {
         v_flex()
             .key_context("TasksModal")
             .w(rems(34.))
@@ -155,8 +160,8 @@ impl Render for TasksModal {
 
 impl EventEmitter<DismissEvent> for TasksModal {}
 
-impl FocusableView for TasksModal {
-    fn focus_handle(&self, cx: &gpui::AppContext) -> gpui::FocusHandle {
+impl Focusable for TasksModal {
+    fn focus_handle(&self, cx: &gpui::App) -> gpui::FocusHandle {
         self.picker.read(cx).focus_handle(cx)
     }
 }
@@ -174,20 +179,26 @@ impl PickerDelegate for TasksModalDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, _cx: &mut ViewContext<picker::Picker<Self>>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        _window: &mut Window,
+        _cx: &mut Context<picker::Picker<Self>>,
+    ) {
         self.selected_index = ix;
     }
 
-    fn placeholder_text(&self, _: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _: &mut App) -> Arc<str> {
         self.placeholder_text.clone()
     }
 
     fn update_matches(
         &mut self,
         query: String,
-        cx: &mut ViewContext<picker::Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<picker::Picker<Self>>,
     ) -> Task<()> {
-        cx.spawn(move |picker, mut cx| async move {
+        cx.spawn_in(window, move |picker, mut cx| async move {
             let Some(candidates) = picker
                 .update(&mut cx, |picker, cx| {
                     match &mut picker.delegate.candidates {
@@ -271,7 +282,12 @@ impl PickerDelegate for TasksModalDelegate {
         })
     }
 
-    fn confirm(&mut self, omit_history_entry: bool, cx: &mut ViewContext<picker::Picker<Self>>) {
+    fn confirm(
+        &mut self,
+        omit_history_entry: bool,
+        _: &mut Window,
+        cx: &mut Context<picker::Picker<Self>>,
+    ) {
         let current_match_index = self.selected_index();
         let task = self
             .matches
@@ -302,7 +318,7 @@ impl PickerDelegate for TasksModalDelegate {
         cx.emit(DismissEvent);
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<picker::Picker<Self>>) {
+    fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<picker::Picker<Self>>) {
         cx.emit(DismissEvent);
     }
 
@@ -310,7 +326,8 @@ impl PickerDelegate for TasksModalDelegate {
         &self,
         ix: usize,
         selected: bool,
-        cx: &mut ViewContext<picker::Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<picker::Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let candidates = self.candidates.as_ref()?;
         let hit = &self.matches[ix];
@@ -336,7 +353,7 @@ impl PickerDelegate for TasksModalDelegate {
         let tooltip_label = if tooltip_label_text.trim().is_empty() {
             None
         } else {
-            Some(Tooltip::text(tooltip_label_text, cx))
+            Some(Tooltip::simple(tooltip_label_text, cx))
         };
 
         let highlighted_location = HighlightedText {
@@ -377,7 +394,7 @@ impl PickerDelegate for TasksModalDelegate {
                 .end_slot::<AnyElement>(history_run_icon)
                 .spacing(ListItemSpacing::Sparse)
                 .when_some(tooltip_label, |list_item, item_label| {
-                    list_item.tooltip(move |_| item_label.clone())
+                    list_item.tooltip(move |_, _| item_label.clone())
                 })
                 .map(|item| {
                     let item = if matches!(source_kind, TaskSourceKind::UserInput)
@@ -390,9 +407,9 @@ impl PickerDelegate for TasksModalDelegate {
                                 .icon_color(Color::Muted)
                                 .size(ButtonSize::None)
                                 .icon_size(IconSize::XSmall)
-                                .on_click(cx.listener(move |picker, _event, cx| {
+                                .on_click(cx.listener(move |picker, _event, window, cx| {
                                     cx.stop_propagation();
-                                    cx.prevent_default();
+                                    window.prevent_default();
 
                                     picker.delegate.delete_previously_used(task_index, cx);
                                     picker.delegate.last_used_candidate_index = picker
@@ -400,10 +417,10 @@ impl PickerDelegate for TasksModalDelegate {
                                         .last_used_candidate_index
                                         .unwrap_or(0)
                                         .checked_sub(1);
-                                    picker.refresh(cx);
+                                    picker.refresh(window, cx);
                                 }))
-                                .tooltip(|cx| {
-                                    Tooltip::text("Delete Previously Scheduled Task", cx)
+                                .tooltip(|_, cx| {
+                                    Tooltip::simple("Delete Previously Scheduled Task", cx)
                                 }),
                         );
                         item.end_hover_slot(delete_button)
@@ -413,14 +430,15 @@ impl PickerDelegate for TasksModalDelegate {
                     item
                 })
                 .toggle_state(selected)
-                .child(highlighted_location.render(cx)),
+                .child(highlighted_location.render(window, cx)),
         )
     }
 
     fn confirm_completion(
         &mut self,
         _: String,
-        _: &mut ViewContext<Picker<Self>>,
+        _window: &mut Window,
+        _: &mut Context<Picker<Self>>,
     ) -> Option<String> {
         let task_index = self.matches.get(self.selected_index())?.candidate_id;
         let tasks = self.candidates.as_ref()?;
@@ -428,7 +446,12 @@ impl PickerDelegate for TasksModalDelegate {
         Some(task.resolved.as_ref()?.command_label.clone())
     }
 
-    fn confirm_input(&mut self, omit_history_entry: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm_input(
+        &mut self,
+        omit_history_entry: bool,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) {
         let Some((task_source_kind, mut task)) = self.spawn_oneshot() else {
             return;
         };
@@ -456,9 +479,13 @@ impl PickerDelegate for TasksModalDelegate {
             Vec::new()
         }
     }
-    fn render_footer(&self, cx: &mut ViewContext<Picker<Self>>) -> Option<gpui::AnyElement> {
+    fn render_footer(
+        &self,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Option<gpui::AnyElement> {
         let is_recent_selected = self.divider_index >= Some(self.selected_index);
-        let current_modifiers = cx.modifiers();
+        let current_modifiers = window.modifiers();
         let left_button = if self
             .task_store
             .read(cx)
@@ -484,13 +511,13 @@ impl PickerDelegate for TasksModalDelegate {
                 .child(
                     left_button
                         .map(|(label, action)| {
-                            let keybind = KeyBinding::for_action(&*action, cx);
+                            let keybind = KeyBinding::for_action(&*action, window);
 
                             Button::new("edit-current-task", label)
                                 .label_size(LabelSize::Small)
                                 .when_some(keybind, |this, keybind| this.key_binding(keybind))
-                                .on_click(move |_, cx| {
-                                    cx.dispatch_action(action.boxed_clone());
+                                .on_click(move |_, window, cx| {
+                                    window.dispatch_action(action.boxed_clone(), cx);
                                 })
                                 .into_any_element()
                         })
@@ -503,7 +530,7 @@ impl PickerDelegate for TasksModalDelegate {
                             secondary: current_modifiers.secondary(),
                         }
                         .boxed_clone();
-                        this.children(KeyBinding::for_action(&*action, cx).map(|keybind| {
+                        this.children(KeyBinding::for_action(&*action, window).map(|keybind| {
                             let spawn_oneshot_label = if current_modifiers.secondary() {
                                 "Spawn Oneshot Without History"
                             } else {
@@ -513,10 +540,12 @@ impl PickerDelegate for TasksModalDelegate {
                             Button::new("spawn-onehshot", spawn_oneshot_label)
                                 .label_size(LabelSize::Small)
                                 .key_binding(keybind)
-                                .on_click(move |_, cx| cx.dispatch_action(action.boxed_clone()))
+                                .on_click(move |_, window, cx| {
+                                    window.dispatch_action(action.boxed_clone(), cx)
+                                })
                         }))
                     } else if current_modifiers.secondary() {
-                        this.children(KeyBinding::for_action(&menu::SecondaryConfirm, cx).map(
+                        this.children(KeyBinding::for_action(&menu::SecondaryConfirm, window).map(
                             |keybind| {
                                 let label = if is_recent_selected {
                                     "Rerun Without History"
@@ -526,23 +555,28 @@ impl PickerDelegate for TasksModalDelegate {
                                 Button::new("spawn", label)
                                     .label_size(LabelSize::Small)
                                     .key_binding(keybind)
-                                    .on_click(move |_, cx| {
-                                        cx.dispatch_action(menu::SecondaryConfirm.boxed_clone())
+                                    .on_click(move |_, window, cx| {
+                                        window.dispatch_action(
+                                            menu::SecondaryConfirm.boxed_clone(),
+                                            cx,
+                                        )
                                     })
                             },
                         ))
                     } else {
-                        this.children(KeyBinding::for_action(&menu::Confirm, cx).map(|keybind| {
-                            let run_entry_label =
-                                if is_recent_selected { "Rerun" } else { "Spawn" };
+                        this.children(KeyBinding::for_action(&menu::Confirm, window).map(
+                            |keybind| {
+                                let run_entry_label =
+                                    if is_recent_selected { "Rerun" } else { "Spawn" };
 
-                            Button::new("spawn", run_entry_label)
-                                .label_size(LabelSize::Small)
-                                .key_binding(keybind)
-                                .on_click(|_, cx| {
-                                    cx.dispatch_action(menu::Confirm.boxed_clone());
-                                })
-                        }))
+                                Button::new("spawn", run_entry_label)
+                                    .label_size(LabelSize::Small)
+                                    .key_binding(keybind)
+                                    .on_click(|_, window, cx| {
+                                        window.dispatch_action(menu::Confirm.boxed_clone(), cx);
+                                    })
+                            },
+                        ))
                     }
                 })
                 .into_any_element(),
@@ -602,7 +636,8 @@ mod tests {
         .await;
 
         let project = Project::test(fs, ["/dir".as_ref()], cx).await;
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+        let (workspace, cx) =
+            cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
 
         let tasks_picker = open_spawn_tasks(&workspace, cx);
         assert_eq!(
@@ -618,8 +653,8 @@ mod tests {
         drop(tasks_picker);
 
         let _ = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_abs_path(PathBuf::from("/dir/a.ts"), true, cx)
+            .update_in(cx, |workspace, window, cx| {
+                workspace.open_abs_path(PathBuf::from("/dir/a.ts"), true, window, cx)
             })
             .await
             .unwrap();
@@ -766,7 +801,8 @@ mod tests {
         .await;
 
         let project = Project::test(fs, ["/dir".as_ref()], cx).await;
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let (workspace, cx) =
+            cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
         let tasks_picker = open_spawn_tasks(&workspace, cx);
         assert_eq!(
@@ -781,8 +817,13 @@ mod tests {
         cx.executor().run_until_parked();
 
         let _ = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_abs_path(PathBuf::from("/dir/file_with.odd_extension"), true, cx)
+            .update_in(cx, |workspace, window, cx| {
+                workspace.open_abs_path(
+                    PathBuf::from("/dir/file_with.odd_extension"),
+                    true,
+                    window,
+                    cx,
+                )
             })
             .await
             .unwrap();
@@ -803,15 +844,22 @@ mod tests {
         cx.executor().run_until_parked();
 
         let second_item = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_abs_path(PathBuf::from("/dir/file_without_extension"), true, cx)
+            .update_in(cx, |workspace, window, cx| {
+                workspace.open_abs_path(
+                    PathBuf::from("/dir/file_without_extension"),
+                    true,
+                    window,
+                    cx,
+                )
             })
             .await
             .unwrap();
 
-        let editor = cx.update(|cx| second_item.act_as::<Editor>(cx)).unwrap();
-        editor.update(cx, |editor, cx| {
-            editor.change_selections(None, cx, |s| {
+        let editor = cx
+            .update(|_window, cx| second_item.act_as::<Editor>(cx))
+            .unwrap();
+        editor.update_in(cx, |editor, window, cx| {
+            editor.change_selections(None, window, cx, |s| {
                 s.select_ranges(Some(Point::new(1, 2)..Point::new(1, 5)))
             })
         });
@@ -902,11 +950,12 @@ mod tests {
                 ))),
             ));
         });
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let (workspace, cx) =
+            cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
         let _ts_file_1 = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_abs_path(PathBuf::from("/dir/a1.ts"), true, cx)
+            .update_in(cx, |workspace, window, cx| {
+                workspace.open_abs_path(PathBuf::from("/dir/a1.ts"), true, window, cx)
             })
             .await
             .unwrap();
@@ -941,8 +990,8 @@ mod tests {
         cx.executor().run_until_parked();
 
         let _ts_file_2 = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_abs_path(PathBuf::from("/dir/a2.ts"), true, cx)
+            .update_in(cx, |workspace, window, cx| {
+                workspace.open_abs_path(PathBuf::from("/dir/a2.ts"), true, window, cx)
             })
             .await
             .unwrap();
@@ -964,8 +1013,8 @@ mod tests {
         cx.executor().run_until_parked();
 
         let _rs_file = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_abs_path(PathBuf::from("/dir/b.rs"), true, cx)
+            .update_in(cx, |workspace, window, cx| {
+                workspace.open_abs_path(PathBuf::from("/dir/b.rs"), true, window, cx)
             })
             .await
             .unwrap();
@@ -979,8 +1028,8 @@ mod tests {
         cx.dispatch_action(CloseInactiveTabsAndPanes::default());
         emulate_task_schedule(tasks_picker, &project, "Rust task", cx);
         let _ts_file_2 = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_abs_path(PathBuf::from("/dir/a2.ts"), true, cx)
+            .update_in(cx, |workspace, window, cx| {
+                workspace.open_abs_path(PathBuf::from("/dir/a2.ts"), true, window, cx)
             })
             .await
             .unwrap();
@@ -999,8 +1048,8 @@ mod tests {
     }
 
     fn emulate_task_schedule(
-        tasks_picker: View<Picker<TasksModalDelegate>>,
-        project: &Model<Project>,
+        tasks_picker: Entity<Picker<TasksModalDelegate>>,
+        project: &Entity<Project>,
         scheduled_task_label: &str,
         cx: &mut VisualTestContext,
     ) {
@@ -1030,9 +1079,9 @@ mod tests {
     }
 
     fn open_spawn_tasks(
-        workspace: &View<Workspace>,
+        workspace: &Entity<Workspace>,
         cx: &mut VisualTestContext,
-    ) -> View<Picker<TasksModalDelegate>> {
+    ) -> Entity<Picker<TasksModalDelegate>> {
         cx.dispatch_action(Spawn::modal());
         workspace.update(cx, |workspace, cx| {
             workspace
@@ -1044,12 +1093,15 @@ mod tests {
         })
     }
 
-    fn query(spawn_tasks: &View<Picker<TasksModalDelegate>>, cx: &mut VisualTestContext) -> String {
+    fn query(
+        spawn_tasks: &Entity<Picker<TasksModalDelegate>>,
+        cx: &mut VisualTestContext,
+    ) -> String {
         spawn_tasks.update(cx, |spawn_tasks, cx| spawn_tasks.query(cx))
     }
 
     fn task_names(
-        spawn_tasks: &View<Picker<TasksModalDelegate>>,
+        spawn_tasks: &Entity<Picker<TasksModalDelegate>>,
         cx: &mut VisualTestContext,
     ) -> Vec<String> {
         spawn_tasks.update(cx, |spawn_tasks, _| {

crates/tasks_ui/src/settings.rs 🔗

@@ -19,10 +19,7 @@ impl Settings for TaskSettings {
 
     type FileContent = TaskSettingsContent;
 
-    fn load(
-        sources: SettingsSources<Self::FileContent>,
-        _: &mut gpui::AppContext,
-    ) -> gpui::Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut gpui::App) -> gpui::Result<Self> {
         sources.json_merge()
     }
 }

crates/tasks_ui/src/lib.rs → crates/tasks_ui/src/tasks_ui.rs 🔗

@@ -1,6 +1,6 @@
 use ::settings::Settings;
 use editor::{tasks::task_context, Editor};
-use gpui::{AppContext, Task as AsyncTask, ViewContext, WindowContext};
+use gpui::{App, Context, Task as AsyncTask, Window};
 use modal::{TaskOverrides, TasksModal};
 use project::{Location, WorktreeId};
 use task::{RevealTarget, TaskId};
@@ -12,13 +12,13 @@ mod settings;
 
 pub use modal::{Rerun, Spawn};
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     settings::TaskSettings::register(cx);
-    cx.observe_new_views(
-        |workspace: &mut Workspace, _: &mut ViewContext<Workspace>| {
+    cx.observe_new(
+        |workspace: &mut Workspace, _window: Option<&mut Window>, _: &mut Context<Workspace>| {
             workspace
                 .register_action(spawn_task_or_modal)
-                .register_action(move |workspace, action: &modal::Rerun, cx| {
+                .register_action(move |workspace, action: &modal::Rerun, window, cx| {
                     if let Some((task_source_kind, mut last_scheduled_task)) = workspace
                         .project()
                         .read(cx)
@@ -43,8 +43,8 @@ pub fn init(cx: &mut AppContext) {
                             if let Some(use_new_terminal) = action.use_new_terminal {
                                 original_task.use_new_terminal = use_new_terminal;
                             }
-                            let context_task = task_context(workspace, cx);
-                            cx.spawn(|workspace, mut cx| async move {
+                            let context_task = task_context(workspace, window, cx);
+                            cx.spawn_in(window, |workspace, mut cx| async move {
                                 let task_context = context_task.await;
                                 workspace
                                     .update(&mut cx, |workspace, cx| {
@@ -79,7 +79,7 @@ pub fn init(cx: &mut AppContext) {
                             );
                         }
                     } else {
-                        toggle_modal(workspace, None, cx).detach();
+                        toggle_modal(workspace, None, window, cx).detach();
                     };
                 });
         },
@@ -87,7 +87,12 @@ pub fn init(cx: &mut AppContext) {
     .detach();
 }
 
-fn spawn_task_or_modal(workspace: &mut Workspace, action: &Spawn, cx: &mut ViewContext<Workspace>) {
+fn spawn_task_or_modal(
+    workspace: &mut Workspace,
+    action: &Spawn,
+    window: &mut Window,
+    cx: &mut Context<Workspace>,
+) {
     match action {
         Spawn::ByName {
             task_name,
@@ -96,16 +101,19 @@ fn spawn_task_or_modal(workspace: &mut Workspace, action: &Spawn, cx: &mut ViewC
             let overrides = reveal_target.map(|reveal_target| TaskOverrides {
                 reveal_target: Some(reveal_target),
             });
-            spawn_task_with_name(task_name.clone(), overrides, cx).detach_and_log_err(cx)
+            spawn_task_with_name(task_name.clone(), overrides, window, cx).detach_and_log_err(cx)
+        }
+        Spawn::ViaModal { reveal_target } => {
+            toggle_modal(workspace, *reveal_target, window, cx).detach()
         }
-        Spawn::ViaModal { reveal_target } => toggle_modal(workspace, *reveal_target, cx).detach(),
     }
 }
 
 fn toggle_modal(
     workspace: &mut Workspace,
     reveal_target: Option<RevealTarget>,
-    cx: &mut ViewContext<Workspace>,
+    window: &mut Window,
+    cx: &mut Context<Workspace>,
 ) -> AsyncTask<()> {
     let task_store = workspace.project().read(cx).task_store().clone();
     let workspace_handle = workspace.weak_handle();
@@ -113,12 +121,12 @@ fn toggle_modal(
         project.is_local() || project.ssh_connection_string(cx).is_some() || project.is_via_ssh()
     });
     if can_open_modal {
-        let context_task = task_context(workspace, cx);
-        cx.spawn(|workspace, mut cx| async move {
+        let context_task = task_context(workspace, window, cx);
+        cx.spawn_in(window, |workspace, mut cx| async move {
             let task_context = context_task.await;
             workspace
-                .update(&mut cx, |workspace, cx| {
-                    workspace.toggle_modal(cx, |cx| {
+                .update_in(&mut cx, |workspace, window, cx| {
+                    workspace.toggle_modal(window, cx, |window, cx| {
                         TasksModal::new(
                             task_store.clone(),
                             task_context,
@@ -126,6 +134,7 @@ fn toggle_modal(
                                 reveal_target: Some(target),
                             }),
                             workspace_handle,
+                            window,
                             cx,
                         )
                     })
@@ -140,11 +149,13 @@ fn toggle_modal(
 fn spawn_task_with_name(
     name: String,
     overrides: Option<TaskOverrides>,
-    cx: &mut ViewContext<Workspace>,
+    window: &mut Window,
+    cx: &mut Context<Workspace>,
 ) -> AsyncTask<anyhow::Result<()>> {
-    cx.spawn(|workspace, mut cx| async move {
-        let context_task =
-            workspace.update(&mut cx, |workspace, cx| task_context(workspace, cx))?;
+    cx.spawn_in(window, |workspace, mut cx| async move {
+        let context_task = workspace.update_in(&mut cx, |workspace, window, cx| {
+            task_context(workspace, window, cx)
+        })?;
         let task_context = context_task.await;
         let tasks = workspace.update(&mut cx, |workspace, cx| {
             let Some(task_inventory) = workspace
@@ -194,12 +205,13 @@ fn spawn_task_with_name(
             .is_some();
         if !did_spawn {
             workspace
-                .update(&mut cx, |workspace, cx| {
+                .update_in(&mut cx, |workspace, window, cx| {
                     spawn_task_or_modal(
                         workspace,
                         &Spawn::ViaModal {
                             reveal_target: overrides.and_then(|overrides| overrides.reveal_target),
                         },
+                        window,
                         cx,
                     );
                 })
@@ -212,7 +224,7 @@ fn spawn_task_with_name(
 
 fn active_item_selection_properties(
     workspace: &Workspace,
-    cx: &mut WindowContext,
+    cx: &mut App,
 ) -> (Option<WorktreeId>, Option<Location>) {
     let active_item = workspace.active_item(cx);
     let worktree_id = active_item
@@ -244,7 +256,7 @@ mod tests {
     use std::{collections::HashMap, sync::Arc};
 
     use editor::Editor;
-    use gpui::{Entity, TestAppContext};
+    use gpui::TestAppContext;
     use language::{Language, LanguageConfig};
     use project::{task_store::TaskStore, BasicContextProvider, FakeFs, Project};
     use serde_json::json;
@@ -324,7 +336,8 @@ mod tests {
         let worktree_id = project.update(cx, |project, cx| {
             project.worktrees(cx).next().unwrap().read(cx).id()
         });
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let (workspace, cx) =
+            cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
 
         let buffer1 = workspace
             .update(cx, |this, cx| {
@@ -336,7 +349,9 @@ mod tests {
         buffer1.update(cx, |this, cx| {
             this.set_language(Some(typescript_language), cx)
         });
-        let editor1 = cx.new_view(|cx| Editor::for_buffer(buffer1, Some(project.clone()), cx));
+        let editor1 = cx.new_window_model(|window, cx| {
+            Editor::for_buffer(buffer1, Some(project.clone()), window, cx)
+        });
         let buffer2 = workspace
             .update(cx, |this, cx| {
                 this.project().update(cx, |this, cx| {
@@ -346,17 +361,18 @@ mod tests {
             .await
             .unwrap();
         buffer2.update(cx, |this, cx| this.set_language(Some(rust_language), cx));
-        let editor2 = cx.new_view(|cx| Editor::for_buffer(buffer2, Some(project), cx));
+        let editor2 = cx
+            .new_window_model(|window, cx| Editor::for_buffer(buffer2, Some(project), window, cx));
 
         let first_context = workspace
-            .update(cx, |workspace, cx| {
-                workspace.add_item_to_center(Box::new(editor1.clone()), cx);
-                workspace.add_item_to_center(Box::new(editor2.clone()), cx);
+            .update_in(cx, |workspace, window, cx| {
+                workspace.add_item_to_center(Box::new(editor1.clone()), window, cx);
+                workspace.add_item_to_center(Box::new(editor2.clone()), window, cx);
                 assert_eq!(
                     workspace.active_item(cx).unwrap().item_id(),
                     editor2.entity_id()
                 );
-                task_context(workspace, cx)
+                task_context(workspace, window, cx)
             })
             .await;
         assert_eq!(
@@ -378,13 +394,17 @@ mod tests {
         );
 
         // And now, let's select an identifier.
-        editor2.update(cx, |editor, cx| {
-            editor.change_selections(None, cx, |selections| selections.select_ranges([14..18]))
+        editor2.update_in(cx, |editor, window, cx| {
+            editor.change_selections(None, window, cx, |selections| {
+                selections.select_ranges([14..18])
+            })
         });
 
         assert_eq!(
             workspace
-                .update(cx, |workspace, cx| { task_context(workspace, cx) })
+                .update_in(cx, |workspace, window, cx| {
+                    task_context(workspace, window, cx)
+                })
                 .await,
             TaskContext {
                 cwd: Some("/dir".into()),
@@ -406,10 +426,10 @@ mod tests {
 
         assert_eq!(
             workspace
-                .update(cx, |workspace, cx| {
+                .update_in(cx, |workspace, window, cx| {
                     // Now, let's switch the active item to .ts file.
-                    workspace.activate_item(&editor1, true, true, cx);
-                    task_context(workspace, cx)
+                    workspace.activate_item(&editor1, true, true, window, cx);
+                    task_context(workspace, window, cx)
                 })
                 .await,
             TaskContext {

crates/terminal/src/terminal.rs 🔗

@@ -58,9 +58,9 @@ use std::{
 use thiserror::Error;
 
 use gpui::{
-    actions, black, px, AnyWindowHandle, AppContext, Bounds, ClipboardItem, EventEmitter, Hsla,
-    Keystroke, ModelContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
-    Pixels, Point, Rgba, ScrollWheelEvent, SharedString, Size, Task, TouchPhase,
+    actions, black, px, AnyWindowHandle, App, Bounds, ClipboardItem, Context, EventEmitter, Hsla,
+    Keystroke, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point,
+    Rgba, ScrollWheelEvent, SharedString, Size, Task, TouchPhase,
 };
 
 use crate::mappings::{colors::to_alac_rgb, keys::to_esc_str};
@@ -156,7 +156,7 @@ impl EventListener for ZedListener {
     }
 }
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     TerminalSettings::register(cx);
 }
 
@@ -334,7 +334,7 @@ impl TerminalBuilder {
         is_ssh_terminal: bool,
         window: AnyWindowHandle,
         completion_tx: Sender<()>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Result<TerminalBuilder> {
         // If the parent environment doesn't have a locale set
         // (As is the case when launched from a .app on MacOS),
@@ -401,7 +401,7 @@ impl TerminalBuilder {
             ..Config::default()
         };
 
-        //Spawn a task so the Alacritty EventLoop can communicate with us in a view context
+        //Spawn a task so the Alacritty EventLoop can communicate with us
         //TODO: Remove with a bounded sender which can be dispatched on &self
         let (events_tx, events_rx) = unbounded();
         //Set up the terminal...
@@ -482,7 +482,7 @@ impl TerminalBuilder {
         })
     }
 
-    pub fn subscribe(mut self, cx: &ModelContext<Terminal>) -> Terminal {
+    pub fn subscribe(mut self, cx: &Context<Terminal>) -> Terminal {
         //Event loop
         cx.spawn(|terminal, mut cx| async move {
             while let Some(event) = self.events_rx.next().await {
@@ -674,7 +674,7 @@ impl TaskStatus {
 }
 
 impl Terminal {
-    fn process_event(&mut self, event: &AlacTermEvent, cx: &mut ModelContext<Self>) {
+    fn process_event(&mut self, event: &AlacTermEvent, cx: &mut Context<Self>) {
         match event {
             AlacTermEvent::Title(title) => {
                 self.breadcrumb_text = title.to_string();
@@ -743,12 +743,11 @@ impl Terminal {
         self.selection_phase == SelectionPhase::Selecting
     }
 
-    ///Takes events from Alacritty and translates them to behavior on this view
     fn process_terminal_event(
         &mut self,
         event: &InternalEvent,
         term: &mut Term<ZedListener>,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match event {
             InternalEvent::Resize(mut new_size) => {
@@ -1002,7 +1001,7 @@ impl Terminal {
         word_match: RangeInclusive<AlacPoint>,
         word: String,
         navigation_target: MaybeNavigationTarget,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if let Some(prev_word) = prev_word {
             if prev_word.word == word && prev_word.word_match == word_match {
@@ -1303,7 +1302,7 @@ impl Terminal {
         self.input(paste_text);
     }
 
-    pub fn sync(&mut self, cx: &mut ModelContext<Self>) {
+    pub fn sync(&mut self, cx: &mut Context<Self>) {
         let term = self.term.clone();
         let mut terminal = term.lock_unfair();
         //Note that the ordering of events matters for event processing
@@ -1479,7 +1478,7 @@ impl Terminal {
         &mut self,
         e: &MouseDownEvent,
         origin: Point<Pixels>,
-        _cx: &mut ModelContext<Self>,
+        _cx: &mut Context<Self>,
     ) {
         let position = e.position - origin;
         let point = grid_point(
@@ -1532,7 +1531,7 @@ impl Terminal {
         }
     }
 
-    pub fn mouse_up(&mut self, e: &MouseUpEvent, origin: Point<Pixels>, cx: &ModelContext<Self>) {
+    pub fn mouse_up(&mut self, e: &MouseUpEvent, origin: Point<Pixels>, cx: &Context<Self>) {
         let setting = TerminalSettings::get_global(cx);
 
         let position = e.position - origin;
@@ -1636,7 +1635,7 @@ impl Terminal {
     pub fn find_matches(
         &self,
         mut searcher: RegexSearch,
-        cx: &ModelContext<Self>,
+        cx: &Context<Self>,
     ) -> Task<Vec<RangeInclusive<AlacPoint>>> {
         let term = self.term.clone();
         cx.background_executor().spawn(async move {
@@ -1728,7 +1727,7 @@ impl Terminal {
         self.task.as_ref()
     }
 
-    pub fn wait_for_completed_task(&self, cx: &AppContext) -> Task<()> {
+    pub fn wait_for_completed_task(&self, cx: &App) -> Task<()> {
         if let Some(task) = self.task() {
             if task.status == TaskStatus::Running {
                 let completion_receiver = task.completion_rx.clone();
@@ -1740,11 +1739,7 @@ impl Terminal {
         Task::ready(())
     }
 
-    fn register_task_finished(
-        &mut self,
-        error_code: Option<i32>,
-        cx: &mut ModelContext<'_, Terminal>,
-    ) {
+    fn register_task_finished(&mut self, error_code: Option<i32>, cx: &mut Context<'_, Terminal>) {
         self.completion_tx.try_send(()).ok();
         let task = match &mut self.task {
             Some(task) => task,

crates/terminal/src/terminal_settings.rs 🔗

@@ -3,7 +3,7 @@ use alacritty_terminal::vte::ansi::{
 };
 use collections::HashMap;
 use gpui::{
-    px, AbsoluteLength, AppContext, FontFallbacks, FontFeatures, FontWeight, Pixels, SharedString,
+    px, AbsoluteLength, App, FontFallbacks, FontFeatures, FontWeight, Pixels, SharedString,
 };
 use schemars::{gen::SchemaGenerator, schema::RootSchema, JsonSchema};
 use serde_derive::{Deserialize, Serialize};
@@ -230,17 +230,14 @@ impl settings::Settings for TerminalSettings {
 
     type FileContent = TerminalSettingsContent;
 
-    fn load(
-        sources: SettingsSources<Self::FileContent>,
-        _: &mut AppContext,
-    ) -> anyhow::Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> anyhow::Result<Self> {
         sources.json_merge()
     }
 
     fn json_schema(
         generator: &mut SchemaGenerator,
         params: &SettingsJsonSchemaParams,
-        _: &AppContext,
+        _: &App,
     ) -> RootSchema {
         let mut root_schema = generator.root_schema_for::<Self::FileContent>();
         root_schema.definitions.extend([

crates/terminal_view/src/persistence.rs 🔗

@@ -2,11 +2,11 @@ use anyhow::Result;
 use async_recursion::async_recursion;
 use collections::HashSet;
 use futures::{stream::FuturesUnordered, StreamExt as _};
-use gpui::{AsyncWindowContext, Axis, Model, Task, View, WeakView};
+use gpui::{AppContext as _, AsyncWindowContext, Axis, Entity, Task, WeakEntity};
 use project::{terminals::TerminalKind, Project};
 use serde::{Deserialize, Serialize};
 use std::path::{Path, PathBuf};
-use ui::{Pixels, ViewContext, VisualContext as _, WindowContext};
+use ui::{App, Context, Pixels, Window};
 use util::ResultExt as _;
 
 use db::{define_connection, query, sqlez::statement::Statement, sqlez_macros::sql};
@@ -23,16 +23,16 @@ use crate::{
 
 pub(crate) fn serialize_pane_group(
     pane_group: &PaneGroup,
-    active_pane: &View<Pane>,
-    cx: &WindowContext,
+    active_pane: &Entity<Pane>,
+    cx: &mut App,
 ) -> SerializedPaneGroup {
     build_serialized_pane_group(&pane_group.root, active_pane, cx)
 }
 
 fn build_serialized_pane_group(
     pane_group: &Member,
-    active_pane: &View<Pane>,
-    cx: &WindowContext,
+    active_pane: &Entity<Pane>,
+    cx: &mut App,
 ) -> SerializedPaneGroup {
     match pane_group {
         Member::Axis(PaneAxis {
@@ -54,7 +54,7 @@ fn build_serialized_pane_group(
     }
 }
 
-fn serialize_pane(pane: &View<Pane>, active: bool, cx: &WindowContext) -> SerializedPane {
+fn serialize_pane(pane: &Entity<Pane>, active: bool, cx: &mut App) -> SerializedPane {
     let mut items_to_serialize = HashSet::default();
     let pane = pane.read(cx);
     let children = pane
@@ -83,16 +83,17 @@ fn serialize_pane(pane: &View<Pane>, active: bool, cx: &WindowContext) -> Serial
 }
 
 pub(crate) fn deserialize_terminal_panel(
-    workspace: WeakView<Workspace>,
-    project: Model<Project>,
+    workspace: WeakEntity<Workspace>,
+    project: Entity<Project>,
     database_id: WorkspaceId,
     serialized_panel: SerializedTerminalPanel,
-    cx: &mut WindowContext,
-) -> Task<anyhow::Result<View<TerminalPanel>>> {
-    cx.spawn(move |mut cx| async move {
-        let terminal_panel = workspace.update(&mut cx, |workspace, cx| {
-            cx.new_view(|cx| {
-                let mut panel = TerminalPanel::new(workspace, cx);
+    window: &mut Window,
+    cx: &mut App,
+) -> Task<anyhow::Result<Entity<TerminalPanel>>> {
+    window.spawn(cx, move |mut cx| async move {
+        let terminal_panel = workspace.update_in(&mut cx, |workspace, window, cx| {
+            cx.new(|cx| {
+                let mut panel = TerminalPanel::new(workspace, window, cx);
                 panel.height = serialized_panel.height.map(|h| h.round());
                 panel.width = serialized_panel.width.map(|w| w.round());
                 panel
@@ -109,9 +110,9 @@ pub(crate) fn deserialize_terminal_panel(
                 )
                 .await;
                 let active_item = serialized_panel.active_item_id;
-                terminal_panel.update(&mut cx, |terminal_panel, cx| {
+                terminal_panel.update_in(&mut cx, |terminal_panel, window, cx| {
                     terminal_panel.active_pane.update(cx, |pane, cx| {
-                        populate_pane_items(pane, items, active_item, cx);
+                        populate_pane_items(pane, items, active_item, window, cx);
                     });
                 })?;
             }
@@ -141,9 +142,10 @@ pub(crate) fn deserialize_terminal_panel(
 
 fn populate_pane_items(
     pane: &mut Pane,
-    items: Vec<View<TerminalView>>,
+    items: Vec<Entity<TerminalView>>,
     active_item: Option<u64>,
-    cx: &mut ViewContext<Pane>,
+    window: &mut Window,
+    cx: &mut Context<Pane>,
 ) {
     let mut item_index = pane.items_len();
     let mut active_item_index = None;
@@ -151,23 +153,23 @@ fn populate_pane_items(
         if Some(item.item_id().as_u64()) == active_item {
             active_item_index = Some(item_index);
         }
-        pane.add_item(Box::new(item), false, false, None, cx);
+        pane.add_item(Box::new(item), false, false, None, window, cx);
         item_index += 1;
     }
     if let Some(index) = active_item_index {
-        pane.activate_item(index, false, false, cx);
+        pane.activate_item(index, false, false, window, cx);
     }
 }
 
 #[async_recursion(?Send)]
 async fn deserialize_pane_group(
-    workspace: WeakView<Workspace>,
-    project: Model<Project>,
-    panel: View<TerminalPanel>,
+    workspace: WeakEntity<Workspace>,
+    project: Entity<Project>,
+    panel: Entity<TerminalPanel>,
     workspace_id: WorkspaceId,
     serialized: &SerializedPaneGroup,
     cx: &mut AsyncWindowContext,
-) -> Option<(Member, Option<View<Pane>>)> {
+) -> Option<(Member, Option<Entity<Pane>>)> {
     match serialized {
         SerializedPaneGroup::Group {
             axis,
@@ -217,11 +219,12 @@ async fn deserialize_pane_group(
             .await;
 
             let pane = panel
-                .update(cx, |terminal_panel, cx| {
+                .update_in(cx, |terminal_panel, window, cx| {
                     new_terminal_pane(
                         workspace.clone(),
                         project.clone(),
                         terminal_panel.active_pane.read(cx).is_zoomed(),
+                        window,
                         cx,
                     )
                 })
@@ -229,8 +232,8 @@ async fn deserialize_pane_group(
             let active_item = serialized_pane.active_item;
 
             let terminal = pane
-                .update(cx, |pane, cx| {
-                    populate_pane_items(pane, new_items, active_item, cx);
+                .update_in(cx, |pane, window, cx| {
+                    populate_pane_items(pane, new_items, active_item, window, cx);
                     // Avoid blank panes in splits
                     if pane.items_len() == 0 {
                         let working_directory = workspace
@@ -240,7 +243,7 @@ async fn deserialize_pane_group(
                         let kind = TerminalKind::Shell(
                             working_directory.as_deref().map(Path::to_path_buf),
                         );
-                        let window = cx.window_handle();
+                        let window = window.window_handle();
                         let terminal = project
                             .update(cx, |project, cx| project.create_terminal(kind, window, cx));
                         Some(Some(terminal))
@@ -252,17 +255,18 @@ async fn deserialize_pane_group(
                 .flatten()?;
             if let Some(terminal) = terminal {
                 let terminal = terminal.await.ok()?;
-                pane.update(cx, |pane, cx| {
-                    let terminal_view = Box::new(cx.new_view(|cx| {
+                pane.update_in(cx, |pane, window, cx| {
+                    let terminal_view = Box::new(cx.new(|cx| {
                         TerminalView::new(
                             terminal,
                             workspace.clone(),
                             Some(workspace_id),
                             project.downgrade(),
+                            window,
                             cx,
                         )
                     }));
-                    pane.add_item(terminal_view, true, false, None, cx);
+                    pane.add_item(terminal_view, true, false, None, window, cx);
                 })
                 .ok()?;
             }
@@ -273,21 +277,22 @@ async fn deserialize_pane_group(
 
 async fn deserialize_terminal_views(
     workspace_id: WorkspaceId,
-    project: Model<Project>,
-    workspace: WeakView<Workspace>,
+    project: Entity<Project>,
+    workspace: WeakEntity<Workspace>,
     item_ids: &[u64],
     cx: &mut AsyncWindowContext,
-) -> Vec<View<TerminalView>> {
+) -> Vec<Entity<TerminalView>> {
     let mut items = Vec::with_capacity(item_ids.len());
     let mut deserialized_items = item_ids
         .iter()
         .map(|item_id| {
-            cx.update(|cx| {
+            cx.update(|window, cx| {
                 TerminalView::deserialize(
                     project.clone(),
                     workspace.clone(),
                     workspace_id,
                     *item_id,
+                    window,
                     cx,
                 )
             })

crates/terminal_view/src/terminal_element.rs 🔗

@@ -1,11 +1,11 @@
 use editor::{CursorLayout, HighlightedRange, HighlightedRangeLine};
 use gpui::{
-    div, fill, point, px, relative, size, AnyElement, AvailableSpace, Bounds, ContentMask,
-    DispatchPhase, Element, ElementId, FocusHandle, Font, FontStyle, FontWeight, GlobalElementId,
-    HighlightStyle, Hitbox, Hsla, InputHandler, InteractiveElement, Interactivity, IntoElement,
-    LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton, MouseMoveEvent, Pixels,
-    Point, ShapedLine, StatefulInteractiveElement, StrikethroughStyle, Styled, TextRun, TextStyle,
-    UTF16Selection, UnderlineStyle, View, WeakView, WhiteSpace, WindowContext, WindowTextSystem,
+    div, fill, point, px, relative, size, AnyElement, App, AvailableSpace, Bounds, ContentMask,
+    Context, DispatchPhase, Element, ElementId, Entity, FocusHandle, Font, FontStyle, FontWeight,
+    GlobalElementId, HighlightStyle, Hitbox, Hsla, InputHandler, InteractiveElement, Interactivity,
+    IntoElement, LayoutId, ModifiersChangedEvent, MouseButton, MouseMoveEvent, Pixels, Point,
+    ShapedLine, StatefulInteractiveElement, StrikethroughStyle, Styled, TextRun, TextStyle,
+    UTF16Selection, UnderlineStyle, WeakEntity, WhiteSpace, Window, WindowTextSystem,
 };
 use itertools::Itertools;
 use language::CursorShape;
@@ -88,7 +88,8 @@ impl LayoutCell {
         origin: Point<Pixels>,
         dimensions: &TerminalSize,
         _visible_bounds: Bounds<Pixels>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let pos = {
             let point = self.point;
@@ -99,7 +100,9 @@ impl LayoutCell {
             )
         };
 
-        self.text.paint(pos, dimensions.line_height, cx).ok();
+        self.text
+            .paint(pos, dimensions.line_height, window, cx)
+            .ok();
     }
 }
 
@@ -127,7 +130,7 @@ impl LayoutRect {
         }
     }
 
-    pub fn paint(&self, origin: Point<Pixels>, dimensions: &TerminalSize, cx: &mut WindowContext) {
+    pub fn paint(&self, origin: Point<Pixels>, dimensions: &TerminalSize, window: &mut Window) {
         let position = {
             let alac_point = self.point;
             point(
@@ -141,16 +144,16 @@ impl LayoutRect {
         )
         .into();
 
-        cx.paint_quad(fill(Bounds::new(position, size), self.color));
+        window.paint_quad(fill(Bounds::new(position, size), self.color));
     }
 }
 
 /// The GPUI element that paints the terminal.
-/// We need to keep a reference to the view for mouse events, do we need it for any other terminal stuff, or can we move that to connection?
+/// We need to keep a reference to the model for mouse events, do we need it for any other terminal stuff, or can we move that to connection?
 pub struct TerminalElement {
-    terminal: Model<Terminal>,
-    terminal_view: View<TerminalView>,
-    workspace: WeakView<Workspace>,
+    terminal: Entity<Terminal>,
+    terminal_view: Entity<TerminalView>,
+    workspace: WeakEntity<Workspace>,
     focus: FocusHandle,
     focused: bool,
     cursor_visible: bool,
@@ -170,9 +173,9 @@ impl StatefulInteractiveElement for TerminalElement {}
 impl TerminalElement {
     #[allow(clippy::too_many_arguments)]
     pub fn new(
-        terminal: Model<Terminal>,
-        terminal_view: View<TerminalView>,
-        workspace: WeakView<Workspace>,
+        terminal: Entity<Terminal>,
+        terminal_view: Entity<TerminalView>,
+        workspace: WeakEntity<Workspace>,
         focus: FocusHandle,
         focused: bool,
         cursor_visible: bool,
@@ -202,7 +205,8 @@ impl TerminalElement {
         // terminal_theme: &TerminalStyle,
         text_system: &WindowTextSystem,
         hyperlink: Option<(HighlightStyle, &RangeInclusive<AlacPoint>)>,
-        cx: &WindowContext,
+        window: &Window,
+        cx: &App,
     ) -> (Vec<LayoutCell>, Vec<LayoutRect>) {
         let theme = cx.theme();
         let mut cells = vec![];
@@ -286,7 +290,7 @@ impl TerminalElement {
                         let layout_cell = text_system
                             .shape_line(
                                 cell_text.into(),
-                                text_style.font_size.to_pixels(cx.rem_size()),
+                                text_style.font_size.to_pixels(window.rem_size()),
                                 &[cell_style],
                             )
                             .unwrap();
@@ -408,13 +412,13 @@ impl TerminalElement {
     }
 
     fn generic_button_handler<E>(
-        connection: Model<Terminal>,
+        connection: Entity<Terminal>,
         origin: Point<Pixels>,
         focus_handle: FocusHandle,
-        f: impl Fn(&mut Terminal, Point<Pixels>, &E, &mut ModelContext<Terminal>),
-    ) -> impl Fn(&E, &mut WindowContext) {
-        move |event, cx| {
-            cx.focus(&focus_handle);
+        f: impl Fn(&mut Terminal, Point<Pixels>, &E, &mut Context<Terminal>),
+    ) -> impl Fn(&E, &mut Window, &mut App) {
+        move |event, window, cx| {
+            window.focus(&focus_handle);
             connection.update(cx, |terminal, cx| {
                 f(terminal, origin, event, cx);
 
@@ -428,7 +432,7 @@ impl TerminalElement {
         origin: Point<Pixels>,
         mode: TermMode,
         hitbox: &Hitbox,
-        cx: &mut WindowContext,
+        window: &mut Window,
     ) {
         let focus = self.focus.clone();
         let terminal = self.terminal.clone();
@@ -436,8 +440,8 @@ impl TerminalElement {
         self.interactivity.on_mouse_down(MouseButton::Left, {
             let terminal = terminal.clone();
             let focus = focus.clone();
-            move |e, cx| {
-                cx.focus(&focus);
+            move |e, window, cx| {
+                window.focus(&focus);
                 terminal.update(cx, |terminal, cx| {
                     terminal.mouse_down(e, origin, cx);
                     cx.notify();
@@ -445,17 +449,17 @@ impl TerminalElement {
             }
         });
 
-        cx.on_mouse_event({
+        window.on_mouse_event({
             let focus = self.focus.clone();
             let terminal = self.terminal.clone();
             let hitbox = hitbox.clone();
-            move |e: &MouseMoveEvent, phase, cx| {
-                if phase != DispatchPhase::Bubble || !focus.is_focused(cx) {
+            move |e: &MouseMoveEvent, phase, window, cx| {
+                if phase != DispatchPhase::Bubble || !focus.is_focused(window) {
                     return;
                 }
 
                 if e.pressed_button.is_some() && !cx.has_active_drag() {
-                    let hovered = hitbox.is_hovered(cx);
+                    let hovered = hitbox.is_hovered(window);
                     terminal.update(cx, |terminal, cx| {
                         if terminal.selection_started() {
                             terminal.mouse_drag(e, origin, hitbox.bounds);
@@ -467,7 +471,7 @@ impl TerminalElement {
                     })
                 }
 
-                if hitbox.is_hovered(cx) {
+                if hitbox.is_hovered(window) {
                     terminal.update(cx, |terminal, cx| {
                         terminal.mouse_move(e, origin);
                         cx.notify();
@@ -500,7 +504,7 @@ impl TerminalElement {
         );
         self.interactivity.on_scroll_wheel({
             let terminal_view = self.terminal_view.downgrade();
-            move |e, cx| {
+            move |e, _, cx| {
                 terminal_view
                     .update(cx, |terminal_view, cx| {
                         terminal_view.scroll_wheel(e, origin, cx);
@@ -549,7 +553,7 @@ impl TerminalElement {
         }
     }
 
-    fn rem_size(&self, cx: &WindowContext) -> Option<Pixels> {
+    fn rem_size(&self, cx: &mut App) -> Option<Pixels> {
         let settings = ThemeSettings::get_global(cx).clone();
         let buffer_font_size = settings.buffer_font_size();
         let rem_size_scale = {
@@ -581,17 +585,18 @@ impl Element for TerminalElement {
     fn request_layout(
         &mut self,
         global_id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
-        let layout_id = self
-            .interactivity
-            .request_layout(global_id, cx, |mut style, cx| {
-                style.size.width = relative(1.).into();
-                style.size.height = relative(1.).into();
-                // style.overflow = point(Overflow::Hidden, Overflow::Hidden);
-
-                cx.request_layout(style, None)
-            });
+        let layout_id =
+            self.interactivity
+                .request_layout(global_id, window, cx, |mut style, window, cx| {
+                    style.size.width = relative(1.).into();
+                    style.size.height = relative(1.).into();
+                    // style.overflow = point(Overflow::Hidden, Overflow::Hidden);
+
+                    window.request_layout(style, None, cx)
+                });
         (layout_id, ())
     }
 
@@ -600,11 +605,17 @@ impl Element for TerminalElement {
         global_id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         _: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Self::PrepaintState {
         let rem_size = self.rem_size(cx);
-        self.interactivity
-            .prepaint(global_id, bounds, bounds.size, cx, |_, _, hitbox, cx| {
+        self.interactivity.prepaint(
+            global_id,
+            bounds,
+            bounds.size,
+            window,
+            cx,
+            |_, _, hitbox, window, cx| {
                 let hitbox = hitbox.unwrap();
                 let settings = ThemeSettings::get_global(cx).clone();
 
@@ -675,7 +686,7 @@ impl Element for TerminalElement {
                 let match_color = theme.colors().search_match_background;
                 let gutter;
                 let dimensions = {
-                    let rem_size = cx.rem_size();
+                    let rem_size = window.rem_size();
                     let font_pixels = text_style.font_size.to_pixels(rem_size);
                     let line_height = font_pixels * line_height.to_pixels(rem_size);
                     let font_id = cx.text_system().resolve_font(&text_style.font());
@@ -721,9 +732,9 @@ impl Element for TerminalElement {
                     let mut element = div()
                         .size_full()
                         .id("terminal-element")
-                        .tooltip(move |cx| Tooltip::text(hovered_word.word.clone(), cx))
+                        .tooltip(Tooltip::text(hovered_word.word.clone()))
                         .into_any_element();
-                    element.prepaint_as_root(offset, bounds.size.into(), cx);
+                    element.prepaint_as_root(offset, bounds.size.into(), window, cx);
                     element
                 });
 
@@ -754,10 +765,11 @@ impl Element for TerminalElement {
                 let (cells, rects) = TerminalElement::layout_grid(
                     cells.iter().cloned(),
                     &text_style,
-                    cx.text_system(),
+                    window.text_system(),
                     last_hovered_word
                         .as_ref()
                         .map(|last_hovered_word| (link_style, &last_hovered_word.word_match)),
+                    window,
                     cx,
                 );
 
@@ -770,10 +782,11 @@ impl Element for TerminalElement {
                     let cursor_text = {
                         let str_trxt = cursor_char.to_string();
                         let len = str_trxt.len();
-                        cx.text_system()
+                        window
+                            .text_system()
                             .shape_line(
                                 str_trxt.into(),
-                                text_style.font_size.to_pixels(cx.rem_size()),
+                                text_style.font_size.to_pixels(window.rem_size()),
                                 &[TextRun {
                                     len,
                                     font: text_style.font(),
@@ -817,6 +830,7 @@ impl Element for TerminalElement {
                         let target_line = terminal.last_content.cursor.point.line.0 + 1;
                         let render = &block.render;
                         let mut block_cx = BlockContext {
+                            window,
                             context: cx,
                             dimensions,
                         };
@@ -831,8 +845,8 @@ impl Element for TerminalElement {
                         let origin = bounds.origin
                             + point(px(0.), target_line as f32 * dimensions.line_height())
                             - point(px(0.), scroll_top);
-                        cx.with_rem_size(rem_size, |cx| {
-                            element.prepaint_as_root(origin, available_space, cx);
+                        window.with_rem_size(rem_size, |window| {
+                            element.prepaint_as_root(origin, available_space, window, cx);
                         });
                         Some(element)
                     } else {
@@ -857,7 +871,8 @@ impl Element for TerminalElement {
                     last_hovered_word,
                     block_below_cursor_element,
                 }
-            })
+            },
+        )
     }
 
     fn paint(
@@ -866,12 +881,13 @@ impl Element for TerminalElement {
         bounds: Bounds<Pixels>,
         _: &mut Self::RequestLayoutState,
         layout: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
+        window.with_content_mask(Some(ContentMask { bounds }), |window| {
             let scroll_top = self.terminal_view.read(cx).scroll_top;
 
-            cx.paint_quad(fill(bounds, layout.background_color));
+            window.paint_quad(fill(bounds, layout.background_color));
             let origin =
                 bounds.origin + Point::new(layout.gutter, px(0.)) - Point::new(px(0.), scroll_top);
 
@@ -884,23 +900,28 @@ impl Element for TerminalElement {
                 workspace: self.workspace.clone(),
             };
 
-            self.register_mouse_listeners(origin, layout.mode, &layout.hitbox, cx);
+            self.register_mouse_listeners(origin, layout.mode, &layout.hitbox, window);
             if self.can_navigate_to_selected_word && layout.last_hovered_word.is_some() {
-                cx.set_cursor_style(gpui::CursorStyle::PointingHand, &layout.hitbox);
+                window.set_cursor_style(gpui::CursorStyle::PointingHand, &layout.hitbox);
             } else {
-                cx.set_cursor_style(gpui::CursorStyle::IBeam, &layout.hitbox);
+                window.set_cursor_style(gpui::CursorStyle::IBeam, &layout.hitbox);
             }
 
             let cursor = layout.cursor.take();
             let hyperlink_tooltip = layout.hyperlink_tooltip.take();
             let block_below_cursor_element = layout.block_below_cursor_element.take();
-            self.interactivity
-                .paint(global_id, bounds, Some(&layout.hitbox), cx, |_, cx| {
-                    cx.handle_input(&self.focus, terminal_input_handler);
-
-                    cx.on_key_event({
+            self.interactivity.paint(
+                global_id,
+                bounds,
+                Some(&layout.hitbox),
+                window,
+                cx,
+                |_, window, cx| {
+                    window.handle_input(&self.focus, terminal_input_handler, cx);
+
+                    window.on_key_event({
                         let this = self.terminal.clone();
-                        move |event: &ModifiersChangedEvent, phase, cx| {
+                        move |event: &ModifiersChangedEvent, phase, window, cx| {
                             if phase != DispatchPhase::Bubble {
                                 return;
                             }
@@ -909,13 +930,13 @@ impl Element for TerminalElement {
                                 .update(cx, |term, _| term.try_modifiers_change(&event.modifiers));
 
                             if handled {
-                                cx.refresh();
+                                window.refresh();
                             }
                         }
                     });
 
                     for rect in &layout.rects {
-                        rect.paint(origin, &layout.dimensions, cx);
+                        rect.paint(origin, &layout.dimensions, window);
                     }
 
                     for (relative_highlighted_range, color) in
@@ -931,28 +952,29 @@ impl Element for TerminalElement {
                                 color: *color,
                                 corner_radius: 0.15 * layout.dimensions.line_height,
                             };
-                            hr.paint(bounds, cx);
+                            hr.paint(bounds, window);
                         }
                     }
 
                     for cell in &layout.cells {
-                        cell.paint(origin, &layout.dimensions, bounds, cx);
+                        cell.paint(origin, &layout.dimensions, bounds, window, cx);
                     }
 
                     if self.cursor_visible {
                         if let Some(mut cursor) = cursor {
-                            cursor.paint(origin, cx);
+                            cursor.paint(origin, window, cx);
                         }
                     }
 
                     if let Some(mut element) = block_below_cursor_element {
-                        element.paint(cx);
+                        element.paint(window, cx);
                     }
 
                     if let Some(mut element) = hyperlink_tooltip {
-                        element.paint(cx);
+                        element.paint(window, cx);
                     }
-                });
+                },
+            );
         });
     }
 }
@@ -966,8 +988,8 @@ impl IntoElement for TerminalElement {
 }
 
 struct TerminalInputHandler {
-    terminal: Model<Terminal>,
-    workspace: WeakView<Workspace>,
+    terminal: Entity<Terminal>,
+    workspace: WeakEntity<Workspace>,
     cursor_bounds: Option<Bounds<Pixels>>,
 }
 
@@ -975,7 +997,8 @@ impl InputHandler for TerminalInputHandler {
     fn selected_text_range(
         &mut self,
         _ignore_disabled_input: bool,
-        cx: &mut WindowContext,
+        _: &mut Window,
+        cx: &mut App,
     ) -> Option<UTF16Selection> {
         if self
             .terminal
@@ -993,7 +1016,7 @@ impl InputHandler for TerminalInputHandler {
         }
     }
 
-    fn marked_text_range(&mut self, _: &mut WindowContext) -> Option<std::ops::Range<usize>> {
+    fn marked_text_range(&mut self, _: &mut Window, _: &mut App) -> Option<std::ops::Range<usize>> {
         None
     }
 
@@ -1001,7 +1024,8 @@ impl InputHandler for TerminalInputHandler {
         &mut self,
         _: std::ops::Range<usize>,
         _: &mut Option<std::ops::Range<usize>>,
-        _: &mut WindowContext,
+        _: &mut Window,
+        _: &mut App,
     ) -> Option<String> {
         None
     }
@@ -1010,7 +1034,8 @@ impl InputHandler for TerminalInputHandler {
         &mut self,
         _replacement_range: Option<std::ops::Range<usize>>,
         text: &str,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         self.terminal.update(cx, |terminal, _| {
             terminal.input(text.into());
@@ -1018,7 +1043,7 @@ impl InputHandler for TerminalInputHandler {
 
         self.workspace
             .update(cx, |this, cx| {
-                cx.invalidate_character_coordinates();
+                window.invalidate_character_coordinates();
                 let project = this.project().read(cx);
                 let telemetry = project.client().telemetry().clone();
                 telemetry.log_edit_event("terminal", project.is_via_ssh());
@@ -1031,16 +1056,18 @@ impl InputHandler for TerminalInputHandler {
         _range_utf16: Option<std::ops::Range<usize>>,
         _new_text: &str,
         _new_selected_range: Option<std::ops::Range<usize>>,
-        _: &mut WindowContext,
+        _window: &mut Window,
+        _cx: &mut App,
     ) {
     }
 
-    fn unmark_text(&mut self, _: &mut WindowContext) {}
+    fn unmark_text(&mut self, _window: &mut Window, _cx: &mut App) {}
 
     fn bounds_for_range(
         &mut self,
         _range_utf16: std::ops::Range<usize>,
-        _: &mut WindowContext,
+        _window: &mut Window,
+        _cx: &mut App,
     ) -> Option<Bounds<Pixels>> {
         self.cursor_bounds
     }

crates/terminal_view/src/terminal_panel.rs 🔗

@@ -12,9 +12,9 @@ use collections::HashMap;
 use db::kvp::KEY_VALUE_STORE;
 use futures::future::join_all;
 use gpui::{
-    actions, Action, AnyView, AppContext, AsyncWindowContext, Corner, Entity, EventEmitter,
-    ExternalPaths, FocusHandle, FocusableView, IntoElement, Model, ParentElement, Pixels, Render,
-    Styled, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
+    actions, Action, AnyView, App, AsyncAppContext, AsyncWindowContext, Context, Corner, Entity,
+    EventEmitter, ExternalPaths, FocusHandle, Focusable, IntoElement, ParentElement, Pixels,
+    Render, Styled, Task, WeakEntity, Window,
 };
 use itertools::Itertools;
 use project::{terminals::TerminalKind, Fs, Project, ProjectEntryId};
@@ -41,21 +41,21 @@ use workspace::{
     Workspace,
 };
 
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use zed_actions::assistant::InlineAssist;
 
 const TERMINAL_PANEL_KEY: &str = "TerminalPanel";
 
 actions!(terminal_panel, [ToggleFocus]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(
-        |workspace: &mut Workspace, _: &mut ViewContext<Workspace>| {
+pub fn init(cx: &mut App) {
+    cx.observe_new(
+        |workspace: &mut Workspace, _window, _: &mut Context<Workspace>| {
             workspace.register_action(TerminalPanel::new_terminal);
             workspace.register_action(TerminalPanel::open_terminal);
-            workspace.register_action(|workspace, _: &ToggleFocus, cx| {
+            workspace.register_action(|workspace, _: &ToggleFocus, window, cx| {
                 if is_enabled_in_workspace(workspace, cx) {
-                    workspace.toggle_panel_focus::<TerminalPanel>(cx);
+                    workspace.toggle_panel_focus::<TerminalPanel>(window, cx);
                 }
             });
         },
@@ -64,10 +64,10 @@ pub fn init(cx: &mut AppContext) {
 }
 
 pub struct TerminalPanel {
-    pub(crate) active_pane: View<Pane>,
+    pub(crate) active_pane: Entity<Pane>,
     pub(crate) center: PaneGroup,
     fs: Arc<dyn Fs>,
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     pub(crate) width: Option<Pixels>,
     pub(crate) height: Option<Pixels>,
     pending_serialization: Task<Option<()>>,
@@ -79,9 +79,9 @@ pub struct TerminalPanel {
 }
 
 impl TerminalPanel {
-    pub fn new(workspace: &Workspace, cx: &mut ViewContext<Self>) -> Self {
+    pub fn new(workspace: &Workspace, window: &mut Window, cx: &mut Context<Self>) -> Self {
         let project = workspace.project();
-        let pane = new_terminal_pane(workspace.weak_handle(), project.clone(), false, cx);
+        let pane = new_terminal_pane(workspace.weak_handle(), project.clone(), false, window, cx);
         let center = PaneGroup::new(pane.clone());
         let terminal_panel = Self {
             center,
@@ -101,17 +101,17 @@ impl TerminalPanel {
         terminal_panel
     }
 
-    pub fn set_assistant_enabled(&mut self, enabled: bool, cx: &mut ViewContext<Self>) {
+    pub fn set_assistant_enabled(&mut self, enabled: bool, cx: &mut Context<Self>) {
         self.assistant_enabled = enabled;
         if enabled {
             let focus_handle = self
                 .active_pane
                 .read(cx)
                 .active_item()
-                .map(|item| item.focus_handle(cx))
+                .map(|item| item.item_focus_handle(cx))
                 .unwrap_or(self.focus_handle(cx));
             self.assistant_tab_bar_button = Some(
-                cx.new_view(move |_| InlineAssistTabBarButton { focus_handle })
+                cx.new(move |_| InlineAssistTabBarButton { focus_handle })
                     .into(),
             );
         } else {
@@ -122,15 +122,15 @@ impl TerminalPanel {
         }
     }
 
-    fn apply_tab_bar_buttons(&self, terminal_pane: &View<Pane>, cx: &mut ViewContext<Self>) {
+    fn apply_tab_bar_buttons(&self, terminal_pane: &Entity<Pane>, cx: &mut Context<Self>) {
         let assistant_tab_bar_button = self.assistant_tab_bar_button.clone();
         terminal_pane.update(cx, |pane, cx| {
-            pane.set_render_tab_bar_buttons(cx, move |pane, cx| {
+            pane.set_render_tab_bar_buttons(cx, move |pane, window, cx| {
                 let split_context = pane
                     .active_item()
                     .and_then(|item| item.downcast::<TerminalView>())
                     .map(|terminal_view| terminal_view.read(cx).focus_handle.clone());
-                if !pane.has_focus(cx) && !pane.context_menu_focused(cx) {
+                if !pane.has_focus(window, cx) && !pane.context_menu_focused(window, cx) {
                     return (None, None);
                 }
                 let focus_handle = pane.focus_handle(cx);
@@ -141,13 +141,13 @@ impl TerminalPanel {
                             .trigger(
                                 IconButton::new("plus", IconName::Plus)
                                     .icon_size(IconSize::Small)
-                                    .tooltip(|cx| Tooltip::text("New…", cx)),
+                                    .tooltip(Tooltip::text("New…")),
                             )
                             .anchor(Corner::TopRight)
                             .with_handle(pane.new_item_context_menu_handle.clone())
-                            .menu(move |cx| {
+                            .menu(move |window, cx| {
                                 let focus_handle = focus_handle.clone();
-                                let menu = ContextMenu::build(cx, |menu, _| {
+                                let menu = ContextMenu::build(window, cx, |menu, _, _| {
                                     menu.context(focus_handle.clone())
                                         .action(
                                             "New Terminal",
@@ -171,14 +171,14 @@ impl TerminalPanel {
                             .trigger(
                                 IconButton::new("terminal-pane-split", IconName::Split)
                                     .icon_size(IconSize::Small)
-                                    .tooltip(|cx| Tooltip::text("Split Pane", cx)),
+                                    .tooltip(Tooltip::text("Split Pane")),
                             )
                             .anchor(Corner::TopRight)
                             .with_handle(pane.split_item_context_menu_handle.clone())
                             .menu({
                                 let split_context = split_context.clone();
-                                move |cx| {
-                                    ContextMenu::build(cx, |menu, _| {
+                                move |window, cx| {
+                                    ContextMenu::build(window, cx, |menu, _, _| {
                                         menu.when_some(
                                             split_context.clone(),
                                             |menu, split_context| menu.context(split_context),
@@ -198,13 +198,14 @@ impl TerminalPanel {
                             .icon_size(IconSize::Small)
                             .toggle_state(zoomed)
                             .selected_icon(IconName::Minimize)
-                            .on_click(cx.listener(|pane, _, cx| {
-                                pane.toggle_zoom(&workspace::ToggleZoom, cx);
+                            .on_click(cx.listener(|pane, _, window, cx| {
+                                pane.toggle_zoom(&workspace::ToggleZoom, window, cx);
                             }))
-                            .tooltip(move |cx| {
+                            .tooltip(move |window, cx| {
                                 Tooltip::for_action(
                                     if zoomed { "Zoom Out" } else { "Zoom In" },
                                     &ToggleZoom,
+                                    window,
                                     cx,
                                 )
                             })
@@ -217,9 +218,9 @@ impl TerminalPanel {
     }
 
     pub async fn load(
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         mut cx: AsyncWindowContext,
-    ) -> Result<View<Self>> {
+    ) -> Result<Entity<Self>> {
         let serialized_panel = cx
             .background_executor()
             .spawn(async move { KEY_VALUE_STORE.read_kvp(TERMINAL_PANEL_KEY) })
@@ -232,29 +233,30 @@ impl TerminalPanel {
             .flatten();
 
         let terminal_panel = workspace
-            .update(&mut cx, |workspace, cx| {
+            .update_in(&mut cx, |workspace, window, cx| {
                 match serialized_panel.zip(workspace.database_id()) {
                     Some((serialized_panel, database_id)) => deserialize_terminal_panel(
                         workspace.weak_handle(),
                         workspace.project().clone(),
                         database_id,
                         serialized_panel,
+                        window,
                         cx,
                     ),
-                    None => Task::ready(Ok(cx.new_view(|cx| TerminalPanel::new(workspace, cx)))),
+                    None => Task::ready(Ok(cx.new(|cx| TerminalPanel::new(workspace, window, cx)))),
                 }
             })?
             .await?;
 
         if let Some(workspace) = workspace.upgrade() {
             terminal_panel
-                .update(&mut cx, |_, cx| {
-                    cx.subscribe(&workspace, |terminal_panel, _, e, cx| {
+                .update_in(&mut cx, |_, window, cx| {
+                    cx.subscribe_in(&workspace, window, |terminal_panel, _, e, window, cx| {
                         if let workspace::Event::SpawnTask {
                             action: spawn_in_terminal,
                         } = e
                         {
-                            terminal_panel.spawn_task(spawn_in_terminal, cx);
+                            terminal_panel.spawn_task(spawn_in_terminal, window, cx);
                         };
                     })
                     .detach();
@@ -264,7 +266,7 @@ impl TerminalPanel {
 
         // Since panels/docks are loaded outside from the workspace, we cleanup here, instead of through the workspace.
         if let Some(workspace) = workspace.upgrade() {
-            let cleanup_task = workspace.update(&mut cx, |workspace, cx| {
+            let cleanup_task = workspace.update_in(&mut cx, |workspace, window, cx| {
                 let alive_item_ids = terminal_panel
                     .read(cx)
                     .center
@@ -273,9 +275,9 @@ impl TerminalPanel {
                     .flat_map(|pane| pane.read(cx).items())
                     .map(|item| item.item_id().as_u64() as ItemId)
                     .collect();
-                workspace
-                    .database_id()
-                    .map(|workspace_id| TerminalView::cleanup(workspace_id, alive_item_ids, cx))
+                workspace.database_id().map(|workspace_id| {
+                    TerminalView::cleanup(workspace_id, alive_item_ids, window, cx)
+                })
             })?;
             if let Some(task) = cleanup_task {
                 task.await.log_err();
@@ -284,17 +286,18 @@ impl TerminalPanel {
 
         if let Some(workspace) = workspace.upgrade() {
             let should_focus = workspace
-                .update(&mut cx, |workspace, cx| {
+                .update_in(&mut cx, |workspace, window, cx| {
                     workspace.active_item(cx).is_none()
-                        && workspace.is_dock_at_position_open(terminal_panel.position(cx), cx)
+                        && workspace
+                            .is_dock_at_position_open(terminal_panel.position(window, cx), cx)
                 })
                 .unwrap_or(false);
 
             if should_focus {
                 terminal_panel
-                    .update(&mut cx, |panel, cx| {
+                    .update_in(&mut cx, |panel, window, cx| {
                         panel.active_pane.update(cx, |pane, cx| {
-                            pane.focus_active_item(cx);
+                            pane.focus_active_item(window, cx);
                         });
                     })
                     .ok();
@@ -306,9 +309,10 @@ impl TerminalPanel {
 
     fn handle_pane_event(
         &mut self,
-        pane: View<Pane>,
+        pane: &Entity<Pane>,
         event: &pane::Event,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match event {
             pane::Event::ActivateItem { .. } => self.serialize(cx),
@@ -325,7 +329,7 @@ impl TerminalPanel {
                     if let Some(focus_on_pane) =
                         focus_on_pane.as_ref().or_else(|| self.center.panes().pop())
                     {
-                        focus_on_pane.focus_handle(cx).focus(cx);
+                        focus_on_pane.focus_handle(cx).focus(window);
                     }
                 }
             }
@@ -350,19 +354,19 @@ impl TerminalPanel {
             pane::Event::AddItem { item } => {
                 if let Some(workspace) = self.workspace.upgrade() {
                     workspace.update(cx, |workspace, cx| {
-                        item.added_to_pane(workspace, pane.clone(), cx)
+                        item.added_to_pane(workspace, pane.clone(), window, cx)
                     })
                 }
                 self.serialize(cx);
             }
             pane::Event::Split(direction) => {
-                let Some(new_pane) = self.new_pane_with_cloned_active_terminal(cx) else {
+                let Some(new_pane) = self.new_pane_with_cloned_active_terminal(window, cx) else {
                     return;
                 };
                 let pane = pane.clone();
                 let direction = *direction;
                 self.center.split(&pane, &new_pane, direction).log_err();
-                cx.focus_view(&new_pane);
+                window.focus(&new_pane.focus_handle(cx));
             }
             pane::Event::Focus => {
                 self.active_pane = pane.clone();
@@ -374,8 +378,9 @@ impl TerminalPanel {
 
     fn new_pane_with_cloned_active_terminal(
         &mut self,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<View<Pane>> {
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Entity<Pane>> {
         let workspace = self.workspace.upgrade()?;
         let workspace = workspace.read(cx);
         let database_id = workspace.database_id();
@@ -397,19 +402,20 @@ impl TerminalPanel {
             })
             .unwrap_or((None, None));
         let kind = TerminalKind::Shell(working_directory);
-        let window = cx.window_handle();
+        let window_handle = window.window_handle();
         let terminal = project
             .update(cx, |project, cx| {
-                project.create_terminal_with_venv(kind, python_venv_directory, window, cx)
+                project.create_terminal_with_venv(kind, python_venv_directory, window_handle, cx)
             })
             .ok()?;
 
-        let terminal_view = Box::new(cx.new_view(|cx| {
+        let terminal_view = Box::new(cx.new(|cx| {
             TerminalView::new(
                 terminal.clone(),
                 weak_workspace.clone(),
                 database_id,
                 project.downgrade(),
+                window,
                 cx,
             )
         }));
@@ -417,11 +423,12 @@ impl TerminalPanel {
             weak_workspace,
             project,
             self.active_pane.read(cx).is_zoomed(),
+            window,
             cx,
         );
         self.apply_tab_bar_buttons(&pane, cx);
         pane.update(cx, |pane, cx| {
-            pane.add_item(terminal_view, true, true, None, cx);
+            pane.add_item(terminal_view, true, true, None, window, cx);
         });
 
         Some(pane)
@@ -430,7 +437,8 @@ impl TerminalPanel {
     pub fn open_terminal(
         workspace: &mut Workspace,
         action: &workspace::OpenTerminal,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
         let Some(terminal_panel) = workspace.panel::<Self>(cx) else {
             return;
@@ -441,13 +449,14 @@ impl TerminalPanel {
                 panel.add_terminal(
                     TerminalKind::Shell(Some(action.working_directory.clone())),
                     RevealStrategy::Always,
+                    window,
                     cx,
                 )
             })
             .detach_and_log_err(cx);
     }
 
-    fn spawn_task(&mut self, task: &SpawnInTerminal, cx: &mut ViewContext<Self>) {
+    fn spawn_task(&mut self, task: &SpawnInTerminal, window: &mut Window, cx: &mut Context<Self>) {
         let Ok(is_local) = self
             .workspace
             .update(cx, |workspace, cx| workspace.project().read(cx).is_local())
@@ -467,31 +476,40 @@ impl TerminalPanel {
         };
 
         if task.allow_concurrent_runs && task.use_new_terminal {
-            self.spawn_in_new_terminal(task, cx).detach_and_log_err(cx);
+            self.spawn_in_new_terminal(task, window, cx)
+                .detach_and_log_err(cx);
             return;
         }
 
         let mut terminals_for_task = self.terminals_for_task(&task.full_label, cx);
         let Some(existing) = terminals_for_task.pop() else {
-            self.spawn_in_new_terminal(task, cx).detach_and_log_err(cx);
+            self.spawn_in_new_terminal(task, window, cx)
+                .detach_and_log_err(cx);
             return;
         };
 
         let (existing_item_index, task_pane, existing_terminal) = existing;
         if task.allow_concurrent_runs {
-            self.replace_terminal(task, task_pane, existing_item_index, existing_terminal, cx)
-                .detach();
+            self.replace_terminal(
+                task,
+                task_pane,
+                existing_item_index,
+                existing_terminal,
+                window,
+                cx,
+            )
+            .detach();
             return;
         }
 
         self.deferred_tasks.insert(
             task.id.clone(),
-            cx.spawn(|terminal_panel, mut cx| async move {
+            cx.spawn_in(window, |terminal_panel, mut cx| async move {
                 wait_for_terminals_tasks(terminals_for_task, &mut cx).await;
-                let task = terminal_panel.update(&mut cx, |terminal_panel, cx| {
+                let task = terminal_panel.update_in(&mut cx, |terminal_panel, window, cx| {
                     if task.use_new_terminal {
                         terminal_panel
-                            .spawn_in_new_terminal(task, cx)
+                            .spawn_in_new_terminal(task, window, cx)
                             .detach_and_log_err(cx);
                         None
                     } else {
@@ -500,6 +518,7 @@ impl TerminalPanel {
                             task_pane,
                             existing_item_index,
                             existing_terminal,
+                            window,
                             cx,
                         ))
                     }
@@ -514,8 +533,9 @@ impl TerminalPanel {
     pub fn spawn_in_new_terminal(
         &mut self,
         spawn_task: SpawnInTerminal,
-        cx: &mut ViewContext<Self>,
-    ) -> Task<Result<Model<Terminal>>> {
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<Terminal>>> {
         let reveal = spawn_task.reveal;
         let reveal_target = spawn_task.reveal_target;
         let kind = TerminalKind::Task(spawn_task);
@@ -523,10 +543,10 @@ impl TerminalPanel {
             RevealTarget::Center => self
                 .workspace
                 .update(cx, |workspace, cx| {
-                    Self::add_center_terminal(workspace, kind, cx)
+                    Self::add_center_terminal(workspace, kind, window, cx)
                 })
                 .unwrap_or_else(|e| Task::ready(Err(e))),
-            RevealTarget::Dock => self.add_terminal(kind, reveal, cx),
+            RevealTarget::Dock => self.add_terminal(kind, reveal, window, cx),
         }
     }
 
@@ -534,7 +554,8 @@ impl TerminalPanel {
     fn new_terminal(
         workspace: &mut Workspace,
         _: &workspace::NewTerminal,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
         let Some(terminal_panel) = workspace.panel::<Self>(cx) else {
             return;
@@ -544,7 +565,7 @@ impl TerminalPanel {
 
         terminal_panel
             .update(cx, |this, cx| {
-                this.add_terminal(kind, RevealStrategy::Always, cx)
+                this.add_terminal(kind, RevealStrategy::Always, window, cx)
             })
             .detach_and_log_err(cx);
     }
@@ -552,13 +573,13 @@ impl TerminalPanel {
     fn terminals_for_task(
         &self,
         label: &str,
-        cx: &mut AppContext,
-    ) -> Vec<(usize, View<Pane>, View<TerminalView>)> {
+        cx: &mut App,
+    ) -> Vec<(usize, Entity<Pane>, Entity<TerminalView>)> {
         let Some(workspace) = self.workspace.upgrade() else {
             return Vec::new();
         };
 
-        let pane_terminal_views = |pane: View<Pane>| {
+        let pane_terminal_views = |pane: Entity<Pane>| {
             pane.read(cx)
                 .items()
                 .enumerate()
@@ -593,46 +614,49 @@ impl TerminalPanel {
 
     fn activate_terminal_view(
         &self,
-        pane: &View<Pane>,
+        pane: &Entity<Pane>,
         item_index: usize,
         focus: bool,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         pane.update(cx, |pane, cx| {
-            pane.activate_item(item_index, true, focus, cx)
+            pane.activate_item(item_index, true, focus, window, cx)
         })
     }
 
     pub fn add_center_terminal(
         workspace: &mut Workspace,
         kind: TerminalKind,
-        cx: &mut ViewContext<Workspace>,
-    ) -> Task<Result<Model<Terminal>>> {
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Task<Result<Entity<Terminal>>> {
         if !is_enabled_in_workspace(workspace, cx) {
             return Task::ready(Err(anyhow!(
                 "terminal not yet supported for remote projects"
             )));
         }
-        let window = cx.window_handle();
+        let window_handle = window.window_handle();
         let project = workspace.project().downgrade();
-        cx.spawn(move |workspace, mut cx| async move {
+        cx.spawn_in(window, move |workspace, mut cx| async move {
             let terminal = project
                 .update(&mut cx, |project, cx| {
-                    project.create_terminal(kind, window, cx)
+                    project.create_terminal(kind, window_handle, cx)
                 })?
                 .await?;
 
-            workspace.update(&mut cx, |workspace, cx| {
-                let view = cx.new_view(|cx| {
+            workspace.update_in(&mut cx, |workspace, window, cx| {
+                let terminal_view = cx.new(|cx| {
                     TerminalView::new(
                         terminal.clone(),
                         workspace.weak_handle(),
                         workspace.database_id(),
                         workspace.project().downgrade(),
+                        window,
                         cx,
                     )
                 });
-                workspace.add_item_to_active_pane(Box::new(view), None, true, cx);
+                workspace.add_item_to_active_pane(Box::new(terminal_view), None, true, window, cx);
             })?;
             Ok(terminal)
         })
@@ -642,10 +666,11 @@ impl TerminalPanel {
         &mut self,
         kind: TerminalKind,
         reveal_strategy: RevealStrategy,
-        cx: &mut ViewContext<Self>,
-    ) -> Task<Result<Model<Terminal>>> {
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<Terminal>>> {
         let workspace = self.workspace.clone();
-        cx.spawn(|terminal_panel, mut cx| async move {
+        cx.spawn_in(window, |terminal_panel, mut cx| async move {
             if workspace.update(&mut cx, |workspace, cx| {
                 !is_enabled_in_workspace(workspace, cx)
             })? {
@@ -656,37 +681,38 @@ impl TerminalPanel {
                 terminal_panel.active_pane.clone()
             })?;
             let project = workspace.update(&mut cx, |workspace, _| workspace.project().clone())?;
-            let window = cx.window_handle();
+            let window_handle = cx.window_handle();
             let terminal = project
                 .update(&mut cx, |project, cx| {
-                    project.create_terminal(kind, window, cx)
+                    project.create_terminal(kind, window_handle, cx)
                 })?
                 .await?;
-            let result = workspace.update(&mut cx, |workspace, cx| {
-                let terminal_view = Box::new(cx.new_view(|cx| {
+            let result = workspace.update_in(&mut cx, |workspace, window, cx| {
+                let terminal_view = Box::new(cx.new(|cx| {
                     TerminalView::new(
                         terminal.clone(),
                         workspace.weak_handle(),
                         workspace.database_id(),
                         workspace.project().downgrade(),
+                        window,
                         cx,
                     )
                 }));
 
                 match reveal_strategy {
                     RevealStrategy::Always => {
-                        workspace.focus_panel::<Self>(cx);
+                        workspace.focus_panel::<Self>(window, cx);
                     }
                     RevealStrategy::NoFocus => {
-                        workspace.open_panel::<Self>(cx);
+                        workspace.open_panel::<Self>(window, cx);
                     }
                     RevealStrategy::Never => {}
                 }
 
                 pane.update(cx, |pane, cx| {
-                    let focus =
-                        pane.has_focus(cx) || matches!(reveal_strategy, RevealStrategy::Always);
-                    pane.add_item(terminal_view, true, focus, None, cx);
+                    let focus = pane.has_focus(window, cx)
+                        || matches!(reveal_strategy, RevealStrategy::Always);
+                    pane.add_item(terminal_view, true, focus, None, window, cx);
                 });
 
                 Ok(terminal)
@@ -699,7 +725,7 @@ impl TerminalPanel {
         })
     }
 
-    fn serialize(&mut self, cx: &mut ViewContext<Self>) {
+    fn serialize(&mut self, cx: &mut Context<Self>) {
         let height = self.height;
         let width = self.width;
         self.pending_serialization = cx.spawn(|terminal_panel, mut cx| async move {
@@ -742,16 +768,17 @@ impl TerminalPanel {
     fn replace_terminal(
         &self,
         spawn_task: SpawnInTerminal,
-        task_pane: View<Pane>,
+        task_pane: Entity<Pane>,
         terminal_item_index: usize,
-        terminal_to_replace: View<TerminalView>,
-        cx: &mut ViewContext<Self>,
+        terminal_to_replace: Entity<TerminalView>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<Option<()>> {
         let reveal = spawn_task.reveal;
         let reveal_target = spawn_task.reveal_target;
-        let window = cx.window_handle();
+        let window_handle = window.window_handle();
         let task_workspace = self.workspace.clone();
-        cx.spawn(move |terminal_panel, mut cx| async move {
+        cx.spawn_in(window, move |terminal_panel, mut cx| async move {
             let project = terminal_panel
                 .update(&mut cx, |this, cx| {
                     this.workspace
@@ -762,14 +789,14 @@ impl TerminalPanel {
                 .flatten()?;
             let new_terminal = project
                 .update(&mut cx, |project, cx| {
-                    project.create_terminal(TerminalKind::Task(spawn_task), window, cx)
+                    project.create_terminal(TerminalKind::Task(spawn_task), window_handle, cx)
                 })
                 .ok()?
                 .await
                 .log_err()?;
             terminal_to_replace
-                .update(&mut cx, |terminal_to_replace, cx| {
-                    terminal_to_replace.set_terminal(new_terminal, cx);
+                .update_in(&mut cx, |terminal_to_replace, window, cx| {
+                    terminal_to_replace.set_terminal(new_terminal, window, cx);
                 })
                 .ok()?;
 
@@ -777,24 +804,25 @@ impl TerminalPanel {
                 RevealStrategy::Always => match reveal_target {
                     RevealTarget::Center => {
                         task_workspace
-                            .update(&mut cx, |workspace, cx| {
+                            .update_in(&mut cx, |workspace, window, cx| {
                                 workspace
                                     .active_item(cx)
                                     .context("retrieving active terminal item in the workspace")
                                     .log_err()?
-                                    .focus_handle(cx)
-                                    .focus(cx);
+                                    .item_focus_handle(cx)
+                                    .focus(window);
                                 Some(())
                             })
                             .ok()??;
                     }
                     RevealTarget::Dock => {
                         terminal_panel
-                            .update(&mut cx, |terminal_panel, cx| {
+                            .update_in(&mut cx, |terminal_panel, window, cx| {
                                 terminal_panel.activate_terminal_view(
                                     &task_pane,
                                     terminal_item_index,
                                     true,
+                                    window,
                                     cx,
                                 )
                             })
@@ -802,7 +830,9 @@ impl TerminalPanel {
 
                         cx.spawn(|mut cx| async move {
                             task_workspace
-                                .update(&mut cx, |workspace, cx| workspace.focus_panel::<Self>(cx))
+                                .update_in(&mut cx, |workspace, window, cx| {
+                                    workspace.focus_panel::<Self>(window, cx)
+                                })
                                 .ok()
                         })
                         .detach();
@@ -811,18 +841,19 @@ impl TerminalPanel {
                 RevealStrategy::NoFocus => match reveal_target {
                     RevealTarget::Center => {
                         task_workspace
-                            .update(&mut cx, |workspace, cx| {
-                                workspace.active_pane().focus_handle(cx).focus(cx);
+                            .update_in(&mut cx, |workspace, window, cx| {
+                                workspace.active_pane().focus_handle(cx).focus(window);
                             })
                             .ok()?;
                     }
                     RevealTarget::Dock => {
                         terminal_panel
-                            .update(&mut cx, |terminal_panel, cx| {
+                            .update_in(&mut cx, |terminal_panel, window, cx| {
                                 terminal_panel.activate_terminal_view(
                                     &task_pane,
                                     terminal_item_index,
                                     false,
+                                    window,
                                     cx,
                                 )
                             })
@@ -830,7 +861,9 @@ impl TerminalPanel {
 
                         cx.spawn(|mut cx| async move {
                             task_workspace
-                                .update(&mut cx, |workspace, cx| workspace.open_panel::<Self>(cx))
+                                .update_in(&mut cx, |workspace, window, cx| {
+                                    workspace.open_panel::<Self>(window, cx)
+                                })
                                 .ok()
                         })
                         .detach();
@@ -843,7 +876,7 @@ impl TerminalPanel {
         })
     }
 
-    fn has_no_terminals(&self, cx: &WindowContext) -> bool {
+    fn has_no_terminals(&self, cx: &App) -> bool {
         self.active_pane.read(cx).items_len() == 0 && self.pending_terminals_to_add == 0
     }
 
@@ -851,44 +884,46 @@ impl TerminalPanel {
         self.assistant_enabled
     }
 
-    fn is_enabled(&self, cx: &WindowContext) -> bool {
+    fn is_enabled(&self, cx: &App) -> bool {
         self.workspace.upgrade().map_or(false, |workspace| {
             is_enabled_in_workspace(workspace.read(cx), cx)
         })
     }
 }
 
-fn is_enabled_in_workspace(workspace: &Workspace, cx: &WindowContext) -> bool {
+fn is_enabled_in_workspace(workspace: &Workspace, cx: &App) -> bool {
     workspace.project().read(cx).supports_terminal(cx)
 }
 
 pub fn new_terminal_pane(
-    workspace: WeakView<Workspace>,
-    project: Model<Project>,
+    workspace: WeakEntity<Workspace>,
+    project: Entity<Project>,
     zoomed: bool,
-    cx: &mut ViewContext<TerminalPanel>,
-) -> View<Pane> {
+    window: &mut Window,
+    cx: &mut Context<TerminalPanel>,
+) -> Entity<Pane> {
     let is_local = project.read(cx).is_local();
-    let terminal_panel = cx.view().clone();
-    let pane = cx.new_view(|cx| {
+    let terminal_panel = cx.model().clone();
+    let pane = cx.new(|cx| {
         let mut pane = Pane::new(
             workspace.clone(),
             project.clone(),
             Default::default(),
             None,
             NewTerminal.boxed_clone(),
+            window,
             cx,
         );
         pane.set_zoomed(zoomed, cx);
         pane.set_can_navigate(false, cx);
         pane.display_nav_history_buttons(None);
-        pane.set_should_display_tab_bar(|_| true);
+        pane.set_should_display_tab_bar(|_, _| true);
         pane.set_zoom_out_on_close(false);
 
         let split_closure_terminal_panel = terminal_panel.downgrade();
-        pane.set_can_split(Some(Arc::new(move |pane, dragged_item, cx| {
+        pane.set_can_split(Some(Arc::new(move |pane, dragged_item, _window, cx| {
             if let Some(tab) = dragged_item.downcast_ref::<DraggedTab>() {
-                let is_current_pane = &tab.pane == cx.view();
+                let is_current_pane = tab.pane == cx.model();
                 let Some(can_drag_away) = split_closure_terminal_panel
                     .update(cx, |terminal_panel, _| {
                         let current_panes = terminal_panel.center.panes();
@@ -914,21 +949,21 @@ pub fn new_terminal_pane(
             false
         })));
 
-        let buffer_search_bar = cx.new_view(search::BufferSearchBar::new);
-        let breadcrumbs = cx.new_view(|_| Breadcrumbs::new());
+        let buffer_search_bar = cx.new(|cx| search::BufferSearchBar::new(window, cx));
+        let breadcrumbs = cx.new(|_| Breadcrumbs::new());
         pane.toolbar().update(cx, |toolbar, cx| {
-            toolbar.add_item(buffer_search_bar, cx);
-            toolbar.add_item(breadcrumbs, cx);
+            toolbar.add_item(buffer_search_bar, window, cx);
+            toolbar.add_item(breadcrumbs, window, cx);
         });
 
         let drop_closure_project = project.downgrade();
         let drop_closure_terminal_panel = terminal_panel.downgrade();
-        pane.set_custom_drop_handle(cx, move |pane, dropped_item, cx| {
+        pane.set_custom_drop_handle(cx, move |pane, dropped_item, window, cx| {
             let Some(project) = drop_closure_project.upgrade() else {
                 return ControlFlow::Break(());
             };
             if let Some(tab) = dropped_item.downcast_ref::<DraggedTab>() {
-                let this_pane = cx.view().clone();
+                let this_pane = cx.model().clone();
                 let item = if tab.pane == this_pane {
                     pane.item_for_index(tab.ix)
                 } else {
@@ -952,6 +987,7 @@ pub fn new_terminal_pane(
                                         workspace.clone(),
                                         project.clone(),
                                         is_zoomed,
+                                        window,
                                         cx,
                                     );
                                     terminal_panel.apply_tab_bar_buttons(&new_pane, cx);
@@ -971,13 +1007,14 @@ pub fn new_terminal_pane(
                         match new_split_pane.transpose() {
                             // Source pane may be the one currently updated, so defer the move.
                             Ok(Some(new_pane)) => cx
-                                .spawn(|_, mut cx| async move {
-                                    cx.update(|cx| {
+                                .spawn_in(window, |_, mut cx| async move {
+                                    cx.update(|window, cx| {
                                         move_item(
                                             &source,
                                             &new_pane,
                                             item_id_to_move,
                                             new_pane.read(cx).active_item_index(),
+                                            window,
                                             cx,
                                         );
                                     })
@@ -996,7 +1033,7 @@ pub fn new_terminal_pane(
                     } else if let Some(project_path) = item.project_path(cx) {
                         if let Some(entry_path) = project.read(cx).absolute_path(&project_path, cx)
                         {
-                            add_paths_to_terminal(pane, &[entry_path], cx);
+                            add_paths_to_terminal(pane, &[entry_path], window, cx);
                         }
                     }
                 }
@@ -1006,11 +1043,11 @@ pub fn new_terminal_pane(
                     .path_for_entry(entry_id, cx)
                     .and_then(|project_path| project.read(cx).absolute_path(&project_path, cx))
                 {
-                    add_paths_to_terminal(pane, &[entry_path], cx);
+                    add_paths_to_terminal(pane, &[entry_path], window, cx);
                 }
             } else if is_local {
                 if let Some(paths) = dropped_item.downcast_ref::<ExternalPaths>() {
-                    add_paths_to_terminal(pane, paths.paths(), cx);
+                    add_paths_to_terminal(pane, paths.paths(), window, cx);
                 }
             }
 
@@ -1020,7 +1057,7 @@ pub fn new_terminal_pane(
         pane
     });
 
-    cx.subscribe(&pane, TerminalPanel::handle_pane_event)
+    cx.subscribe_in(&pane, window, TerminalPanel::handle_pane_event)
         .detach();
     cx.observe(&pane, |_, _, cx| cx.notify()).detach();
 
@@ -1028,8 +1065,8 @@ pub fn new_terminal_pane(
 }
 
 async fn wait_for_terminals_tasks(
-    terminals_for_task: Vec<(usize, View<Pane>, View<TerminalView>)>,
-    cx: &mut AsyncWindowContext,
+    terminals_for_task: Vec<(usize, Entity<Pane>, Entity<TerminalView>)>,
+    cx: &mut AsyncAppContext,
 ) {
     let pending_tasks = terminals_for_task.iter().filter_map(|(_, _, terminal)| {
         terminal
@@ -1043,12 +1080,17 @@ async fn wait_for_terminals_tasks(
     let _: Vec<()> = join_all(pending_tasks).await;
 }
 
-fn add_paths_to_terminal(pane: &mut Pane, paths: &[PathBuf], cx: &mut ViewContext<Pane>) {
+fn add_paths_to_terminal(
+    pane: &mut Pane,
+    paths: &[PathBuf],
+    window: &mut Window,
+    cx: &mut Context<Pane>,
+) {
     if let Some(terminal_view) = pane
         .active_item()
         .and_then(|item| item.downcast::<TerminalView>())
     {
-        cx.focus_view(&terminal_view);
+        window.focus(&terminal_view.focus_handle(cx));
         let mut new_text = paths.iter().map(|path| format!(" {path:?}")).join("");
         new_text.push(' ');
         terminal_view.update(cx, |terminal_view, cx| {
@@ -1062,9 +1104,9 @@ fn add_paths_to_terminal(pane: &mut Pane, paths: &[PathBuf], cx: &mut ViewContex
 impl EventEmitter<PanelEvent> for TerminalPanel {}
 
 impl Render for TerminalPanel {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let mut registrar = DivRegistrar::new(
-            |panel, cx| {
+            |panel, _, cx| {
                 panel
                     .active_pane
                     .read(cx)
@@ -1085,75 +1127,80 @@ impl Render for TerminalPanel {
                     &self.active_pane,
                     workspace.zoomed_item(),
                     workspace.app_state(),
+                    window,
                     cx,
                 ))
             })
             .ok()
             .map(|div| {
                 div.on_action({
-                    cx.listener(|terminal_panel, action: &ActivatePaneInDirection, cx| {
-                        if let Some(pane) = terminal_panel.center.find_pane_in_direction(
-                            &terminal_panel.active_pane,
-                            action.0,
-                            cx,
-                        ) {
-                            cx.focus_view(&pane);
-                        } else {
-                            terminal_panel
-                                .workspace
-                                .update(cx, |workspace, cx| {
-                                    workspace.activate_pane_in_direction(action.0, cx)
-                                })
-                                .ok();
-                        }
-                    })
+                    cx.listener(
+                        |terminal_panel, action: &ActivatePaneInDirection, window, cx| {
+                            if let Some(pane) = terminal_panel.center.find_pane_in_direction(
+                                &terminal_panel.active_pane,
+                                action.0,
+                                cx,
+                            ) {
+                                window.focus(&pane.focus_handle(cx));
+                            } else {
+                                terminal_panel
+                                    .workspace
+                                    .update(cx, |workspace, cx| {
+                                        workspace.activate_pane_in_direction(action.0, window, cx)
+                                    })
+                                    .ok();
+                            }
+                        },
+                    )
                 })
                 .on_action(
-                    cx.listener(|terminal_panel, _action: &ActivateNextPane, cx| {
+                    cx.listener(|terminal_panel, _action: &ActivateNextPane, window, cx| {
                         let panes = terminal_panel.center.panes();
                         if let Some(ix) = panes
                             .iter()
                             .position(|pane| **pane == terminal_panel.active_pane)
                         {
                             let next_ix = (ix + 1) % panes.len();
-                            cx.focus_view(&panes[next_ix]);
+                            window.focus(&panes[next_ix].focus_handle(cx));
                         }
                     }),
                 )
-                .on_action(
-                    cx.listener(|terminal_panel, _action: &ActivatePreviousPane, cx| {
+                .on_action(cx.listener(
+                    |terminal_panel, _action: &ActivatePreviousPane, window, cx| {
                         let panes = terminal_panel.center.panes();
                         if let Some(ix) = panes
                             .iter()
                             .position(|pane| **pane == terminal_panel.active_pane)
                         {
                             let prev_ix = cmp::min(ix.wrapping_sub(1), panes.len() - 1);
-                            cx.focus_view(&panes[prev_ix]);
+                            window.focus(&panes[prev_ix].focus_handle(cx));
+                        }
+                    },
+                ))
+                .on_action(
+                    cx.listener(|terminal_panel, action: &ActivatePane, window, cx| {
+                        let panes = terminal_panel.center.panes();
+                        if let Some(&pane) = panes.get(action.0) {
+                            window.focus(&pane.read(cx).focus_handle(cx));
+                        } else {
+                            if let Some(new_pane) =
+                                terminal_panel.new_pane_with_cloned_active_terminal(window, cx)
+                            {
+                                terminal_panel
+                                    .center
+                                    .split(
+                                        &terminal_panel.active_pane,
+                                        &new_pane,
+                                        SplitDirection::Right,
+                                    )
+                                    .log_err();
+                                window.focus(&new_pane.focus_handle(cx));
+                            }
                         }
                     }),
                 )
-                .on_action(cx.listener(|terminal_panel, action: &ActivatePane, cx| {
-                    let panes = terminal_panel.center.panes();
-                    if let Some(&pane) = panes.get(action.0) {
-                        cx.focus_view(pane);
-                    } else {
-                        if let Some(new_pane) =
-                            terminal_panel.new_pane_with_cloned_active_terminal(cx)
-                        {
-                            terminal_panel
-                                .center
-                                .split(
-                                    &terminal_panel.active_pane,
-                                    &new_pane,
-                                    SplitDirection::Right,
-                                )
-                                .log_err();
-                            cx.focus_view(&new_pane);
-                        }
-                    }
-                }))
                 .on_action(
-                    cx.listener(|terminal_panel, action: &SwapPaneInDirection, cx| {
+                    cx.listener(|terminal_panel, action: &SwapPaneInDirection, _, cx| {
                         if let Some(to) = terminal_panel
                             .center
                             .find_pane_in_direction(&terminal_panel.active_pane, action.0, cx)

crates/terminal_view/src/terminal_tab_tooltip.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{IntoElement, Render, ViewContext};
+use gpui::{IntoElement, Render};
 use ui::{prelude::*, tooltip_container, Divider};
 
 pub struct TerminalTooltip {
@@ -16,10 +16,10 @@ impl TerminalTooltip {
 }
 
 impl Render for TerminalTooltip {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        tooltip_container(cx, move |this, _cx| {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+        tooltip_container(window, cx, move |this, _window, _cx| {
             this.occlude()
-                .on_mouse_move(|_, cx| cx.stop_propagation())
+                .on_mouse_move(|_, _window, cx| cx.stop_propagation())
                 .child(
                     v_flex()
                         .gap_1()

crates/terminal_view/src/terminal_view.rs 🔗

@@ -8,10 +8,9 @@ use collections::HashSet;
 use editor::{actions::SelectAll, scroll::ScrollbarAutoHide, Editor, EditorSettings};
 use futures::{stream::FuturesUnordered, StreamExt};
 use gpui::{
-    anchored, deferred, div, impl_actions, AnyElement, AppContext, DismissEvent, EventEmitter,
-    FocusHandle, FocusableView, KeyContext, KeyDownEvent, Keystroke, Model, MouseButton,
-    MouseDownEvent, Pixels, Render, ScrollWheelEvent, Stateful, Styled, Subscription, Task, View,
-    VisualContext, WeakModel, WeakView,
+    anchored, deferred, div, impl_actions, AnyElement, App, DismissEvent, Entity, EventEmitter,
+    FocusHandle, Focusable, KeyContext, KeyDownEvent, Keystroke, MouseButton, MouseDownEvent,
+    Pixels, Render, ScrollWheelEvent, Stateful, Styled, Subscription, Task, WeakEntity,
 };
 use persistence::TERMINAL_DB;
 use project::{search::SearchQuery, terminals::TerminalKind, Fs, Metadata, Project};
@@ -47,7 +46,7 @@ use workspace::{
     WorkspaceId,
 };
 
-use anyhow::Context;
+use anyhow::Context as _;
 use serde::Deserialize;
 use settings::{Settings, SettingsStore};
 use smol::Timer;
@@ -82,13 +81,13 @@ pub struct SendKeystroke(String);
 
 impl_actions!(terminal, [SendText, SendKeystroke]);
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     terminal_panel::init(cx);
     terminal::init(cx);
 
     register_serializable_item::<TerminalView>(cx);
 
-    cx.observe_new_views(|workspace: &mut Workspace, _cx| {
+    cx.observe_new(|workspace: &mut Workspace, _window, _cx| {
         workspace.register_action(TerminalView::deploy);
     })
     .detach();
@@ -100,19 +99,20 @@ pub struct BlockProperties {
 }
 
 pub struct BlockContext<'a, 'b> {
-    pub context: &'b mut WindowContext<'a>,
+    pub window: &'a mut Window,
+    pub context: &'b mut App,
     pub dimensions: TerminalSize,
 }
 
 ///A terminal view, maintains the PTY's file handles and communicates with the terminal
 pub struct TerminalView {
-    terminal: Model<Terminal>,
-    workspace: WeakView<Workspace>,
-    project: WeakModel<Project>,
+    terminal: Entity<Terminal>,
+    workspace: WeakEntity<Workspace>,
+    project: WeakEntity<Project>,
     focus_handle: FocusHandle,
     //Currently using iTerm bell, show bell emoji in tab until input is received
     has_bell: bool,
-    context_menu: Option<(View<ContextMenu>, gpui::Point<Pixels>, Subscription)>,
+    context_menu: Option<(Entity<ContextMenu>, gpui::Point<Pixels>, Subscription)>,
     cursor_shape: CursorShape,
     blink_state: bool,
     blinking_terminal_enabled: bool,
@@ -135,8 +135,8 @@ impl EventEmitter<Event> for TerminalView {}
 impl EventEmitter<ItemEvent> for TerminalView {}
 impl EventEmitter<SearchEvent> for TerminalView {}
 
-impl FocusableView for TerminalView {
-    fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
+impl Focusable for TerminalView {
+    fn focus_handle(&self, _cx: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -146,30 +146,42 @@ impl TerminalView {
     pub fn deploy(
         workspace: &mut Workspace,
         _: &NewCenterTerminal,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
         let working_directory = default_working_directory(workspace, cx);
-        TerminalPanel::add_center_terminal(workspace, TerminalKind::Shell(working_directory), cx)
-            .detach_and_log_err(cx);
+        TerminalPanel::add_center_terminal(
+            workspace,
+            TerminalKind::Shell(working_directory),
+            window,
+            cx,
+        )
+        .detach_and_log_err(cx);
     }
 
     pub fn new(
-        terminal: Model<Terminal>,
-        workspace: WeakView<Workspace>,
+        terminal: Entity<Terminal>,
+        workspace: WeakEntity<Workspace>,
         workspace_id: Option<WorkspaceId>,
-        project: WeakModel<Project>,
-        cx: &mut ViewContext<Self>,
+        project: WeakEntity<Project>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let workspace_handle = workspace.clone();
-        let terminal_subscriptions = subscribe_for_terminal_events(&terminal, workspace, cx);
+        let terminal_subscriptions =
+            subscribe_for_terminal_events(&terminal, workspace, window, cx);
 
         let focus_handle = cx.focus_handle();
-        let focus_in = cx.on_focus_in(&focus_handle, |terminal_view, cx| {
-            terminal_view.focus_in(cx);
-        });
-        let focus_out = cx.on_focus_out(&focus_handle, |terminal_view, _event, cx| {
-            terminal_view.focus_out(cx);
+        let focus_in = cx.on_focus_in(&focus_handle, window, |terminal_view, window, cx| {
+            terminal_view.focus_in(window, cx);
         });
+        let focus_out = cx.on_focus_out(
+            &focus_handle,
+            window,
+            |terminal_view, _event, window, cx| {
+                terminal_view.focus_out(window, cx);
+            },
+        );
         let cursor_shape = TerminalSettings::get_global(cx)
             .cursor_shape
             .unwrap_or_default();
@@ -206,7 +218,7 @@ impl TerminalView {
         }
     }
 
-    pub fn model(&self) -> &Model<Terminal> {
+    pub fn model(&self) -> &Entity<Terminal> {
         &self.terminal
     }
 
@@ -214,7 +226,7 @@ impl TerminalView {
         self.has_bell
     }
 
-    pub fn clear_bell(&mut self, cx: &mut ViewContext<TerminalView>) {
+    pub fn clear_bell(&mut self, cx: &mut Context<TerminalView>) {
         self.has_bell = false;
         cx.emit(Event::Wakeup);
     }
@@ -222,7 +234,8 @@ impl TerminalView {
     pub fn deploy_context_menu(
         &mut self,
         position: gpui::Point<Pixels>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let assistant_enabled = self
             .workspace
@@ -231,7 +244,7 @@ impl TerminalView {
             .map_or(false, |terminal_panel| {
                 terminal_panel.read(cx).assistant_enabled()
             });
-        let context_menu = ContextMenu::build(cx, |menu, _| {
+        let context_menu = ContextMenu::build(window, cx, |menu, _, _| {
             menu.context(self.focus_handle.clone())
                 .action("New Terminal", Box::new(NewTerminal))
                 .separator()
@@ -247,22 +260,25 @@ impl TerminalView {
                 .action("Close", Box::new(CloseActiveItem { save_intent: None }))
         });
 
-        cx.focus_view(&context_menu);
-        let subscription =
-            cx.subscribe(&context_menu, |this, _, _: &DismissEvent, cx| {
+        window.focus(&context_menu.focus_handle(cx));
+        let subscription = cx.subscribe_in(
+            &context_menu,
+            window,
+            |this, _, _: &DismissEvent, window, cx| {
                 if this.context_menu.as_ref().is_some_and(|context_menu| {
-                    context_menu.0.focus_handle(cx).contains_focused(cx)
+                    context_menu.0.focus_handle(cx).contains_focused(window, cx)
                 }) {
-                    cx.focus_self();
+                    cx.focus_self(window);
                 }
                 this.context_menu.take();
                 cx.notify();
-            });
+            },
+        );
 
         self.context_menu = Some((context_menu, position, subscription));
     }
 
-    fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
+    fn settings_changed(&mut self, cx: &mut Context<Self>) {
         let settings = TerminalSettings::get_global(cx);
         self.show_breadcrumbs = settings.toolbar.breadcrumbs;
 
@@ -278,7 +294,12 @@ impl TerminalView {
         cx.notify();
     }
 
-    fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
+    fn show_character_palette(
+        &mut self,
+        _: &ShowCharacterPalette,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if self
             .terminal
             .read(cx)
@@ -293,22 +314,22 @@ impl TerminalView {
                 )
             });
         } else {
-            cx.show_character_palette();
+            window.show_character_palette();
         }
     }
 
-    fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
+    fn select_all(&mut self, _: &SelectAll, _: &mut Window, cx: &mut Context<Self>) {
         self.terminal.update(cx, |term, _| term.select_all());
         cx.notify();
     }
 
-    fn clear(&mut self, _: &Clear, cx: &mut ViewContext<Self>) {
+    fn clear(&mut self, _: &Clear, _: &mut Window, cx: &mut Context<Self>) {
         self.scroll_top = px(0.);
         self.terminal.update(cx, |term, _| term.clear());
         cx.notify();
     }
 
-    fn max_scroll_top(&self, cx: &AppContext) -> Pixels {
+    fn max_scroll_top(&self, cx: &App) -> Pixels {
         let terminal = self.terminal.read(cx);
 
         let Some(block) = self.block_below_cursor.as_ref() else {
@@ -343,7 +364,7 @@ impl TerminalView {
         &mut self,
         event: &ScrollWheelEvent,
         origin: gpui::Point<Pixels>,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         let terminal_content = self.terminal.read(cx).last_content();
 
@@ -364,7 +385,7 @@ impl TerminalView {
             .update(cx, |term, _| term.scroll_wheel(event, origin));
     }
 
-    fn scroll_line_up(&mut self, _: &ScrollLineUp, cx: &mut ViewContext<Self>) {
+    fn scroll_line_up(&mut self, _: &ScrollLineUp, _: &mut Window, cx: &mut Context<Self>) {
         let terminal_content = self.terminal.read(cx).last_content();
         if self.block_below_cursor.is_some()
             && terminal_content.display_offset == 0
@@ -379,7 +400,7 @@ impl TerminalView {
         cx.notify();
     }
 
-    fn scroll_line_down(&mut self, _: &ScrollLineDown, cx: &mut ViewContext<Self>) {
+    fn scroll_line_down(&mut self, _: &ScrollLineDown, _: &mut Window, cx: &mut Context<Self>) {
         let terminal_content = self.terminal.read(cx).last_content();
         if self.block_below_cursor.is_some() && terminal_content.display_offset == 0 {
             let max_scroll_top = self.max_scroll_top(cx);
@@ -394,7 +415,7 @@ impl TerminalView {
         cx.notify();
     }
 
-    fn scroll_page_up(&mut self, _: &ScrollPageUp, cx: &mut ViewContext<Self>) {
+    fn scroll_page_up(&mut self, _: &ScrollPageUp, _: &mut Window, cx: &mut Context<Self>) {
         if self.scroll_top == Pixels::ZERO {
             self.terminal.update(cx, |term, _| term.scroll_page_up());
         } else {
@@ -414,7 +435,7 @@ impl TerminalView {
         cx.notify();
     }
 
-    fn scroll_page_down(&mut self, _: &ScrollPageDown, cx: &mut ViewContext<Self>) {
+    fn scroll_page_down(&mut self, _: &ScrollPageDown, _: &mut Window, cx: &mut Context<Self>) {
         self.terminal.update(cx, |term, _| term.scroll_page_down());
         let terminal = self.terminal.read(cx);
         if terminal.last_content().display_offset < terminal.viewport_lines() {
@@ -423,12 +444,12 @@ impl TerminalView {
         cx.notify();
     }
 
-    fn scroll_to_top(&mut self, _: &ScrollToTop, cx: &mut ViewContext<Self>) {
+    fn scroll_to_top(&mut self, _: &ScrollToTop, _: &mut Window, cx: &mut Context<Self>) {
         self.terminal.update(cx, |term, _| term.scroll_to_top());
         cx.notify();
     }
 
-    fn scroll_to_bottom(&mut self, _: &ScrollToBottom, cx: &mut ViewContext<Self>) {
+    fn scroll_to_bottom(&mut self, _: &ScrollToBottom, _: &mut Window, cx: &mut Context<Self>) {
         self.terminal.update(cx, |term, _| term.scroll_to_bottom());
         if self.block_below_cursor.is_some() {
             self.scroll_top = self.max_scroll_top(cx);
@@ -436,12 +457,12 @@ impl TerminalView {
         cx.notify();
     }
 
-    fn toggle_vi_mode(&mut self, _: &ToggleViMode, cx: &mut ViewContext<Self>) {
+    fn toggle_vi_mode(&mut self, _: &ToggleViMode, _: &mut Window, cx: &mut Context<Self>) {
         self.terminal.update(cx, |term, _| term.toggle_vi_mode());
         cx.notify();
     }
 
-    pub fn should_show_cursor(&self, focused: bool, cx: &mut ViewContext<Self>) -> bool {
+    pub fn should_show_cursor(&self, focused: bool, cx: &mut Context<Self>) -> bool {
         //Don't blink the cursor when not focused, blinking is disabled, or paused
         if !focused
             || self.blinking_paused
@@ -466,45 +487,54 @@ impl TerminalView {
         }
     }
 
-    fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
+    fn blink_cursors(&mut self, epoch: usize, window: &mut Window, cx: &mut Context<Self>) {
         if epoch == self.blink_epoch && !self.blinking_paused {
             self.blink_state = !self.blink_state;
             cx.notify();
 
             let epoch = self.next_blink_epoch();
-            cx.spawn(|this, mut cx| async move {
+            cx.spawn_in(window, |this, mut cx| async move {
                 Timer::after(CURSOR_BLINK_INTERVAL).await;
-                this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx))
-                    .ok();
+                this.update_in(&mut cx, |this, window, cx| {
+                    this.blink_cursors(epoch, window, cx)
+                })
+                .ok();
             })
             .detach();
         }
     }
 
-    pub fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn pause_cursor_blinking(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.blink_state = true;
         cx.notify();
 
         let epoch = self.next_blink_epoch();
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             Timer::after(CURSOR_BLINK_INTERVAL).await;
-            this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
-                .ok();
+            this.update_in(&mut cx, |this, window, cx| {
+                this.resume_cursor_blinking(epoch, window, cx)
+            })
+            .ok();
         })
         .detach();
     }
 
-    pub fn terminal(&self) -> &Model<Terminal> {
+    pub fn terminal(&self) -> &Entity<Terminal> {
         &self.terminal
     }
 
-    pub fn set_block_below_cursor(&mut self, block: BlockProperties, cx: &mut ViewContext<Self>) {
+    pub fn set_block_below_cursor(
+        &mut self,
+        block: BlockProperties,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.block_below_cursor = Some(Rc::new(block));
-        self.scroll_to_bottom(&ScrollToBottom, cx);
+        self.scroll_to_bottom(&ScrollToBottom, window, cx);
         cx.notify();
     }
 
-    pub fn clear_block_below_cursor(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn clear_block_below_cursor(&mut self, cx: &mut Context<Self>) {
         self.block_below_cursor = None;
         self.scroll_top = Pixels::ZERO;
         cx.notify();
@@ -515,35 +545,40 @@ impl TerminalView {
         self.blink_epoch
     }
 
-    fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
+    fn resume_cursor_blinking(
+        &mut self,
+        epoch: usize,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if epoch == self.blink_epoch {
             self.blinking_paused = false;
-            self.blink_cursors(epoch, cx);
+            self.blink_cursors(epoch, window, cx);
         }
     }
 
     ///Attempt to paste the clipboard into the terminal
-    fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
+    fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
         self.terminal.update(cx, |term, _| term.copy());
         cx.notify();
     }
 
     ///Attempt to paste the clipboard into the terminal
-    fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
+    fn paste(&mut self, _: &Paste, _: &mut Window, cx: &mut Context<Self>) {
         if let Some(clipboard_string) = cx.read_from_clipboard().and_then(|item| item.text()) {
             self.terminal
                 .update(cx, |terminal, _cx| terminal.paste(&clipboard_string));
         }
     }
 
-    fn send_text(&mut self, text: &SendText, cx: &mut ViewContext<Self>) {
+    fn send_text(&mut self, text: &SendText, _: &mut Window, cx: &mut Context<Self>) {
         self.clear_bell(cx);
         self.terminal.update(cx, |term, _| {
             term.input(text.0.to_string());
         });
     }
 
-    fn send_keystroke(&mut self, text: &SendKeystroke, cx: &mut ViewContext<Self>) {
+    fn send_keystroke(&mut self, text: &SendKeystroke, _: &mut Window, cx: &mut Context<Self>) {
         if let Some(keystroke) = Keystroke::parse(&text.0).log_err() {
             self.clear_bell(cx);
             self.terminal.update(cx, |term, cx| {
@@ -552,7 +587,7 @@ impl TerminalView {
         }
     }
 
-    fn dispatch_context(&self, cx: &AppContext) -> KeyContext {
+    fn dispatch_context(&self, cx: &App) -> KeyContext {
         let mut dispatch_context = KeyContext::new_with_defaults();
         dispatch_context.add("Terminal");
 
@@ -627,9 +662,14 @@ impl TerminalView {
         dispatch_context
     }
 
-    fn set_terminal(&mut self, terminal: Model<Terminal>, cx: &mut ViewContext<TerminalView>) {
+    fn set_terminal(
+        &mut self,
+        terminal: Entity<Terminal>,
+        window: &mut Window,
+        cx: &mut Context<TerminalView>,
+    ) {
         self._terminal_subscriptions =
-            subscribe_for_terminal_events(&terminal, self.workspace.clone(), cx);
+            subscribe_for_terminal_events(&terminal, self.workspace.clone(), window, cx);
         self.terminal = terminal;
     }
 
@@ -645,7 +685,7 @@ impl TerminalView {
         }
     }
 
-    fn should_show_scrollbar(cx: &AppContext) -> bool {
+    fn should_show_scrollbar(cx: &App) -> bool {
         let show = TerminalSettings::get_global(cx)
             .scrollbar
             .show
@@ -662,7 +702,7 @@ impl TerminalView {
         }
     }
 
-    fn should_autohide_scrollbar(cx: &AppContext) -> bool {
+    fn should_autohide_scrollbar(cx: &App) -> bool {
         let show = TerminalSettings::get_global(cx)
             .scrollbar
             .show
@@ -681,7 +721,7 @@ impl TerminalView {
         }
     }
 
-    fn hide_scrollbar(&mut self, cx: &mut ViewContext<Self>) {
+    fn hide_scrollbar(&mut self, cx: &mut Context<Self>) {
         const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1);
         if !Self::should_autohide_scrollbar(cx) {
             return;
@@ -699,7 +739,7 @@ impl TerminalView {
         }))
     }
 
-    fn render_scrollbar(&self, cx: &mut ViewContext<Self>) -> Option<Stateful<Div>> {
+    fn render_scrollbar(&self, cx: &mut Context<Self>) -> Option<Stateful<Div>> {
         if !Self::should_show_scrollbar(cx)
             || !(self.show_scrollbar || self.scrollbar_state.is_dragging())
         {
@@ -727,21 +767,21 @@ impl TerminalView {
             div()
                 .occlude()
                 .id("terminal-view-scroll")
-                .on_mouse_move(cx.listener(|_, _, cx| {
+                .on_mouse_move(cx.listener(|_, _, _window, cx| {
                     cx.notify();
                     cx.stop_propagation()
                 }))
-                .on_hover(|_, cx| {
+                .on_hover(|_, _window, cx| {
                     cx.stop_propagation();
                 })
-                .on_any_mouse_down(|_, cx| {
+                .on_any_mouse_down(|_, _window, cx| {
                     cx.stop_propagation();
                 })
                 .on_mouse_up(
                     MouseButton::Left,
-                    cx.listener(|terminal_view, _, cx| {
+                    cx.listener(|terminal_view, _, window, cx| {
                         if !terminal_view.scrollbar_state.is_dragging()
-                            && !terminal_view.focus_handle.contains_focused(cx)
+                            && !terminal_view.focus_handle.contains_focused(window, cx)
                         {
                             terminal_view.hide_scrollbar(cx);
                             cx.notify();
@@ -749,7 +789,7 @@ impl TerminalView {
                         cx.stop_propagation();
                     }),
                 )
-                .on_scroll_wheel(cx.listener(|_, _, cx| {
+                .on_scroll_wheel(cx.listener(|_, _, _window, cx| {
                     cx.notify();
                 }))
                 .h_full()
@@ -765,13 +805,16 @@ impl TerminalView {
 }
 
 fn subscribe_for_terminal_events(
-    terminal: &Model<Terminal>,
-    workspace: WeakView<Workspace>,
-    cx: &mut ViewContext<TerminalView>,
+    terminal: &Entity<Terminal>,
+    workspace: WeakEntity<Workspace>,
+    window: &mut Window,
+    cx: &mut Context<TerminalView>,
 ) -> Vec<Subscription> {
     let terminal_subscription = cx.observe(terminal, |_, _, cx| cx.notify());
-    let terminal_events_subscription =
-        cx.subscribe(terminal, move |this, _, event, cx| match event {
+    let terminal_events_subscription = cx.subscribe_in(
+        terminal,
+        window,
+        move |this, _, event, window, cx| match event {
             Event::Wakeup => {
                 cx.notify();
                 cx.emit(Event::Wakeup);
@@ -838,7 +881,7 @@ fn subscribe_for_terminal_events(
                     };
 
                     let path_like_target = path_like_target.clone();
-                    cx.spawn(|terminal_view, mut cx| async move {
+                    cx.spawn_in(window, |terminal_view, mut cx| async move {
                         let valid_files_to_open = terminal_view
                             .update(&mut cx, |_, cx| {
                                 possible_open_targets(
@@ -855,11 +898,12 @@ fn subscribe_for_terminal_events(
                             .map(|(p, _)| p.path.clone())
                             .collect();
                         let opened_items = task_workspace
-                            .update(&mut cx, |workspace, cx| {
+                            .update_in(&mut cx, |workspace, window, cx| {
                                 workspace.open_paths(
                                     paths_to_open,
                                     OpenVisible::OnlyDirectories,
                                     None,
+                                    window,
                                     cx,
                                 )
                             })
@@ -879,12 +923,13 @@ fn subscribe_for_terminal_events(
                                     if let Some(active_editor) = opened_item.downcast::<Editor>() {
                                         active_editor
                                             .downgrade()
-                                            .update(&mut cx, |editor, cx| {
+                                            .update_in(&mut cx, |editor, window, cx| {
                                                 editor.go_to_singleton_buffer_point(
                                                     language::Point::new(
                                                         row.saturating_sub(1),
                                                         col.saturating_sub(1),
                                                     ),
+                                                    window,
                                                     cx,
                                                 )
                                             })
@@ -910,10 +955,11 @@ fn subscribe_for_terminal_events(
             Event::BreadcrumbsChanged => cx.emit(ItemEvent::UpdateBreadcrumbs),
             Event::CloseTerminal => cx.emit(ItemEvent::CloseItem),
             Event::SelectionsChanged => {
-                cx.invalidate_character_coordinates();
+                window.invalidate_character_coordinates();
                 cx.emit(SearchEvent::ActiveMatchChanged)
             }
-        });
+        },
+    );
     vec![terminal_subscription, terminal_events_subscription]
 }
 
@@ -922,7 +968,7 @@ fn possible_open_paths_metadata(
     row: Option<u32>,
     column: Option<u32>,
     potential_paths: HashSet<PathBuf>,
-    cx: &mut ViewContext<TerminalView>,
+    cx: &mut Context<TerminalView>,
 ) -> Task<Vec<(PathWithPosition, Metadata)>> {
     cx.background_executor().spawn(async move {
         let mut canonical_paths = HashSet::default();
@@ -964,10 +1010,11 @@ fn possible_open_paths_metadata(
 
 fn possible_open_targets(
     fs: Arc<dyn Fs>,
-    workspace: &WeakView<Workspace>,
+    workspace: &WeakEntity<Workspace>,
     cwd: &Option<PathBuf>,
     maybe_path: &String,
-    cx: &mut ViewContext<TerminalView>,
+
+    cx: &mut Context<TerminalView>,
 ) -> Task<Vec<(PathWithPosition, Metadata)>> {
     let path_position = PathWithPosition::parse_str(maybe_path.as_str());
     let row = path_position.row;
@@ -1040,9 +1087,9 @@ pub fn regex_search_for_query(query: &project::search::SearchQuery) -> Option<Re
 }
 
 impl TerminalView {
-    fn key_down(&mut self, event: &KeyDownEvent, cx: &mut ViewContext<Self>) {
+    fn key_down(&mut self, event: &KeyDownEvent, window: &mut Window, cx: &mut Context<Self>) {
         self.clear_bell(cx);
-        self.pause_cursor_blinking(cx);
+        self.pause_cursor_blinking(window, cx);
 
         self.terminal.update(cx, |term, cx| {
             let handled = term.try_keystroke(
@@ -1055,17 +1102,17 @@ impl TerminalView {
         });
     }
 
-    fn focus_in(&mut self, cx: &mut ViewContext<Self>) {
+    fn focus_in(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.terminal.update(cx, |terminal, _| {
             terminal.set_cursor_shape(self.cursor_shape);
             terminal.focus_in();
         });
-        self.blink_cursors(self.blink_epoch, cx);
-        cx.invalidate_character_coordinates();
+        self.blink_cursors(self.blink_epoch, window, cx);
+        window.invalidate_character_coordinates();
         cx.notify();
     }
 
-    fn focus_out(&mut self, cx: &mut ViewContext<Self>) {
+    fn focus_out(&mut self, _: &mut Window, cx: &mut Context<Self>) {
         self.terminal.update(cx, |terminal, _| {
             terminal.focus_out();
             terminal.set_cursor_shape(CursorShape::Hollow);
@@ -1076,11 +1123,11 @@ impl TerminalView {
 }
 
 impl Render for TerminalView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let terminal_handle = self.terminal.clone();
-        let terminal_view_handle = cx.view().clone();
+        let terminal_view_handle = cx.model().clone();
 
-        let focused = self.focus_handle.is_focused(cx);
+        let focused = self.focus_handle.is_focused(window);
 
         div()
             .occlude()
@@ -1106,19 +1153,19 @@ impl Render for TerminalView {
             .on_key_down(cx.listener(Self::key_down))
             .on_mouse_down(
                 MouseButton::Right,
-                cx.listener(|this, event: &MouseDownEvent, cx| {
+                cx.listener(|this, event: &MouseDownEvent, window, cx| {
                     if !this.terminal.read(cx).mouse_mode(event.modifiers.shift) {
-                        this.deploy_context_menu(event.position, cx);
+                        this.deploy_context_menu(event.position, window, cx);
                         cx.notify();
                     }
                 }),
             )
-            .on_hover(cx.listener(|this, hovered, cx| {
+            .on_hover(cx.listener(|this, hovered, window, cx| {
                 if *hovered {
                     this.show_scrollbar = true;
                     this.hide_scrollbar_task.take();
                     cx.notify();
-                } else if !this.focus_handle.contains_focused(cx) {
+                } else if !this.focus_handle.contains_focused(window, cx) {
                     this.hide_scrollbar(cx);
                 }
             }))
@@ -1155,20 +1202,17 @@ impl Render for TerminalView {
 impl Item for TerminalView {
     type Event = ItemEvent;
 
-    fn tab_tooltip_content(&self, cx: &AppContext) -> Option<TabTooltipContent> {
+    fn tab_tooltip_content(&self, cx: &App) -> Option<TabTooltipContent> {
         let terminal = self.terminal().read(cx);
         let title = terminal.title(false);
         let pid = terminal.pty_info.pid_getter().fallback_pid();
 
-        Some(TabTooltipContent::Custom(Box::new(
-            move |cx: &mut WindowContext| {
-                cx.new_view(|_| TerminalTooltip::new(title.clone(), pid))
-                    .into()
-            },
-        )))
+        Some(TabTooltipContent::Custom(Box::new(move |_window, cx| {
+            cx.new(|_| TerminalTooltip::new(title.clone(), pid)).into()
+        })))
     }
 
-    fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement {
+    fn tab_content(&self, params: TabContentParams, _window: &Window, cx: &App) -> AnyElement {
         let terminal = self.terminal().read(cx);
         let title = terminal.title(true);
         let rerun_button = |task_id: task::TaskId| {
@@ -1177,14 +1221,17 @@ impl Item for TerminalView {
                 .size(ButtonSize::Compact)
                 .icon_color(Color::Default)
                 .shape(ui::IconButtonShape::Square)
-                .tooltip(|cx| Tooltip::text("Rerun task", cx))
-                .on_click(move |_, cx| {
-                    cx.dispatch_action(Box::new(zed_actions::Rerun {
-                        task_id: Some(task_id.0.clone()),
-                        allow_concurrent_runs: Some(true),
-                        use_new_terminal: Some(false),
-                        reevaluate_context: false,
-                    }));
+                .tooltip(Tooltip::text("Rerun task"))
+                .on_click(move |_, window, cx| {
+                    window.dispatch_action(
+                        Box::new(zed_actions::Rerun {
+                            task_id: Some(task_id.0.clone()),
+                            allow_concurrent_runs: Some(true),
+                            use_new_terminal: Some(false),
+                            reevaluate_context: false,
+                        }),
+                        cx,
+                    );
                 })
         };
 
@@ -1245,9 +1292,10 @@ impl Item for TerminalView {
     fn clone_on_split(
         &self,
         workspace_id: Option<WorkspaceId>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<View<Self>> {
-        let window = cx.window_handle();
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Entity<Self>> {
+        let window_handle = window.window_handle();
         let terminal = self
             .project
             .update(cx, |project, cx| {
@@ -1259,44 +1307,45 @@ impl Item for TerminalView {
                 project.create_terminal_with_venv(
                     TerminalKind::Shell(working_directory),
                     python_venv_directory,
-                    window,
+                    window_handle,
                     cx,
                 )
             })
             .ok()?
             .log_err()?;
 
-        Some(cx.new_view(|cx| {
+        Some(cx.new(|cx| {
             TerminalView::new(
                 terminal,
                 self.workspace.clone(),
                 workspace_id,
                 self.project.clone(),
+                window,
                 cx,
             )
         }))
     }
 
-    fn is_dirty(&self, cx: &gpui::AppContext) -> bool {
+    fn is_dirty(&self, cx: &gpui::App) -> bool {
         match self.terminal.read(cx).task() {
             Some(task) => task.status == TaskStatus::Running,
             None => self.has_bell(),
         }
     }
 
-    fn has_conflict(&self, _cx: &AppContext) -> bool {
+    fn has_conflict(&self, _cx: &App) -> bool {
         false
     }
 
-    fn is_singleton(&self, _cx: &AppContext) -> bool {
+    fn is_singleton(&self, _cx: &App) -> bool {
         true
     }
 
-    fn as_searchable(&self, handle: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
+    fn as_searchable(&self, handle: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {
         Some(Box::new(handle.clone()))
     }
 
-    fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation {
+    fn breadcrumb_location(&self, cx: &App) -> ToolbarItemLocation {
         if self.show_breadcrumbs && !self.terminal().read(cx).breadcrumb_text.trim().is_empty() {
             ToolbarItemLocation::PrimaryLeft
         } else {
@@ -1304,7 +1353,7 @@ impl Item for TerminalView {
         }
     }
 
-    fn breadcrumbs(&self, _: &theme::Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
+    fn breadcrumbs(&self, _: &theme::Theme, cx: &App) -> Option<Vec<BreadcrumbText>> {
         Some(vec![BreadcrumbText {
             text: self.terminal().read(cx).breadcrumb_text.clone(),
             highlights: None,
@@ -1312,7 +1361,12 @@ impl Item for TerminalView {
         }])
     }
 
-    fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
+    fn added_to_workspace(
+        &mut self,
+        workspace: &mut Workspace,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if self.terminal().read(cx).task().is_none() {
             if let Some((new_id, old_id)) = workspace.database_id().zip(self.workspace_id) {
                 cx.background_executor()
@@ -1336,9 +1390,12 @@ impl SerializableItem for TerminalView {
     fn cleanup(
         workspace_id: WorkspaceId,
         alive_items: Vec<workspace::ItemId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<gpui::Result<()>> {
-        cx.spawn(|_| TERMINAL_DB.delete_unloaded_items(workspace_id, alive_items))
+        window.spawn(cx, |_| {
+            TERMINAL_DB.delete_unloaded_items(workspace_id, alive_items)
+        })
     }
 
     fn serialize(
@@ -1346,7 +1403,8 @@ impl SerializableItem for TerminalView {
         _workspace: &mut Workspace,
         item_id: workspace::ItemId,
         _closing: bool,
-        cx: &mut ViewContext<Self>,
+        _: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<Task<gpui::Result<()>>> {
         let terminal = self.terminal().read(cx);
         if terminal.task().is_some() {
@@ -1369,16 +1427,17 @@ impl SerializableItem for TerminalView {
     }
 
     fn deserialize(
-        project: Model<Project>,
-        workspace: WeakView<Workspace>,
+        project: Entity<Project>,
+        workspace: WeakEntity<Workspace>,
         workspace_id: workspace::WorkspaceId,
         item_id: workspace::ItemId,
-        cx: &mut WindowContext,
-    ) -> Task<anyhow::Result<View<Self>>> {
-        let window = cx.window_handle();
-        cx.spawn(|mut cx| async move {
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Task<anyhow::Result<Entity<Self>>> {
+        let window_handle = window.window_handle();
+        window.spawn(cx, |mut cx| async move {
             let cwd = cx
-                .update(|cx| {
+                .update(|_window, cx| {
                     let from_db = TERMINAL_DB
                         .get_working_directory(item_id, workspace_id)
                         .log_err()
@@ -1399,16 +1458,17 @@ impl SerializableItem for TerminalView {
 
             let terminal = project
                 .update(&mut cx, |project, cx| {
-                    project.create_terminal(TerminalKind::Shell(cwd), window, cx)
+                    project.create_terminal(TerminalKind::Shell(cwd), window_handle, cx)
                 })?
                 .await?;
-            cx.update(|cx| {
-                cx.new_view(|cx| {
+            cx.update(|window, cx| {
+                cx.new(|cx| {
                     TerminalView::new(
                         terminal,
                         workspace,
                         Some(workspace_id),
                         project.downgrade(),
+                        window,
                         cx,
                     )
                 })
@@ -1431,18 +1491,23 @@ impl SearchableItem for TerminalView {
     }
 
     /// Clear stored matches
-    fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
+    fn clear_matches(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
         self.terminal().update(cx, |term, _| term.matches.clear())
     }
 
     /// Store matches returned from find_matches somewhere for rendering
-    fn update_matches(&mut self, matches: &[Self::Match], cx: &mut ViewContext<Self>) {
+    fn update_matches(
+        &mut self,
+        matches: &[Self::Match],
+        _window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.terminal()
             .update(cx, |term, _| term.matches = matches.to_vec())
     }
 
     /// Returns the selection content to pre-load into this search
-    fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
+    fn query_suggestion(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> String {
         self.terminal()
             .read(cx)
             .last_content
@@ -1452,14 +1517,20 @@ impl SearchableItem for TerminalView {
     }
 
     /// Focus match at given index into the Vec of matches
-    fn activate_match(&mut self, index: usize, _: &[Self::Match], cx: &mut ViewContext<Self>) {
+    fn activate_match(
+        &mut self,
+        index: usize,
+        _: &[Self::Match],
+        _window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.terminal()
             .update(cx, |term, _| term.activate_match(index));
         cx.notify();
     }
 
     /// Add selections for all matches given.
-    fn select_matches(&mut self, matches: &[Self::Match], cx: &mut ViewContext<Self>) {
+    fn select_matches(&mut self, matches: &[Self::Match], _: &mut Window, cx: &mut Context<Self>) {
         self.terminal()
             .update(cx, |term, _| term.select_matches(matches));
         cx.notify();
@@ -1469,7 +1540,8 @@ impl SearchableItem for TerminalView {
     fn find_matches(
         &mut self,
         query: Arc<SearchQuery>,
-        cx: &mut ViewContext<Self>,
+        _: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<Vec<Self::Match>> {
         let searcher = match &*query {
             SearchQuery::Text { .. } => regex_search_for_query(
@@ -1499,7 +1571,8 @@ impl SearchableItem for TerminalView {
     fn active_match_index(
         &mut self,
         matches: &[Self::Match],
-        cx: &mut ViewContext<Self>,
+        _: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<usize> {
         // Selection head might have a value if there's a selection that isn't
         // associated with a match. Therefore, if there are no matches, we should
@@ -1531,14 +1604,20 @@ impl SearchableItem for TerminalView {
 
         res
     }
-    fn replace(&mut self, _: &Self::Match, _: &SearchQuery, _: &mut ViewContext<Self>) {
+    fn replace(
+        &mut self,
+        _: &Self::Match,
+        _: &SearchQuery,
+        _window: &mut Window,
+        _: &mut Context<Self>,
+    ) {
         // Replacement is not supported in terminal view, so this is a no-op.
     }
 }
 
 ///Gets the working directory for the given workspace, respecting the user's settings.
 /// None implies "~" on whichever machine we end up on.
-pub(crate) fn default_working_directory(workspace: &Workspace, cx: &AppContext) -> Option<PathBuf> {
+pub(crate) fn default_working_directory(workspace: &Workspace, cx: &App) -> Option<PathBuf> {
     match &TerminalSettings::get_global(cx).working_directory {
         WorkingDirectory::CurrentProjectDirectory => workspace
             .project()

crates/theme/src/font_family_cache.rs 🔗

@@ -1,7 +1,7 @@
 use std::sync::Arc;
 use std::time::Instant;
 
-use gpui::{AppContext, Global, ReadGlobal, SharedString};
+use gpui::{App, Global, ReadGlobal, SharedString};
 use parking_lot::RwLock;
 
 #[derive(Default)]
@@ -26,17 +26,17 @@ impl Global for GlobalFontFamilyCache {}
 
 impl FontFamilyCache {
     /// Initializes the global font family cache.
-    pub fn init_global(cx: &mut AppContext) {
+    pub fn init_global(cx: &mut App) {
         cx.default_global::<GlobalFontFamilyCache>();
     }
 
     /// Returns the global font family cache.
-    pub fn global(cx: &AppContext) -> Arc<Self> {
+    pub fn global(cx: &App) -> Arc<Self> {
         GlobalFontFamilyCache::global(cx).0.clone()
     }
 
     /// Returns the list of font families.
-    pub fn list_font_families(&self, cx: &AppContext) -> Vec<SharedString> {
+    pub fn list_font_families(&self, cx: &App) -> Vec<SharedString> {
         if self.state.read().loaded_at.is_some() {
             return self.state.read().font_families.clone();
         }

crates/theme/src/registry.rs 🔗

@@ -1,12 +1,12 @@
 use std::sync::Arc;
 use std::{fmt::Debug, path::Path};
 
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use collections::HashMap;
 use derive_more::{Deref, DerefMut};
 use fs::Fs;
 use futures::StreamExt;
-use gpui::{AppContext, AssetSource, Global, SharedString};
+use gpui::{App, AssetSource, Global, SharedString};
 use parking_lot::RwLock;
 use util::ResultExt;
 
@@ -49,19 +49,19 @@ pub struct ThemeRegistry {
 
 impl ThemeRegistry {
     /// Returns the global [`ThemeRegistry`].
-    pub fn global(cx: &AppContext) -> Arc<Self> {
+    pub fn global(cx: &App) -> Arc<Self> {
         cx.global::<GlobalThemeRegistry>().0.clone()
     }
 
     /// Returns the global [`ThemeRegistry`].
     ///
     /// Inserts a default [`ThemeRegistry`] if one does not yet exist.
-    pub fn default_global(cx: &mut AppContext) -> Arc<Self> {
+    pub fn default_global(cx: &mut App) -> Arc<Self> {
         cx.default_global::<GlobalThemeRegistry>().0.clone()
     }
 
     /// Sets the global [`ThemeRegistry`].
-    pub(crate) fn set_global(assets: Box<dyn AssetSource>, cx: &mut AppContext) {
+    pub(crate) fn set_global(assets: Box<dyn AssetSource>, cx: &mut App) {
         cx.set_global(GlobalThemeRegistry(Arc::new(ThemeRegistry::new(assets))));
     }
 

crates/theme/src/scale.rs 🔗

@@ -1,5 +1,5 @@
 #![allow(missing_docs)]
-use gpui::{AppContext, Hsla, SharedString};
+use gpui::{App, Hsla, SharedString};
 
 use crate::{ActiveTheme, Appearance};
 
@@ -282,14 +282,14 @@ impl ColorScaleSet {
         &self.dark_alpha
     }
 
-    pub fn step(&self, cx: &AppContext, step: ColorScaleStep) -> Hsla {
+    pub fn step(&self, cx: &App, step: ColorScaleStep) -> Hsla {
         match cx.theme().appearance {
             Appearance::Light => self.light().step(step),
             Appearance::Dark => self.dark().step(step),
         }
     }
 
-    pub fn step_alpha(&self, cx: &AppContext, step: ColorScaleStep) -> Hsla {
+    pub fn step_alpha(&self, cx: &App, step: ColorScaleStep) -> Hsla {
         match cx.theme().appearance {
             Appearance::Light => self.light_alpha.step(step),
             Appearance::Dark => self.dark_alpha.step(step),

crates/theme/src/settings.rs 🔗

@@ -6,8 +6,7 @@ use crate::{
 use anyhow::Result;
 use derive_more::{Deref, DerefMut};
 use gpui::{
-    px, AppContext, Font, FontFallbacks, FontFeatures, FontStyle, FontWeight, Global, Pixels,
-    WindowContext,
+    px, App, Font, FontFallbacks, FontFeatures, FontStyle, FontWeight, Global, Pixels, Window,
 };
 use refineable::Refineable;
 use schemars::{
@@ -146,7 +145,7 @@ impl ThemeSettings {
     ///
     /// Reads the [`ThemeSettings`] to know which theme should be loaded,
     /// taking into account the current [`SystemAppearance`].
-    pub fn reload_current_theme(cx: &mut AppContext) {
+    pub fn reload_current_theme(cx: &mut App) {
         let mut theme_settings = ThemeSettings::get_global(cx).clone();
         let system_appearance = SystemAppearance::global(cx);
 
@@ -184,7 +183,7 @@ impl Global for GlobalSystemAppearance {}
 
 impl SystemAppearance {
     /// Initializes the [`SystemAppearance`] for the application.
-    pub fn init(cx: &mut AppContext) {
+    pub fn init(cx: &mut App) {
         *cx.default_global::<GlobalSystemAppearance>() =
             GlobalSystemAppearance(SystemAppearance(cx.window_appearance().into()));
     }
@@ -192,17 +191,17 @@ impl SystemAppearance {
     /// Returns the global [`SystemAppearance`].
     ///
     /// Inserts a default [`SystemAppearance`] if one does not yet exist.
-    pub(crate) fn default_global(cx: &mut AppContext) -> Self {
+    pub(crate) fn default_global(cx: &mut App) -> Self {
         cx.default_global::<GlobalSystemAppearance>().0
     }
 
     /// Returns the global [`SystemAppearance`].
-    pub fn global(cx: &AppContext) -> Self {
+    pub fn global(cx: &App) -> Self {
         cx.global::<GlobalSystemAppearance>().0
     }
 
     /// Returns a mutable reference to the global [`SystemAppearance`].
-    pub fn global_mut(cx: &mut AppContext) -> &mut Self {
+    pub fn global_mut(cx: &mut App) -> &mut Self {
         cx.global_mut::<GlobalSystemAppearance>()
     }
 }
@@ -449,7 +448,7 @@ impl ThemeSettings {
     ///
     /// Returns a `Some` containing the new theme if it was successful.
     /// Returns `None` otherwise.
-    pub fn switch_theme(&mut self, theme: &str, cx: &mut AppContext) -> Option<Arc<Theme>> {
+    pub fn switch_theme(&mut self, theme: &str, cx: &mut App) -> Option<Arc<Theme>> {
         let themes = ThemeRegistry::default_global(cx);
 
         let mut new_theme = None;
@@ -495,14 +494,14 @@ impl ThemeSettings {
 
 // TODO: Make private, change usages to use `get_ui_font_size` instead.
 #[allow(missing_docs)]
-pub fn setup_ui_font(cx: &mut WindowContext) -> gpui::Font {
+pub fn setup_ui_font(window: &mut Window, cx: &mut App) -> gpui::Font {
     let (ui_font, ui_font_size) = {
         let theme_settings = ThemeSettings::get_global(cx);
         let font = theme_settings.ui_font.clone();
         (font, theme_settings.ui_font_size)
     };
 
-    cx.set_rem_size(ui_font_size);
+    window.set_rem_size(ui_font_size);
     ui_font
 }
 
@@ -515,7 +514,7 @@ impl settings::Settings for ThemeSettings {
 
     type FileContent = ThemeSettingsContent;
 
-    fn load(sources: SettingsSources<Self::FileContent>, cx: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, cx: &mut App) -> Result<Self> {
         let themes = ThemeRegistry::default_global(cx);
         let system_appearance = SystemAppearance::default_global(cx);
 
@@ -636,7 +635,7 @@ impl settings::Settings for ThemeSettings {
     fn json_schema(
         generator: &mut SchemaGenerator,
         params: &SettingsJsonSchemaParams,
-        cx: &AppContext,
+        cx: &App,
     ) -> schemars::schema::RootSchema {
         let mut root_schema = generator.root_schema_for::<ThemeSettingsContent>();
         let theme_names = ThemeRegistry::global(cx)

crates/theme/src/styles/colors.rs 🔗

@@ -1,6 +1,6 @@
 #![allow(missing_docs)]
 
-use gpui::{Hsla, SharedString, WindowBackgroundAppearance, WindowContext};
+use gpui::{App, Hsla, SharedString, WindowBackgroundAppearance};
 use refineable::Refineable;
 use std::sync::Arc;
 use strum::{AsRefStr, EnumIter, IntoEnumIterator};
@@ -512,7 +512,7 @@ impl ThemeColors {
     }
 }
 
-pub fn all_theme_colors(cx: &WindowContext) -> Vec<(Hsla, SharedString)> {
+pub fn all_theme_colors(cx: &mut App) -> Vec<(Hsla, SharedString)> {
     let theme = cx.theme();
     ThemeColorField::iter()
         .map(|field| {

crates/theme/src/theme.rs 🔗

@@ -26,8 +26,8 @@ use ::settings::Settings;
 use anyhow::Result;
 use fs::Fs;
 use gpui::{
-    px, AppContext, AssetSource, HighlightStyle, Hsla, Pixels, Refineable, SharedString,
-    WindowAppearance, WindowBackgroundAppearance,
+    px, App, AssetSource, HighlightStyle, Hsla, Pixels, Refineable, SharedString, WindowAppearance,
+    WindowBackgroundAppearance,
 };
 use serde::Deserialize;
 use uuid::Uuid;
@@ -87,7 +87,7 @@ pub enum LoadThemes {
 }
 
 /// Initialize the theme system.
-pub fn init(themes_to_load: LoadThemes, cx: &mut AppContext) {
+pub fn init(themes_to_load: LoadThemes, cx: &mut App) {
     let (assets, load_user_themes) = match themes_to_load {
         LoadThemes::JustBase => (Box::new(()) as Box<dyn AssetSource>, false),
         LoadThemes::All(assets) => (assets, true),
@@ -108,7 +108,7 @@ pub trait ActiveTheme {
     fn theme(&self) -> &Arc<Theme>;
 }
 
-impl ActiveTheme for AppContext {
+impl ActiveTheme for App {
     fn theme(&self) -> &Arc<Theme> {
         &ThemeSettings::get_global(self).active_theme
     }

crates/theme_extension/src/theme_extension.rs 🔗

@@ -4,7 +4,7 @@ use std::sync::Arc;
 use anyhow::Result;
 use extension::{ExtensionHostProxy, ExtensionThemeProxy};
 use fs::Fs;
-use gpui::{AppContext, BackgroundExecutor, SharedString, Task};
+use gpui::{App, BackgroundExecutor, SharedString, Task};
 use theme::{ThemeRegistry, ThemeSettings};
 
 pub fn init(
@@ -41,7 +41,7 @@ impl ExtensionThemeProxy for ThemeRegistryProxy {
             .spawn(async move { theme_registry.load_user_theme(&theme_path, fs).await })
     }
 
-    fn reload_current_theme(&self, cx: &mut AppContext) {
+    fn reload_current_theme(&self, cx: &mut App) {
         ThemeSettings::reload_current_theme(cx)
     }
 

crates/theme_importer/src/main.rs 🔗

@@ -6,7 +6,7 @@ use std::fs::File;
 use std::io::Write;
 use std::path::PathBuf;
 
-use anyhow::{Context, Result};
+use anyhow::{Context as _, Result};
 use clap::{Parser, Subcommand};
 use indexmap::IndexMap;
 use log::LevelFilter;

crates/theme_selector/src/theme_selector.rs 🔗

@@ -1,8 +1,8 @@
 use fs::Fs;
 use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
 use gpui::{
-    actions, AppContext, DismissEvent, EventEmitter, FocusableView, Render, UpdateGlobal, View,
-    ViewContext, VisualContext, WeakView,
+    actions, App, Context, DismissEvent, Entity, EventEmitter, Focusable, Render, UpdateGlobal,
+    WeakEntity, Window,
 };
 use picker::{Picker, PickerDelegate};
 use settings::{update_settings_file, SettingsStore};
@@ -15,51 +15,60 @@ use zed_actions::theme_selector::Toggle;
 
 actions!(theme_selector, [Reload]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(
-        |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
+pub fn init(cx: &mut App) {
+    cx.observe_new(
+        |workspace: &mut Workspace, _window, _cx: &mut Context<Workspace>| {
             workspace.register_action(toggle);
         },
     )
     .detach();
 }
 
-pub fn toggle(workspace: &mut Workspace, toggle: &Toggle, cx: &mut ViewContext<Workspace>) {
+pub fn toggle(
+    workspace: &mut Workspace,
+    toggle: &Toggle,
+    window: &mut Window,
+    cx: &mut Context<Workspace>,
+) {
     let fs = workspace.app_state().fs.clone();
-    workspace.toggle_modal(cx, |cx| {
+    workspace.toggle_modal(window, cx, |window, cx| {
         let delegate = ThemeSelectorDelegate::new(
-            cx.view().downgrade(),
+            cx.model().downgrade(),
             fs,
             toggle.themes_filter.as_ref(),
             cx,
         );
-        ThemeSelector::new(delegate, cx)
+        ThemeSelector::new(delegate, window, cx)
     });
 }
 
 impl ModalView for ThemeSelector {}
 
 pub struct ThemeSelector {
-    picker: View<Picker<ThemeSelectorDelegate>>,
+    picker: Entity<Picker<ThemeSelectorDelegate>>,
 }
 
 impl EventEmitter<DismissEvent> for ThemeSelector {}
 
-impl FocusableView for ThemeSelector {
-    fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
+impl Focusable for ThemeSelector {
+    fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
 
 impl Render for ThemeSelector {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         v_flex().w(rems(34.)).child(self.picker.clone())
     }
 }
 
 impl ThemeSelector {
-    pub fn new(delegate: ThemeSelectorDelegate, cx: &mut ViewContext<Self>) -> Self {
-        let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
+    pub fn new(
+        delegate: ThemeSelectorDelegate,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Self {
+        let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
         Self { picker }
     }
 }
@@ -71,15 +80,15 @@ pub struct ThemeSelectorDelegate {
     original_theme: Arc<Theme>,
     selection_completed: bool,
     selected_index: usize,
-    view: WeakView<ThemeSelector>,
+    selector: WeakEntity<ThemeSelector>,
 }
 
 impl ThemeSelectorDelegate {
     fn new(
-        weak_view: WeakView<ThemeSelector>,
+        selector: WeakEntity<ThemeSelector>,
         fs: Arc<dyn Fs>,
         themes_filter: Option<&Vec<String>>,
-        cx: &mut ViewContext<ThemeSelector>,
+        cx: &mut Context<ThemeSelector>,
     ) -> Self {
         let original_theme = cx.theme().clone();
 
@@ -118,14 +127,14 @@ impl ThemeSelectorDelegate {
             original_theme: original_theme.clone(),
             selected_index: 0,
             selection_completed: false,
-            view: weak_view,
+            selector,
         };
 
         this.select_if_matching(&original_theme.name);
         this
     }
 
-    fn show_selected_theme(&mut self, cx: &mut ViewContext<Picker<ThemeSelectorDelegate>>) {
+    fn show_selected_theme(&mut self, cx: &mut Context<Picker<ThemeSelectorDelegate>>) {
         if let Some(mat) = self.matches.get(self.selected_index) {
             let registry = ThemeRegistry::global(cx);
             match registry.get(&mat.string) {
@@ -147,13 +156,13 @@ impl ThemeSelectorDelegate {
             .unwrap_or(self.selected_index);
     }
 
-    fn set_theme(theme: Arc<Theme>, cx: &mut AppContext) {
+    fn set_theme(theme: Arc<Theme>, cx: &mut App) {
         SettingsStore::update_global(cx, |store, cx| {
             let mut theme_settings = store.get::<ThemeSettings>(None).clone();
             theme_settings.active_theme = theme;
             theme_settings.apply_theme_overrides();
             store.override_global(theme_settings);
-            cx.refresh();
+            cx.refresh_windows();
         });
     }
 }
@@ -161,7 +170,7 @@ impl ThemeSelectorDelegate {
 impl PickerDelegate for ThemeSelectorDelegate {
     type ListItem = ui::ListItem;
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Select Theme...".into()
     }
 
@@ -169,33 +178,38 @@ impl PickerDelegate for ThemeSelectorDelegate {
         self.matches.len()
     }
 
-    fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<ThemeSelectorDelegate>>) {
+    fn confirm(
+        &mut self,
+        _: bool,
+        window: &mut Window,
+        cx: &mut Context<Picker<ThemeSelectorDelegate>>,
+    ) {
         self.selection_completed = true;
 
         let theme_name = cx.theme().name.clone();
 
         telemetry::event!("Settings Changed", setting = "theme", value = theme_name);
 
-        let appearance = Appearance::from(cx.appearance());
+        let appearance = Appearance::from(window.appearance());
 
         update_settings_file::<ThemeSettings>(self.fs.clone(), cx, move |settings, _| {
             settings.set_theme(theme_name.to_string(), appearance);
         });
 
-        self.view
+        self.selector
             .update(cx, |_, cx| {
                 cx.emit(DismissEvent);
             })
             .ok();
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<ThemeSelectorDelegate>>) {
+    fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<ThemeSelectorDelegate>>) {
         if !self.selection_completed {
             Self::set_theme(self.original_theme.clone(), cx);
             self.selection_completed = true;
         }
 
-        self.view
+        self.selector
             .update(cx, |_, cx| cx.emit(DismissEvent))
             .log_err();
     }
@@ -207,7 +221,8 @@ impl PickerDelegate for ThemeSelectorDelegate {
     fn set_selected_index(
         &mut self,
         ix: usize,
-        cx: &mut ViewContext<Picker<ThemeSelectorDelegate>>,
+        _: &mut Window,
+        cx: &mut Context<Picker<ThemeSelectorDelegate>>,
     ) {
         self.selected_index = ix;
         self.show_selected_theme(cx);
@@ -216,7 +231,8 @@ impl PickerDelegate for ThemeSelectorDelegate {
     fn update_matches(
         &mut self,
         query: String,
-        cx: &mut ViewContext<Picker<ThemeSelectorDelegate>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<ThemeSelectorDelegate>>,
     ) -> gpui::Task<()> {
         let background = cx.background_executor().clone();
         let candidates = self
@@ -226,7 +242,7 @@ impl PickerDelegate for ThemeSelectorDelegate {
             .map(|(id, meta)| StringMatchCandidate::new(id, &meta.name))
             .collect::<Vec<_>>();
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let matches = if query.is_empty() {
                 candidates
                     .into_iter()
@@ -266,7 +282,8 @@ impl PickerDelegate for ThemeSelectorDelegate {
         &self,
         ix: usize,
         selected: bool,
-        _cx: &mut ViewContext<Picker<Self>>,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let theme_match = &self.matches[ix];
 

crates/title_bar/src/application_menu.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{impl_actions, OwnedMenu, OwnedMenuItem, View};
+use gpui::{impl_actions, Entity, OwnedMenu, OwnedMenuItem};
 use schemars::JsonSchema;
 use serde::Deserialize;
 use smallvec::SmallVec;
@@ -27,7 +27,7 @@ pub struct ApplicationMenu {
 }
 
 impl ApplicationMenu {
-    pub fn new(cx: &mut ViewContext<Self>) -> Self {
+    pub fn new(_: &mut Window, cx: &mut Context<Self>) -> Self {
         let menus = cx.get_menus().unwrap_or_default();
         Self {
             entries: menus
@@ -75,10 +75,14 @@ impl ApplicationMenu {
         cleaned
     }
 
-    fn build_menu_from_items(entry: MenuEntry, cx: &mut WindowContext) -> View<ContextMenu> {
-        ContextMenu::build(cx, |menu, cx| {
+    fn build_menu_from_items(
+        entry: MenuEntry,
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Entity<ContextMenu> {
+        ContextMenu::build(window, cx, |menu, window, cx| {
             // Grab current focus handle so menu can shown items in context with the focused element
-            let menu = menu.when_some(cx.focused(), |menu, focused| menu.context(focused));
+            let menu = menu.when_some(window.focused(cx), |menu, focused| menu.context(focused));
             let sanitized_items = Self::sanitize_menu_items(entry.menu.items);
 
             sanitized_items
@@ -114,7 +118,9 @@ impl ApplicationMenu {
             .occlude()
             .child(
                 PopoverMenu::new(SharedString::from(format!("{}-menu-popover", menu_name)))
-                    .menu(move |cx| Self::build_menu_from_items(entry.clone(), cx).into())
+                    .menu(move |window, cx| {
+                        Self::build_menu_from_items(entry.clone(), window, cx).into()
+                    })
                     .trigger(
                         IconButton::new(
                             SharedString::from(format!("{}-menu-trigger", menu_name)),
@@ -123,7 +129,7 @@ impl ApplicationMenu {
                         .style(ButtonStyle::Subtle)
                         .icon_size(IconSize::Small)
                         .when(!handle.is_deployed(), |this| {
-                            this.tooltip(|cx| Tooltip::text("Open Application Menu", cx))
+                            this.tooltip(Tooltip::text("Open Application Menu"))
                         }),
                     )
                     .with_handle(handle),
@@ -147,7 +153,9 @@ impl ApplicationMenu {
             .occlude()
             .child(
                 PopoverMenu::new(SharedString::from(format!("{}-menu-popover", menu_name)))
-                    .menu(move |cx| Self::build_menu_from_items(entry.clone(), cx).into())
+                    .menu(move |window, cx| {
+                        Self::build_menu_from_items(entry.clone(), window, cx).into()
+                    })
                     .trigger(
                         Button::new(
                             SharedString::from(format!("{}-menu-trigger", menu_name)),
@@ -158,19 +166,24 @@ impl ApplicationMenu {
                     )
                     .with_handle(current_handle.clone()),
             )
-            .on_hover(move |hover_enter, cx| {
+            .on_hover(move |hover_enter, window, cx| {
                 if *hover_enter && !current_handle.is_deployed() {
                     all_handles.iter().for_each(|h| h.hide(cx));
 
                     // We need to defer this so that this menu handle can take focus from the previous menu
                     let handle = current_handle.clone();
-                    cx.defer(move |cx| handle.show(cx));
+                    window.defer(cx, move |window, cx| handle.show(window, cx));
                 }
             })
     }
 
     #[cfg(not(target_os = "macos"))]
-    pub fn open_menu(&mut self, action: &OpenApplicationMenu, _cx: &mut ViewContext<Self>) {
+    pub fn open_menu(
+        &mut self,
+        action: &OpenApplicationMenu,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
+    ) {
         self.pending_menu_open = Some(action.0.clone());
     }
 
@@ -178,7 +191,8 @@ impl ApplicationMenu {
     pub fn navigate_menus_in_direction(
         &mut self,
         action: &NavigateApplicationMenuInDirection,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let current_index = self
             .entries
@@ -210,7 +224,7 @@ impl ApplicationMenu {
 
         // We need to defer this so that this menu handle can take focus from the previous menu
         let next_handle = self.entries[next_index].handle.clone();
-        cx.defer(move |_, cx| next_handle.show(cx));
+        cx.defer_in(window, move |_, window, cx| next_handle.show(window, cx));
     }
 
     pub fn all_menus_shown(&self) -> bool {
@@ -220,7 +234,7 @@ impl ApplicationMenu {
 }
 
 impl Render for ApplicationMenu {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let all_menus_shown = self.all_menus_shown();
 
         if let Some(pending_menu_open) = self.pending_menu_open.take() {
@@ -240,14 +254,14 @@ impl Render for ApplicationMenu {
                 if handles_to_hide.is_empty() {
                     // We need to wait for the next frame to show all menus first,
                     // before we can handle show/hide operations
-                    cx.window_context().on_next_frame(move |cx| {
+                    window.on_next_frame(move |window, cx| {
                         handles_to_hide.iter().for_each(|handle| handle.hide(cx));
-                        cx.defer(move |cx| handle_to_show.show(cx));
+                        window.defer(cx, move |window, cx| handle_to_show.show(window, cx));
                     });
                 } else {
                     // Since menus are already shown, we can directly handle show/hide operations
                     handles_to_hide.iter().for_each(|handle| handle.hide(cx));
-                    cx.defer(move |_, cx| handle_to_show.show(cx));
+                    cx.defer_in(window, move |_, window, cx| handle_to_show.show(window, cx));
                 }
             }
         }

crates/title_bar/src/collab.rs 🔗

@@ -2,7 +2,7 @@ use std::sync::Arc;
 
 use call::{ActiveCall, ParticipantLocation, Room};
 use client::{proto::PeerId, User};
-use gpui::{actions, AppContext, Task, WindowContext};
+use gpui::{actions, App, Task, Window};
 use gpui::{canvas, point, AnyElement, Hsla, IntoElement, MouseButton, Path, Styled};
 use rpc::proto::{self};
 use theme::ActiveTheme;
@@ -16,7 +16,7 @@ actions!(
     [ToggleScreenSharing, ToggleMute, ToggleDeafen, LeaveCall]
 );
 
-fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut WindowContext) {
+fn toggle_screen_sharing(_: &ToggleScreenSharing, window: &mut Window, cx: &mut App) {
     let call = ActiveCall::global(cx).read(cx);
     if let Some(room) = call.room().cloned() {
         let toggle_screen_sharing = room.update(cx, |room, cx| {
@@ -36,11 +36,11 @@ fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut WindowContext) {
                 room.share_screen(cx)
             }
         });
-        toggle_screen_sharing.detach_and_prompt_err("Sharing Screen Failed", cx, |e, _| Some(format!("{:?}\n\nPlease check that you have given Zed permissions to record your screen in Settings.", e)));
+        toggle_screen_sharing.detach_and_prompt_err("Sharing Screen Failed", window, cx, |e, _, _| Some(format!("{:?}\n\nPlease check that you have given Zed permissions to record your screen in Settings.", e)));
     }
 }
 
-fn toggle_mute(_: &ToggleMute, cx: &mut AppContext) {
+fn toggle_mute(_: &ToggleMute, cx: &mut App) {
     let call = ActiveCall::global(cx).read(cx);
     if let Some(room) = call.room().cloned() {
         room.update(cx, |room, cx| {
@@ -60,7 +60,7 @@ fn toggle_mute(_: &ToggleMute, cx: &mut AppContext) {
     }
 }
 
-fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) {
+fn toggle_deafen(_: &ToggleDeafen, cx: &mut App) {
     if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
         room.update(cx, |room, cx| room.toggle_deafen(cx));
     }
@@ -68,8 +68,8 @@ fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) {
 
 fn render_color_ribbon(color: Hsla) -> impl Element {
     canvas(
-        move |_, _| {},
-        move |bounds, _, cx| {
+        move |_, _, _| {},
+        move |bounds, _, window, _| {
             let height = bounds.size.height;
             let horizontal_offset = height;
             let vertical_offset = px(height.0 / 2.0);
@@ -84,7 +84,7 @@ fn render_color_ribbon(color: Hsla) -> impl Element {
                 bounds.top_right() + point(px(0.0), vertical_offset),
             );
             path.line_to(bounds.bottom_left());
-            cx.paint_path(path, color);
+            window.paint_path(path, color);
         },
     )
     .h_1()
@@ -92,7 +92,11 @@ fn render_color_ribbon(color: Hsla) -> impl Element {
 }
 
 impl TitleBar {
-    pub(crate) fn render_collaborator_list(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    pub(crate) fn render_collaborator_list(
+        &self,
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> impl IntoElement {
         let room = ActiveCall::global(cx).read(cx).room().cloned();
         let current_user = self.user_store.read(cx).current_user();
         let client = self.client.clone();
@@ -128,7 +132,7 @@ impl TitleBar {
 
                     this.children(current_user_face_pile.map(|face_pile| {
                         v_flex()
-                            .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
+                            .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
                             .child(face_pile)
                             .child(render_color_ribbon(player_colors.local().cursor))
                     }))
@@ -165,13 +169,13 @@ impl TitleBar {
                                 .cursor_pointer()
                                 .on_click({
                                     let peer_id = collaborator.peer_id;
-                                    cx.listener(move |this, _, cx| {
+                                    cx.listener(move |this, _, window, cx| {
                                         this.workspace
                                             .update(cx, |workspace, cx| {
                                                 if is_following {
-                                                    workspace.unfollow(peer_id, cx);
+                                                    workspace.unfollow(peer_id, window, cx);
                                                 } else {
-                                                    workspace.follow(peer_id, cx);
+                                                    workspace.follow(peer_id, window, cx);
                                                 }
                                             })
                                             .ok();
@@ -179,7 +183,7 @@ impl TitleBar {
                                 })
                                 .tooltip({
                                     let login = collaborator.user.github_login.clone();
-                                    move |cx| Tooltip::text(format!("Follow {login}"), cx)
+                                    Tooltip::text(format!("Follow {login}"))
                                 }),
                         )
                     }))
@@ -199,7 +203,7 @@ impl TitleBar {
         room: &Room,
         project_id: Option<u64>,
         current_user: &Arc<User>,
-        cx: &ViewContext<Self>,
+        cx: &App,
     ) -> Option<Div> {
         if room.role_for_user(user.id) == Some(proto::ChannelRole::Guest) {
             return None;
@@ -235,12 +239,7 @@ impl TitleBar {
                                         AvatarAudioStatusIndicator::new(ui::AudioStatus::Muted)
                                             .tooltip({
                                                 let github_login = user.github_login.clone();
-                                                move |cx| {
-                                                    Tooltip::text(
-                                                        format!("{} is muted", github_login),
-                                                        cx,
-                                                    )
-                                                }
+                                                Tooltip::text(format!("{} is muted", github_login))
                                             }),
                                     )
                                 }),
@@ -277,14 +276,18 @@ impl TitleBar {
         )
     }
 
-    pub(crate) fn render_call_controls(&self, cx: &mut ViewContext<Self>) -> Vec<AnyElement> {
+    pub(crate) fn render_call_controls(
+        &self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Vec<AnyElement> {
         let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() else {
             return Vec::new();
         };
 
         let is_connecting_to_project = self
             .workspace
-            .update(cx, |workspace, cx| workspace.has_active_modal(cx))
+            .update(cx, |workspace, cx| workspace.has_active_modal(window, cx))
             .unwrap_or(false);
 
         let room = room.read(cx);
@@ -310,23 +313,18 @@ impl TitleBar {
                     "toggle_sharing",
                     if is_shared { "Unshare" } else { "Share" },
                 )
-                .tooltip(move |cx| {
-                    Tooltip::text(
-                        if is_shared {
-                            "Stop sharing project with call participants"
-                        } else {
-                            "Share project with call participants"
-                        },
-                        cx,
-                    )
-                })
+                .tooltip(Tooltip::text(if is_shared {
+                    "Stop sharing project with call participants"
+                } else {
+                    "Share project with call participants"
+                }))
                 .style(ButtonStyle::Subtle)
                 .selected_style(ButtonStyle::Tinted(TintColor::Accent))
                 .toggle_state(is_shared)
                 .label_size(LabelSize::Small)
-                .on_click(cx.listener(move |this, _, cx| {
+                .on_click(cx.listener(move |this, _, window, cx| {
                     if is_shared {
-                        this.unshare_project(&Default::default(), cx);
+                        this.unshare_project(&Default::default(), window, cx);
                     } else {
                         this.share_project(&Default::default(), cx);
                     }
@@ -341,9 +339,9 @@ impl TitleBar {
                 .child(
                     IconButton::new("leave-call", ui::IconName::Exit)
                         .style(ButtonStyle::Subtle)
-                        .tooltip(|cx| Tooltip::text("Leave call", cx))
+                        .tooltip(Tooltip::text("Leave call"))
                         .icon_size(IconSize::Small)
-                        .on_click(move |_, cx| {
+                        .on_click(move |_, _window, cx| {
                             ActiveCall::global(cx)
                                 .update(cx, |call, cx| call.hang_up(cx))
                                 .detach_and_log_err(cx);
@@ -362,27 +360,28 @@ impl TitleBar {
                         ui::IconName::Mic
                     },
                 )
-                .tooltip(move |cx| {
+                .tooltip(move |window, cx| {
                     if is_muted {
                         if is_deafened {
                             Tooltip::with_meta(
                                 "Unmute Microphone",
                                 None,
                                 "Audio will be unmuted",
+                                window,
                                 cx,
                             )
                         } else {
-                            Tooltip::text("Unmute Microphone", cx)
+                            Tooltip::simple("Unmute Microphone", cx)
                         }
                     } else {
-                        Tooltip::text("Mute Microphone", cx)
+                        Tooltip::simple("Mute Microphone", cx)
                     }
                 })
                 .style(ButtonStyle::Subtle)
                 .icon_size(IconSize::Small)
                 .toggle_state(is_muted)
                 .selected_style(ButtonStyle::Tinted(TintColor::Error))
-                .on_click(move |_, cx| {
+                .on_click(move |_, _window, cx| {
                     toggle_mute(&Default::default(), cx);
                 })
                 .into_any_element(),
@@ -401,26 +400,32 @@ impl TitleBar {
                 .selected_style(ButtonStyle::Tinted(TintColor::Error))
                 .icon_size(IconSize::Small)
                 .toggle_state(is_deafened)
-                .tooltip(move |cx| {
+                .tooltip(move |window, cx| {
                     if is_deafened {
                         let label = "Unmute Audio";
 
                         if !muted_by_user {
-                            Tooltip::with_meta(label, None, "Microphone will be unmuted", cx)
+                            Tooltip::with_meta(
+                                label,
+                                None,
+                                "Microphone will be unmuted",
+                                window,
+                                cx,
+                            )
                         } else {
-                            Tooltip::text(label, cx)
+                            Tooltip::simple(label, cx)
                         }
                     } else {
                         let label = "Mute Audio";
 
                         if !muted_by_user {
-                            Tooltip::with_meta(label, None, "Microphone will be muted", cx)
+                            Tooltip::with_meta(label, None, "Microphone will be muted", window, cx)
                         } else {
-                            Tooltip::text(label, cx)
+                            Tooltip::simple(label, cx)
                         }
                     }
                 })
-                .on_click(move |_, cx| toggle_deafen(&Default::default(), cx))
+                .on_click(move |_, _, cx| toggle_deafen(&Default::default(), cx))
                 .into_any_element(),
             );
         }
@@ -432,17 +437,14 @@ impl TitleBar {
                     .icon_size(IconSize::Small)
                     .toggle_state(is_screen_sharing)
                     .selected_style(ButtonStyle::Tinted(TintColor::Accent))
-                    .tooltip(move |cx| {
-                        Tooltip::text(
-                            if is_screen_sharing {
-                                "Stop Sharing Screen"
-                            } else {
-                                "Share Screen"
-                            },
-                            cx,
-                        )
+                    .tooltip(Tooltip::text(if is_screen_sharing {
+                        "Stop Sharing Screen"
+                    } else {
+                        "Share Screen"
+                    }))
+                    .on_click(move |_, window, cx| {
+                        toggle_screen_sharing(&Default::default(), window, cx)
                     })
-                    .on_click(move |_, cx| toggle_screen_sharing(&Default::default(), cx))
                     .into_any_element(),
             );
         }

crates/title_bar/src/platforms/platform_linux.rs 🔗

@@ -18,12 +18,12 @@ impl LinuxWindowControls {
 }
 
 impl RenderOnce for LinuxWindowControls {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
         h_flex()
             .id("generic-window-controls")
             .px_3()
             .gap_3()
-            .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
+            .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
             .child(WindowControl::new(
                 "minimize",
                 WindowControlType::Minimize,
@@ -31,7 +31,7 @@ impl RenderOnce for LinuxWindowControls {
             ))
             .child(WindowControl::new(
                 "maximize-or-restore",
-                if cx.is_maximized() {
+                if window.is_maximized() {
                     WindowControlType::Restore
                 } else {
                     WindowControlType::Maximize

crates/title_bar/src/platforms/platform_windows.rs 🔗

@@ -33,7 +33,7 @@ impl WindowsWindowControls {
 }
 
 impl RenderOnce for WindowsWindowControls {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, window: &mut Window, _: &mut App) -> impl IntoElement {
         let close_button_hover_color = Rgba {
             r: 232.0 / 255.0,
             g: 17.0 / 255.0,
@@ -41,7 +41,7 @@ impl RenderOnce for WindowsWindowControls {
             a: 1.0,
         };
 
-        let button_hover_color = match cx.appearance() {
+        let button_hover_color = match window.appearance() {
             WindowAppearance::Light | WindowAppearance::VibrantLight => Rgba {
                 r: 0.1,
                 g: 0.1,
@@ -72,7 +72,7 @@ impl RenderOnce for WindowsWindowControls {
             ))
             .child(WindowsCaptionButton::new(
                 "maximize-or-restore",
-                if cx.is_maximized() {
+                if window.is_maximized() {
                     WindowsCaptionButtonIcon::Restore
                 } else {
                     WindowsCaptionButtonIcon::Maximize
@@ -117,7 +117,7 @@ impl WindowsCaptionButton {
 }
 
 impl RenderOnce for WindowsCaptionButton {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         // todo(windows) report this width to the Windows platform API
         // NOTE: this is intentionally hard coded. An option to use the 'native' size
         //       could be added when the width is reported to the Windows platform API

crates/title_bar/src/stories/application_menu.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{Render, View};
+use gpui::{Entity, Render};
 use story::{Story, StoryItem, StorySection};
 
 use ui::prelude::*;
@@ -6,19 +6,19 @@ use ui::prelude::*;
 use crate::application_menu::ApplicationMenu;
 
 pub struct ApplicationMenuStory {
-    menu: View<ApplicationMenu>,
+    menu: Entity<ApplicationMenu>,
 }
 
 impl ApplicationMenuStory {
-    pub fn new(cx: &mut WindowContext) -> Self {
+    pub fn new(window: &mut Window, cx: &mut App) -> Self {
         Self {
-            menu: cx.new_view(ApplicationMenu::new),
+            menu: cx.new(|cx| ApplicationMenu::new(window, cx)),
         }
     }
 }
 
 impl Render for ApplicationMenuStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title_for::<ApplicationMenu>())
             .child(StorySection::new().child(StoryItem::new(

crates/title_bar/src/title_bar.rs 🔗

@@ -19,9 +19,9 @@ use feature_flags::{FeatureFlagAppExt, GitUiFeatureFlag, ZedPro};
 use git_ui::repository_selector::RepositorySelector;
 use git_ui::repository_selector::RepositorySelectorPopoverMenu;
 use gpui::{
-    actions, div, px, Action, AnyElement, AppContext, Decorations, Element, InteractiveElement,
-    Interactivity, IntoElement, Model, MouseButton, ParentElement, Render, Stateful,
-    StatefulInteractiveElement, Styled, Subscription, View, ViewContext, VisualContext, WeakView,
+    actions, div, px, Action, AnyElement, App, Context, Decorations, Element, Entity,
+    InteractiveElement, Interactivity, IntoElement, MouseButton, ParentElement, Render, Stateful,
+    StatefulInteractiveElement, Styled, Subscription, WeakEntity, Window,
 };
 use project::Project;
 use rpc::proto;
@@ -57,20 +57,23 @@ actions!(
     ]
 );
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(|workspace: &mut Workspace, cx| {
-        let item = cx.new_view(|cx| TitleBar::new("title-bar", workspace, cx));
-        workspace.set_titlebar_item(item.into(), cx);
+pub fn init(cx: &mut App) {
+    cx.observe_new(|workspace: &mut Workspace, window, cx| {
+        let Some(window) = window else {
+            return;
+        };
+        let item = cx.new(|cx| TitleBar::new("title-bar", workspace, window, cx));
+        workspace.set_titlebar_item(item.into(), window, cx);
 
         #[cfg(not(target_os = "macos"))]
-        workspace.register_action(|workspace, action: &OpenApplicationMenu, cx| {
+        workspace.register_action(|workspace, action: &OpenApplicationMenu, window, cx| {
             if let Some(titlebar) = workspace
                 .titlebar_item()
                 .and_then(|item| item.downcast::<TitleBar>().ok())
             {
                 titlebar.update(cx, |titlebar, cx| {
                     if let Some(ref menu) = titlebar.application_menu {
-                        menu.update(cx, |menu, cx| menu.open_menu(action, cx));
+                        menu.update(cx, |menu, cx| menu.open_menu(action, window, cx));
                     }
                 });
             }
@@ -78,7 +81,7 @@ pub fn init(cx: &mut AppContext) {
 
         #[cfg(not(target_os = "macos"))]
         workspace.register_action(
-            |workspace, action: &NavigateApplicationMenuInDirection, cx| {
+            |workspace, action: &NavigateApplicationMenuInDirection, window, cx| {
                 if let Some(titlebar) = workspace
                     .titlebar_item()
                     .and_then(|item| item.downcast::<TitleBar>().ok())
@@ -86,7 +89,7 @@ pub fn init(cx: &mut AppContext) {
                     titlebar.update(cx, |titlebar, cx| {
                         if let Some(ref menu) = titlebar.application_menu {
                             menu.update(cx, |menu, cx| {
-                                menu.navigate_menus_in_direction(action, cx)
+                                menu.navigate_menus_in_direction(action, window, cx)
                             });
                         }
                     });
@@ -101,25 +104,25 @@ pub struct TitleBar {
     platform_style: PlatformStyle,
     content: Stateful<Div>,
     children: SmallVec<[AnyElement; 2]>,
-    repository_selector: View<RepositorySelector>,
-    project: Model<Project>,
-    user_store: Model<UserStore>,
+    repository_selector: Entity<RepositorySelector>,
+    project: Entity<Project>,
+    user_store: Entity<UserStore>,
     client: Arc<Client>,
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     should_move: bool,
-    application_menu: Option<View<ApplicationMenu>>,
+    application_menu: Option<Entity<ApplicationMenu>>,
     _subscriptions: Vec<Subscription>,
     git_ui_enabled: Arc<AtomicBool>,
 }
 
 impl Render for TitleBar {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let close_action = Box::new(workspace::CloseWindow);
-        let height = Self::height(cx);
-        let supported_controls = cx.window_controls();
-        let decorations = cx.window_decorations();
+        let height = Self::height(window);
+        let supported_controls = window.window_controls();
+        let decorations = window.window_decorations();
         let titlebar_color = if cfg!(any(target_os = "linux", target_os = "freebsd")) {
-            if cx.is_window_active() && !self.should_move {
+            if window.is_window_active() && !self.should_move {
                 cx.theme().colors().title_bar_background
             } else {
                 cx.theme().colors().title_bar_inactive_background
@@ -133,7 +136,7 @@ impl Render for TitleBar {
             .w_full()
             .h(height)
             .map(|this| {
-                if cx.is_fullscreen() {
+                if window.is_fullscreen() {
                     this.pl_2()
                 } else if self.platform_style == PlatformStyle::Mac {
                     this.pl(px(platform_mac::TRAFFIC_LIGHT_PADDING))
@@ -166,9 +169,9 @@ impl Render for TitleBar {
                     .w_full()
                     // Note: On Windows the title bar behavior is handled by the platform implementation.
                     .when(self.platform_style != PlatformStyle::Windows, |this| {
-                        this.on_click(|event, cx| {
+                        this.on_click(|event, window, _| {
                             if event.up.click_count == 2 {
-                                cx.zoom_window();
+                                window.zoom_window();
                             }
                         })
                     })
@@ -190,15 +193,15 @@ impl Render for TitleBar {
                                             .children(self.render_project_branch(cx))
                                     })
                             })
-                            .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation()),
+                            .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation()),
                     )
-                    .child(self.render_collaborator_list(cx))
+                    .child(self.render_collaborator_list(window, cx))
                     .child(
                         h_flex()
                             .gap_1()
                             .pr_1()
-                            .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
-                            .children(self.render_call_controls(cx))
+                            .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
+                            .children(self.render_call_controls(window, cx))
                             .map(|el| {
                                 let status = self.client.status();
                                 let status = &*status.borrow();
@@ -212,44 +215,47 @@ impl Render for TitleBar {
                             }),
                     ),
             )
-            .when(!cx.is_fullscreen(), |title_bar| match self.platform_style {
-                PlatformStyle::Mac => title_bar,
-                PlatformStyle::Linux => {
-                    if matches!(decorations, Decorations::Client { .. }) {
-                        title_bar
-                            .child(platform_linux::LinuxWindowControls::new(close_action))
-                            .when(supported_controls.window_menu, |titlebar| {
-                                titlebar.on_mouse_down(gpui::MouseButton::Right, move |ev, cx| {
-                                    cx.show_window_menu(ev.position)
+            .when(!window.is_fullscreen(), |title_bar| {
+                match self.platform_style {
+                    PlatformStyle::Mac => title_bar,
+                    PlatformStyle::Linux => {
+                        if matches!(decorations, Decorations::Client { .. }) {
+                            title_bar
+                                .child(platform_linux::LinuxWindowControls::new(close_action))
+                                .when(supported_controls.window_menu, |titlebar| {
+                                    titlebar.on_mouse_down(
+                                        gpui::MouseButton::Right,
+                                        move |ev, window, _| window.show_window_menu(ev.position),
+                                    )
                                 })
-                            })
-                            .on_mouse_move(cx.listener(move |this, _ev, cx| {
-                                if this.should_move {
-                                    this.should_move = false;
-                                    cx.start_window_move();
-                                }
-                            }))
-                            .on_mouse_down_out(cx.listener(move |this, _ev, _cx| {
-                                this.should_move = false;
-                            }))
-                            .on_mouse_up(
-                                gpui::MouseButton::Left,
-                                cx.listener(move |this, _ev, _cx| {
+                                .on_mouse_move(cx.listener(move |this, _ev, window, _| {
+                                    if this.should_move {
+                                        this.should_move = false;
+                                        window.start_window_move();
+                                    }
+                                }))
+                                .on_mouse_down_out(cx.listener(move |this, _ev, _window, _cx| {
                                     this.should_move = false;
-                                }),
-                            )
-                            .on_mouse_down(
-                                gpui::MouseButton::Left,
-                                cx.listener(move |this, _ev, _cx| {
-                                    this.should_move = true;
-                                }),
-                            )
-                    } else {
-                        title_bar
+                                }))
+                                .on_mouse_up(
+                                    gpui::MouseButton::Left,
+                                    cx.listener(move |this, _ev, _window, _cx| {
+                                        this.should_move = false;
+                                    }),
+                                )
+                                .on_mouse_down(
+                                    gpui::MouseButton::Left,
+                                    cx.listener(move |this, _ev, _window, _cx| {
+                                        this.should_move = true;
+                                    }),
+                                )
+                        } else {
+                            title_bar
+                        }
+                    }
+                    PlatformStyle::Windows => {
+                        title_bar.child(platform_windows::WindowsWindowControls::new(height))
                     }
-                }
-                PlatformStyle::Windows => {
-                    title_bar.child(platform_windows::WindowsWindowControls::new(height))
                 }
             })
     }
@@ -259,7 +265,8 @@ impl TitleBar {
     pub fn new(
         id: impl Into<ElementId>,
         workspace: &Workspace,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let project = workspace.project().clone();
         let user_store = workspace.app_state().user_store.clone();
@@ -270,13 +277,13 @@ impl TitleBar {
         let application_menu = match platform_style {
             PlatformStyle::Mac => {
                 if option_env!("ZED_USE_CROSS_PLATFORM_MENU").is_some() {
-                    Some(cx.new_view(ApplicationMenu::new))
+                    Some(cx.new(|cx| ApplicationMenu::new(window, cx)))
                 } else {
                     None
                 }
             }
             PlatformStyle::Linux | PlatformStyle::Windows => {
-                Some(cx.new_view(ApplicationMenu::new))
+                Some(cx.new(|cx| ApplicationMenu::new(window, cx)))
             }
         };
 
@@ -288,7 +295,7 @@ impl TitleBar {
         );
         subscriptions.push(cx.observe(&project, |_, _, cx| cx.notify()));
         subscriptions.push(cx.observe(&active_call, |this, _, cx| this.active_call_changed(cx)));
-        subscriptions.push(cx.observe_window_activation(Self::window_activation_changed));
+        subscriptions.push(cx.observe_window_activation(window, Self::window_activation_changed));
         subscriptions.push(cx.observe(&user_store, |_, _, cx| cx.notify()));
 
         let is_git_ui_enabled = Arc::new(AtomicBool::new(false));
@@ -304,7 +311,7 @@ impl TitleBar {
             content: div().id(id.into()),
             children: SmallVec::new(),
             application_menu,
-            repository_selector: cx.new_view(|cx| RepositorySelector::new(project.clone(), cx)),
+            repository_selector: cx.new(|cx| RepositorySelector::new(project.clone(), window, cx)),
             workspace: workspace.weak_handle(),
             should_move: false,
             project,
@@ -316,12 +323,12 @@ impl TitleBar {
     }
 
     #[cfg(not(target_os = "windows"))]
-    pub fn height(cx: &mut WindowContext) -> Pixels {
-        (1.75 * cx.rem_size()).max(px(34.))
+    pub fn height(window: &mut Window) -> Pixels {
+        (1.75 * window.rem_size()).max(px(34.))
     }
 
     #[cfg(target_os = "windows")]
-    pub fn height(_cx: &mut WindowContext) -> Pixels {
+    pub fn height(_window: &mut Window) -> Pixels {
         // todo(windows) instead of hard coded size report the actual size to the Windows platform API
         px(32.)
     }
@@ -332,7 +339,7 @@ impl TitleBar {
         self
     }
 
-    fn render_ssh_project_host(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
+    fn render_ssh_project_host(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
         let options = self.project.read(cx).ssh_connection_options(cx)?;
         let host: SharedString = options.connection_string().into();
 
@@ -390,17 +397,23 @@ impl TitleBar {
                                 .text_ellipsis(),
                         ),
                 )
-                .tooltip(move |cx| {
-                    Tooltip::with_meta("Remote Project", Some(&OpenRemote), meta.clone(), cx)
+                .tooltip(move |window, cx| {
+                    Tooltip::with_meta(
+                        "Remote Project",
+                        Some(&OpenRemote),
+                        meta.clone(),
+                        window,
+                        cx,
+                    )
                 })
-                .on_click(|_, cx| {
-                    cx.dispatch_action(OpenRemote.boxed_clone());
+                .on_click(|_, window, cx| {
+                    window.dispatch_action(OpenRemote.boxed_clone(), cx);
                 })
                 .into_any_element(),
         )
     }
 
-    pub fn render_project_host(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
+    pub fn render_project_host(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
         if self.project.read(cx).is_via_ssh() {
             return self.render_ssh_project_host(cx);
         }
@@ -428,21 +441,16 @@ impl TitleBar {
                 .color(Color::Player(participant_index.0))
                 .style(ButtonStyle::Subtle)
                 .label_size(LabelSize::Small)
-                .tooltip(move |cx| {
-                    Tooltip::text(
-                        format!(
-                            "{} is sharing this project. Click to follow.",
-                            host_user.github_login.clone()
-                        ),
-                        cx,
-                    )
-                })
+                .tooltip(Tooltip::text(format!(
+                    "{} is sharing this project. Click to follow.",
+                    host_user.github_login.clone()
+                )))
                 .on_click({
                     let host_peer_id = host.peer_id;
-                    cx.listener(move |this, _, cx| {
+                    cx.listener(move |this, _, window, cx| {
                         this.workspace
                             .update(cx, |workspace, cx| {
-                                workspace.follow(host_peer_id, cx);
+                                workspace.follow(host_peer_id, window, cx);
                             })
                             .log_err();
                     })
@@ -451,7 +459,7 @@ impl TitleBar {
         )
     }
 
-    pub fn render_project_name(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    pub fn render_project_name(&self, cx: &mut Context<Self>) -> impl IntoElement {
         let name = {
             let mut names = self.project.read(cx).visible_worktrees(cx).map(|worktree| {
                 let worktree = worktree.read(cx);
@@ -471,30 +479,29 @@ impl TitleBar {
             .when(!is_project_selected, |b| b.color(Color::Muted))
             .style(ButtonStyle::Subtle)
             .label_size(LabelSize::Small)
-            .tooltip(move |cx| {
+            .tooltip(move |window, cx| {
                 Tooltip::for_action(
                     "Recent Projects",
                     &zed_actions::OpenRecent {
                         create_new_window: false,
                     },
+                    window,
                     cx,
                 )
             })
-            .on_click(cx.listener(move |_, _, cx| {
-                cx.dispatch_action(
+            .on_click(cx.listener(move |_, _, window, cx| {
+                window.dispatch_action(
                     OpenRecent {
                         create_new_window: false,
                     }
                     .boxed_clone(),
+                    cx,
                 );
             }))
     }
 
     // NOTE: Not sure we want to keep this in the titlebar, but for while we are working on Git it is helpful in the short term
-    pub fn render_current_repository(
-        &self,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<impl IntoElement> {
+    pub fn render_current_repository(&self, cx: &mut Context<Self>) -> Option<impl IntoElement> {
         if !self.git_ui_enabled.load(Ordering::SeqCst) {
             return None;
         }
@@ -528,7 +535,7 @@ impl TitleBar {
         ))
     }
 
-    pub fn render_project_branch(&self, cx: &mut ViewContext<Self>) -> Option<impl IntoElement> {
+    pub fn render_project_branch(&self, cx: &mut Context<Self>) -> Option<impl IntoElement> {
         let entry = {
             let mut names_and_branches =
                 self.project.read(cx).visible_worktrees(cx).map(|worktree| {
@@ -548,24 +555,25 @@ impl TitleBar {
                 .color(Color::Muted)
                 .style(ButtonStyle::Subtle)
                 .label_size(LabelSize::Small)
-                .tooltip(move |cx| {
+                .tooltip(move |window, cx| {
                     Tooltip::with_meta(
                         "Recent Branches",
                         Some(&zed_actions::branches::OpenRecent),
                         "Local branches only",
+                        window,
                         cx,
                     )
                 })
-                .on_click(move |_, cx| {
+                .on_click(move |_, window, cx| {
                     let _ = workspace.update(cx, |_this, cx| {
-                        cx.dispatch_action(zed_actions::branches::OpenRecent.boxed_clone());
+                        window.dispatch_action(zed_actions::branches::OpenRecent.boxed_clone(), cx);
                     });
                 }),
         )
     }
 
-    fn window_activation_changed(&mut self, cx: &mut ViewContext<Self>) {
-        if cx.is_window_active() {
+    fn window_activation_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        if window.is_window_active() {
             ActiveCall::global(cx)
                 .update(cx, |call, cx| call.set_location(Some(&self.project), cx))
                 .detach_and_log_err(cx);
@@ -576,16 +584,16 @@ impl TitleBar {
         }
         self.workspace
             .update(cx, |workspace, cx| {
-                workspace.update_active_view_for_followers(cx);
+                workspace.update_active_view_for_followers(window, cx);
             })
             .ok();
     }
 
-    fn active_call_changed(&mut self, cx: &mut ViewContext<Self>) {
+    fn active_call_changed(&mut self, cx: &mut Context<Self>) {
         cx.notify();
     }
 
-    fn share_project(&mut self, _: &ShareProject, cx: &mut ViewContext<Self>) {
+    fn share_project(&mut self, _: &ShareProject, cx: &mut Context<Self>) {
         let active_call = ActiveCall::global(cx);
         let project = self.project.clone();
         active_call
@@ -593,7 +601,7 @@ impl TitleBar {
             .detach_and_log_err(cx);
     }
 
-    fn unshare_project(&mut self, _: &UnshareProject, cx: &mut ViewContext<Self>) {
+    fn unshare_project(&mut self, _: &UnshareProject, _: &mut Window, cx: &mut Context<Self>) {
         let active_call = ActiveCall::global(cx);
         let project = self.project.clone();
         active_call
@@ -604,7 +612,7 @@ impl TitleBar {
     fn render_connection_status(
         &self,
         status: &client::Status,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<AnyElement> {
         match status {
             client::Status::ConnectionError
@@ -615,7 +623,7 @@ impl TitleBar {
                 div()
                     .id("disconnected")
                     .child(Icon::new(IconName::Disconnected).size(IconSize::Small))
-                    .tooltip(|cx| Tooltip::text("Disconnected", cx))
+                    .tooltip(Tooltip::text("Disconnected"))
                     .into_any_element(),
             ),
             client::Status::UpgradeRequired => {
@@ -633,14 +641,14 @@ impl TitleBar {
                 Some(
                     Button::new("connection-status", label)
                         .label_size(LabelSize::Small)
-                        .on_click(|_, cx| {
+                        .on_click(|_, window, cx| {
                             if let Some(auto_updater) = auto_update::AutoUpdater::get(cx) {
                                 if auto_updater.read(cx).status().is_updated() {
                                     workspace::reload(&Default::default(), cx);
                                     return;
                                 }
                             }
-                            auto_update::check(&Default::default(), cx);
+                            auto_update::check(&Default::default(), window, cx);
                         })
                         .into_any_element(),
                 )
@@ -649,29 +657,30 @@ impl TitleBar {
         }
     }
 
-    pub fn render_sign_in_button(&mut self, _: &mut ViewContext<Self>) -> Button {
+    pub fn render_sign_in_button(&mut self, _: &mut Context<Self>) -> Button {
         let client = self.client.clone();
         Button::new("sign_in", "Sign in")
             .label_size(LabelSize::Small)
-            .on_click(move |_, cx| {
+            .on_click(move |_, window, cx| {
                 let client = client.clone();
-                cx.spawn(move |mut cx| async move {
-                    client
-                        .authenticate_and_connect(true, &cx)
-                        .await
-                        .notify_async_err(&mut cx);
-                })
-                .detach();
+                window
+                    .spawn(cx, move |mut cx| async move {
+                        client
+                            .authenticate_and_connect(true, &cx)
+                            .await
+                            .notify_async_err(&mut cx);
+                    })
+                    .detach();
             })
     }
 
-    pub fn render_user_menu_button(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    pub fn render_user_menu_button(&mut self, cx: &mut Context<Self>) -> impl Element {
         let user_store = self.user_store.read(cx);
         if let Some(user) = user_store.current_user() {
             let plan = user_store.current_plan();
             PopoverMenu::new("user-menu")
-                .menu(move |cx| {
-                    ContextMenu::build(cx, |menu, cx| {
+                .menu(move |window, cx| {
+                    ContextMenu::build(window, cx, |menu, _, cx| {
                         menu.when(cx.has_flag::<ZedPro>(), |menu| {
                             menu.action(
                                 format!(
@@ -722,13 +731,13 @@ impl TitleBar {
                                 ),
                         )
                         .style(ButtonStyle::Subtle)
-                        .tooltip(move |cx| Tooltip::text("Toggle User Menu", cx)),
+                        .tooltip(Tooltip::text("Toggle User Menu")),
                 )
                 .anchor(gpui::Corner::TopRight)
         } else {
             PopoverMenu::new("user-menu")
-                .menu(|cx| {
-                    ContextMenu::build(cx, |menu, _| {
+                .menu(|window, cx| {
+                    ContextMenu::build(window, cx, |menu, _, _| {
                         menu.action("Settings", zed_actions::OpenSettings.boxed_clone())
                             .action("Key Bindings", Box::new(zed_actions::OpenKeymap))
                             .action(
@@ -750,7 +759,7 @@ impl TitleBar {
                 .trigger(
                     IconButton::new("user-menu", IconName::ChevronDown)
                         .icon_size(IconSize::Small)
-                        .tooltip(move |cx| Tooltip::text("Toggle User Menu", cx)),
+                        .tooltip(Tooltip::text("Toggle User Menu")),
                 )
         }
     }

crates/title_bar/src/window_controls.rs 🔗

@@ -33,7 +33,7 @@ pub struct WindowControlStyle {
 }
 
 impl WindowControlStyle {
-    pub fn default(cx: &WindowContext) -> Self {
+    pub fn default(cx: &mut App) -> Self {
         let colors = cx.theme().colors();
 
         Self {
@@ -82,7 +82,7 @@ pub struct WindowControl {
 }
 
 impl WindowControl {
-    pub fn new(id: impl Into<ElementId>, icon: WindowControlType, cx: &WindowContext) -> Self {
+    pub fn new(id: impl Into<ElementId>, icon: WindowControlType, cx: &mut App) -> Self {
         let style = WindowControlStyle::default(cx);
 
         Self {
@@ -97,7 +97,7 @@ impl WindowControl {
         id: impl Into<ElementId>,
         icon: WindowControlType,
         close_action: Box<dyn Action>,
-        cx: &WindowContext,
+        cx: &mut App,
     ) -> Self {
         let style = WindowControlStyle::default(cx);
 
@@ -125,7 +125,7 @@ impl WindowControl {
 }
 
 impl RenderOnce for WindowControl {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         let icon = svg()
             .size_4()
             .flex_none()
@@ -145,18 +145,19 @@ impl RenderOnce for WindowControl {
             .hover(|this| this.bg(self.style.background_hover))
             .active(|this| this.bg(self.style.background_hover))
             .child(icon)
-            .on_mouse_move(|_, cx| cx.stop_propagation())
-            .on_click(move |_, cx| {
+            .on_mouse_move(|_, _, cx| cx.stop_propagation())
+            .on_click(move |_, window, cx| {
                 cx.stop_propagation();
                 match self.icon {
-                    WindowControlType::Minimize => cx.minimize_window(),
-                    WindowControlType::Restore => cx.zoom_window(),
-                    WindowControlType::Maximize => cx.zoom_window(),
-                    WindowControlType::Close => cx.dispatch_action(
+                    WindowControlType::Minimize => window.minimize_window(),
+                    WindowControlType::Restore => window.zoom_window(),
+                    WindowControlType::Maximize => window.zoom_window(),
+                    WindowControlType::Close => window.dispatch_action(
                         self.close_action
                             .as_ref()
                             .expect("Use WindowControl::new_close() for close control.")
                             .boxed_clone(),
+                        cx,
                     ),
                 }
             })

crates/toolchain_selector/src/active_toolchain.rs 🔗

@@ -1,7 +1,7 @@
 use editor::Editor;
 use gpui::{
-    div, AsyncWindowContext, IntoElement, ParentElement, Render, Subscription, Task, View,
-    ViewContext, WeakModel, WeakView,
+    div, AsyncWindowContext, Context, Entity, IntoElement, ParentElement, Render, Subscription,
+    Task, WeakEntity, Window,
 };
 use language::{Buffer, BufferEvent, LanguageName, Toolchain};
 use project::{Project, WorktreeId};
@@ -13,24 +13,24 @@ use crate::ToolchainSelector;
 pub struct ActiveToolchain {
     active_toolchain: Option<Toolchain>,
     term: SharedString,
-    workspace: WeakView<Workspace>,
-    active_buffer: Option<(WorktreeId, WeakModel<Buffer>, Subscription)>,
+    workspace: WeakEntity<Workspace>,
+    active_buffer: Option<(WorktreeId, WeakEntity<Buffer>, Subscription)>,
     _update_toolchain_task: Task<Option<()>>,
 }
 
 impl ActiveToolchain {
-    pub fn new(workspace: &Workspace, cx: &mut ViewContext<Self>) -> Self {
+    pub fn new(workspace: &Workspace, window: &mut Window, cx: &mut Context<Self>) -> Self {
         Self {
             active_toolchain: None,
             active_buffer: None,
             term: SharedString::new_static("Toolchain"),
             workspace: workspace.weak_handle(),
 
-            _update_toolchain_task: Self::spawn_tracker_task(cx),
+            _update_toolchain_task: Self::spawn_tracker_task(window, cx),
         }
     }
-    fn spawn_tracker_task(cx: &mut ViewContext<Self>) -> Task<Option<()>> {
-        cx.spawn(|this, mut cx| async move {
+    fn spawn_tracker_task(window: &mut Window, cx: &mut Context<Self>) -> Task<Option<()>> {
+        cx.spawn_in(window, |this, mut cx| async move {
             let active_file = this
                 .update(&mut cx, |this, _| {
                     this.active_buffer
@@ -62,7 +62,7 @@ impl ActiveToolchain {
                 .ok()
                 .flatten()?;
             let toolchain =
-                Self::active_toolchain(workspace, worktree_id, language_name, cx.clone()).await?;
+                Self::active_toolchain(workspace, worktree_id, language_name, &mut cx).await?;
             let _ = this.update(&mut cx, |this, cx| {
                 this.active_toolchain = Some(toolchain);
 
@@ -72,17 +72,26 @@ impl ActiveToolchain {
         })
     }
 
-    fn update_lister(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
+    fn update_lister(
+        &mut self,
+        editor: Entity<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let editor = editor.read(cx);
         if let Some((_, buffer, _)) = editor.active_excerpt(cx) {
             if let Some(worktree_id) = buffer.read(cx).file().map(|file| file.worktree_id(cx)) {
-                let subscription = cx.subscribe(&buffer, |this, _, event: &BufferEvent, cx| {
-                    if matches!(event, BufferEvent::LanguageChanged) {
-                        this._update_toolchain_task = Self::spawn_tracker_task(cx);
-                    }
-                });
+                let subscription = cx.subscribe_in(
+                    &buffer,
+                    window,
+                    |this, _, event: &BufferEvent, window, cx| {
+                        if matches!(event, BufferEvent::LanguageChanged) {
+                            this._update_toolchain_task = Self::spawn_tracker_task(window, cx);
+                        }
+                    },
+                );
                 self.active_buffer = Some((worktree_id, buffer.downgrade(), subscription));
-                self._update_toolchain_task = Self::spawn_tracker_task(cx);
+                self._update_toolchain_task = Self::spawn_tracker_task(window, cx);
             }
         }
 
@@ -90,10 +99,10 @@ impl ActiveToolchain {
     }
 
     fn active_toolchain(
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         worktree_id: WorktreeId,
         language_name: LanguageName,
-        cx: AsyncWindowContext,
+        cx: &mut AsyncWindowContext,
     ) -> Task<Option<Toolchain>> {
         cx.spawn(move |mut cx| async move {
             let workspace_id = workspace
@@ -115,7 +124,7 @@ impl ActiveToolchain {
                     .update(&mut cx, |this, _| this.project().clone())
                     .ok()?;
                 let toolchains = cx
-                    .update(|cx| {
+                    .update(|_, cx| {
                         project
                             .read(cx)
                             .available_toolchains(worktree_id, language_name, cx)
@@ -143,20 +152,20 @@ impl ActiveToolchain {
 }
 
 impl Render for ActiveToolchain {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         div().when_some(self.active_toolchain.as_ref(), |el, active_toolchain| {
             let term = self.term.clone();
             el.child(
                 Button::new("change-toolchain", active_toolchain.name.clone())
                     .label_size(LabelSize::Small)
-                    .on_click(cx.listener(|this, _, cx| {
+                    .on_click(cx.listener(|this, _, window, cx| {
                         if let Some(workspace) = this.workspace.upgrade() {
                             workspace.update(cx, |workspace, cx| {
-                                ToolchainSelector::toggle(workspace, cx)
+                                ToolchainSelector::toggle(workspace, window, cx)
                             });
                         }
                     }))
-                    .tooltip(move |cx| Tooltip::text(format!("Select {}", &term), cx)),
+                    .tooltip(Tooltip::text(format!("Select {}", &term))),
             )
         })
     }
@@ -166,11 +175,12 @@ impl StatusItemView for ActiveToolchain {
     fn set_active_pane_item(
         &mut self,
         active_pane_item: Option<&dyn ItemHandle>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         if let Some(editor) = active_pane_item.and_then(|item| item.downcast::<Editor>()) {
             self.active_toolchain.take();
-            self.update_lister(editor, cx);
+            self.update_lister(editor, window, cx);
         }
         cx.notify();
     }

crates/toolchain_selector/src/toolchain_selector.rs 🔗

@@ -4,8 +4,8 @@ pub use active_toolchain::ActiveToolchain;
 use editor::Editor;
 use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
 use gpui::{
-    actions, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Model,
-    ParentElement, Render, Styled, Task, View, ViewContext, VisualContext, WeakView,
+    actions, App, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable,
+    ParentElement, Render, Styled, Task, WeakEntity, Window,
 };
 use language::{LanguageName, Toolchain, ToolchainList};
 use picker::{Picker, PickerDelegate};
@@ -17,22 +17,30 @@ use workspace::{ModalView, Workspace};
 
 actions!(toolchain, [Select]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(ToolchainSelector::register).detach();
+pub fn init(cx: &mut App) {
+    cx.observe_new(ToolchainSelector::register).detach();
 }
 
 pub struct ToolchainSelector {
-    picker: View<Picker<ToolchainSelectorDelegate>>,
+    picker: Entity<Picker<ToolchainSelectorDelegate>>,
 }
 
 impl ToolchainSelector {
-    fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-        workspace.register_action(move |workspace, _: &Select, cx| {
-            Self::toggle(workspace, cx);
+    fn register(
+        workspace: &mut Workspace,
+        _window: Option<&mut Window>,
+        _: &mut Context<Workspace>,
+    ) {
+        workspace.register_action(move |workspace, _: &Select, window, cx| {
+            Self::toggle(workspace, window, cx);
         });
     }
 
-    fn toggle(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) -> Option<()> {
+    fn toggle(
+        workspace: &mut Workspace,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Option<()> {
         let (_, buffer, _) = workspace
             .active_item(cx)?
             .act_as::<Editor>(cx)?
@@ -49,15 +57,15 @@ impl ToolchainSelector {
             .abs_path();
         let workspace_id = workspace.database_id()?;
         let weak = workspace.weak_handle();
-        cx.spawn(move |workspace, mut cx| async move {
+        cx.spawn_in(window, move |workspace, mut cx| async move {
             let active_toolchain = workspace::WORKSPACE_DB
                 .toolchain(workspace_id, worktree_id, language_name.clone())
                 .await
                 .ok()
                 .flatten();
             workspace
-                .update(&mut cx, |this, cx| {
-                    this.toggle_modal(cx, move |cx| {
+                .update_in(&mut cx, |this, window, cx| {
+                    this.toggle_modal(window, cx, move |window, cx| {
                         ToolchainSelector::new(
                             weak,
                             project,
@@ -65,6 +73,7 @@ impl ToolchainSelector {
                             worktree_id,
                             worktree_root_path,
                             language_name,
+                            window,
                             cx,
                         )
                     });
@@ -76,41 +85,44 @@ impl ToolchainSelector {
         Some(())
     }
 
+    #[allow(clippy::too_many_arguments)]
     fn new(
-        workspace: WeakView<Workspace>,
-        project: Model<Project>,
+        workspace: WeakEntity<Workspace>,
+        project: Entity<Project>,
         active_toolchain: Option<Toolchain>,
         worktree_id: WorktreeId,
         worktree_root: Arc<Path>,
         language_name: LanguageName,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
-        let view = cx.view().downgrade();
-        let picker = cx.new_view(|cx| {
+        let toolchain_selector = cx.model().downgrade();
+        let picker = cx.new(|cx| {
             let delegate = ToolchainSelectorDelegate::new(
                 active_toolchain,
-                view,
+                toolchain_selector,
                 workspace,
                 worktree_id,
                 worktree_root,
                 project,
                 language_name,
+                window,
                 cx,
             );
-            Picker::uniform_list(delegate, cx)
+            Picker::uniform_list(delegate, window, cx)
         });
         Self { picker }
     }
 }
 
 impl Render for ToolchainSelector {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         v_flex().w(rems(34.)).child(self.picker.clone())
     }
 }
 
-impl FocusableView for ToolchainSelector {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for ToolchainSelector {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
@@ -119,11 +131,11 @@ impl EventEmitter<DismissEvent> for ToolchainSelector {}
 impl ModalView for ToolchainSelector {}
 
 pub struct ToolchainSelectorDelegate {
-    toolchain_selector: WeakView<ToolchainSelector>,
+    toolchain_selector: WeakEntity<ToolchainSelector>,
     candidates: ToolchainList,
     matches: Vec<StringMatch>,
     selected_index: usize,
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     worktree_id: WorktreeId,
     worktree_abs_path_root: Arc<Path>,
     placeholder_text: Arc<str>,
@@ -134,15 +146,16 @@ impl ToolchainSelectorDelegate {
     #[allow(clippy::too_many_arguments)]
     fn new(
         active_toolchain: Option<Toolchain>,
-        language_selector: WeakView<ToolchainSelector>,
-        workspace: WeakView<Workspace>,
+        toolchain_selector: WeakEntity<ToolchainSelector>,
+        workspace: WeakEntity<Workspace>,
         worktree_id: WorktreeId,
         worktree_abs_path_root: Arc<Path>,
-        project: Model<Project>,
+        project: Entity<Project>,
         language_name: LanguageName,
-        cx: &mut ViewContext<Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> Self {
-        let _fetch_candidates_task = cx.spawn({
+        let _fetch_candidates_task = cx.spawn_in(window, {
             let project = project.clone();
             move |this, mut cx| async move {
                 let term = project
@@ -152,9 +165,9 @@ impl ToolchainSelectorDelegate {
                     .ok()?
                     .await?;
                 let placeholder_text = format!("Select a {}…", term.to_lowercase()).into();
-                let _ = this.update(&mut cx, move |this, cx| {
+                let _ = this.update_in(&mut cx, move |this, window, cx| {
                     this.delegate.placeholder_text = placeholder_text;
-                    this.refresh_placeholder(cx);
+                    this.refresh_placeholder(window, cx);
                 });
                 let available_toolchains = project
                     .update(&mut cx, |this, cx| {
@@ -163,7 +176,7 @@ impl ToolchainSelectorDelegate {
                     .ok()?
                     .await?;
 
-                let _ = this.update(&mut cx, move |this, cx| {
+                let _ = this.update_in(&mut cx, move |this, window, cx| {
                     this.delegate.candidates = available_toolchains;
 
                     if let Some(active_toolchain) = active_toolchain {
@@ -174,10 +187,10 @@ impl ToolchainSelectorDelegate {
                             .iter()
                             .position(|toolchain| *toolchain == active_toolchain)
                         {
-                            this.delegate.set_selected_index(position, cx);
+                            this.delegate.set_selected_index(position, window, cx);
                         }
                     }
-                    this.update_matches(this.query(cx), cx);
+                    this.update_matches(this.query(cx), window, cx);
                 });
 
                 Some(())
@@ -185,7 +198,7 @@ impl ToolchainSelectorDelegate {
         });
         let placeholder_text = "Select a toolchain…".to_string().into();
         Self {
-            toolchain_selector: language_selector,
+            toolchain_selector,
             candidates: Default::default(),
             matches: vec![],
             selected_index: 0,
@@ -209,7 +222,7 @@ impl ToolchainSelectorDelegate {
 impl PickerDelegate for ToolchainSelectorDelegate {
     type ListItem = ListItem;
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         self.placeholder_text.clone()
     }
 
@@ -217,7 +230,7 @@ impl PickerDelegate for ToolchainSelectorDelegate {
         self.matches.len()
     }
 
-    fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
         if let Some(string_match) = self.matches.get(self.selected_index) {
             let toolchain = self.candidates.toolchains[string_match.candidate_id].clone();
             if let Some(workspace_id) = self
@@ -228,7 +241,7 @@ impl PickerDelegate for ToolchainSelectorDelegate {
             {
                 let workspace = self.workspace.clone();
                 let worktree_id = self.worktree_id;
-                cx.spawn(|_, mut cx| async move {
+                cx.spawn_in(window, |_, mut cx| async move {
                     workspace::WORKSPACE_DB
                         .set_toolchain(workspace_id, worktree_id, toolchain.clone())
                         .await
@@ -246,10 +259,10 @@ impl PickerDelegate for ToolchainSelectorDelegate {
                 .detach();
             }
         }
-        self.dismissed(cx);
+        self.dismissed(window, cx);
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+    fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         self.toolchain_selector
             .update(cx, |_, cx| cx.emit(DismissEvent))
             .log_err();
@@ -259,19 +272,25 @@ impl PickerDelegate for ToolchainSelectorDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        _window: &mut Window,
+        _: &mut Context<Picker<Self>>,
+    ) {
         self.selected_index = ix;
     }
 
     fn update_matches(
         &mut self,
         query: String,
-        cx: &mut ViewContext<Picker<Self>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
     ) -> gpui::Task<()> {
         let background = cx.background_executor().clone();
         let candidates = self.candidates.clone();
         let worktree_root_path = self.worktree_abs_path_root.clone();
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let matches = if query.is_empty() {
                 candidates
                     .toolchains
@@ -326,7 +345,8 @@ impl PickerDelegate for ToolchainSelectorDelegate {
         &self,
         ix: usize,
         selected: bool,
-        _: &mut ViewContext<Picker<Self>>,
+        _window: &mut Window,
+        _: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let mat = &self.matches[ix];
         let toolchain = &self.candidates.toolchains[mat.candidate_id];

crates/ui/src/components/avatar/avatar.rs 🔗

@@ -71,7 +71,7 @@ impl Avatar {
 }
 
 impl RenderOnce for Avatar {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
         let border_width = if self.border_color.is_some() {
             px(2.)
         } else {
@@ -79,7 +79,7 @@ impl RenderOnce for Avatar {
         };
 
         let image_size = self.size.unwrap_or_else(|| rems(1.).into());
-        let container_size = image_size.to_pixels(cx.rem_size()) + border_width * 2.;
+        let container_size = image_size.to_pixels(window.rem_size()) + border_width * 2.;
 
         div()
             .size(container_size)

crates/ui/src/components/avatar/avatar_audio_status_indicator.rs 🔗

@@ -16,7 +16,7 @@ pub enum AudioStatus {
 #[derive(IntoElement)]
 pub struct AvatarAudioStatusIndicator {
     audio_status: AudioStatus,
-    tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
+    tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView>>,
 }
 
 impl AvatarAudioStatusIndicator {
@@ -29,17 +29,17 @@ impl AvatarAudioStatusIndicator {
     }
 
     /// Sets the tooltip for the indicator.
-    pub fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
+    pub fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self {
         self.tooltip = Some(Box::new(tooltip));
         self
     }
 }
 
 impl RenderOnce for AvatarAudioStatusIndicator {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
         let icon_size = IconSize::Indicator;
 
-        let width_in_px = icon_size.rems() * cx.rem_size();
+        let width_in_px = icon_size.rems() * window.rem_size();
         let padding_x = px(4.);
 
         div()
@@ -65,7 +65,7 @@ impl RenderOnce for AvatarAudioStatusIndicator {
                         .color(Color::Error),
                     )
                     .when_some(self.tooltip, |this, tooltip| {
-                        this.tooltip(move |cx| tooltip(cx))
+                        this.tooltip(move |window, cx| tooltip(window, cx))
                     }),
             )
     }

crates/ui/src/components/avatar/avatar_availability_indicator.rs 🔗

@@ -29,8 +29,8 @@ impl AvatarAvailabilityIndicator {
 }
 
 impl RenderOnce for AvatarAvailabilityIndicator {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
-        let avatar_size = self.avatar_size.unwrap_or_else(|| cx.rem_size());
+    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
+        let avatar_size = self.avatar_size.unwrap_or_else(|| window.rem_size());
 
         // HACK: non-integer sizes result in oval indicators.
         let indicator_size = (avatar_size * 0.4).round();

crates/ui/src/components/button/button.rs 🔗

@@ -271,7 +271,7 @@ impl Clickable for Button {
     /// Sets the click event handler for the button.
     fn on_click(
         mut self,
-        handler: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
+        handler: impl Fn(&gpui::ClickEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.base = self.base.on_click(handler);
         self
@@ -359,16 +359,14 @@ impl ButtonCommon for Button {
     /// use ui::Tooltip;
     ///
     /// Button::new("button_id", "Click me!")
-    ///     .tooltip(move |cx| {
-    ///         Tooltip::text("This is a tooltip", cx)
-    ///     })
+    ///     .tooltip(Tooltip::text_f("This is a tooltip", cx))
     ///     .on_click(|event, cx| {
     ///         // Handle click event
     ///     });
     /// ```
     ///
     /// This will create a button with a tooltip that displays "This is a tooltip" when hovered over.
-    fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
+    fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self {
         self.base = self.base.tooltip(tooltip);
         self
     }
@@ -381,7 +379,7 @@ impl ButtonCommon for Button {
 
 impl RenderOnce for Button {
     #[allow(refining_impl_trait)]
-    fn render(self, cx: &mut WindowContext) -> ButtonLike {
+    fn render(self, _window: &mut Window, cx: &mut App) -> ButtonLike {
         let is_disabled = self.base.disabled;
         let is_selected = self.base.selected;
 
@@ -445,7 +443,7 @@ impl ComponentPreview for Button {
         "A button allows users to take actions, and make choices, with a single tap."
     }
 
-    fn examples(_: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
+    fn examples(_window: &mut Window, _: &mut App) -> Vec<ComponentExampleGroup<Self>> {
         vec![
             example_group_with_title(
                 "Styles",

crates/ui/src/components/button/button_icon.rs 🔗

@@ -80,7 +80,7 @@ impl SelectableButton for ButtonIcon {
 }
 
 impl RenderOnce for ButtonIcon {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         let icon = self
             .selected_icon
             .filter(|_| self.selected)

crates/ui/src/components/button/button_like.rs 🔗

@@ -33,7 +33,7 @@ pub trait ButtonCommon: Clickable + Disableable {
     ///
     /// Nearly all interactable elements should have a tooltip. Some example
     /// exceptions might a scroll bar, or a slider.
-    fn tooltip(self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self;
+    fn tooltip(self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self;
 
     fn layer(self, elevation: ElevationIndex) -> Self;
 }
@@ -55,7 +55,7 @@ pub enum TintColor {
 }
 
 impl TintColor {
-    fn button_like_style(self, cx: &mut WindowContext) -> ButtonLikeStyles {
+    fn button_like_style(self, cx: &mut App) -> ButtonLikeStyles {
         match self {
             TintColor::Accent => ButtonLikeStyles {
                 background: cx.theme().status().info_background,
@@ -146,7 +146,7 @@ pub(crate) struct ButtonLikeStyles {
     pub icon_color: Hsla,
 }
 
-fn element_bg_from_elevation(elevation: Option<ElevationIndex>, cx: &mut WindowContext) -> Hsla {
+fn element_bg_from_elevation(elevation: Option<ElevationIndex>, cx: &mut App) -> Hsla {
     match elevation {
         Some(ElevationIndex::Background) => cx.theme().colors().element_background,
         Some(ElevationIndex::ElevatedSurface) => cx.theme().colors().elevated_surface_background,
@@ -160,7 +160,8 @@ impl ButtonStyle {
     pub(crate) fn enabled(
         self,
         elevation: Option<ElevationIndex>,
-        cx: &mut WindowContext,
+
+        cx: &mut App,
     ) -> ButtonLikeStyles {
         match self {
             ButtonStyle::Filled => ButtonLikeStyles {
@@ -188,7 +189,8 @@ impl ButtonStyle {
     pub(crate) fn hovered(
         self,
         elevation: Option<ElevationIndex>,
-        cx: &mut WindowContext,
+
+        cx: &mut App,
     ) -> ButtonLikeStyles {
         match self {
             ButtonStyle::Filled => {
@@ -225,7 +227,7 @@ impl ButtonStyle {
         }
     }
 
-    pub(crate) fn active(self, cx: &mut WindowContext) -> ButtonLikeStyles {
+    pub(crate) fn active(self, cx: &mut App) -> ButtonLikeStyles {
         match self {
             ButtonStyle::Filled => ButtonLikeStyles {
                 background: cx.theme().colors().element_active,
@@ -252,7 +254,7 @@ impl ButtonStyle {
     }
 
     #[allow(unused)]
-    pub(crate) fn focused(self, cx: &mut WindowContext) -> ButtonLikeStyles {
+    pub(crate) fn focused(self, window: &mut Window, cx: &mut App) -> ButtonLikeStyles {
         match self {
             ButtonStyle::Filled => ButtonLikeStyles {
                 background: cx.theme().colors().element_background,
@@ -280,7 +282,8 @@ impl ButtonStyle {
     pub(crate) fn disabled(
         self,
         elevation: Option<ElevationIndex>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> ButtonLikeStyles {
         match self {
             ButtonStyle::Filled => ButtonLikeStyles {
@@ -347,9 +350,9 @@ pub struct ButtonLike {
     pub(super) layer: Option<ElevationIndex>,
     size: ButtonSize,
     rounding: Option<ButtonLikeRounding>,
-    tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
+    tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView>>,
     cursor_style: CursorStyle,
-    on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
+    on_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
     children: SmallVec<[AnyElement; 2]>,
 }
 
@@ -415,7 +418,7 @@ impl SelectableButton for ButtonLike {
 }
 
 impl Clickable for ButtonLike {
-    fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
+    fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self {
         self.on_click = Some(Box::new(handler));
         self
     }
@@ -453,7 +456,7 @@ impl ButtonCommon for ButtonLike {
         self
     }
 
-    fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
+    fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self {
         self.tooltip = Some(Box::new(tooltip));
         self
     }
@@ -478,7 +481,7 @@ impl ParentElement for ButtonLike {
 }
 
 impl RenderOnce for ButtonLike {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
         let style = self
             .selected_style
             .filter(|_| self.selected)
@@ -515,15 +518,15 @@ impl RenderOnce for ButtonLike {
             .when_some(
                 self.on_click.filter(|_| !self.disabled),
                 |this, on_click| {
-                    this.on_mouse_down(MouseButton::Left, |_, cx| cx.prevent_default())
-                        .on_click(move |event, cx| {
+                    this.on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
+                        .on_click(move |event, window, cx| {
                             cx.stop_propagation();
-                            (on_click)(event, cx)
+                            (on_click)(event, window, cx)
                         })
                 },
             )
             .when_some(self.tooltip, |this, tooltip| {
-                this.tooltip(move |cx| tooltip(cx))
+                this.tooltip(move |window, cx| tooltip(window, cx))
             })
             .children(self.children)
     }

crates/ui/src/components/button/icon_button.rs 🔗

@@ -90,7 +90,7 @@ impl SelectableButton for IconButton {
 impl Clickable for IconButton {
     fn on_click(
         mut self,
-        handler: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
+        handler: impl Fn(&gpui::ClickEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.base = self.base.on_click(handler);
         self
@@ -129,7 +129,7 @@ impl ButtonCommon for IconButton {
         self
     }
 
-    fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
+    fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self {
         self.base = self.base.tooltip(tooltip);
         self
     }
@@ -148,7 +148,7 @@ impl VisibleOnHover for IconButton {
 }
 
 impl RenderOnce for IconButton {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
         let is_disabled = self.base.disabled;
         let is_selected = self.base.selected;
         let selected_style = self.base.selected_style;
@@ -157,7 +157,7 @@ impl RenderOnce for IconButton {
         self.base
             .map(|this| match self.shape {
                 IconButtonShape::Square => {
-                    let size = self.icon_size.square(cx);
+                    let size = self.icon_size.square(window, cx);
                     this.width(size.into()).height(size.into())
                 }
                 IconButtonShape::Wide => this,

crates/ui/src/components/button/toggle_button.rs 🔗

@@ -79,7 +79,7 @@ impl Disableable for ToggleButton {
 }
 
 impl Clickable for ToggleButton {
-    fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
+    fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self {
         self.base = self.base.on_click(handler);
         self
     }
@@ -105,7 +105,7 @@ impl ButtonCommon for ToggleButton {
         self
     }
 
-    fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
+    fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self {
         self.base = self.base.tooltip(tooltip);
         self
     }
@@ -117,7 +117,7 @@ impl ButtonCommon for ToggleButton {
 }
 
 impl RenderOnce for ToggleButton {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         let is_disabled = self.base.disabled;
         let is_selected = self.base.selected;
 

crates/ui/src/components/content_group.rs 🔗

@@ -67,7 +67,7 @@ impl Styled for ContentGroup {
 }
 
 impl RenderOnce for ContentGroup {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         // TODO:
         // Baked in padding will make scrollable views inside of content boxes awkward.
         //
@@ -95,7 +95,7 @@ impl ComponentPreview for ContentGroup {
         ExampleLabelSide::Bottom
     }
 
-    fn examples(_: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
+    fn examples(_window: &mut Window, _: &mut App) -> Vec<ComponentExampleGroup<Self>> {
         vec![example_group(vec![
             single_example(
                 "Default",

crates/ui/src/components/context_menu.rs 🔗

@@ -4,8 +4,8 @@ use crate::{
     List, ListItem, ListSeparator, ListSubHeader,
 };
 use gpui::{
-    px, Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView,
-    IntoElement, Render, Subscription, View, VisualContext,
+    px, Action, AnyElement, App, AppContext as _, DismissEvent, Entity, EventEmitter, FocusHandle,
+    Focusable, IntoElement, Render, Subscription,
 };
 use menu::{SelectFirst, SelectLast, SelectNext, SelectPrev};
 use settings::Settings;
@@ -18,20 +18,20 @@ pub enum ContextMenuItem {
     Label(SharedString),
     Entry(ContextMenuEntry),
     CustomEntry {
-        entry_render: Box<dyn Fn(&mut WindowContext) -> AnyElement>,
-        handler: Rc<dyn Fn(Option<&FocusHandle>, &mut WindowContext)>,
+        entry_render: Box<dyn Fn(&mut Window, &mut App) -> AnyElement>,
+        handler: Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>,
         selectable: bool,
     },
 }
 
 impl ContextMenuItem {
     pub fn custom_entry(
-        entry_render: impl Fn(&mut WindowContext) -> AnyElement + 'static,
-        handler: impl Fn(&mut WindowContext) + 'static,
+        entry_render: impl Fn(&mut Window, &mut App) -> AnyElement + 'static,
+        handler: impl Fn(&mut Window, &mut App) + 'static,
     ) -> Self {
         Self::CustomEntry {
             entry_render: Box::new(entry_render),
-            handler: Rc::new(move |_, cx| handler(cx)),
+            handler: Rc::new(move |_, window, cx| handler(window, cx)),
             selectable: true,
         }
     }
@@ -44,7 +44,7 @@ pub struct ContextMenuEntry {
     icon_position: IconPosition,
     icon_size: IconSize,
     icon_color: Option<Color>,
-    handler: Rc<dyn Fn(Option<&FocusHandle>, &mut WindowContext)>,
+    handler: Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>,
     action: Option<Box<dyn Action>>,
     disabled: bool,
 }
@@ -58,7 +58,7 @@ impl ContextMenuEntry {
             icon_position: IconPosition::Start,
             icon_size: IconSize::Small,
             icon_color: None,
-            handler: Rc::new(|_, _| {}),
+            handler: Rc::new(|_, _, _| {}),
             action: None,
             disabled: false,
         }
@@ -94,8 +94,8 @@ impl ContextMenuEntry {
         self
     }
 
-    pub fn handler(mut self, handler: impl Fn(&mut WindowContext) + 'static) -> Self {
-        self.handler = Rc::new(move |_, cx| handler(cx));
+    pub fn handler(mut self, handler: impl Fn(&mut Window, &mut App) + 'static) -> Self {
+        self.handler = Rc::new(move |_, window, cx| handler(window, cx));
         self
     }
 
@@ -122,8 +122,8 @@ pub struct ContextMenu {
     keep_open_on_confirm: bool,
 }
 
-impl FocusableView for ContextMenu {
-    fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
+impl Focusable for ContextMenu {
+    fn focus_handle(&self, _cx: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -134,15 +134,18 @@ impl FluentBuilder for ContextMenu {}
 
 impl ContextMenu {
     pub fn build(
-        cx: &mut WindowContext,
-        f: impl FnOnce(Self, &mut ViewContext<Self>) -> Self,
-    ) -> View<Self> {
-        cx.new_view(|cx| {
+        window: &mut Window,
+        cx: &mut App,
+        f: impl FnOnce(Self, &mut Window, &mut Context<Self>) -> Self,
+    ) -> Entity<Self> {
+        cx.new(|cx| {
             let focus_handle = cx.focus_handle();
-            let _on_blur_subscription = cx.on_blur(&focus_handle, |this: &mut ContextMenu, cx| {
-                this.cancel(&menu::Cancel, cx)
-            });
-            cx.refresh();
+            let _on_blur_subscription = cx.on_blur(
+                &focus_handle,
+                window,
+                |this: &mut ContextMenu, window, cx| this.cancel(&menu::Cancel, window, cx),
+            );
+            window.refresh();
             f(
                 Self {
                     items: Default::default(),
@@ -154,6 +157,7 @@ impl ContextMenu {
                     _on_blur_subscription,
                     keep_open_on_confirm: false,
                 },
+                window,
                 cx,
             )
         })
@@ -188,12 +192,12 @@ impl ContextMenu {
         mut self,
         label: impl Into<SharedString>,
         action: Option<Box<dyn Action>>,
-        handler: impl Fn(&mut WindowContext) + 'static,
+        handler: impl Fn(&mut Window, &mut App) + 'static,
     ) -> Self {
         self.items.push(ContextMenuItem::Entry(ContextMenuEntry {
             toggle: None,
             label: label.into(),
-            handler: Rc::new(move |_, cx| handler(cx)),
+            handler: Rc::new(move |_, window, cx| handler(window, cx)),
             icon: None,
             icon_position: IconPosition::End,
             icon_size: IconSize::Small,
@@ -210,12 +214,12 @@ impl ContextMenu {
         toggled: bool,
         position: IconPosition,
         action: Option<Box<dyn Action>>,
-        handler: impl Fn(&mut WindowContext) + 'static,
+        handler: impl Fn(&mut Window, &mut App) + 'static,
     ) -> Self {
         self.items.push(ContextMenuItem::Entry(ContextMenuEntry {
             toggle: Some((position, toggled)),
             label: label.into(),
-            handler: Rc::new(move |_, cx| handler(cx)),
+            handler: Rc::new(move |_, window, cx| handler(window, cx)),
             icon: None,
             icon_position: position,
             icon_size: IconSize::Small,
@@ -228,11 +232,11 @@ impl ContextMenu {
 
     pub fn custom_row(
         mut self,
-        entry_render: impl Fn(&mut WindowContext) -> AnyElement + 'static,
+        entry_render: impl Fn(&mut Window, &mut App) -> AnyElement + 'static,
     ) -> Self {
         self.items.push(ContextMenuItem::CustomEntry {
             entry_render: Box::new(entry_render),
-            handler: Rc::new(|_, _| {}),
+            handler: Rc::new(|_, _, _| {}),
             selectable: false,
         });
         self
@@ -240,12 +244,12 @@ impl ContextMenu {
 
     pub fn custom_entry(
         mut self,
-        entry_render: impl Fn(&mut WindowContext) -> AnyElement + 'static,
-        handler: impl Fn(&mut WindowContext) + 'static,
+        entry_render: impl Fn(&mut Window, &mut App) -> AnyElement + 'static,
+        handler: impl Fn(&mut Window, &mut App) + 'static,
     ) -> Self {
         self.items.push(ContextMenuItem::CustomEntry {
             entry_render: Box::new(entry_render),
-            handler: Rc::new(move |_, cx| handler(cx)),
+            handler: Rc::new(move |_, window, cx| handler(window, cx)),
             selectable: true,
         });
         self
@@ -261,12 +265,11 @@ impl ContextMenu {
             toggle: None,
             label: label.into(),
             action: Some(action.boxed_clone()),
-
-            handler: Rc::new(move |context, cx| {
+            handler: Rc::new(move |context, window, cx| {
                 if let Some(context) = &context {
-                    cx.focus(context);
+                    window.focus(context);
                 }
-                cx.dispatch_action(action.boxed_clone());
+                window.dispatch_action(action.boxed_clone(), cx);
             }),
             icon: None,
             icon_position: IconPosition::End,
@@ -287,11 +290,11 @@ impl ContextMenu {
             label: label.into(),
             action: Some(action.boxed_clone()),
 
-            handler: Rc::new(move |context, cx| {
+            handler: Rc::new(move |context, window, cx| {
                 if let Some(context) = &context {
-                    cx.focus(context);
+                    window.focus(context);
                 }
-                cx.dispatch_action(action.boxed_clone());
+                window.dispatch_action(action.boxed_clone(), cx);
             }),
             icon: None,
             icon_size: IconSize::Small,
@@ -308,7 +311,7 @@ impl ContextMenu {
             label: label.into(),
 
             action: Some(action.boxed_clone()),
-            handler: Rc::new(move |_, cx| cx.dispatch_action(action.boxed_clone())),
+            handler: Rc::new(move |_, window, cx| window.dispatch_action(action.boxed_clone(), cx)),
             icon: Some(IconName::ArrowUpRight),
             icon_size: IconSize::XSmall,
             icon_position: IconPosition::End,
@@ -323,7 +326,7 @@ impl ContextMenu {
         self
     }
 
-    pub fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
+    pub fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
         let context = self.action_context.as_ref();
         if let Some(
             ContextMenuItem::Entry(ContextMenuEntry {
@@ -334,7 +337,7 @@ impl ContextMenu {
             | ContextMenuItem::CustomEntry { handler, .. },
         ) = self.selected_index.and_then(|ix| self.items.get(ix))
         {
-            (handler)(context, cx)
+            (handler)(context, window, cx)
         }
 
         if !self.keep_open_on_confirm {
@@ -342,12 +345,12 @@ impl ContextMenu {
         }
     }
 
-    pub fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
+    pub fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {
         cx.emit(DismissEvent);
         cx.emit(DismissEvent);
     }
 
-    fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext<Self>) {
+    fn select_first(&mut self, _: &SelectFirst, _: &mut Window, cx: &mut Context<Self>) {
         self.selected_index = self.items.iter().position(|item| item.is_selectable());
         cx.notify();
     }
@@ -362,17 +365,17 @@ impl ContextMenu {
         None
     }
 
-    fn handle_select_last(&mut self, _: &SelectLast, cx: &mut ViewContext<Self>) {
+    fn handle_select_last(&mut self, _: &SelectLast, _: &mut Window, cx: &mut Context<Self>) {
         if self.select_last().is_some() {
             cx.notify();
         }
     }
 
-    fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
+    fn select_next(&mut self, _: &SelectNext, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(ix) = self.selected_index {
             let next_index = ix + 1;
             if self.items.len() <= next_index {
-                self.select_first(&SelectFirst, cx);
+                self.select_first(&SelectFirst, window, cx);
             } else {
                 for (ix, item) in self.items.iter().enumerate().skip(next_index) {
                     if item.is_selectable() {
@@ -383,14 +386,14 @@ impl ContextMenu {
                 }
             }
         } else {
-            self.select_first(&SelectFirst, cx);
+            self.select_first(&SelectFirst, window, cx);
         }
     }
 
-    pub fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext<Self>) {
+    pub fn select_prev(&mut self, _: &SelectPrev, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(ix) = self.selected_index {
             if ix == 0 {
-                self.handle_select_last(&SelectLast, cx);
+                self.handle_select_last(&SelectLast, window, cx);
             } else {
                 for (ix, item) in self.items.iter().enumerate().take(ix).rev() {
                     if item.is_selectable() {
@@ -401,11 +404,16 @@ impl ContextMenu {
                 }
             }
         } else {
-            self.handle_select_last(&SelectLast, cx);
+            self.handle_select_last(&SelectLast, window, cx);
         }
     }
 
-    pub fn on_action_dispatch(&mut self, dispatched: &dyn Action, cx: &mut ViewContext<Self>) {
+    pub fn on_action_dispatch(
+        &mut self,
+        dispatched: &dyn Action,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if self.clicked {
             cx.propagate();
             return;
@@ -427,13 +435,15 @@ impl ContextMenu {
             self.delayed = true;
             cx.notify();
             let action = dispatched.boxed_clone();
-            cx.spawn(|this, mut cx| async move {
+            cx.spawn_in(window, |this, mut cx| async move {
                 cx.background_executor()
                     .timer(Duration::from_millis(50))
                     .await;
-                this.update(&mut cx, |this, cx| {
-                    this.cancel(&menu::Cancel, cx);
-                    cx.dispatch_action(action);
+                cx.update(|window, cx| {
+                    this.update(cx, |this, cx| {
+                        this.cancel(&menu::Cancel, window, cx);
+                        window.dispatch_action(action, cx);
+                    })
                 })
             })
             .detach_and_log_err(cx);
@@ -461,7 +471,7 @@ impl ContextMenuItem {
 }
 
 impl Render for ContextMenu {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
 
         WithRemSize::new(ui_font_size)
@@ -473,11 +483,13 @@ impl Render for ContextMenu {
                 v_flex()
                     .id("context-menu")
                     .min_w(px(200.))
-                    .max_h(vh(0.75, cx))
+                    .max_h(vh(0.75, window))
                     .flex_1()
                     .overflow_y_scroll()
                     .track_focus(&self.focus_handle(cx))
-                    .on_mouse_down_out(cx.listener(|this, _, cx| this.cancel(&menu::Cancel, cx)))
+                    .on_mouse_down_out(
+                        cx.listener(|this, _, window, cx| this.cancel(&menu::Cancel, window, cx)),
+                    )
                     .key_context("menu")
                     .on_action(cx.listener(ContextMenu::select_first))
                     .on_action(cx.listener(ContextMenu::handle_select_last))
@@ -527,7 +539,7 @@ impl Render for ContextMenu {
                                     disabled,
                                 }) => {
                                     let handler = handler.clone();
-                                    let menu = cx.view().downgrade();
+                                    let menu = cx.model().downgrade();
                                     let icon_color = if *disabled {
                                         Color::Muted
                                     } else {
@@ -595,19 +607,21 @@ impl Render for ContextMenu {
                                                         .as_ref()
                                                         .map(|focus| {
                                                             KeyBinding::for_action_in(
-                                                                &**action, focus, cx,
+                                                                &**action, focus, window,
                                                             )
                                                         })
                                                         .unwrap_or_else(|| {
-                                                            KeyBinding::for_action(&**action, cx)
+                                                            KeyBinding::for_action(
+                                                                &**action, window,
+                                                            )
                                                         })
                                                         .map(|binding| div().ml_4().child(binding))
                                                 })),
                                         )
                                         .on_click({
                                             let context = self.action_context.clone();
-                                            move |_, cx| {
-                                                handler(context.as_ref(), cx);
+                                            move |_, window, cx| {
+                                                handler(context.as_ref(), window, cx);
                                                 menu.update(cx, |menu, cx| {
                                                     menu.clicked = true;
                                                     cx.emit(DismissEvent);
@@ -623,7 +637,7 @@ impl Render for ContextMenu {
                                     selectable,
                                 } => {
                                     let handler = handler.clone();
-                                    let menu = cx.view().downgrade();
+                                    let menu = cx.model().downgrade();
                                     let selectable = *selectable;
                                     ListItem::new(ix)
                                         .inset(true)
@@ -636,8 +650,8 @@ impl Render for ContextMenu {
                                         .when(selectable, |item| {
                                             item.on_click({
                                                 let context = self.action_context.clone();
-                                                move |_, cx| {
-                                                    handler(context.as_ref(), cx);
+                                                move |_, window, cx| {
+                                                    handler(context.as_ref(), window, cx);
                                                     menu.update(cx, |menu, cx| {
                                                         menu.clicked = true;
                                                         cx.emit(DismissEvent);
@@ -646,7 +660,7 @@ impl Render for ContextMenu {
                                                 }
                                             })
                                         })
-                                        .child(entry_render(cx))
+                                        .child(entry_render(window, cx))
                                         .into_any_element()
                                 }
                             }

crates/ui/src/components/disclosure.rs 🔗

@@ -10,7 +10,7 @@ pub struct Disclosure {
     id: ElementId,
     is_open: bool,
     selected: bool,
-    on_toggle: Option<Arc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
+    on_toggle: Option<Arc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
     cursor_style: CursorStyle,
 }
 
@@ -27,7 +27,7 @@ impl Disclosure {
 
     pub fn on_toggle(
         mut self,
-        handler: impl Into<Option<Arc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>>,
+        handler: impl Into<Option<Arc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>>,
     ) -> Self {
         self.on_toggle = handler.into();
         self
@@ -42,7 +42,7 @@ impl Toggleable for Disclosure {
 }
 
 impl Clickable for Disclosure {
-    fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
+    fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self {
         self.on_toggle = Some(Arc::new(handler));
         self
     }
@@ -54,7 +54,7 @@ impl Clickable for Disclosure {
 }
 
 impl RenderOnce for Disclosure {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         IconButton::new(
             self.id,
             match self.is_open {
@@ -67,7 +67,7 @@ impl RenderOnce for Disclosure {
         .icon_size(IconSize::Small)
         .toggle_state(self.selected)
         .when_some(self.on_toggle, move |this, on_toggle| {
-            this.on_click(move |event, cx| on_toggle(event, cx))
+            this.on_click(move |event, window, cx| on_toggle(event, window, cx))
         })
     }
 }

crates/ui/src/components/divider.rs 🔗

@@ -24,7 +24,7 @@ pub enum DividerColor {
 }
 
 impl DividerColor {
-    pub fn hsla(self, cx: &WindowContext) -> Hsla {
+    pub fn hsla(self, cx: &mut App) -> Hsla {
         match self {
             DividerColor::Border => cx.theme().colors().border,
             DividerColor::BorderVariant => cx.theme().colors().border_variant,
@@ -41,7 +41,7 @@ pub struct Divider {
 }
 
 impl RenderOnce for Divider {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
         match self.style {
             DividerStyle::Solid => self.render_solid(cx).into_any_element(),
             DividerStyle::Dashed => self.render_dashed(cx).into_any_element(),
@@ -96,7 +96,7 @@ impl Divider {
         self
     }
 
-    pub fn render_solid(self, cx: &WindowContext) -> impl IntoElement {
+    pub fn render_solid(self, cx: &mut App) -> impl IntoElement {
         div()
             .map(|this| match self.direction {
                 DividerDirection::Horizontal => {
@@ -111,7 +111,7 @@ impl Divider {
 
     // TODO: Use canvas or a shader here
     // This obviously is a short term approach
-    pub fn render_dashed(self, cx: &WindowContext) -> impl IntoElement {
+    pub fn render_dashed(self, cx: &mut App) -> impl IntoElement {
         let segment_count = 128;
         let segment_count_f = segment_count as f32;
         let segment_min_w = 6.;

crates/ui/src/components/dropdown_menu.rs 🔗

@@ -1,5 +1,5 @@
 #![allow(missing_docs)]
-use gpui::{ClickEvent, Corner, CursorStyle, MouseButton, View};
+use gpui::{ClickEvent, Corner, CursorStyle, Entity, MouseButton};
 
 use crate::{prelude::*, ContextMenu, PopoverMenu};
 
@@ -7,7 +7,7 @@ use crate::{prelude::*, ContextMenu, PopoverMenu};
 pub struct DropdownMenu {
     id: ElementId,
     label: SharedString,
-    menu: View<ContextMenu>,
+    menu: Entity<ContextMenu>,
     full_width: bool,
     disabled: bool,
 }
@@ -16,7 +16,7 @@ impl DropdownMenu {
     pub fn new(
         id: impl Into<ElementId>,
         label: impl Into<SharedString>,
-        menu: View<ContextMenu>,
+        menu: Entity<ContextMenu>,
     ) -> Self {
         Self {
             id: id.into(),
@@ -41,10 +41,10 @@ impl Disableable for DropdownMenu {
 }
 
 impl RenderOnce for DropdownMenu {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         PopoverMenu::new(self.id)
             .full_width(self.full_width)
-            .menu(move |_cx| Some(self.menu.clone()))
+            .menu(move |_window, _cx| Some(self.menu.clone()))
             .trigger(DropdownMenuTrigger::new(self.label).full_width(self.full_width))
             .attach(Corner::BottomLeft)
     }
@@ -57,7 +57,7 @@ struct DropdownMenuTrigger {
     selected: bool,
     disabled: bool,
     cursor_style: CursorStyle,
-    on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
+    on_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
 }
 
 impl DropdownMenuTrigger {
@@ -93,7 +93,7 @@ impl Toggleable for DropdownMenuTrigger {
 }
 
 impl Clickable for DropdownMenuTrigger {
-    fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
+    fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self {
         self.on_click = Some(Box::new(handler));
         self
     }
@@ -105,7 +105,7 @@ impl Clickable for DropdownMenuTrigger {
 }
 
 impl RenderOnce for DropdownMenuTrigger {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let disabled = self.disabled;
 
         h_flex()
@@ -147,10 +147,10 @@ impl RenderOnce for DropdownMenuTrigger {
                     }),
             )
             .when_some(self.on_click.filter(|_| !disabled), |el, on_click| {
-                el.on_mouse_down(MouseButton::Left, |_, cx| cx.prevent_default())
-                    .on_click(move |event, cx| {
+                el.on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
+                    .on_click(move |event, window, cx| {
                         cx.stop_propagation();
-                        (on_click)(event, cx)
+                        (on_click)(event, window, cx)
                     })
             })
     }

crates/ui/src/components/facepile.rs 🔗

@@ -43,7 +43,7 @@ impl Facepile {
 }
 
 impl RenderOnce for Facepile {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         // Lay the faces out in reverse so they overlap in the desired order (left to right, front to back)
         self.base
             .flex()
@@ -67,7 +67,7 @@ impl ComponentPreview for Facepile {
         \n\nFacepiles are used to display a group of people or things,\
         such as a list of participants in a collaboration session."
     }
-    fn examples(_: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
+    fn examples(_window: &mut Window, _: &mut App) -> Vec<ComponentExampleGroup<Self>> {
         let few_faces: [&'static str; 3] = [
             "https://avatars.githubusercontent.com/u/1714999?s=60&v=4",
             "https://avatars.githubusercontent.com/u/67129314?s=60&v=4",

crates/ui/src/components/icon.rs 🔗

@@ -49,7 +49,7 @@ impl From<AnimationElement<Icon>> for AnyIcon {
 }
 
 impl RenderOnce for AnyIcon {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         match self {
             Self::Icon(icon) => icon.into_any_element(),
             Self::AnimatedIcon(animated_icon) => animated_icon.into_any_element(),
@@ -88,8 +88,8 @@ impl IconSize {
     /// The returned tuple contains:
     ///   1. The length of one side of the square
     ///   2. The padding of one side of the square
-    pub fn square_components(&self, cx: &mut WindowContext) -> (Pixels, Pixels) {
-        let icon_size = self.rems() * cx.rem_size();
+    pub fn square_components(&self, window: &mut Window, cx: &mut App) -> (Pixels, Pixels) {
+        let icon_size = self.rems() * window.rem_size();
         let padding = match self {
             IconSize::Indicator => DynamicSpacing::Base00.px(cx),
             IconSize::XSmall => DynamicSpacing::Base02.px(cx),
@@ -102,8 +102,8 @@ impl IconSize {
     }
 
     /// Returns the length of a side of the square that contains this [`IconSize`], with padding.
-    pub fn square(&self, cx: &mut WindowContext) -> Pixels {
-        let (icon_size, padding) = self.square_components(cx);
+    pub fn square(&self, window: &mut Window, cx: &mut App) -> Pixels {
+        let (icon_size, padding) = self.square_components(window, cx);
 
         icon_size + padding * 2.
     }
@@ -408,7 +408,7 @@ impl Icon {
 }
 
 impl RenderOnce for Icon {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
         match self.source {
             IconSource::Svg(path) => svg()
                 .with_transformation(self.transformation)
@@ -461,7 +461,7 @@ impl IconWithIndicator {
 }
 
 impl RenderOnce for IconWithIndicator {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let indicator_border_color = self
             .indicator_border_color
             .unwrap_or_else(|| cx.theme().colors().elevated_surface_background);
@@ -486,7 +486,7 @@ impl RenderOnce for IconWithIndicator {
 }
 
 impl ComponentPreview for Icon {
-    fn examples(_cx: &mut WindowContext) -> Vec<ComponentExampleGroup<Icon>> {
+    fn examples(_window: &mut Window, _cx: &mut App) -> Vec<ComponentExampleGroup<Icon>> {
         let arrow_icons = vec![
             IconName::ArrowDown,
             IconName::ArrowLeft,

crates/ui/src/components/icon/decorated_icon.rs 🔗

@@ -17,7 +17,7 @@ impl DecoratedIcon {
 }
 
 impl RenderOnce for DecoratedIcon {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         div()
             .relative()
             .size(self.icon.size)
@@ -27,7 +27,7 @@ impl RenderOnce for DecoratedIcon {
 }
 
 impl ComponentPreview for DecoratedIcon {
-    fn examples(cx: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
+    fn examples(_: &mut Window, cx: &mut App) -> Vec<ComponentExampleGroup<Self>> {
         let icon_1 = Icon::new(IconName::FileDoc);
         let icon_2 = Icon::new(IconName::FileDoc);
         let icon_3 = Icon::new(IconName::FileDoc);

crates/ui/src/components/icon/icon_decoration.rs 🔗

@@ -61,7 +61,7 @@ pub struct IconDecoration {
 
 impl IconDecoration {
     /// Creates a new [`IconDecoration`].
-    pub fn new(kind: IconDecorationKind, knockout_color: Hsla, cx: &WindowContext) -> Self {
+    pub fn new(kind: IconDecorationKind, knockout_color: Hsla, cx: &App) -> Self {
         let color = cx.theme().colors().icon;
         let position = Point::default();
 
@@ -116,7 +116,7 @@ impl IconDecoration {
 }
 
 impl RenderOnce for IconDecoration {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         let foreground = svg()
             .absolute()
             .bottom_0()
@@ -151,7 +151,7 @@ impl RenderOnce for IconDecoration {
 }
 
 impl ComponentPreview for IconDecoration {
-    fn examples(cx: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
+    fn examples(_: &mut Window, cx: &mut App) -> Vec<ComponentExampleGroup<Self>> {
         let all_kinds = IconDecorationKind::iter().collect::<Vec<_>>();
 
         let examples = all_kinds

crates/ui/src/components/image.rs 🔗

@@ -1,5 +1,5 @@
 #![allow(missing_docs)]
-use gpui::{svg, IntoElement, Rems, RenderOnce, Size, Styled, WindowContext};
+use gpui::{svg, App, IntoElement, Rems, RenderOnce, Size, Styled, Window};
 use serde::{Deserialize, Serialize};
 use strum::{EnumIter, EnumString, IntoStaticStr};
 use ui_macros::{path_str, DerivePathStr};
@@ -69,7 +69,7 @@ impl Vector {
 }
 
 impl RenderOnce for Vector {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let width = self.size.width;
         let height = self.size.height;
 
@@ -97,7 +97,7 @@ pub mod story {
     pub struct VectorStory;
 
     impl Render for VectorStory {
-        fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+        fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
             Story::container().child(StorySection::new().children(VectorName::iter().map(
                 |vector| StoryItem::new(format!("{:?}", vector), Vector::square(vector, rems(8.))),
             )))

crates/ui/src/components/indent_guides.rs 🔗

@@ -2,7 +2,7 @@
 use std::{cmp::Ordering, ops::Range, rc::Rc};
 
 use gpui::{
-    fill, point, size, AnyElement, AppContext, Bounds, Hsla, Point, UniformListDecoration, View,
+    fill, point, size, AnyElement, App, Bounds, Entity, Hsla, Point, UniformListDecoration,
 };
 use smallvec::SmallVec;
 
@@ -21,7 +21,7 @@ pub struct IndentGuideColors {
 
 impl IndentGuideColors {
     /// Returns the indent guide colors that should be used for panels.
-    pub fn panel(cx: &AppContext) -> Self {
+    pub fn panel(cx: &App) -> Self {
         Self {
             default: cx.theme().colors().panel_indent_guide,
             hover: cx.theme().colors().panel_indent_guide_hover,
@@ -33,27 +33,28 @@ impl IndentGuideColors {
 pub struct IndentGuides {
     colors: IndentGuideColors,
     indent_size: Pixels,
-    compute_indents_fn: Box<dyn Fn(Range<usize>, &mut WindowContext) -> SmallVec<[usize; 64]>>,
+    compute_indents_fn: Box<dyn Fn(Range<usize>, &mut Window, &mut App) -> SmallVec<[usize; 64]>>,
     render_fn: Option<
         Box<
             dyn Fn(
                 RenderIndentGuideParams,
-                &mut WindowContext,
+                &mut Window,
+                &mut App,
             ) -> SmallVec<[RenderedIndentGuide; 12]>,
         >,
     >,
-    on_click: Option<Rc<dyn Fn(&IndentGuideLayout, &mut WindowContext)>>,
+    on_click: Option<Rc<dyn Fn(&IndentGuideLayout, &mut Window, &mut App)>>,
 }
 
 pub fn indent_guides<V: Render>(
-    view: View<V>,
+    model: Entity<V>,
     indent_size: Pixels,
     colors: IndentGuideColors,
-    compute_indents_fn: impl Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> SmallVec<[usize; 64]>
+    compute_indents_fn: impl Fn(&mut V, Range<usize>, &mut Window, &mut Context<V>) -> SmallVec<[usize; 64]>
         + 'static,
 ) -> IndentGuides {
-    let compute_indents_fn = Box::new(move |range, cx: &mut WindowContext| {
-        view.update(cx, |this, cx| compute_indents_fn(this, range, cx))
+    let compute_indents_fn = Box::new(move |range, window: &mut Window, cx: &mut App| {
+        model.update(cx, |this, cx| compute_indents_fn(this, range, window, cx))
     });
     IndentGuides {
         colors,
@@ -68,7 +69,7 @@ impl IndentGuides {
     /// Sets the callback that will be called when the user clicks on an indent guide.
     pub fn on_click(
         mut self,
-        on_click: impl Fn(&IndentGuideLayout, &mut WindowContext) + 'static,
+        on_click: impl Fn(&IndentGuideLayout, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.on_click = Some(Rc::new(on_click));
         self
@@ -77,16 +78,17 @@ impl IndentGuides {
     /// Sets a custom callback that will be called when the indent guides need to be rendered.
     pub fn with_render_fn<V: Render>(
         mut self,
-        view: View<V>,
+        model: Entity<V>,
         render_fn: impl Fn(
                 &mut V,
                 RenderIndentGuideParams,
-                &mut WindowContext,
+                &mut Window,
+                &mut App,
             ) -> SmallVec<[RenderedIndentGuide; 12]>
             + 'static,
     ) -> Self {
-        let render_fn = move |params, cx: &mut WindowContext| {
-            view.update(cx, |this, cx| render_fn(this, params, cx))
+        let render_fn = move |params, window: &mut Window, cx: &mut App| {
+            model.update(cx, |this, cx| render_fn(this, params, window, cx))
         };
         self.render_fn = Some(Box::new(render_fn));
         self
@@ -141,7 +143,8 @@ mod uniform_list {
             bounds: Bounds<Pixels>,
             item_height: Pixels,
             item_count: usize,
-            cx: &mut WindowContext,
+            window: &mut Window,
+            cx: &mut App,
         ) -> AnyElement {
             let mut visible_range = visible_range.clone();
             let includes_trailing_indent = visible_range.end < item_count;
@@ -151,7 +154,7 @@ mod uniform_list {
             if includes_trailing_indent {
                 visible_range.end += 1;
             }
-            let visible_entries = &(self.compute_indents_fn)(visible_range.clone(), cx);
+            let visible_entries = &(self.compute_indents_fn)(visible_range.clone(), window, cx);
             let indent_guides = compute_indent_guides(
                 &visible_entries,
                 visible_range.start,
@@ -163,7 +166,7 @@ mod uniform_list {
                     indent_size: self.indent_size,
                     item_height,
                 };
-                custom_render(params, cx)
+                custom_render(params, window, cx)
             } else {
                 indent_guides
                     .into_iter()
@@ -200,14 +203,15 @@ mod uniform_list {
     struct IndentGuidesElement {
         colors: IndentGuideColors,
         indent_guides: Rc<SmallVec<[RenderedIndentGuide; 12]>>,
-        on_hovered_indent_guide_click: Option<Rc<dyn Fn(&IndentGuideLayout, &mut WindowContext)>>,
+        on_hovered_indent_guide_click:
+            Option<Rc<dyn Fn(&IndentGuideLayout, &mut Window, &mut App)>>,
     }
 
     enum IndentGuidesElementPrepaintState {
         Static,
         Interactive {
             hitboxes: Rc<SmallVec<[Hitbox; 12]>>,
-            on_hovered_indent_guide_click: Rc<dyn Fn(&IndentGuideLayout, &mut WindowContext)>,
+            on_hovered_indent_guide_click: Rc<dyn Fn(&IndentGuideLayout, &mut Window, &mut App)>,
         },
     }
 
@@ -222,9 +226,10 @@ mod uniform_list {
         fn request_layout(
             &mut self,
             _id: Option<&gpui::GlobalElementId>,
-            cx: &mut WindowContext,
+            window: &mut Window,
+            cx: &mut App,
         ) -> (gpui::LayoutId, Self::RequestLayoutState) {
-            (cx.request_layout(gpui::Style::default(), []), ())
+            (window.request_layout(gpui::Style::default(), [], cx), ())
         }
 
         fn prepaint(
@@ -232,7 +237,8 @@ mod uniform_list {
             _id: Option<&gpui::GlobalElementId>,
             _bounds: Bounds<Pixels>,
             _request_layout: &mut Self::RequestLayoutState,
-            cx: &mut WindowContext,
+            window: &mut Window,
+            _cx: &mut App,
         ) -> Self::PrepaintState {
             if let Some(on_hovered_indent_guide_click) = self.on_hovered_indent_guide_click.clone()
             {
@@ -240,7 +246,7 @@ mod uniform_list {
                     .indent_guides
                     .as_ref()
                     .iter()
-                    .map(|guide| cx.insert_hitbox(guide.hitbox.unwrap_or(guide.bounds), false))
+                    .map(|guide| window.insert_hitbox(guide.hitbox.unwrap_or(guide.bounds), false))
                     .collect();
                 Self::PrepaintState::Interactive {
                     hitboxes: Rc::new(hitboxes),
@@ -257,7 +263,8 @@ mod uniform_list {
             _bounds: Bounds<Pixels>,
             _request_layout: &mut Self::RequestLayoutState,
             prepaint: &mut Self::PrepaintState,
-            cx: &mut WindowContext,
+            window: &mut Window,
+            _cx: &mut App,
         ) {
             match prepaint {
                 IndentGuidesElementPrepaintState::Static => {
@@ -268,22 +275,22 @@ mod uniform_list {
                             self.colors.default
                         };
 
-                        cx.paint_quad(fill(indent_guide.bounds, fill_color));
+                        window.paint_quad(fill(indent_guide.bounds, fill_color));
                     }
                 }
                 IndentGuidesElementPrepaintState::Interactive {
                     hitboxes,
                     on_hovered_indent_guide_click,
                 } => {
-                    cx.on_mouse_event({
+                    window.on_mouse_event({
                         let hitboxes = hitboxes.clone();
                         let indent_guides = self.indent_guides.clone();
                         let on_hovered_indent_guide_click = on_hovered_indent_guide_click.clone();
-                        move |event: &MouseDownEvent, phase, cx| {
+                        move |event: &MouseDownEvent, phase, window, cx| {
                             if phase == DispatchPhase::Bubble && event.button == MouseButton::Left {
                                 let mut active_hitbox_ix = None;
                                 for (i, hitbox) in hitboxes.iter().enumerate() {
-                                    if hitbox.is_hovered(cx) {
+                                    if hitbox.is_hovered(window) {
                                         active_hitbox_ix = Some(i);
                                         break;
                                     }
@@ -294,18 +301,18 @@ mod uniform_list {
                                 };
 
                                 let active_indent_guide = &indent_guides[active_hitbox_ix].layout;
-                                on_hovered_indent_guide_click(active_indent_guide, cx);
+                                on_hovered_indent_guide_click(active_indent_guide, window, cx);
 
                                 cx.stop_propagation();
-                                cx.prevent_default();
+                                window.prevent_default();
                             }
                         }
                     });
                     let mut hovered_hitbox_id = None;
                     for (i, hitbox) in hitboxes.iter().enumerate() {
-                        cx.set_cursor_style(gpui::CursorStyle::PointingHand, hitbox);
+                        window.set_cursor_style(gpui::CursorStyle::PointingHand, hitbox);
                         let indent_guide = &self.indent_guides[i];
-                        let fill_color = if hitbox.is_hovered(cx) {
+                        let fill_color = if hitbox.is_hovered(window) {
                             hovered_hitbox_id = Some(hitbox.id);
                             self.colors.hover
                         } else if indent_guide.is_active {
@@ -314,16 +321,16 @@ mod uniform_list {
                             self.colors.default
                         };
 
-                        cx.paint_quad(fill(indent_guide.bounds, fill_color));
+                        window.paint_quad(fill(indent_guide.bounds, fill_color));
                     }
 
-                    cx.on_mouse_event({
+                    window.on_mouse_event({
                         let prev_hovered_hitbox_id = hovered_hitbox_id;
                         let hitboxes = hitboxes.clone();
-                        move |_: &MouseMoveEvent, phase, cx| {
+                        move |_: &MouseMoveEvent, phase, window, _cx| {
                             let mut hovered_hitbox_id = None;
                             for hitbox in hitboxes.as_ref() {
-                                if hitbox.is_hovered(cx) {
+                                if hitbox.is_hovered(window) {
                                     hovered_hitbox_id = Some(hitbox.id);
                                     break;
                                 }
@@ -333,14 +340,14 @@ mod uniform_list {
                                 match (prev_hovered_hitbox_id, hovered_hitbox_id) {
                                     (Some(prev_id), Some(id)) => {
                                         if prev_id != id {
-                                            cx.refresh();
+                                            window.refresh();
                                         }
                                     }
                                     (None, Some(_)) => {
-                                        cx.refresh();
+                                        window.refresh();
                                     }
                                     (Some(_), None) => {
-                                        cx.refresh();
+                                        window.refresh();
                                     }
                                     (None, None) => {}
                                 }

crates/ui/src/components/indicator.rs 🔗

@@ -55,7 +55,7 @@ impl Indicator {
 }
 
 impl RenderOnce for Indicator {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let container = div().flex_none();
         let container = if let Some(border_color) = self.border_color {
             if matches!(self.kind, IndicatorKind::Dot | IndicatorKind::Bar) {
@@ -89,7 +89,7 @@ impl ComponentPreview for Indicator {
         "An indicator visually represents a status or state."
     }
 
-    fn examples(_: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
+    fn examples(_window: &mut Window, _: &mut App) -> Vec<ComponentExampleGroup<Self>> {
         vec![
             example_group_with_title(
                 "Types",

crates/ui/src/components/keybinding.rs 🔗

@@ -1,7 +1,7 @@
 #![allow(missing_docs)]
 use crate::PlatformStyle;
 use crate::{h_flex, prelude::*, Icon, IconName, IconSize};
-use gpui::{relative, Action, FocusHandle, IntoElement, Keystroke, WindowContext};
+use gpui::{relative, Action, App, FocusHandle, IntoElement, Keystroke, Window};
 
 #[derive(Debug, IntoElement, Clone)]
 pub struct KeyBinding {
@@ -18,8 +18,12 @@ pub struct KeyBinding {
 impl KeyBinding {
     /// Returns the highest precedence keybinding for an action. This is the last binding added to
     /// the keymap. User bindings are added after built-in bindings so that they take precedence.
-    pub fn for_action(action: &dyn Action, cx: &mut WindowContext) -> Option<Self> {
-        let key_binding = cx.bindings_for_action(action).into_iter().rev().next()?;
+    pub fn for_action(action: &dyn Action, window: &mut Window) -> Option<Self> {
+        let key_binding = window
+            .bindings_for_action(action)
+            .into_iter()
+            .rev()
+            .next()?;
         Some(Self::new(key_binding))
     }
 
@@ -27,9 +31,9 @@ impl KeyBinding {
     pub fn for_action_in(
         action: &dyn Action,
         focus: &FocusHandle,
-        cx: &mut WindowContext,
+        window: &mut Window,
     ) -> Option<Self> {
-        let key_binding = cx
+        let key_binding = window
             .bindings_for_action_in(action, focus)
             .into_iter()
             .rev()
@@ -76,7 +80,7 @@ impl KeyBinding {
 }
 
 impl RenderOnce for KeyBinding {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         h_flex()
             .debug_selector(|| {
                 format!(
@@ -152,7 +156,7 @@ pub struct Key {
 }
 
 impl RenderOnce for Key {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let single_char = self.key.len() == 1;
 
         div()
@@ -187,7 +191,7 @@ pub struct KeyIcon {
 }
 
 impl RenderOnce for KeyIcon {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         Icon::new(self.icon)
             .size(IconSize::XSmall)
             .color(Color::Muted)
@@ -201,8 +205,8 @@ impl KeyIcon {
 }
 
 /// Returns a textual representation of the key binding for the given [`Action`].
-pub fn text_for_action(action: &dyn Action, cx: &WindowContext) -> Option<String> {
-    let bindings = cx.bindings_for_action(action);
+pub fn text_for_action(action: &dyn Action, window: &Window) -> Option<String> {
+    let bindings = window.bindings_for_action(action);
     let key_binding = bindings.last()?;
     Some(text_for_key_binding(key_binding, PlatformStyle::platform()))
 }
@@ -212,9 +216,9 @@ pub fn text_for_action(action: &dyn Action, cx: &WindowContext) -> Option<String
 pub fn text_for_action_in(
     action: &dyn Action,
     focus: &FocusHandle,
-    cx: &mut WindowContext,
+    window: &mut Window,
 ) -> Option<String> {
-    let bindings = cx.bindings_for_action_in(action, focus);
+    let bindings = window.bindings_for_action_in(action, focus);
     let key_binding = bindings.last()?;
     Some(text_for_key_binding(key_binding, PlatformStyle::platform()))
 }

crates/ui/src/components/label/highlighted_label.rs 🔗

@@ -107,7 +107,7 @@ pub fn highlight_ranges(
 }
 
 impl RenderOnce for HighlightedLabel {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
         let highlight_color = cx.theme().colors().text_accent;
 
         let highlights = highlight_ranges(
@@ -119,7 +119,7 @@ impl RenderOnce for HighlightedLabel {
             },
         );
 
-        let mut text_style = cx.text_style();
+        let mut text_style = window.text_style();
         text_style.color = self.base.color.color(cx);
 
         self.base

crates/ui/src/components/label/label.rs 🔗

@@ -1,6 +1,6 @@
 #![allow(missing_docs)]
 
-use gpui::{StyleRefinement, WindowContext};
+use gpui::{App, StyleRefinement, Window};
 
 use crate::{prelude::*, LabelCommon, LabelLike, LabelSize, LineHeightStyle};
 
@@ -175,7 +175,7 @@ impl LabelCommon for Label {
 }
 
 impl RenderOnce for Label {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         self.base.child(self.label)
     }
 }

crates/ui/src/components/label/label_like.rs 🔗

@@ -168,9 +168,7 @@ impl ParentElement for LabelLike {
 }
 
 impl RenderOnce for LabelLike {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
-        let settings = ThemeSettings::get_global(cx);
-
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let mut color = self.color.color(cx);
         if let Some(alpha) = self.alpha {
             color.fade_out(1.0 - alpha);
@@ -203,7 +201,10 @@ impl RenderOnce for LabelLike {
                 this.overflow_x_hidden().text_ellipsis()
             })
             .text_color(color)
-            .font_weight(self.weight.unwrap_or(settings.ui_font.weight))
+            .font_weight(
+                self.weight
+                    .unwrap_or(ThemeSettings::get_global(cx).ui_font.weight),
+            )
             .children(self.children)
     }
 }

crates/ui/src/components/list/list.rs 🔗

@@ -77,7 +77,7 @@ impl From<AnyElement> for EmptyMessage {
 }
 
 impl RenderOnce for List {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         v_flex()
             .w_full()
             .py(DynamicSpacing::Base04.rems(cx))

crates/ui/src/components/list/list_header.rs 🔗

@@ -20,7 +20,7 @@ pub struct ListHeader {
     /// It will obscure the `end_slot` when visible.
     end_hover_slot: Option<AnyElement>,
     toggle: Option<bool>,
-    on_toggle: Option<Arc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
+    on_toggle: Option<Arc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
     inset: bool,
     selected: bool,
 }
@@ -46,7 +46,7 @@ impl ListHeader {
 
     pub fn on_toggle(
         mut self,
-        on_toggle: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
+        on_toggle: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.on_toggle = Some(Arc::new(on_toggle));
         self
@@ -81,7 +81,7 @@ impl Toggleable for ListHeader {
 }
 
 impl RenderOnce for ListHeader {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let ui_density = ThemeSettings::get_global(cx).ui_density;
 
         h_flex()
@@ -120,7 +120,9 @@ impl RenderOnce for ListHeader {
                                     .children(self.start_slot)
                                     .child(Label::new(self.label.clone()).color(Color::Muted))
                                     .when_some(self.on_toggle, |this, on_toggle| {
-                                        this.on_click(move |event, cx| on_toggle(event, cx))
+                                        this.on_click(move |event, window, cx| {
+                                            on_toggle(event, window, cx)
+                                        })
                                     }),
                             ),
                     )

crates/ui/src/components/list/list_item.rs 🔗

@@ -33,10 +33,10 @@ pub struct ListItem {
     end_hover_slot: Option<AnyElement>,
     toggle: Option<bool>,
     inset: bool,
-    on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
-    on_toggle: Option<Arc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
-    tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>>,
-    on_secondary_mouse_down: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
+    on_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
+    on_toggle: Option<Arc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
+    tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView + 'static>>,
+    on_secondary_mouse_down: Option<Box<dyn Fn(&MouseDownEvent, &mut Window, &mut App) + 'static>>,
     children: SmallVec<[AnyElement; 2]>,
     selectable: bool,
     outlined: bool,
@@ -80,20 +80,23 @@ impl ListItem {
         self
     }
 
-    pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
+    pub fn on_click(
+        mut self,
+        handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
+    ) -> Self {
         self.on_click = Some(Box::new(handler));
         self
     }
 
     pub fn on_secondary_mouse_down(
         mut self,
-        handler: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
+        handler: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.on_secondary_mouse_down = Some(Box::new(handler));
         self
     }
 
-    pub fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
+    pub fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self {
         self.tooltip = Some(Box::new(tooltip));
         self
     }
@@ -120,7 +123,7 @@ impl ListItem {
 
     pub fn on_toggle(
         mut self,
-        on_toggle: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
+        on_toggle: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.on_toggle = Some(Arc::new(on_toggle));
         self
@@ -178,7 +181,7 @@ impl ParentElement for ListItem {
 }
 
 impl RenderOnce for ListItem {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         h_flex()
             .id(self.id)
             .w_full()
@@ -256,8 +259,8 @@ impl RenderOnce for ListItem {
                             .overflow_hidden()
                     })
                     .when_some(self.on_secondary_mouse_down, |this, on_mouse_down| {
-                        this.on_mouse_down(MouseButton::Right, move |event, cx| {
-                            (on_mouse_down)(event, cx)
+                        this.on_mouse_down(MouseButton::Right, move |event, window, cx| {
+                            (on_mouse_down)(event, window, cx)
                         })
                     })
                     .when_some(self.tooltip, |this, tooltip| this.tooltip(tooltip))

crates/ui/src/components/list/list_separator.rs 🔗

@@ -6,7 +6,7 @@ use crate::prelude::*;
 pub struct ListSeparator;
 
 impl RenderOnce for ListSeparator {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         div()
             .h_px()
             .w_full()

crates/ui/src/components/list/list_sub_header.rs 🔗

@@ -40,7 +40,7 @@ impl Toggleable for ListSubHeader {
 }
 
 impl RenderOnce for ListSubHeader {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         h_flex()
             .flex_1()
             .w_full()

crates/ui/src/components/modal.rs 🔗

@@ -66,7 +66,7 @@ impl ParentElement for Modal {
 }
 
 impl RenderOnce for Modal {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         v_flex()
             .id(self.id.clone())
             .size_full()
@@ -143,7 +143,7 @@ impl ParentElement for ModalHeader {
 }
 
 impl RenderOnce for ModalHeader {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let mut children = self.children;
 
         if self.headline.is_some() {
@@ -168,8 +168,8 @@ impl RenderOnce for ModalHeader {
                 this.child(
                     IconButton::new("back", IconName::ArrowLeft)
                         .shape(IconButtonShape::Square)
-                        .on_click(|_, cx| {
-                            cx.dispatch_action(menu::Cancel.boxed_clone());
+                        .on_click(|_, window, cx| {
+                            window.dispatch_action(menu::Cancel.boxed_clone(), cx);
                         }),
                 )
             })
@@ -178,8 +178,8 @@ impl RenderOnce for ModalHeader {
                 this.child(
                     IconButton::new("dismiss", IconName::Close)
                         .shape(IconButtonShape::Square)
-                        .on_click(|_, cx| {
-                            cx.dispatch_action(menu::Cancel.boxed_clone());
+                        .on_click(|_, window, cx| {
+                            window.dispatch_action(menu::Cancel.boxed_clone(), cx);
                         }),
                 )
             })
@@ -212,7 +212,7 @@ impl ParentElement for ModalRow {
 }
 
 impl RenderOnce for ModalRow {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         h_flex().w_full().py_1().children(self.children)
     }
 }
@@ -249,7 +249,7 @@ impl ModalFooter {
 }
 
 impl RenderOnce for ModalFooter {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         h_flex()
             .flex_none()
             .w_full()
@@ -323,7 +323,7 @@ impl ParentElement for Section {
 }
 
 impl RenderOnce for Section {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let mut section_bg = cx.theme().colors().text;
         section_bg.fade_out(0.96);
 
@@ -395,7 +395,7 @@ impl SectionHeader {
 }
 
 impl RenderOnce for SectionHeader {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         h_flex()
             .id(self.label.clone())
             .w_full()

crates/ui/src/components/navigable.rs 🔗

@@ -18,14 +18,14 @@ pub struct NavigableEntry {
 
 impl NavigableEntry {
     /// Creates a new [NavigableEntry] for a given scroll handle.
-    pub fn new(scroll_handle: &ScrollHandle, cx: &WindowContext) -> Self {
+    pub fn new(scroll_handle: &ScrollHandle, cx: &mut App) -> Self {
         Self {
             focus_handle: cx.focus_handle(),
             scroll_anchor: Some(ScrollAnchor::for_handle(scroll_handle.clone())),
         }
     }
     /// Create a new [NavigableEntry] that cannot be scrolled to.
-    pub fn focusable(cx: &WindowContext) -> Self {
+    pub fn focusable(cx: &mut App) -> Self {
         Self {
             focus_handle: cx.focus_handle(),
             scroll_anchor: None,
@@ -51,43 +51,44 @@ impl Navigable {
 
     fn find_focused(
         selectable_children: &[NavigableEntry],
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<usize> {
         selectable_children
             .iter()
-            .position(|entry| entry.focus_handle.contains_focused(cx))
+            .position(|entry| entry.focus_handle.contains_focused(window, cx))
     }
 }
 impl RenderOnce for Navigable {
-    fn render(self, _: &mut WindowContext) -> impl crate::IntoElement {
+    fn render(self, _window: &mut Window, _: &mut App) -> impl crate::IntoElement {
         div()
             .on_action({
                 let children = self.selectable_children.clone();
 
-                move |_: &menu::SelectNext, cx| {
-                    let target = Self::find_focused(&children, cx)
+                move |_: &menu::SelectNext, window, cx| {
+                    let target = Self::find_focused(&children, window, cx)
                         .and_then(|index| {
                             index.checked_add(1).filter(|index| *index < children.len())
                         })
                         .unwrap_or(0);
                     if let Some(entry) = children.get(target) {
-                        entry.focus_handle.focus(cx);
+                        entry.focus_handle.focus(window);
                         if let Some(anchor) = &entry.scroll_anchor {
-                            anchor.scroll_to(cx);
+                            anchor.scroll_to(window, cx);
                         }
                     }
                 }
             })
             .on_action({
                 let children = self.selectable_children;
-                move |_: &menu::SelectPrev, cx| {
-                    let target = Self::find_focused(&children, cx)
+                move |_: &menu::SelectPrev, window, cx| {
+                    let target = Self::find_focused(&children, window, cx)
                         .and_then(|index| index.checked_sub(1))
                         .or(children.len().checked_sub(1));
                     if let Some(entry) = target.and_then(|target| children.get(target)) {
-                        entry.focus_handle.focus(cx);
+                        entry.focus_handle.focus(window);
                         if let Some(anchor) = &entry.scroll_anchor {
-                            anchor.scroll_to(cx);
+                            anchor.scroll_to(window, cx);
                         }
                     }
                 }

crates/ui/src/components/numeric_stepper.rs 🔗

@@ -8,19 +8,19 @@ use crate::{prelude::*, IconButtonShape};
 pub struct NumericStepper {
     id: ElementId,
     value: SharedString,
-    on_decrement: Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>,
-    on_increment: Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>,
+    on_decrement: Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>,
+    on_increment: Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>,
     /// Whether to reserve space for the reset button.
     reserve_space_for_reset: bool,
-    on_reset: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
+    on_reset: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
 }
 
 impl NumericStepper {
     pub fn new(
         id: impl Into<ElementId>,
         value: impl Into<SharedString>,
-        on_decrement: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
-        on_increment: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
+        on_decrement: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
+        on_increment: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         Self {
             id: id.into(),
@@ -39,7 +39,7 @@ impl NumericStepper {
 
     pub fn on_reset(
         mut self,
-        on_reset: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
+        on_reset: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.on_reset = Some(Box::new(on_reset));
         self
@@ -47,7 +47,7 @@ impl NumericStepper {
 }
 
 impl RenderOnce for NumericStepper {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
         let shape = IconButtonShape::Square;
         let icon_size = IconSize::Small;
 
@@ -65,7 +65,7 @@ impl RenderOnce for NumericStepper {
                 } else if self.reserve_space_for_reset {
                     element.child(
                         h_flex()
-                            .size(icon_size.square(cx))
+                            .size(icon_size.square(window, cx))
                             .flex_none()
                             .into_any_element(),
                     )

crates/ui/src/components/popover.rs 🔗

@@ -3,7 +3,7 @@
 use crate::prelude::*;
 use crate::v_flex;
 use gpui::{
-    div, AnyElement, Element, IntoElement, ParentElement, Pixels, RenderOnce, Styled, WindowContext,
+    div, AnyElement, App, Element, IntoElement, ParentElement, Pixels, RenderOnce, Styled, Window,
 };
 use smallvec::SmallVec;
 
@@ -44,7 +44,7 @@ pub struct Popover {
 }
 
 impl RenderOnce for Popover {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
         div()
             .flex()
             .gap_1()

crates/ui/src/components/popover_menu.rs 🔗

@@ -3,10 +3,10 @@
 use std::{cell::RefCell, rc::Rc};
 
 use gpui::{
-    anchored, deferred, div, point, prelude::FluentBuilder, px, size, AnyElement, Bounds, Corner,
-    DismissEvent, DispatchPhase, Element, ElementId, GlobalElementId, HitboxId, InteractiveElement,
-    IntoElement, LayoutId, Length, ManagedView, MouseDownEvent, ParentElement, Pixels, Point,
-    Style, View, VisualContext, WindowContext,
+    anchored, deferred, div, point, prelude::FluentBuilder, px, size, AnyElement, App, Bounds,
+    Corner, DismissEvent, DispatchPhase, Element, ElementId, Entity, Focusable as _,
+    GlobalElementId, HitboxId, InteractiveElement, IntoElement, LayoutId, Length, ManagedView,
+    MouseDownEvent, ParentElement, Pixels, Point, Style, Window,
 };
 
 use crate::prelude::*;
@@ -19,7 +19,10 @@ impl<T: Clickable> Clickable for gpui::AnimationElement<T>
 where
     T: Clickable + 'static,
 {
-    fn on_click(self, handler: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static) -> Self {
+    fn on_click(
+        self,
+        handler: impl Fn(&gpui::ClickEvent, &mut Window, &mut App) + 'static,
+    ) -> Self {
         self.map_element(|e| e.on_click(handler))
     }
 
@@ -52,18 +55,18 @@ impl<M> Default for PopoverMenuHandle<M> {
 }
 
 struct PopoverMenuHandleState<M> {
-    menu_builder: Rc<dyn Fn(&mut WindowContext) -> Option<View<M>>>,
-    menu: Rc<RefCell<Option<View<M>>>>,
+    menu_builder: Rc<dyn Fn(&mut Window, &mut App) -> Option<Entity<M>>>,
+    menu: Rc<RefCell<Option<Entity<M>>>>,
 }
 
 impl<M: ManagedView> PopoverMenuHandle<M> {
-    pub fn show(&self, cx: &mut WindowContext) {
+    pub fn show(&self, window: &mut Window, cx: &mut App) {
         if let Some(state) = self.0.borrow().as_ref() {
-            show_menu(&state.menu_builder, &state.menu, cx);
+            show_menu(&state.menu_builder, &state.menu, window, cx);
         }
     }
 
-    pub fn hide(&self, cx: &mut WindowContext) {
+    pub fn hide(&self, cx: &mut App) {
         if let Some(state) = self.0.borrow().as_ref() {
             if let Some(menu) = state.menu.borrow().as_ref() {
                 menu.update(cx, |_, cx| cx.emit(DismissEvent));
@@ -71,12 +74,12 @@ impl<M: ManagedView> PopoverMenuHandle<M> {
         }
     }
 
-    pub fn toggle(&self, cx: &mut WindowContext) {
+    pub fn toggle(&self, window: &mut Window, cx: &mut App) {
         if let Some(state) = self.0.borrow().as_ref() {
             if state.menu.borrow().is_some() {
                 self.hide(cx);
             } else {
-                self.show(cx);
+                self.show(window, cx);
             }
         }
     }
@@ -88,13 +91,13 @@ impl<M: ManagedView> PopoverMenuHandle<M> {
             .map_or(false, |state| state.menu.borrow().as_ref().is_some())
     }
 
-    pub fn is_focused(&self, cx: &WindowContext) -> bool {
+    pub fn is_focused(&self, window: &Window, cx: &App) -> bool {
         self.0.borrow().as_ref().map_or(false, |state| {
             state
                 .menu
                 .borrow()
                 .as_ref()
-                .map_or(false, |view| view.focus_handle(cx).is_focused(cx))
+                .map_or(false, |model| model.focus_handle(cx).is_focused(window))
         })
     }
 }
@@ -104,13 +107,13 @@ pub struct PopoverMenu<M: ManagedView> {
     child_builder: Option<
         Box<
             dyn FnOnce(
-                    Rc<RefCell<Option<View<M>>>>,
-                    Option<Rc<dyn Fn(&mut WindowContext) -> Option<View<M>> + 'static>>,
+                    Rc<RefCell<Option<Entity<M>>>>,
+                    Option<Rc<dyn Fn(&mut Window, &mut App) -> Option<Entity<M>> + 'static>>,
                 ) -> AnyElement
                 + 'static,
         >,
     >,
-    menu_builder: Option<Rc<dyn Fn(&mut WindowContext) -> Option<View<M>> + 'static>>,
+    menu_builder: Option<Rc<dyn Fn(&mut Window, &mut App) -> Option<Entity<M>> + 'static>>,
     anchor: Corner,
     attach: Option<Corner>,
     offset: Option<Point<Pixels>>,
@@ -138,7 +141,10 @@ impl<M: ManagedView> PopoverMenu<M> {
         self
     }
 
-    pub fn menu(mut self, f: impl Fn(&mut WindowContext) -> Option<View<M>> + 'static) -> Self {
+    pub fn menu(
+        mut self,
+        f: impl Fn(&mut Window, &mut App) -> Option<Entity<M>> + 'static,
+    ) -> Self {
         self.menu_builder = Some(Rc::new(f));
         self
     }
@@ -153,7 +159,7 @@ impl<M: ManagedView> PopoverMenu<M> {
             let open = menu.borrow().is_some();
             t.toggle_state(open)
                 .when_some(builder, |el, builder| {
-                    el.on_click(move |_, cx| show_menu(&builder, &menu, cx))
+                    el.on_click(move |_event, window, cx| show_menu(&builder, &menu, window, cx))
                 })
                 .into_any_element()
         }));
@@ -188,10 +194,10 @@ impl<M: ManagedView> PopoverMenu<M> {
         })
     }
 
-    fn resolved_offset(&self, cx: &WindowContext) -> Point<Pixels> {
+    fn resolved_offset(&self, window: &mut Window) -> Point<Pixels> {
         self.offset.unwrap_or_else(|| {
             // Default offset = 4px padding + 1px border
-            let offset = rems_from_px(5.) * cx.rem_size();
+            let offset = rems_from_px(5.) * window.rem_size();
             match self.anchor {
                 Corner::TopRight | Corner::BottomRight => point(offset, px(0.)),
                 Corner::TopLeft | Corner::BottomLeft => point(-offset, px(0.)),
@@ -201,33 +207,35 @@ impl<M: ManagedView> PopoverMenu<M> {
 }
 
 fn show_menu<M: ManagedView>(
-    builder: &Rc<dyn Fn(&mut WindowContext) -> Option<View<M>>>,
-    menu: &Rc<RefCell<Option<View<M>>>>,
-    cx: &mut WindowContext,
+    builder: &Rc<dyn Fn(&mut Window, &mut App) -> Option<Entity<M>>>,
+    menu: &Rc<RefCell<Option<Entity<M>>>>,
+    window: &mut Window,
+    cx: &mut App,
 ) {
-    let Some(new_menu) = (builder)(cx) else {
+    let Some(new_menu) = (builder)(window, cx) else {
         return;
     };
     let menu2 = menu.clone();
-    let previous_focus_handle = cx.focused();
+    let previous_focus_handle = window.focused(cx);
 
-    cx.subscribe(&new_menu, move |modal, _: &DismissEvent, cx| {
-        if modal.focus_handle(cx).contains_focused(cx) {
-            if let Some(previous_focus_handle) = previous_focus_handle.as_ref() {
-                cx.focus(previous_focus_handle);
+    window
+        .subscribe(&new_menu, cx, move |modal, _: &DismissEvent, window, cx| {
+            if modal.focus_handle(cx).contains_focused(window, cx) {
+                if let Some(previous_focus_handle) = previous_focus_handle.as_ref() {
+                    window.focus(previous_focus_handle);
+                }
             }
-        }
-        *menu2.borrow_mut() = None;
-        cx.refresh();
-    })
-    .detach();
-    cx.focus_view(&new_menu);
+            *menu2.borrow_mut() = None;
+            window.refresh();
+        })
+        .detach();
+    window.focus(&new_menu.focus_handle(cx));
     *menu.borrow_mut() = Some(new_menu);
-    cx.refresh();
+    window.refresh();
 }
 
 pub struct PopoverMenuElementState<M> {
-    menu: Rc<RefCell<Option<View<M>>>>,
+    menu: Rc<RefCell<Option<Entity<M>>>>,
     child_bounds: Option<Bounds<Pixels>>,
 }
 
@@ -253,7 +261,7 @@ pub struct PopoverMenuFrameState<M: ManagedView> {
     child_layout_id: Option<LayoutId>,
     child_element: Option<AnyElement>,
     menu_element: Option<AnyElement>,
-    menu_handle: Rc<RefCell<Option<View<M>>>>,
+    menu_handle: Rc<RefCell<Option<Entity<M>>>>,
 }
 
 impl<M: ManagedView> Element for PopoverMenu<M> {
@@ -267,16 +275,17 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
     fn request_layout(
         &mut self,
         global_id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (gpui::LayoutId, Self::RequestLayoutState) {
-        cx.with_element_state(
+        window.with_element_state(
             global_id.unwrap(),
-            |element_state: Option<PopoverMenuElementState<M>>, cx| {
+            |element_state: Option<PopoverMenuElementState<M>>, window| {
                 let element_state = element_state.unwrap_or_default();
                 let mut menu_layout_id = None;
 
                 let menu_element = element_state.menu.borrow_mut().as_mut().map(|menu| {
-                    let offset = self.resolved_offset(cx);
+                    let offset = self.resolved_offset(window);
                     let mut anchored = anchored()
                         .snap_to_window_with_margin(px(8.))
                         .anchor(self.anchor)
@@ -289,7 +298,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
                         .with_priority(1)
                         .into_any();
 
-                    menu_layout_id = Some(element.request_layout(cx));
+                    menu_layout_id = Some(element.request_layout(window, cx));
                     element
                 });
 
@@ -308,15 +317,18 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
 
                 let child_layout_id = child_element
                     .as_mut()
-                    .map(|child_element| child_element.request_layout(cx));
+                    .map(|child_element| child_element.request_layout(window, cx));
 
                 let mut style = Style::default();
                 if self.full_width {
                     style.size = size(relative(1.).into(), Length::Auto);
                 }
 
-                let layout_id =
-                    cx.request_layout(style, menu_layout_id.into_iter().chain(child_layout_id));
+                let layout_id = window.request_layout(
+                    style,
+                    menu_layout_id.into_iter().chain(child_layout_id),
+                    cx,
+                );
 
                 (
                     (
@@ -339,25 +351,26 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
         global_id: Option<&GlobalElementId>,
         _bounds: Bounds<Pixels>,
         request_layout: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<HitboxId> {
         if let Some(child) = request_layout.child_element.as_mut() {
-            child.prepaint(cx);
+            child.prepaint(window, cx);
         }
 
         if let Some(menu) = request_layout.menu_element.as_mut() {
-            menu.prepaint(cx);
+            menu.prepaint(window, cx);
         }
 
         request_layout.child_layout_id.map(|layout_id| {
-            let bounds = cx.layout_bounds(layout_id);
-            cx.with_element_state(global_id.unwrap(), |element_state, _cx| {
+            let bounds = window.layout_bounds(layout_id);
+            window.with_element_state(global_id.unwrap(), |element_state, _cx| {
                 let mut element_state: PopoverMenuElementState<M> = element_state.unwrap();
                 element_state.child_bounds = Some(bounds);
                 ((), element_state)
             });
 
-            cx.insert_hitbox(bounds, false).id
+            window.insert_hitbox(bounds, false).id
         })
     }
 
@@ -367,21 +380,22 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
         _: Bounds<gpui::Pixels>,
         request_layout: &mut Self::RequestLayoutState,
         child_hitbox: &mut Option<HitboxId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         if let Some(mut child) = request_layout.child_element.take() {
-            child.paint(cx);
+            child.paint(window, cx);
         }
 
         if let Some(mut menu) = request_layout.menu_element.take() {
-            menu.paint(cx);
+            menu.paint(window, cx);
 
             if let Some(child_hitbox) = *child_hitbox {
                 let menu_handle = request_layout.menu_handle.clone();
                 // Mouse-downing outside the menu dismisses it, so we don't
                 // want a click on the toggle to re-open it.
-                cx.on_mouse_event(move |_: &MouseDownEvent, phase, cx| {
-                    if phase == DispatchPhase::Bubble && child_hitbox.is_hovered(cx) {
+                window.on_mouse_event(move |_: &MouseDownEvent, phase, window, cx| {
+                    if phase == DispatchPhase::Bubble && child_hitbox.is_hovered(window) {
                         if let Some(menu) = menu_handle.borrow().as_ref() {
                             menu.update(cx, |_, cx| {
                                 cx.emit(DismissEvent);

crates/ui/src/components/radio.rs 🔗

@@ -10,7 +10,7 @@ pub struct RadioWithLabel {
     id: ElementId,
     label: Label,
     selected: bool,
-    on_click: Arc<dyn Fn(&bool, &mut WindowContext) + 'static>,
+    on_click: Arc<dyn Fn(&bool, &mut Window, &mut App) + 'static>,
 }
 
 impl RadioWithLabel {
@@ -18,7 +18,7 @@ impl RadioWithLabel {
         id: impl Into<ElementId>,
         label: Label,
         selected: bool,
-        on_click: impl Fn(&bool, &mut WindowContext) + 'static,
+        on_click: impl Fn(&bool, &mut Window, &mut App) + 'static,
     ) -> Self {
         Self {
             id: id.into(),
@@ -30,7 +30,7 @@ impl RadioWithLabel {
 }
 
 impl RenderOnce for RadioWithLabel {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let inner_diameter = rems_from_px(6.);
         let outer_diameter = rems_from_px(16.);
         let border_width = rems_from_px(1.);
@@ -56,8 +56,8 @@ impl RenderOnce for RadioWithLabel {
                     }),
             )
             .child(self.label)
-            .on_click(move |_event, cx| {
-                (self.on_click)(&true, cx);
+            .on_click(move |_event, window, cx| {
+                (self.on_click)(&true, window, cx);
             })
     }
 }

crates/ui/src/components/right_click_menu.rs 🔗

@@ -3,21 +3,22 @@
 use std::{cell::RefCell, rc::Rc};
 
 use gpui::{
-    anchored, deferred, div, px, AnyElement, Bounds, Corner, DismissEvent, DispatchPhase, Element,
-    ElementId, GlobalElementId, Hitbox, InteractiveElement, IntoElement, LayoutId, ManagedView,
-    MouseButton, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext, WindowContext,
+    anchored, deferred, div, px, AnyElement, App, Bounds, Corner, DismissEvent, DispatchPhase,
+    Element, ElementId, Entity, Focusable as _, GlobalElementId, Hitbox, InteractiveElement,
+    IntoElement, LayoutId, ManagedView, MouseButton, MouseDownEvent, ParentElement, Pixels, Point,
+    Window,
 };
 
 pub struct RightClickMenu<M: ManagedView> {
     id: ElementId,
     child_builder: Option<Box<dyn FnOnce(bool) -> AnyElement + 'static>>,
-    menu_builder: Option<Rc<dyn Fn(&mut WindowContext) -> View<M> + 'static>>,
+    menu_builder: Option<Rc<dyn Fn(&mut Window, &mut App) -> Entity<M> + 'static>>,
     anchor: Option<Corner>,
     attach: Option<Corner>,
 }
 
 impl<M: ManagedView> RightClickMenu<M> {
-    pub fn menu(mut self, f: impl Fn(&mut WindowContext) -> View<M> + 'static) -> Self {
+    pub fn menu(mut self, f: impl Fn(&mut Window, &mut App) -> Entity<M> + 'static) -> Self {
         self.menu_builder = Some(Rc::new(f));
         self
     }
@@ -43,14 +44,15 @@ impl<M: ManagedView> RightClickMenu<M> {
     fn with_element_state<R>(
         &mut self,
         global_id: &GlobalElementId,
-        cx: &mut WindowContext,
-        f: impl FnOnce(&mut Self, &mut MenuHandleElementState<M>, &mut WindowContext) -> R,
+        window: &mut Window,
+        cx: &mut App,
+        f: impl FnOnce(&mut Self, &mut MenuHandleElementState<M>, &mut Window, &mut App) -> R,
     ) -> R {
-        cx.with_optional_element_state::<MenuHandleElementState<M>, _>(
+        window.with_optional_element_state::<MenuHandleElementState<M>, _>(
             Some(global_id),
-            |element_state, cx| {
+            |element_state, window| {
                 let mut element_state = element_state.unwrap().unwrap_or_default();
-                let result = f(self, &mut element_state, cx);
+                let result = f(self, &mut element_state, window, cx);
                 (result, Some(element_state))
             },
         )
@@ -69,7 +71,7 @@ pub fn right_click_menu<M: ManagedView>(id: impl Into<ElementId>) -> RightClickM
 }
 
 pub struct MenuHandleElementState<M> {
-    menu: Rc<RefCell<Option<View<M>>>>,
+    menu: Rc<RefCell<Option<Entity<M>>>>,
     position: Rc<RefCell<Point<Pixels>>>,
 }
 
@@ -113,49 +115,56 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
     fn request_layout(
         &mut self,
         id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (gpui::LayoutId, Self::RequestLayoutState) {
-        self.with_element_state(id.unwrap(), cx, |this, element_state, cx| {
-            let mut menu_layout_id = None;
-
-            let menu_element = element_state.menu.borrow_mut().as_mut().map(|menu| {
-                let mut anchored = anchored().snap_to_window_with_margin(px(8.));
-                if let Some(anchor) = this.anchor {
-                    anchored = anchored.anchor(anchor);
-                }
-                anchored = anchored.position(*element_state.position.borrow());
-
-                let mut element = deferred(anchored.child(div().occlude().child(menu.clone())))
-                    .with_priority(1)
-                    .into_any();
-
-                menu_layout_id = Some(element.request_layout(cx));
-                element
-            });
-
-            let mut child_element = this
-                .child_builder
-                .take()
-                .map(|child_builder| (child_builder)(element_state.menu.borrow().is_some()));
-
-            let child_layout_id = child_element
-                .as_mut()
-                .map(|child_element| child_element.request_layout(cx));
-
-            let layout_id = cx.request_layout(
-                gpui::Style::default(),
-                menu_layout_id.into_iter().chain(child_layout_id),
-            );
-
-            (
-                layout_id,
-                RequestLayoutState {
-                    child_element,
-                    child_layout_id,
-                    menu_element,
-                },
-            )
-        })
+        self.with_element_state(
+            id.unwrap(),
+            window,
+            cx,
+            |this, element_state, window, cx| {
+                let mut menu_layout_id = None;
+
+                let menu_element = element_state.menu.borrow_mut().as_mut().map(|menu| {
+                    let mut anchored = anchored().snap_to_window_with_margin(px(8.));
+                    if let Some(anchor) = this.anchor {
+                        anchored = anchored.anchor(anchor);
+                    }
+                    anchored = anchored.position(*element_state.position.borrow());
+
+                    let mut element = deferred(anchored.child(div().occlude().child(menu.clone())))
+                        .with_priority(1)
+                        .into_any();
+
+                    menu_layout_id = Some(element.request_layout(window, cx));
+                    element
+                });
+
+                let mut child_element = this
+                    .child_builder
+                    .take()
+                    .map(|child_builder| (child_builder)(element_state.menu.borrow().is_some()));
+
+                let child_layout_id = child_element
+                    .as_mut()
+                    .map(|child_element| child_element.request_layout(window, cx));
+
+                let layout_id = window.request_layout(
+                    gpui::Style::default(),
+                    menu_layout_id.into_iter().chain(child_layout_id),
+                    cx,
+                );
+
+                (
+                    layout_id,
+                    RequestLayoutState {
+                        child_element,
+                        child_layout_id,
+                        menu_element,
+                    },
+                )
+            },
+        )
     }
 
     fn prepaint(
@@ -163,23 +172,24 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
         _id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         request_layout: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> PrepaintState {
-        let hitbox = cx.insert_hitbox(bounds, false);
+        let hitbox = window.insert_hitbox(bounds, false);
 
         if let Some(child) = request_layout.child_element.as_mut() {
-            child.prepaint(cx);
+            child.prepaint(window, cx);
         }
 
         if let Some(menu) = request_layout.menu_element.as_mut() {
-            menu.prepaint(cx);
+            menu.prepaint(window, cx);
         }
 
         PrepaintState {
             hitbox,
             child_bounds: request_layout
                 .child_layout_id
-                .map(|layout_id| cx.layout_bounds(layout_id)),
+                .map(|layout_id| window.layout_bounds(layout_id)),
         }
     }
 
@@ -189,65 +199,74 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
         _bounds: Bounds<gpui::Pixels>,
         request_layout: &mut Self::RequestLayoutState,
         prepaint_state: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        self.with_element_state(id.unwrap(), cx, |this, element_state, cx| {
-            if let Some(mut child) = request_layout.child_element.take() {
-                child.paint(cx);
-            }
-
-            if let Some(mut menu) = request_layout.menu_element.take() {
-                menu.paint(cx);
-                return;
-            }
-
-            let Some(builder) = this.menu_builder.take() else {
-                return;
-            };
-
-            let attach = this.attach;
-            let menu = element_state.menu.clone();
-            let position = element_state.position.clone();
-            let child_bounds = prepaint_state.child_bounds;
-
-            let hitbox_id = prepaint_state.hitbox.id;
-            cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
-                if phase == DispatchPhase::Bubble
-                    && event.button == MouseButton::Right
-                    && hitbox_id.is_hovered(cx)
-                {
-                    cx.stop_propagation();
-                    cx.prevent_default();
-
-                    let new_menu = (builder)(cx);
-                    let menu2 = menu.clone();
-                    let previous_focus_handle = cx.focused();
-
-                    cx.subscribe(&new_menu, move |modal, _: &DismissEvent, cx| {
-                        if modal.focus_handle(cx).contains_focused(cx) {
-                            if let Some(previous_focus_handle) = previous_focus_handle.as_ref() {
-                                cx.focus(previous_focus_handle);
+        self.with_element_state(
+            id.unwrap(),
+            window,
+            cx,
+            |this, element_state, window, cx| {
+                if let Some(mut child) = request_layout.child_element.take() {
+                    child.paint(window, cx);
+                }
+
+                if let Some(mut menu) = request_layout.menu_element.take() {
+                    menu.paint(window, cx);
+                    return;
+                }
+
+                let Some(builder) = this.menu_builder.take() else {
+                    return;
+                };
+
+                let attach = this.attach;
+                let menu = element_state.menu.clone();
+                let position = element_state.position.clone();
+                let child_bounds = prepaint_state.child_bounds;
+
+                let hitbox_id = prepaint_state.hitbox.id;
+                window.on_mouse_event(move |event: &MouseDownEvent, phase, window, cx| {
+                    if phase == DispatchPhase::Bubble
+                        && event.button == MouseButton::Right
+                        && hitbox_id.is_hovered(window)
+                    {
+                        cx.stop_propagation();
+                        window.prevent_default();
+
+                        let new_menu = (builder)(window, cx);
+                        let menu2 = menu.clone();
+                        let previous_focus_handle = window.focused(cx);
+
+                        window
+                            .subscribe(&new_menu, cx, move |modal, _: &DismissEvent, window, cx| {
+                                if modal.focus_handle(cx).contains_focused(window, cx) {
+                                    if let Some(previous_focus_handle) =
+                                        previous_focus_handle.as_ref()
+                                    {
+                                        window.focus(previous_focus_handle);
+                                    }
+                                }
+                                *menu2.borrow_mut() = None;
+                                window.refresh();
+                            })
+                            .detach();
+                        window.focus(&new_menu.focus_handle(cx));
+                        *menu.borrow_mut() = Some(new_menu);
+                        *position.borrow_mut() = if let Some(child_bounds) = child_bounds {
+                            if let Some(attach) = attach {
+                                child_bounds.corner(attach)
+                            } else {
+                                window.mouse_position()
                             }
-                        }
-                        *menu2.borrow_mut() = None;
-                        cx.refresh();
-                    })
-                    .detach();
-                    cx.focus_view(&new_menu);
-                    *menu.borrow_mut() = Some(new_menu);
-                    *position.borrow_mut() = if let Some(child_bounds) = child_bounds {
-                        if let Some(attach) = attach {
-                            child_bounds.corner(attach)
                         } else {
-                            cx.mouse_position()
-                        }
-                    } else {
-                        cx.mouse_position()
-                    };
-                    cx.refresh();
-                }
-            });
-        })
+                            window.mouse_position()
+                        };
+                        window.refresh();
+                    }
+                });
+            },
+        )
     }
 }
 

crates/ui/src/components/scrollbar.rs 🔗

@@ -3,10 +3,10 @@ use std::{any::Any, cell::Cell, fmt::Debug, ops::Range, rc::Rc, sync::Arc};
 
 use crate::{prelude::*, px, relative, IntoElement};
 use gpui::{
-    point, quad, Along, Axis as ScrollbarAxis, Bounds, ContentMask, Corners, Edges, Element,
+    point, quad, Along, App, Axis as ScrollbarAxis, Bounds, ContentMask, Corners, Edges, Element,
     ElementId, Entity, EntityId, GlobalElementId, Hitbox, Hsla, LayoutId, MouseDownEvent,
     MouseMoveEvent, MouseUpEvent, Pixels, Point, ScrollHandle, ScrollWheelEvent, Size, Style,
-    UniformListScrollHandle, View, WindowContext,
+    UniformListScrollHandle, Window,
 };
 
 pub struct Scrollbar {
@@ -113,8 +113,8 @@ impl ScrollbarState {
         }
     }
 
-    /// Set a parent view which should be notified whenever this Scrollbar gets a scroll event.
-    pub fn parent_view<V: 'static>(mut self, v: &View<V>) -> Self {
+    /// Set a parent model which should be notified whenever this Scrollbar gets a scroll event.
+    pub fn parent_model<V: 'static>(mut self, v: &Entity<V>) -> Self {
         self.parent_id = Some(v.entity_id());
         self
     }
@@ -194,7 +194,8 @@ impl Element for Scrollbar {
     fn request_layout(
         &mut self,
         _id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
         let mut style = Style::default();
         style.flex_grow = 1.;
@@ -208,7 +209,7 @@ impl Element for Scrollbar {
             style.size.height = px(12.).into();
         }
 
-        (cx.request_layout(style, None), ())
+        (window.request_layout(style, None, cx), ())
     }
 
     fn prepaint(
@@ -216,10 +217,11 @@ impl Element for Scrollbar {
         _id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        _: &mut App,
     ) -> Self::PrepaintState {
-        cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
-            cx.insert_hitbox(bounds, false)
+        window.with_content_mask(Some(ContentMask { bounds }), |window| {
+            window.insert_hitbox(bounds, false)
         })
     }
 
@@ -229,9 +231,10 @@ impl Element for Scrollbar {
         bounds: Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
         _prepaint: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
+        window.with_content_mask(Some(ContentMask { bounds }), |window| {
             let colors = cx.theme().colors();
             let thumb_background = colors
                 .surface_background
@@ -282,7 +285,7 @@ impl Element for Scrollbar {
                 thumb_bounds.size.height /= 1.5;
                 Corners::all(thumb_bounds.size.height / 2.0)
             };
-            cx.paint_quad(quad(
+            window.paint_quad(quad(
                 thumb_bounds,
                 corners,
                 thumb_background,
@@ -294,11 +297,11 @@ impl Element for Scrollbar {
             let kind = self.kind;
             let thumb_percentage_size = self.thumb.end - self.thumb.start;
 
-            cx.on_mouse_event({
+            window.on_mouse_event({
                 let scroll = scroll.clone();
                 let state = self.state.clone();
                 let axis = self.kind;
-                move |event: &MouseDownEvent, phase, _cx| {
+                move |event: &MouseDownEvent, phase, _, _| {
                     if !(phase.bubble() && bounds.contains(&event.position)) {
                         return;
                     }
@@ -333,19 +336,20 @@ impl Element for Scrollbar {
                     }
                 }
             });
-            cx.on_mouse_event({
+            window.on_mouse_event({
                 let scroll = scroll.clone();
-                move |event: &ScrollWheelEvent, phase, cx| {
+                move |event: &ScrollWheelEvent, phase, window, _| {
                     if phase.bubble() && bounds.contains(&event.position) {
                         let current_offset = scroll.offset();
-                        scroll
-                            .set_offset(current_offset + event.delta.pixel_delta(cx.line_height()));
+                        scroll.set_offset(
+                            current_offset + event.delta.pixel_delta(window.line_height()),
+                        );
                     }
                 }
             });
             let state = self.state.clone();
             let kind = self.kind;
-            cx.on_mouse_event(move |event: &MouseMoveEvent, _, cx| {
+            window.on_mouse_event(move |event: &MouseMoveEvent, _, _, cx| {
                 if let Some(drag_state) = state.drag.get().filter(|_| event.dragging()) {
                     if let Some(ContentSize {
                         size: item_size, ..
@@ -375,7 +379,7 @@ impl Element for Scrollbar {
                         };
 
                         if let Some(id) = state.parent_id {
-                            cx.notify(Some(id));
+                            cx.notify(id);
                         }
                     }
                 } else {
@@ -383,11 +387,11 @@ impl Element for Scrollbar {
                 }
             });
             let state = self.state.clone();
-            cx.on_mouse_event(move |_event: &MouseUpEvent, phase, cx| {
+            window.on_mouse_event(move |_event: &MouseUpEvent, phase, _, cx| {
                 if phase.bubble() {
                     state.drag.take();
                     if let Some(id) = state.parent_id {
-                        cx.notify(Some(id));
+                        cx.notify(id);
                     }
                 }
             });

crates/ui/src/components/settings_container.rs 🔗

@@ -31,7 +31,7 @@ impl ParentElement for SettingsContainer {
 }
 
 impl RenderOnce for SettingsContainer {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         v_flex().px_2().gap_1().children(self.children)
     }
 }

crates/ui/src/components/settings_group.rs 🔗

@@ -28,7 +28,7 @@ impl ParentElement for SettingsGroup {
 }
 
 impl RenderOnce for SettingsGroup {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         v_flex()
             .p_1()
             .gap_2()

crates/ui/src/components/stories/avatar.rs 🔗

@@ -7,7 +7,7 @@ use crate::{Avatar, AvatarAudioStatusIndicator};
 pub struct AvatarStory;
 
 impl Render for AvatarStory {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title_for::<Avatar>())
             .child(

crates/ui/src/components/stories/button.rs 🔗

@@ -7,7 +7,7 @@ use crate::{Button, ButtonStyle};
 pub struct ButtonStory;
 
 impl Render for ButtonStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title_for::<Button>())
             .child(Story::label("Default"))

crates/ui/src/components/stories/context_menu.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{actions, Corner, Render, View};
+use gpui::{actions, Corner, Entity, Render};
 use story::Story;
 
 use crate::prelude::*;
@@ -6,29 +6,35 @@ use crate::{right_click_menu, ContextMenu, Label};
 
 actions!(context_menu, [PrintCurrentDate, PrintBestFood]);
 
-fn build_menu(cx: &mut WindowContext, header: impl Into<SharedString>) -> View<ContextMenu> {
-    ContextMenu::build(cx, |menu, _| {
+fn build_menu(
+    window: &mut Window,
+    cx: &mut App,
+    header: impl Into<SharedString>,
+) -> Entity<ContextMenu> {
+    ContextMenu::build(window, cx, |menu, _, _| {
         menu.header(header)
             .separator()
             .action("Print current time", Box::new(PrintCurrentDate))
-            .entry("Print best food", Some(Box::new(PrintBestFood)), |cx| {
-                cx.dispatch_action(Box::new(PrintBestFood))
-            })
+            .entry(
+                "Print best food",
+                Some(Box::new(PrintBestFood)),
+                |window, cx| window.dispatch_action(Box::new(PrintBestFood), cx),
+            )
     })
 }
 
 pub struct ContextMenuStory;
 
 impl Render for ContextMenuStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         Story::container()
-            .on_action(|_: &PrintCurrentDate, _| {
+            .on_action(|_: &PrintCurrentDate, _, _| {
                 println!("printing unix time!");
                 if let Ok(unix_time) = std::time::UNIX_EPOCH.elapsed() {
                     println!("Current Unix time is {:?}", unix_time.as_secs());
                 }
             })
-            .on_action(|_: &PrintBestFood, _| {
+            .on_action(|_: &PrintBestFood, _, _| {
                 println!("burrito");
             })
             .flex()
@@ -42,14 +48,14 @@ impl Render for ContextMenuStory {
                     .child(
                         right_click_menu("test2")
                             .trigger(Label::new("TOP LEFT"))
-                            .menu(move |cx| build_menu(cx, "top left")),
+                            .menu(move |window, cx| build_menu(window, cx, "top left")),
                     )
                     .child(
                         right_click_menu("test1")
                             .trigger(Label::new("BOTTOM LEFT"))
                             .anchor(Corner::BottomLeft)
                             .attach(Corner::TopLeft)
-                            .menu(move |cx| build_menu(cx, "bottom left")),
+                            .menu(move |window, cx| build_menu(window, cx, "bottom left")),
                     ),
             )
             .child(
@@ -61,14 +67,14 @@ impl Render for ContextMenuStory {
                         right_click_menu("test3")
                             .trigger(Label::new("TOP RIGHT"))
                             .anchor(Corner::TopRight)
-                            .menu(move |cx| build_menu(cx, "top right")),
+                            .menu(move |window, cx| build_menu(window, cx, "top right")),
                     )
                     .child(
                         right_click_menu("test4")
                             .trigger(Label::new("BOTTOM RIGHT"))
                             .anchor(Corner::BottomRight)
                             .attach(Corner::TopRight)
-                            .menu(move |cx| build_menu(cx, "bottom right")),
+                            .menu(move |window, cx| build_menu(window, cx, "bottom right")),
                     ),
             )
     }

crates/ui/src/components/stories/disclosure.rs 🔗

@@ -7,7 +7,7 @@ use crate::Disclosure;
 pub struct DisclosureStory;
 
 impl Render for DisclosureStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title_for::<Disclosure>())
             .child(Story::label("Toggled"))

crates/ui/src/components/stories/icon.rs 🔗

@@ -8,7 +8,7 @@ use crate::{Icon, IconName};
 pub struct IconStory;
 
 impl Render for IconStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         let icons = IconName::iter();
 
         Story::container()

crates/ui/src/components/stories/icon_button.rs 🔗

@@ -7,7 +7,7 @@ use crate::{IconButton, IconName};
 pub struct IconButtonStory;
 
 impl Render for IconButtonStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         let default_button = StoryItem::new(
             "Default",
             IconButton::new("default_icon_button", IconName::Hash),
@@ -60,9 +60,11 @@ impl Render for IconButtonStory {
 
         let with_on_click_button = StoryItem::new(
             "With `on_click`",
-            IconButton::new("with_on_click_button", IconName::Ai).on_click(|_event, _cx| {
-                println!("Clicked!");
-            }),
+            IconButton::new("with_on_click_button", IconName::Ai).on_click(
+                |_event, _window, _cx| {
+                    println!("Clicked!");
+                },
+            ),
         )
         .description("Displays an icon button which triggers an event on click.")
         .usage(
@@ -76,13 +78,13 @@ impl Render for IconButtonStory {
         let with_tooltip_button = StoryItem::new(
             "With `tooltip`",
             IconButton::new("with_tooltip_button", IconName::MessageBubbles)
-                .tooltip(|cx| Tooltip::text("Open messages", cx)),
+                .tooltip(Tooltip::text("Open messages")),
         )
         .description("Displays an icon button that has a tooltip when hovered.")
         .usage(
             r#"
             IconButton::new("with_tooltip_button", Icon::MessageBubbles)
-                .tooltip(|cx| Tooltip::text("Open messages", cx))
+                .tooltip(Tooltip::text_f("Open messages"))
         "#,
         );
 
@@ -90,14 +92,14 @@ impl Render for IconButtonStory {
             "Selected with `tooltip`",
             IconButton::new("selected_with_tooltip_button", IconName::InlayHint)
                 .toggle_state(true)
-                .tooltip(|cx| Tooltip::text("Toggle inlay hints", cx)),
+                .tooltip(Tooltip::text("Toggle inlay hints")),
         )
         .description("Displays a selected icon button with tooltip.")
         .usage(
             r#"
             IconButton::new("selected_with_tooltip_button", Icon::InlayHint)
                 .selected(true)
-                .tooltip(|cx| Tooltip::text("Toggle inlay hints", cx))
+                .tooltip(Tooltip::text_f("Toggle inlay hints"))
         "#,
         );
 

crates/ui/src/components/stories/keybinding.rs 🔗

@@ -12,7 +12,7 @@ pub fn binding(key: &str) -> gpui::KeyBinding {
 }
 
 impl Render for KeybindingStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         let all_modifier_permutations = ["ctrl", "alt", "cmd", "shift"].into_iter().permutations(2);
 
         Story::container()

crates/ui/src/components/stories/label.rs 🔗

@@ -7,7 +7,7 @@ use story::Story;
 pub struct LabelStory;
 
 impl Render for LabelStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title_for::<Label>())
             .child(Story::label("Default"))

crates/ui/src/components/stories/list.rs 🔗

@@ -7,7 +7,7 @@ use crate::{List, ListItem};
 pub struct ListStory;
 
 impl Render for ListStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title_for::<List>())
             .child(Story::label("Default"))

crates/ui/src/components/stories/list_header.rs 🔗

@@ -7,7 +7,7 @@ use crate::{IconName, ListHeader};
 pub struct ListHeaderStory;
 
 impl Render for ListHeaderStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title_for::<ListHeader>())
             .child(Story::label("Default"))

crates/ui/src/components/stories/list_item.rs 🔗

@@ -9,7 +9,7 @@ const OVERFLOWING_TEXT: &str = "Lorem ipsum dolor sit amet, consectetur adipisci
 pub struct ListItemStory;
 
 impl Render for ListItemStory {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         Story::container()
             .bg(cx.theme().colors().background)
             .child(Story::title_for::<ListItem>())
@@ -85,18 +85,16 @@ impl Render for ListItemStory {
                     )),
             )
             .child(Story::label("With `on_click`"))
-            .child(
-                ListItem::new("with_on_click")
-                    .child("Click me")
-                    .on_click(|_event, _cx| {
-                        println!("Clicked!");
-                    }),
-            )
+            .child(ListItem::new("with_on_click").child("Click me").on_click(
+                |_event, _window, _cx| {
+                    println!("Clicked!");
+                },
+            ))
             .child(Story::label("With `on_secondary_mouse_down`"))
             .child(
                 ListItem::new("with_on_secondary_mouse_down")
                     .child("Right click me")
-                    .on_secondary_mouse_down(|_event, _cx| {
+                    .on_secondary_mouse_down(|_event, _window, _cx| {
                         println!("Right mouse down!");
                     }),
             )

crates/ui/src/components/stories/tab.rs 🔗

@@ -9,7 +9,7 @@ use crate::{Indicator, Tab};
 pub struct TabStory;
 
 impl Render for TabStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title_for::<Tab>())
             .child(Story::label("Default"))

crates/ui/src/components/stories/tab_bar.rs 🔗

@@ -6,7 +6,7 @@ use crate::{prelude::*, Tab, TabBar, TabPosition};
 pub struct TabBarStory;
 
 impl Render for TabBarStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         let tab_count = 20;
         let selected_tab_index = 3;
 

crates/ui/src/components/stories/toggle_button.rs 🔗

@@ -6,7 +6,7 @@ use crate::{prelude::*, ToggleButton};
 pub struct ToggleButtonStory;
 
 impl Render for ToggleButtonStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title_for::<ToggleButton>())
             .child(

crates/ui/src/components/stories/tool_strip.rs 🔗

@@ -6,7 +6,7 @@ use crate::{prelude::*, ToolStrip, Tooltip};
 pub struct ToolStripStory;
 
 impl Render for ToolStripStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title_for::<ToolStrip>())
             .child(
@@ -16,15 +16,15 @@ impl Render for ToolStripStory {
                         ToolStrip::vertical("tool_strip_example")
                             .tool(
                                 IconButton::new("example_tool", IconName::AudioOn)
-                                    .tooltip(|cx| Tooltip::text("Example tool", cx)),
+                                    .tooltip(Tooltip::text("Example tool")),
                             )
                             .tool(
                                 IconButton::new("example_tool_2", IconName::MicMute)
-                                    .tooltip(|cx| Tooltip::text("Example tool 2", cx)),
+                                    .tooltip(Tooltip::text("Example tool 2")),
                             )
                             .tool(
                                 IconButton::new("example_tool_3", IconName::Screen)
-                                    .tooltip(|cx| Tooltip::text("Example tool 3", cx)),
+                                    .tooltip(Tooltip::text("Example tool 3")),
                             ),
                     ),
                 )),

crates/ui/src/components/tab.rs 🔗

@@ -74,11 +74,11 @@ impl Tab {
         self
     }
 
-    pub fn content_height(cx: &mut WindowContext) -> Pixels {
+    pub fn content_height(cx: &mut App) -> Pixels {
         DynamicSpacing::Base32.px(cx) - px(1.)
     }
 
-    pub fn container_height(cx: &mut WindowContext) -> Pixels {
+    pub fn container_height(cx: &mut App) -> Pixels {
         DynamicSpacing::Base32.px(cx)
     }
 }
@@ -106,7 +106,7 @@ impl ParentElement for Tab {
 
 impl RenderOnce for Tab {
     #[allow(refining_impl_trait)]
-    fn render(self, cx: &mut WindowContext) -> Stateful<Div> {
+    fn render(self, _: &mut Window, cx: &mut App) -> Stateful<Div> {
         let (text_color, tab_bg, _tab_hover_bg, _tab_active_bg) = match self.selected {
             false => (
                 cx.theme().colors().text_muted,

crates/ui/src/components/tab_bar.rs 🔗

@@ -91,7 +91,7 @@ impl ParentElement for TabBar {
 }
 
 impl RenderOnce for TabBar {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
         div()
             .id(self.id)
             .group("tab_bar")

crates/ui/src/components/table.rs 🔗

@@ -49,7 +49,7 @@ impl Table {
         self
     }
 
-    fn base_cell_style(cx: &WindowContext) -> Div {
+    fn base_cell_style(cx: &mut App) -> Div {
         div()
             .px_1p5()
             .flex_1()
@@ -74,7 +74,7 @@ impl Table {
 }
 
 impl RenderOnce for Table {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
         let header = div()
             .flex()
             .flex_row()
@@ -160,7 +160,7 @@ impl ComponentPreview for Table {
         ExampleLabelSide::Top
     }
 
-    fn examples(_: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
+    fn examples(_window: &mut Window, _: &mut App) -> Vec<ComponentExampleGroup<Self>> {
         vec![
             example_group(vec![
                 single_example(

crates/ui/src/components/toggle.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{div, hsla, prelude::*, AnyView, ElementId, Hsla, IntoElement, Styled, WindowContext};
+use gpui::{div, hsla, prelude::*, AnyView, ElementId, Hsla, IntoElement, Styled, Window};
 use std::sync::Arc;
 
 use crate::utils::is_light;
@@ -41,10 +41,10 @@ pub struct Checkbox {
     id: ElementId,
     toggle_state: ToggleState,
     disabled: bool,
-    on_click: Option<Box<dyn Fn(&ToggleState, &mut WindowContext) + 'static>>,
+    on_click: Option<Box<dyn Fn(&ToggleState, &mut Window, &mut App) + 'static>>,
     filled: bool,
     style: ToggleStyle,
-    tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
+    tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView>>,
 }
 
 impl Checkbox {
@@ -70,7 +70,7 @@ impl Checkbox {
     /// Binds a handler to the [`Checkbox`] that will be called when clicked.
     pub fn on_click(
         mut self,
-        handler: impl Fn(&ToggleState, &mut WindowContext) + 'static,
+        handler: impl Fn(&ToggleState, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.on_click = Some(Box::new(handler));
         self
@@ -95,14 +95,14 @@ impl Checkbox {
     }
 
     /// Sets the tooltip for the checkbox.
-    pub fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
+    pub fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self {
         self.tooltip = Some(Box::new(tooltip));
         self
     }
 }
 
 impl Checkbox {
-    fn bg_color(&self, cx: &WindowContext) -> Hsla {
+    fn bg_color(&self, cx: &App) -> Hsla {
         let style = self.style.clone();
         match (style, self.filled) {
             (ToggleStyle::Ghost, false) => cx.theme().colors().ghost_element_background,
@@ -114,7 +114,7 @@ impl Checkbox {
         }
     }
 
-    fn border_color(&self, cx: &WindowContext) -> Hsla {
+    fn border_color(&self, cx: &App) -> Hsla {
         if self.disabled {
             return cx.theme().colors().border_disabled;
         }
@@ -128,7 +128,7 @@ impl Checkbox {
 }
 
 impl RenderOnce for Checkbox {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
         let group_id = format!("checkbox_group_{:?}", self.id);
         let icon = match self.toggle_state {
             ToggleState::Selected => Some(Icon::new(IconName::Check).size(IconSize::Small).color(
@@ -181,11 +181,13 @@ impl RenderOnce for Checkbox {
             .when_some(
                 self.on_click.filter(|_| !self.disabled),
                 |this, on_click| {
-                    this.on_click(move |_, cx| on_click(&self.toggle_state.inverse(), cx))
+                    this.on_click(move |_, window, cx| {
+                        on_click(&self.toggle_state.inverse(), window, cx)
+                    })
                 },
             )
             .when_some(self.tooltip, |this, tooltip| {
-                this.tooltip(move |cx| tooltip(cx))
+                this.tooltip(move |window, cx| tooltip(window, cx))
             })
     }
 }
@@ -196,7 +198,7 @@ pub struct CheckboxWithLabel {
     id: ElementId,
     label: Label,
     checked: ToggleState,
-    on_click: Arc<dyn Fn(&ToggleState, &mut WindowContext) + 'static>,
+    on_click: Arc<dyn Fn(&ToggleState, &mut Window, &mut App) + 'static>,
     filled: bool,
     style: ToggleStyle,
 }
@@ -207,7 +209,7 @@ impl CheckboxWithLabel {
         id: impl Into<ElementId>,
         label: Label,
         checked: ToggleState,
-        on_click: impl Fn(&ToggleState, &mut WindowContext) + 'static,
+        on_click: impl Fn(&ToggleState, &mut Window, &mut App) + 'static,
     ) -> Self {
         Self {
             id: id.into(),
@@ -239,7 +241,7 @@ impl CheckboxWithLabel {
 }
 
 impl RenderOnce for CheckboxWithLabel {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         h_flex()
             .gap(DynamicSpacing::Base08.rems(cx))
             .child(
@@ -248,16 +250,16 @@ impl RenderOnce for CheckboxWithLabel {
                     .when(self.filled, Checkbox::fill)
                     .on_click({
                         let on_click = self.on_click.clone();
-                        move |checked, cx| {
-                            (on_click)(checked, cx);
+                        move |checked, window, cx| {
+                            (on_click)(checked, window, cx);
                         }
                     }),
             )
             .child(
                 div()
                     .id(SharedString::from(format!("{}-label", self.id)))
-                    .on_click(move |_event, cx| {
-                        (self.on_click)(&self.checked.inverse(), cx);
+                    .on_click(move |_event, window, cx| {
+                        (self.on_click)(&self.checked.inverse(), window, cx);
                     })
                     .child(self.label),
             )
@@ -272,7 +274,7 @@ pub struct Switch {
     id: ElementId,
     toggle_state: ToggleState,
     disabled: bool,
-    on_click: Option<Box<dyn Fn(&ToggleState, &mut WindowContext) + 'static>>,
+    on_click: Option<Box<dyn Fn(&ToggleState, &mut Window, &mut App) + 'static>>,
     label: Option<SharedString>,
     key_binding: Option<KeyBinding>,
 }
@@ -299,7 +301,7 @@ impl Switch {
     /// Binds a handler to the [`Switch`] that will be called when clicked.
     pub fn on_click(
         mut self,
-        handler: impl Fn(&ToggleState, &mut WindowContext) + 'static,
+        handler: impl Fn(&ToggleState, &mut Window, &mut App) + 'static,
     ) -> Self {
         self.on_click = Some(Box::new(handler));
         self
@@ -319,7 +321,7 @@ impl Switch {
 }
 
 impl RenderOnce for Switch {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let is_on = self.toggle_state == ToggleState::Selected;
         let adjust_ratio = if is_light(cx) { 1.5 } else { 1.0 };
         let base_color = cx.theme().colors().text;
@@ -386,7 +388,9 @@ impl RenderOnce for Switch {
             .when_some(
                 self.on_click.filter(|_| !self.disabled),
                 |this, on_click| {
-                    this.on_click(move |_, cx| on_click(&self.toggle_state.inverse(), cx))
+                    this.on_click(move |_, window, cx| {
+                        on_click(&self.toggle_state.inverse(), window, cx)
+                    })
                 },
             )
             .when_some(self.label, |this, label| {
@@ -401,7 +405,7 @@ impl ComponentPreview for Checkbox {
         "A checkbox lets people choose between a pair of opposing states, like enabled and disabled, using a different appearance to indicate each state."
     }
 
-    fn examples(_: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
+    fn examples(_window: &mut Window, _: &mut App) -> Vec<ComponentExampleGroup<Self>> {
         vec![
             example_group_with_title(
                 "Default",
@@ -595,18 +599,18 @@ impl ComponentPreview for Switch {
         "A switch toggles between two mutually exclusive states, typically used for enabling or disabling a setting."
     }
 
-    fn examples(_cx: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
+    fn examples(_window: &mut Window, _cx: &mut App) -> Vec<ComponentExampleGroup<Self>> {
         vec![
             example_group_with_title(
                 "Default",
                 vec![
                     single_example(
                         "Off",
-                        Switch::new("switch_off", ToggleState::Unselected).on_click(|_, _cx| {}),
+                        Switch::new("switch_off", ToggleState::Unselected).on_click(|_, _, _cx| {}),
                     ),
                     single_example(
                         "On",
-                        Switch::new("switch_on", ToggleState::Selected).on_click(|_, _cx| {}),
+                        Switch::new("switch_on", ToggleState::Selected).on_click(|_, _, _cx| {}),
                     ),
                 ],
             ),
@@ -647,7 +651,7 @@ impl ComponentPreview for CheckboxWithLabel {
         "A checkbox with an associated label, allowing users to select an option while providing a descriptive text."
     }
 
-    fn examples(_: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
+    fn examples(_window: &mut Window, _: &mut App) -> Vec<ComponentExampleGroup<Self>> {
         vec![example_group(vec![
             single_example(
                 "Unselected",
@@ -655,7 +659,7 @@ impl ComponentPreview for CheckboxWithLabel {
                     "checkbox_with_label_unselected",
                     Label::new("Always save on quit"),
                     ToggleState::Unselected,
-                    |_, _| {},
+                    |_, _, _| {},
                 ),
             ),
             single_example(
@@ -664,7 +668,7 @@ impl ComponentPreview for CheckboxWithLabel {
                     "checkbox_with_label_indeterminate",
                     Label::new("Always save on quit"),
                     ToggleState::Indeterminate,
-                    |_, _| {},
+                    |_, _, _| {},
                 ),
             ),
             single_example(
@@ -673,7 +677,7 @@ impl ComponentPreview for CheckboxWithLabel {
                     "checkbox_with_label_selected",
                     Label::new("Always save on quit"),
                     ToggleState::Selected,
-                    |_, _| {},
+                    |_, _, _| {},
                 ),
             ),
         ])]

crates/ui/src/components/tool_strip.rs 🔗

@@ -36,7 +36,7 @@ impl ToolStrip {
 }
 
 impl RenderOnce for ToolStrip {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let group = format!("tool_strip_{}", self.id.clone());
 
         div()

crates/ui/src/components/tooltip.rs 🔗

@@ -1,6 +1,6 @@
 #![allow(missing_docs)]
 
-use gpui::{Action, AnyView, FocusHandle, IntoElement, Render, VisualContext};
+use gpui::{Action, AnyView, AppContext as _, FocusHandle, IntoElement, Render};
 use settings::Settings;
 use theme::ThemeSettings;
 
@@ -14,8 +14,8 @@ pub struct Tooltip {
 }
 
 impl Tooltip {
-    pub fn text(title: impl Into<SharedString>, cx: &mut WindowContext) -> AnyView {
-        cx.new_view(|_cx| Self {
+    pub fn simple(title: impl Into<SharedString>, cx: &mut App) -> AnyView {
+        cx.new(|_| Self {
             title: title.into(),
             meta: None,
             key_binding: None,
@@ -23,15 +23,28 @@ impl Tooltip {
         .into()
     }
 
+    pub fn text(title: impl Into<SharedString>) -> impl Fn(&mut Window, &mut App) -> AnyView {
+        let title = title.into();
+        move |_, cx| {
+            cx.new(|_| Self {
+                title: title.clone(),
+                meta: None,
+                key_binding: None,
+            })
+            .into()
+        }
+    }
+
     pub fn for_action(
         title: impl Into<SharedString>,
         action: &dyn Action,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> AnyView {
-        cx.new_view(|cx| Self {
+        cx.new(|_| Self {
             title: title.into(),
             meta: None,
-            key_binding: KeyBinding::for_action(action, cx),
+            key_binding: KeyBinding::for_action(action, window),
         })
         .into()
     }
@@ -40,12 +53,13 @@ impl Tooltip {
         title: impl Into<SharedString>,
         action: &dyn Action,
         focus_handle: &FocusHandle,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> AnyView {
-        cx.new_view(|cx| Self {
+        cx.new(|_| Self {
             title: title.into(),
             meta: None,
-            key_binding: KeyBinding::for_action_in(action, focus_handle, cx),
+            key_binding: KeyBinding::for_action_in(action, focus_handle, window),
         })
         .into()
     }
@@ -54,12 +68,13 @@ impl Tooltip {
         title: impl Into<SharedString>,
         action: Option<&dyn Action>,
         meta: impl Into<SharedString>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> AnyView {
-        cx.new_view(|cx| Self {
+        cx.new(|_| Self {
             title: title.into(),
             meta: Some(meta.into()),
-            key_binding: action.and_then(|action| KeyBinding::for_action(action, cx)),
+            key_binding: action.and_then(|action| KeyBinding::for_action(action, window)),
         })
         .into()
     }
@@ -69,13 +84,14 @@ impl Tooltip {
         action: Option<&dyn Action>,
         meta: impl Into<SharedString>,
         focus_handle: &FocusHandle,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> AnyView {
-        cx.new_view(|cx| Self {
+        cx.new(|_| Self {
             title: title.into(),
             meta: Some(meta.into()),
             key_binding: action
-                .and_then(|action| KeyBinding::for_action_in(action, focus_handle, cx)),
+                .and_then(|action| KeyBinding::for_action_in(action, focus_handle, window)),
         })
         .into()
     }
@@ -100,8 +116,8 @@ impl Tooltip {
 }
 
 impl Render for Tooltip {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        tooltip_container(cx, |el, _| {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+        tooltip_container(window, cx, |el, _, _| {
             el.child(
                 h_flex()
                     .gap_4()
@@ -118,8 +134,9 @@ impl Render for Tooltip {
 }
 
 pub fn tooltip_container<V>(
-    cx: &mut ViewContext<V>,
-    f: impl FnOnce(Div, &mut ViewContext<V>) -> Div,
+    window: &mut Window,
+    cx: &mut Context<V>,
+    f: impl FnOnce(Div, &mut Window, &mut Context<V>) -> Div,
 ) -> impl IntoElement {
     let ui_font = ThemeSettings::get_global(cx).ui_font.clone();
 
@@ -132,7 +149,7 @@ pub fn tooltip_container<V>(
             .text_color(cx.theme().colors().text)
             .py_1()
             .px_2()
-            .map(|el| f(el, cx)),
+            .map(|el| f(el, window, cx)),
     )
 }
 
@@ -141,7 +158,7 @@ pub struct LinkPreview {
 }
 
 impl LinkPreview {
-    pub fn new(url: &str, cx: &mut WindowContext) -> AnyView {
+    pub fn new(url: &str, cx: &mut App) -> AnyView {
         let mut wrapped_url = String::new();
         for (i, ch) in url.chars().enumerate() {
             if i == 500 {
@@ -153,7 +170,7 @@ impl LinkPreview {
             }
             wrapped_url.push(ch);
         }
-        cx.new_view(|_cx| LinkPreview {
+        cx.new(|_| LinkPreview {
             link: wrapped_url.into(),
         })
         .into()
@@ -161,8 +178,8 @@ impl LinkPreview {
 }
 
 impl Render for LinkPreview {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        tooltip_container(cx, |el, _| {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+        tooltip_container(window, cx, |el, _, _| {
             el.child(
                 Label::new(self.link.clone())
                     .size(LabelSize::XSmall)

crates/ui/src/prelude.rs 🔗

@@ -2,9 +2,8 @@
 
 pub use gpui::prelude::*;
 pub use gpui::{
-    div, px, relative, rems, AbsoluteLength, DefiniteLength, Div, Element, ElementId,
-    InteractiveElement, ParentElement, Pixels, Rems, RenderOnce, SharedString, Styled, ViewContext,
-    WindowContext,
+    div, px, relative, rems, AbsoluteLength, App, Context, DefiniteLength, Div, Element, ElementId,
+    InteractiveElement, ParentElement, Pixels, Rems, RenderOnce, SharedString, Styled, Window,
 };
 
 pub use crate::styles::{rems_from_px, vh, vw, PlatformStyle, StyledTypography, TextSize};

crates/ui/src/styles/appearance.rs 🔗

@@ -1,8 +1,8 @@
 use crate::prelude::*;
-use gpui::{WindowBackgroundAppearance, WindowContext};
+use gpui::{App, WindowBackgroundAppearance};
 
 /// Returns the [WindowBackgroundAppearance].
-fn window_appearance(cx: &WindowContext) -> WindowBackgroundAppearance {
+fn window_appearance(cx: &mut App) -> WindowBackgroundAppearance {
     cx.theme().styles.window_background_appearance
 }
 
@@ -11,7 +11,7 @@ fn window_appearance(cx: &WindowContext) -> WindowBackgroundAppearance {
 ///
 /// Helps determine if you need to take extra steps to prevent
 /// transparent backgrounds.
-pub fn window_is_transparent(cx: &WindowContext) -> bool {
+pub fn theme_is_transparent(cx: &mut App) -> bool {
     matches!(
         window_appearance(cx),
         WindowBackgroundAppearance::Transparent | WindowBackgroundAppearance::Blurred

crates/ui/src/styles/color.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{Hsla, WindowContext};
+use gpui::{App, Hsla};
 use theme::ActiveTheme;
 
 /// Sets a color that has a consistent meaning across all themes.
@@ -62,7 +62,7 @@ pub enum Color {
 
 impl Color {
     /// Returns the Color's HSLA value.
-    pub fn color(&self, cx: &WindowContext) -> Hsla {
+    pub fn color(&self, cx: &App) -> Hsla {
         match self {
             Color::Default => cx.theme().colors().text,
             Color::Muted => cx.theme().colors().text_muted,

crates/ui/src/styles/elevation.rs 🔗

@@ -1,6 +1,6 @@
 use std::fmt::{self, Display, Formatter};
 
-use gpui::{hsla, point, px, BoxShadow, Hsla, WindowContext};
+use gpui::{hsla, point, px, App, BoxShadow, Hsla};
 use smallvec::{smallvec, SmallVec};
 use theme::ActiveTheme;
 
@@ -78,7 +78,7 @@ impl ElevationIndex {
     }
 
     /// Returns the background color for the given elevation index.
-    pub fn bg(&self, cx: &WindowContext) -> Hsla {
+    pub fn bg(&self, cx: &mut App) -> Hsla {
         match self {
             ElevationIndex::Background => cx.theme().colors().background,
             ElevationIndex::Surface => cx.theme().colors().surface_background,
@@ -89,7 +89,7 @@ impl ElevationIndex {
     }
 
     /// Returns a color that is appropriate a filled element on this elevation
-    pub fn on_elevation_bg(&self, cx: &WindowContext) -> Hsla {
+    pub fn on_elevation_bg(&self, cx: &App) -> Hsla {
         match self {
             ElevationIndex::Background => cx.theme().colors().surface_background,
             ElevationIndex::Surface => cx.theme().colors().background,
@@ -102,7 +102,7 @@ impl ElevationIndex {
     /// Attempts to return a darker background color than the current elevation index's background.
     ///
     /// If the current background color is already dark, it will return a lighter color instead.
-    pub fn darker_bg(&self, cx: &WindowContext) -> Hsla {
+    pub fn darker_bg(&self, cx: &App) -> Hsla {
         match self {
             ElevationIndex::Background => cx.theme().colors().surface_background,
             ElevationIndex::Surface => cx.theme().colors().editor_background,

crates/ui/src/styles/spacing.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{px, rems, Pixels, Rems, WindowContext};
+use gpui::{px, rems, App, Pixels, Rems};
 use settings::Settings;
 use theme::{ThemeSettings, UiDensity};
 use ui_macros::derive_dynamic_spacing;
@@ -50,6 +50,6 @@ derive_dynamic_spacing![
 /// Do not use this to calculate spacing values.
 ///
 /// Always use [DynamicSpacing] for spacing values.
-pub fn ui_density(cx: &WindowContext) -> UiDensity {
+pub fn ui_density(cx: &mut App) -> UiDensity {
     ThemeSettings::get_global(cx).ui_density
 }

crates/ui/src/styles/typography.rs 🔗

@@ -1,6 +1,5 @@
 use gpui::{
-    div, rems, AppContext, IntoElement, ParentElement, Rems, RenderOnce, SharedString, Styled,
-    WindowContext,
+    div, rems, App, IntoElement, ParentElement, Rems, RenderOnce, SharedString, Styled, Window,
 };
 use settings::Settings;
 use theme::{ActiveTheme, ThemeSettings};
@@ -10,7 +9,7 @@ use crate::{rems_from_px, Color};
 /// Extends [`gpui::Styled`] with typography-related styling methods.
 pub trait StyledTypography: Styled + Sized {
     /// Sets the font family to the buffer font.
-    fn font_buffer(self, cx: &WindowContext) -> Self {
+    fn font_buffer(self, cx: &App) -> Self {
         let settings = ThemeSettings::get_global(cx);
         let buffer_font_family = settings.buffer_font.family.clone();
 
@@ -18,7 +17,7 @@ pub trait StyledTypography: Styled + Sized {
     }
 
     /// Sets the font family to the UI font.
-    fn font_ui(self, cx: &WindowContext) -> Self {
+    fn font_ui(self, cx: &App) -> Self {
         let settings = ThemeSettings::get_global(cx);
         let ui_font_family = settings.ui_font.family.clone();
 
@@ -26,7 +25,7 @@ pub trait StyledTypography: Styled + Sized {
     }
 
     /// Sets the text size using a [`UiTextSize`].
-    fn text_ui_size(self, size: TextSize, cx: &WindowContext) -> Self {
+    fn text_ui_size(self, size: TextSize, cx: &App) -> Self {
         self.text_size(size.rems(cx))
     }
 
@@ -37,7 +36,7 @@ pub trait StyledTypography: Styled + Sized {
     /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
     ///
     /// Use `text_ui` for regular-sized text.
-    fn text_ui_lg(self, cx: &WindowContext) -> Self {
+    fn text_ui_lg(self, cx: &App) -> Self {
         self.text_size(TextSize::Large.rems(cx))
     }
 
@@ -48,7 +47,7 @@ pub trait StyledTypography: Styled + Sized {
     /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
     ///
     /// Use `text_ui_sm` for smaller text.
-    fn text_ui(self, cx: &WindowContext) -> Self {
+    fn text_ui(self, cx: &App) -> Self {
         self.text_size(TextSize::default().rems(cx))
     }
 
@@ -59,7 +58,7 @@ pub trait StyledTypography: Styled + Sized {
     /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
     ///
     /// Use `text_ui` for regular-sized text.
-    fn text_ui_sm(self, cx: &WindowContext) -> Self {
+    fn text_ui_sm(self, cx: &App) -> Self {
         self.text_size(TextSize::Small.rems(cx))
     }
 
@@ -70,7 +69,7 @@ pub trait StyledTypography: Styled + Sized {
     /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
     ///
     /// Use `text_ui` for regular-sized text.
-    fn text_ui_xs(self, cx: &WindowContext) -> Self {
+    fn text_ui_xs(self, cx: &App) -> Self {
         self.text_size(TextSize::XSmall.rems(cx))
     }
 
@@ -80,7 +79,7 @@ pub trait StyledTypography: Styled + Sized {
     ///
     /// This should only be used for text that is displayed in a buffer,
     /// or other places that text needs to match the user's buffer font size.
-    fn text_buffer(self, cx: &WindowContext) -> Self {
+    fn text_buffer(self, cx: &App) -> Self {
         let settings = ThemeSettings::get_global(cx);
         self.text_size(settings.buffer_font_size())
     }
@@ -131,7 +130,7 @@ pub enum TextSize {
 
 impl TextSize {
     /// Returns the text size in rems.
-    pub fn rems(self, cx: &AppContext) -> Rems {
+    pub fn rems(self, cx: &App) -> Rems {
         let theme_settings = ThemeSettings::get_global(cx);
 
         match self {
@@ -197,7 +196,7 @@ pub struct Headline {
 }
 
 impl RenderOnce for Headline {
-    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let ui_font = ThemeSettings::get_global(cx).ui_font.clone();
 
         div()

crates/ui/src/styles/units.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{rems, Length, Rems, WindowContext};
+use gpui::{rems, Length, Rems, Window};
 
 /// The base size of a rem, in pixels.
 pub const BASE_REM_SIZE_IN_PX: f32 = 16.;
@@ -17,13 +17,13 @@ pub fn rems_from_px(px: f32) -> Rems {
 /// Returns a [`Length`] corresponding to the specified percentage of the viewport's width.
 ///
 /// `percent` should be a value between `0.0` and `1.0`.
-pub fn vw(percent: f32, cx: &mut WindowContext) -> Length {
-    Length::from(cx.viewport_size().width * percent)
+pub fn vw(percent: f32, window: &mut Window) -> Length {
+    Length::from(window.viewport_size().width * percent)
 }
 
 /// Returns a [`Length`] corresponding to the specified percentage of the viewport's height.
 ///
 /// `percent` should be a value between `0.0` and `1.0`.
-pub fn vh(percent: f32, cx: &mut WindowContext) -> Length {
-    Length::from(cx.viewport_size().height * percent)
+pub fn vh(percent: f32, window: &mut Window) -> Length {
+    Length::from(window.viewport_size().height * percent)
 }

crates/ui/src/traits/clickable.rs 🔗

@@ -1,9 +1,9 @@
-use gpui::{ClickEvent, CursorStyle, WindowContext};
+use gpui::{App, ClickEvent, CursorStyle, Window};
 
 /// A trait for elements that can be clicked. Enables the use of the `on_click` method.
 pub trait Clickable {
     /// Sets the click handler that will fire whenever the element is clicked.
-    fn on_click(self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self;
+    fn on_click(self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self;
     /// Sets the cursor style when hovering over the element.
     fn cursor_style(self, cursor_style: CursorStyle) -> Self;
 }

crates/ui/src/traits/component_preview.rs 🔗

@@ -30,20 +30,20 @@ pub trait ComponentPreview: IntoElement {
         ExampleLabelSide::default()
     }
 
-    fn examples(_cx: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>>;
+    fn examples(_window: &mut Window, _cx: &mut App) -> Vec<ComponentExampleGroup<Self>>;
 
-    fn custom_example(_cx: &WindowContext) -> impl Into<Option<AnyElement>> {
+    fn custom_example(_window: &mut Window, _cx: &mut App) -> impl Into<Option<AnyElement>> {
         None::<AnyElement>
     }
 
-    fn component_previews(cx: &mut WindowContext) -> Vec<AnyElement> {
-        Self::examples(cx)
+    fn component_previews(window: &mut Window, cx: &mut App) -> Vec<AnyElement> {
+        Self::examples(window, cx)
             .into_iter()
             .map(|example| Self::render_example_group(example))
             .collect()
     }
 
-    fn render_component_previews(cx: &mut WindowContext) -> AnyElement {
+    fn render_component_previews(window: &mut Window, cx: &mut App) -> AnyElement {
         let title = Self::title();
         let (source, title) = title
             .rsplit_once("::")
@@ -78,10 +78,11 @@ pub trait ComponentPreview: IntoElement {
                         )
                     }),
             )
-            .when_some(Self::custom_example(cx).into(), |this, custom_example| {
-                this.child(custom_example)
-            })
-            .children(Self::component_previews(cx))
+            .when_some(
+                Self::custom_example(window, cx).into(),
+                |this, custom_example| this.child(custom_example),
+            )
+            .children(Self::component_previews(window, cx))
             .into_any_element()
     }
 

crates/ui/src/traits/styled_ext.rs 🔗

@@ -1,9 +1,9 @@
-use gpui::{hsla, Styled, WindowContext};
+use gpui::{hsla, App, Styled};
 
 use crate::prelude::*;
 use crate::ElevationIndex;
 
-fn elevated<E: Styled>(this: E, cx: &WindowContext, index: ElevationIndex) -> E {
+fn elevated<E: Styled>(this: E, cx: &mut App, index: ElevationIndex) -> E {
     this.bg(cx.theme().colors().elevated_surface_background)
         .rounded_lg()
         .border_1()
@@ -11,7 +11,7 @@ fn elevated<E: Styled>(this: E, cx: &WindowContext, index: ElevationIndex) -> E
         .shadow(index.shadow())
 }
 
-fn elevated_borderless<E: Styled>(this: E, cx: &WindowContext, index: ElevationIndex) -> E {
+fn elevated_borderless<E: Styled>(this: E, cx: &mut App, index: ElevationIndex) -> E {
     this.bg(cx.theme().colors().elevated_surface_background)
         .rounded_lg()
         .shadow(index.shadow())
@@ -38,14 +38,14 @@ pub trait StyledExt: Styled + Sized {
     /// Sets `bg()`, `rounded_lg()`, `border()`, `border_color()`, `shadow()`
     ///
     /// Example Elements: Title Bar, Panel, Tab Bar, Editor
-    fn elevation_1(self, cx: &WindowContext) -> Self {
+    fn elevation_1(self, cx: &mut App) -> Self {
         elevated(self, cx, ElevationIndex::Surface)
     }
 
     /// See [`elevation_1`].
     ///
     /// Renders a borderless version [`elevation_1`].
-    fn elevation_1_borderless(self, cx: &WindowContext) -> Self {
+    fn elevation_1_borderless(self, cx: &mut App) -> Self {
         elevated_borderless(self, cx, ElevationIndex::Surface)
     }
 
@@ -54,14 +54,14 @@ pub trait StyledExt: Styled + Sized {
     /// Sets `bg()`, `rounded_lg()`, `border()`, `border_color()`, `shadow()`
     ///
     /// Examples: Notifications, Palettes, Detached/Floating Windows, Detached/Floating Panels
-    fn elevation_2(self, cx: &WindowContext) -> Self {
+    fn elevation_2(self, cx: &mut App) -> Self {
         elevated(self, cx, ElevationIndex::ElevatedSurface)
     }
 
     /// See [`elevation_2`].
     ///
     /// Renders a borderless version [`elevation_2`].
-    fn elevation_2_borderless(self, cx: &WindowContext) -> Self {
+    fn elevation_2_borderless(self, cx: &mut App) -> Self {
         elevated_borderless(self, cx, ElevationIndex::ElevatedSurface)
     }
 
@@ -74,24 +74,24 @@ pub trait StyledExt: Styled + Sized {
     /// Sets `bg()`, `rounded_lg()`, `border()`, `border_color()`, `shadow()`
     ///
     /// Examples: Settings Modal, Channel Management, Wizards/Setup UI, Dialogs
-    fn elevation_3(self, cx: &WindowContext) -> Self {
+    fn elevation_3(self, cx: &mut App) -> Self {
         elevated(self, cx, ElevationIndex::ModalSurface)
     }
 
     /// See [`elevation_3`].
     ///
     /// Renders a borderless version [`elevation_3`].
-    fn elevation_3_borderless(self, cx: &WindowContext) -> Self {
+    fn elevation_3_borderless(self, cx: &mut App) -> Self {
         elevated_borderless(self, cx, ElevationIndex::ModalSurface)
     }
 
     /// The theme's primary border color.
-    fn border_primary(self, cx: &WindowContext) -> Self {
+    fn border_primary(self, cx: &mut App) -> Self {
         self.border_color(cx.theme().colors().border)
     }
 
     /// The theme's secondary or muted border color.
-    fn border_muted(self, cx: &WindowContext) -> Self {
+    fn border_muted(self, cx: &mut App) -> Self {
         self.border_color(cx.theme().colors().border_variant)
     }
 

crates/ui/src/utils.rs 🔗

@@ -1,6 +1,6 @@
 //! UI-related utilities
 
-use gpui::WindowContext;
+use gpui::App;
 use theme::ActiveTheme;
 
 mod color_contrast;
@@ -14,6 +14,6 @@ pub use search_input::*;
 pub use with_rem_size::*;
 
 /// Returns true if the current theme is light or vibrant light.
-pub fn is_light(cx: &WindowContext) -> bool {
+pub fn is_light(cx: &mut App) -> bool {
     cx.theme().appearance.is_light()
 }

crates/ui/src/utils/with_rem_size.rs 🔗

@@ -1,7 +1,7 @@
 use gpui::{
-    div, AnyElement, Bounds, Div, DivFrameState, Element, ElementId, GlobalElementId, Hitbox,
+    div, AnyElement, App, Bounds, Div, DivFrameState, Element, ElementId, GlobalElementId, Hitbox,
     InteractiveElement as _, IntoElement, LayoutId, ParentElement, Pixels, StyleRefinement, Styled,
-    WindowContext,
+    Window,
 };
 
 /// An element that sets a particular rem size for its children.
@@ -51,9 +51,12 @@ impl Element for WithRemSize {
     fn request_layout(
         &mut self,
         id: Option<&GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (LayoutId, Self::RequestLayoutState) {
-        cx.with_rem_size(Some(self.rem_size), |cx| self.div.request_layout(id, cx))
+        window.with_rem_size(Some(self.rem_size), |window| {
+            self.div.request_layout(id, window, cx)
+        })
     }
 
     fn prepaint(
@@ -61,10 +64,11 @@ impl Element for WithRemSize {
         id: Option<&GlobalElementId>,
         bounds: Bounds<Pixels>,
         request_layout: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Self::PrepaintState {
-        cx.with_rem_size(Some(self.rem_size), |cx| {
-            self.div.prepaint(id, bounds, request_layout, cx)
+        window.with_rem_size(Some(self.rem_size), |window| {
+            self.div.prepaint(id, bounds, request_layout, window, cx)
         })
     }
 
@@ -74,10 +78,12 @@ impl Element for WithRemSize {
         bounds: Bounds<Pixels>,
         request_layout: &mut Self::RequestLayoutState,
         prepaint: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
-        cx.with_rem_size(Some(self.rem_size), |cx| {
-            self.div.paint(id, bounds, request_layout, prepaint, cx)
+        window.with_rem_size(Some(self.rem_size), |window| {
+            self.div
+                .paint(id, bounds, request_layout, prepaint, window, cx)
         })
     }
 }

crates/ui_input/src/ui_input.rs 🔗

@@ -6,7 +6,7 @@
 //!
 
 use editor::{Editor, EditorElement, EditorStyle};
-use gpui::{AppContext, FocusHandle, FocusableView, FontStyle, Hsla, TextStyle, View};
+use gpui::{App, Entity, FocusHandle, Focusable, FontStyle, Hsla, TextStyle};
 use settings::Settings;
 use theme::ThemeSettings;
 use ui::prelude::*;
@@ -24,9 +24,9 @@ pub struct TextFieldStyle {
     border_color: Hsla,
 }
 
-/// A Text Field view that can be used to create text fields like search inputs, form fields, etc.
+/// A Text Field that can be used to create text fields like search inputs, form fields, etc.
 ///
-/// It wraps a single line [`Editor`] view and allows for common field properties like labels, placeholders, icons, etc.
+/// It wraps a single line [`Editor`] and allows for common field properties like labels, placeholders, icons, etc.
 pub struct TextField {
     /// An optional label for the text field.
     ///
@@ -34,10 +34,10 @@ pub struct TextField {
     label: SharedString,
     /// The placeholder text for the text field.
     placeholder: SharedString,
-    /// Exposes the underlying [`View<Editor>`] to allow for customizing the editor beyond the provided API.
+    /// Exposes the underlying [`Model<Editor>`] to allow for customizing the editor beyond the provided API.
     ///
     /// This likely will only be public in the short term, ideally the API will be expanded to cover necessary use cases.
-    pub editor: View<Editor>,
+    pub editor: Entity<Editor>,
     /// An optional icon that is displayed at the start of the text field.
     ///
     /// For example, a magnifying glass icon in a search field.
@@ -48,22 +48,23 @@ pub struct TextField {
     disabled: bool,
 }
 
-impl FocusableView for TextField {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for TextField {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.editor.focus_handle(cx)
     }
 }
 
 impl TextField {
     pub fn new(
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
         label: impl Into<SharedString>,
         placeholder: impl Into<SharedString>,
     ) -> Self {
         let placeholder_text = placeholder.into();
 
-        let editor = cx.new_view(|cx| {
-            let mut input = Editor::single_line(cx);
+        let editor = cx.new(|cx| {
+            let mut input = Editor::single_line(window, cx);
             input.set_placeholder_text(placeholder_text.clone(), cx);
             input
         });
@@ -88,19 +89,19 @@ impl TextField {
         self
     }
 
-    pub fn set_disabled(&mut self, disabled: bool, cx: &mut ViewContext<Self>) {
+    pub fn set_disabled(&mut self, disabled: bool, cx: &mut Context<Self>) {
         self.disabled = disabled;
         self.editor
             .update(cx, |editor, _| editor.set_read_only(disabled))
     }
 
-    pub fn editor(&self) -> &View<Editor> {
+    pub fn editor(&self) -> &Entity<Editor> {
         &self.editor
     }
 }
 
 impl Render for TextField {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let settings = ThemeSettings::get_global(cx);
         let theme_color = cx.theme().colors();
 

crates/ui_macros/src/dynamic_spacing.rs 🔗

@@ -143,7 +143,7 @@ pub fn derive_spacing(input: TokenStream) -> TokenStream {
 
         impl DynamicSpacing {
             /// Returns the spacing ratio, should only be used internally.
-            fn spacing_ratio(&self, cx: &WindowContext) -> f32 {
+            fn spacing_ratio(&self, cx: &App) -> f32 {
                 const BASE_REM_SIZE_IN_PX: f32 = 16.0;
                 match self {
                     #(#spacing_ratios,)*
@@ -151,12 +151,12 @@ pub fn derive_spacing(input: TokenStream) -> TokenStream {
             }
 
             /// Returns the spacing value in rems.
-            pub fn rems(&self, cx: &WindowContext) -> Rems {
+            pub fn rems(&self, cx: &App) -> Rems {
                 rems(self.spacing_ratio(cx))
             }
 
             /// Returns the spacing value in pixels.
-            pub fn px(&self, cx: &WindowContext) -> Pixels {
+            pub fn px(&self, cx: &App) -> Pixels {
                 let ui_font_size_f32: f32 = ThemeSettings::get_global(cx).ui_font_size.into();
                 px(ui_font_size_f32 * self.spacing_ratio(cx))
             }

crates/vcs_menu/src/lib.rs 🔗

@@ -1,10 +1,10 @@
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use fuzzy::{StringMatch, StringMatchCandidate};
 use git::repository::Branch;
 use gpui::{
-    rems, AnyElement, AppContext, AsyncAppContext, DismissEvent, EventEmitter, FocusHandle,
-    FocusableView, InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled,
-    Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
+    rems, AnyElement, App, AsyncAppContext, Context, DismissEvent, Entity, EventEmitter,
+    FocusHandle, Focusable, InteractiveElement, IntoElement, ParentElement, Render, SharedString,
+    Styled, Subscription, Task, WeakEntity, Window,
 };
 use picker::{Picker, PickerDelegate};
 use project::ProjectPath;
@@ -15,37 +15,49 @@ use workspace::notifications::DetachAndPromptErr;
 use workspace::{ModalView, Workspace};
 use zed_actions::branches::OpenRecent;
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(|workspace: &mut Workspace, _| {
+pub fn init(cx: &mut App) {
+    cx.observe_new(|workspace: &mut Workspace, _, _| {
         workspace.register_action(BranchList::open);
     })
     .detach();
 }
 
 pub struct BranchList {
-    pub picker: View<Picker<BranchListDelegate>>,
+    pub picker: Entity<Picker<BranchListDelegate>>,
     rem_width: f32,
     _subscription: Subscription,
 }
 
 impl BranchList {
-    pub fn open(_: &mut Workspace, _: &OpenRecent, cx: &mut ViewContext<Workspace>) {
-        let this = cx.view().clone();
-        cx.spawn(|_, mut cx| async move {
+    pub fn open(
+        _: &mut Workspace,
+        _: &OpenRecent,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) {
+        let this = cx.model().clone();
+        cx.spawn_in(window, |_, mut cx| async move {
             // Modal branch picker has a longer trailoff than a popover one.
             let delegate = BranchListDelegate::new(this.clone(), 70, &cx).await?;
 
-            this.update(&mut cx, |workspace, cx| {
-                workspace.toggle_modal(cx, |cx| BranchList::new(delegate, 34., cx))
+            this.update_in(&mut cx, |workspace, window, cx| {
+                workspace.toggle_modal(window, cx, |window, cx| {
+                    BranchList::new(delegate, 34., window, cx)
+                })
             })?;
 
             Ok(())
         })
-        .detach_and_prompt_err("Failed to read branches", cx, |_, _| None)
+        .detach_and_prompt_err("Failed to read branches", window, cx, |_, _, _| None)
     }
 
-    fn new(delegate: BranchListDelegate, rem_width: f32, cx: &mut ViewContext<Self>) -> Self {
-        let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
+    fn new(
+        delegate: BranchListDelegate,
+        rem_width: f32,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Self {
+        let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
         let _subscription = cx.subscribe(&picker, |_, _, _, cx| cx.emit(DismissEvent));
         Self {
             picker,
@@ -57,20 +69,20 @@ impl BranchList {
 impl ModalView for BranchList {}
 impl EventEmitter<DismissEvent> for BranchList {}
 
-impl FocusableView for BranchList {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+impl Focusable for BranchList {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
 
 impl Render for BranchList {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         v_flex()
             .w(rems(self.rem_width))
             .child(self.picker.clone())
-            .on_mouse_down_out(cx.listener(|this, _, cx| {
+            .on_mouse_down_out(cx.listener(|this, _, window, cx| {
                 this.picker.update(cx, |this, cx| {
-                    this.cancel(&Default::default(), cx);
+                    this.cancel(&Default::default(), window, cx);
                 })
             }))
     }
@@ -94,7 +106,7 @@ impl BranchEntry {
 pub struct BranchListDelegate {
     matches: Vec<BranchEntry>,
     all_branches: Vec<Branch>,
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     selected_index: usize,
     last_query: String,
     /// Max length of branch name before we truncate it and add a trailing `...`.
@@ -103,7 +115,7 @@ pub struct BranchListDelegate {
 
 impl BranchListDelegate {
     async fn new(
-        workspace: View<Workspace>,
+        workspace: Entity<Workspace>,
         branch_name_trailoff_after: usize,
         cx: &AsyncAppContext,
     ) -> Result<Self> {
@@ -140,7 +152,7 @@ impl BranchListDelegate {
 impl PickerDelegate for BranchListDelegate {
     type ListItem = ListItem;
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Select branch...".into()
     }
 
@@ -152,15 +164,25 @@ impl PickerDelegate for BranchListDelegate {
         self.selected_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Picker<Self>>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        _window: &mut Window,
+        _: &mut Context<Picker<Self>>,
+    ) {
         self.selected_index = ix;
     }
 
-    fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
-        cx.spawn(move |picker, mut cx| async move {
-            let candidates = picker.update(&mut cx, |view, _| {
+    fn update_matches(
+        &mut self,
+        query: String,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Task<()> {
+        cx.spawn_in(window, move |picker, mut cx| async move {
+            let candidates = picker.update(&mut cx, |picker, _| {
                 const RECENT_BRANCHES_COUNT: usize = 10;
-                let mut branches = view.delegate.all_branches.clone();
+                let mut branches = picker.delegate.all_branches.clone();
                 if query.is_empty() {
                     if branches.len() > RECENT_BRANCHES_COUNT {
                         // Truncate list of recent branches
@@ -229,11 +251,11 @@ impl PickerDelegate for BranchListDelegate {
         })
     }
 
-    fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
+    fn confirm(&mut self, _: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
         let Some(branch) = self.matches.get(self.selected_index()) else {
             return;
         };
-        cx.spawn({
+        cx.spawn_in(window, {
             let branch = branch.clone();
             |picker, mut cx| async move {
                 let branch_change_task = picker.update(&mut cx, |this, cx| {
@@ -266,10 +288,10 @@ impl PickerDelegate for BranchListDelegate {
                 })
             }
         })
-        .detach_and_prompt_err("Failed to change branch", cx, |_, _| None);
+        .detach_and_prompt_err("Failed to change branch", window, cx, |_, _, _| None);
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+    fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<Self>>) {
         cx.emit(DismissEvent);
     }
 
@@ -277,7 +299,8 @@ impl PickerDelegate for BranchListDelegate {
         &self,
         ix: usize,
         selected: bool,
-        _cx: &mut ViewContext<Picker<Self>>,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let hit = &self.matches[ix];
         let shortened_branch_name =
@@ -306,7 +329,11 @@ impl PickerDelegate for BranchListDelegate {
         )
     }
 
-    fn render_header(&self, _: &mut ViewContext<Picker<Self>>) -> Option<AnyElement> {
+    fn render_header(
+        &self,
+        _window: &mut Window,
+        _: &mut Context<Picker<Self>>,
+    ) -> Option<AnyElement> {
         let label = if self.last_query.is_empty() {
             Label::new("Recent Branches")
                 .size(LabelSize::Small)

crates/vim/src/change_list.rs 🔗

@@ -1,21 +1,26 @@
 use editor::{display_map::ToDisplayPoint, movement, scroll::Autoscroll, Bias, Direction, Editor};
-use gpui::{actions, ViewContext};
+use gpui::{actions, Context, Window};
 
 use crate::{state::Mode, Vim};
 
 actions!(vim, [ChangeListOlder, ChangeListNewer]);
 
-pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
-    Vim::action(editor, cx, |vim, _: &ChangeListOlder, cx| {
-        vim.move_to_change(Direction::Prev, cx);
+pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
+    Vim::action(editor, cx, |vim, _: &ChangeListOlder, window, cx| {
+        vim.move_to_change(Direction::Prev, window, cx);
     });
-    Vim::action(editor, cx, |vim, _: &ChangeListNewer, cx| {
-        vim.move_to_change(Direction::Next, cx);
+    Vim::action(editor, cx, |vim, _: &ChangeListNewer, window, cx| {
+        vim.move_to_change(Direction::Next, window, cx);
     });
 }
 
 impl Vim {
-    fn move_to_change(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
+    fn move_to_change(
+        &mut self,
+        direction: Direction,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let count = Vim::take_count(cx).unwrap_or(1);
         if self.change_list.is_empty() {
             return;
@@ -31,8 +36,8 @@ impl Vim {
         let Some(selections) = self.change_list.get(next).cloned() else {
             return;
         };
-        self.update_editor(cx, |_, editor, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                 let map = s.display_map();
                 s.select_display_ranges(selections.into_iter().map(|a| {
                     let point = a.to_display_point(&map);
@@ -42,8 +47,8 @@ impl Vim {
         });
     }
 
-    pub(crate) fn push_to_change_list(&mut self, cx: &mut ViewContext<Self>) {
-        let Some((map, selections)) = self.update_editor(cx, |_, editor, cx| {
+    pub(crate) fn push_to_change_list(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        let Some((map, selections)) = self.update_editor(window, cx, |_, editor, _, cx| {
             editor.selections.all_adjusted_display(cx)
         }) else {
             return;

crates/vim/src/command.rs 🔗

@@ -7,9 +7,7 @@ use editor::{
     scroll::Autoscroll,
     Bias, Editor, ToPoint,
 };
-use gpui::{
-    actions, impl_internal_actions, Action, AppContext, Global, ViewContext, WindowContext,
-};
+use gpui::{actions, impl_internal_actions, Action, App, Context, Global, Window};
 use language::Point;
 use multi_buffer::MultiBufferRow;
 use regex::Regex;
@@ -101,27 +99,27 @@ impl Deref for WrappedAction {
     }
 }
 
-pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
-    Vim::action(editor, cx, |vim, _: &VisualCommand, cx| {
-        let Some(workspace) = vim.workspace(cx) else {
+pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
+    Vim::action(editor, cx, |vim, _: &VisualCommand, window, cx| {
+        let Some(workspace) = vim.workspace(window) else {
             return;
         };
         workspace.update(cx, |workspace, cx| {
-            command_palette::CommandPalette::toggle(workspace, "'<,'>", cx);
+            command_palette::CommandPalette::toggle(workspace, "'<,'>", window, cx);
         })
     });
 
-    Vim::action(editor, cx, |vim, _: &ShellCommand, cx| {
-        let Some(workspace) = vim.workspace(cx) else {
+    Vim::action(editor, cx, |vim, _: &ShellCommand, window, cx| {
+        let Some(workspace) = vim.workspace(window) else {
             return;
         };
         workspace.update(cx, |workspace, cx| {
-            command_palette::CommandPalette::toggle(workspace, "'<,'>!", cx);
+            command_palette::CommandPalette::toggle(workspace, "'<,'>!", window, cx);
         })
     });
 
-    Vim::action(editor, cx, |vim, _: &CountCommand, cx| {
-        let Some(workspace) = vim.workspace(cx) else {
+    Vim::action(editor, cx, |vim, _: &CountCommand, window, cx| {
+        let Some(workspace) = vim.workspace(window) else {
             return;
         };
         let count = Vim::take_count(cx).unwrap_or(1);
@@ -131,27 +129,27 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
             ".".to_string()
         };
         workspace.update(cx, |workspace, cx| {
-            command_palette::CommandPalette::toggle(workspace, &n, cx);
+            command_palette::CommandPalette::toggle(workspace, &n, window, cx);
         })
     });
 
-    Vim::action(editor, cx, |vim, action: &GoToLine, cx| {
-        vim.switch_mode(Mode::Normal, false, cx);
-        let result = vim.update_editor(cx, |vim, editor, cx| {
-            let snapshot = editor.snapshot(cx);
-            let buffer_row = action.range.head().buffer_row(vim, editor, cx)?;
+    Vim::action(editor, cx, |vim, action: &GoToLine, window, cx| {
+        vim.switch_mode(Mode::Normal, false, window, cx);
+        let result = vim.update_editor(window, cx, |vim, editor, window, cx| {
+            let snapshot = editor.snapshot(window, cx);
+            let buffer_row = action.range.head().buffer_row(vim, editor, window, cx)?;
             let current = editor.selections.newest::<Point>(cx);
             let target = snapshot
                 .buffer_snapshot
                 .clip_point(Point::new(buffer_row.0, current.head().column), Bias::Left);
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                 s.select_ranges([target..target]);
             });
 
             anyhow::Ok(())
         });
         if let Some(e @ Err(_)) = result {
-            let Some(workspace) = vim.workspace(cx) else {
+            let Some(workspace) = vim.workspace(window) else {
                 return;
             };
             workspace.update(cx, |workspace, cx| {
@@ -161,10 +159,10 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
         }
     });
 
-    Vim::action(editor, cx, |vim, action: &YankCommand, cx| {
-        vim.update_editor(cx, |vim, editor, cx| {
-            let snapshot = editor.snapshot(cx);
-            if let Ok(range) = action.range.buffer_range(vim, editor, cx) {
+    Vim::action(editor, cx, |vim, action: &YankCommand, window, cx| {
+        vim.update_editor(window, cx, |vim, editor, window, cx| {
+            let snapshot = editor.snapshot(window, cx);
+            if let Ok(range) = action.range.buffer_range(vim, editor, window, cx) {
                 let end = if range.end < snapshot.buffer_snapshot.max_row() {
                     Point::new(range.end.0 + 1, 0)
                 } else {
@@ -181,21 +179,21 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
         });
     });
 
-    Vim::action(editor, cx, |_, action: &WithCount, cx| {
+    Vim::action(editor, cx, |_, action: &WithCount, window, cx| {
         for _ in 0..action.count {
-            cx.dispatch_action(action.action.boxed_clone())
+            window.dispatch_action(action.action.boxed_clone(), cx)
         }
     });
 
-    Vim::action(editor, cx, |vim, action: &WithRange, cx| {
-        let result = vim.update_editor(cx, |vim, editor, cx| {
-            action.range.buffer_range(vim, editor, cx)
+    Vim::action(editor, cx, |vim, action: &WithRange, window, cx| {
+        let result = vim.update_editor(window, cx, |vim, editor, window, cx| {
+            action.range.buffer_range(vim, editor, window, cx)
         });
 
         let range = match result {
             None => return,
             Some(e @ Err(_)) => {
-                let Some(workspace) = vim.workspace(cx) else {
+                let Some(workspace) = vim.workspace(window) else {
                     return;
                 };
                 workspace.update(cx, |workspace, cx| {
@@ -207,24 +205,24 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
         };
 
         let previous_selections = vim
-            .update_editor(cx, |_, editor, cx| {
+            .update_editor(window, cx, |_, editor, window, cx| {
                 let selections = action.restore_selection.then(|| {
                     editor
                         .selections
                         .disjoint_anchor_ranges()
                         .collect::<Vec<_>>()
                 });
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     let end = Point::new(range.end.0, s.buffer().line_len(range.end));
                     s.select_ranges([end..Point::new(range.start.0, 0)]);
                 });
                 selections
             })
             .flatten();
-        cx.dispatch_action(action.action.boxed_clone());
-        cx.defer(move |vim, cx| {
-            vim.update_editor(cx, |_, editor, cx| {
-                editor.change_selections(None, cx, |s| {
+        window.dispatch_action(action.action.boxed_clone(), cx);
+        cx.defer_in(window, move |vim, window, cx| {
+            vim.update_editor(window, cx, |_, editor, window, cx| {
+                editor.change_selections(None, window, cx, |s| {
                     if let Some(previous_selections) = previous_selections {
                         s.select_ranges(previous_selections);
                     } else {
@@ -237,12 +235,12 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
         });
     });
 
-    Vim::action(editor, cx, |vim, action: &OnMatchingLines, cx| {
-        action.run(vim, cx)
+    Vim::action(editor, cx, |vim, action: &OnMatchingLines, window, cx| {
+        action.run(vim, window, cx)
     });
 
-    Vim::action(editor, cx, |vim, action: &ShellExec, cx| {
-        action.run(vim, cx)
+    Vim::action(editor, cx, |vim, action: &ShellExec, window, cx| {
+        action.run(vim, window, cx)
     })
 }
 
@@ -306,7 +304,7 @@ impl VimCommand {
         &self,
         mut query: &str,
         range: &Option<CommandRange>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<Box<dyn Action>> {
         let has_bang = query.ends_with('!');
         if has_bang {
@@ -463,9 +461,10 @@ impl Position {
         &self,
         vim: &Vim,
         editor: &mut Editor,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Result<MultiBufferRow> {
-        let snapshot = editor.snapshot(cx);
+        let snapshot = editor.snapshot(window, cx);
         let target = match self {
             Position::Line { row, offset } => {
                 if let Some(anchor) = editor.active_excerpt(cx).and_then(|(_, buffer, _)| {
@@ -524,11 +523,12 @@ impl CommandRange {
         &self,
         vim: &Vim,
         editor: &mut Editor,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Result<Range<MultiBufferRow>> {
-        let start = self.start.buffer_row(vim, editor, cx)?;
+        let start = self.start.buffer_row(vim, editor, window, cx)?;
         let end = if let Some(end) = self.end.as_ref() {
-            end.buffer_row(vim, editor, cx)?
+            end.buffer_row(vim, editor, window, cx)?
         } else {
             start
         };
@@ -552,7 +552,7 @@ impl CommandRange {
     }
 }
 
-fn generate_commands(_: &AppContext) -> Vec<VimCommand> {
+fn generate_commands(_: &App) -> Vec<VimCommand> {
     vec![
         VimCommand::new(
             ("w", "rite"),
@@ -758,7 +758,7 @@ struct VimCommands(Vec<VimCommand>);
 unsafe impl Sync for VimCommands {}
 impl Global for VimCommands {}
 
-fn commands(cx: &AppContext) -> &Vec<VimCommand> {
+fn commands(cx: &App) -> &Vec<VimCommand> {
     static COMMANDS: OnceLock<VimCommands> = OnceLock::new();
     &COMMANDS
         .get_or_init(|| VimCommands(generate_commands(cx)))
@@ -797,7 +797,7 @@ fn wrap_count(action: Box<dyn Action>, range: &CommandRange) -> Option<Box<dyn A
     })
 }
 
-pub fn command_interceptor(mut input: &str, cx: &AppContext) -> Option<CommandInterceptResult> {
+pub fn command_interceptor(mut input: &str, cx: &App) -> Option<CommandInterceptResult> {
     // NOTE: We also need to support passing arguments to commands like :w
     // (ideally with filename autocompletion).
     while input.starts_with(':') {
@@ -939,7 +939,7 @@ impl OnMatchingLines {
         mut chars: Peekable<Chars>,
         invert: bool,
         range: CommandRange,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<Self> {
         let delimiter = chars.next().filter(|c| {
             !c.is_alphanumeric() && *c != '"' && *c != '|' && *c != '\'' && *c != '!'
@@ -981,15 +981,15 @@ impl OnMatchingLines {
         })
     }
 
-    pub fn run(&self, vim: &mut Vim, cx: &mut ViewContext<Vim>) {
-        let result = vim.update_editor(cx, |vim, editor, cx| {
-            self.range.buffer_range(vim, editor, cx)
+    pub fn run(&self, vim: &mut Vim, window: &mut Window, cx: &mut Context<Vim>) {
+        let result = vim.update_editor(window, cx, |vim, editor, window, cx| {
+            self.range.buffer_range(vim, editor, window, cx)
         });
 
         let range = match result {
             None => return,
             Some(e @ Err(_)) => {
-                let Some(workspace) = vim.workspace(cx) else {
+                let Some(workspace) = vim.workspace(window) else {
                     return;
                 };
                 workspace.update(cx, |workspace, cx| {
@@ -1006,7 +1006,7 @@ impl OnMatchingLines {
         let mut regexes = match Regex::new(&self.search) {
             Ok(regex) => vec![(regex, !self.invert)],
             e @ Err(_) => {
-                let Some(workspace) = vim.workspace(cx) else {
+                let Some(workspace) = vim.workspace(window) else {
                     return;
                 };
                 workspace.update(cx, |workspace, cx| {
@@ -1028,15 +1028,16 @@ impl OnMatchingLines {
             regexes.push((regex, !inner.invert))
         }
 
-        if let Some(pane) = vim.pane(cx) {
+        if let Some(pane) = vim.pane(window, cx) {
             pane.update(cx, |pane, cx| {
                 if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>()
                 {
                     search_bar.update(cx, |search_bar, cx| {
-                        if search_bar.show(cx) {
+                        if search_bar.show(window, cx) {
                             let _ = search_bar.search(
                                 &last_pattern,
                                 Some(SearchOptions::REGEX | SearchOptions::CASE_SENSITIVE),
+                                window,
                                 cx,
                             );
                         }
@@ -1045,15 +1046,15 @@ impl OnMatchingLines {
             });
         };
 
-        vim.update_editor(cx, |_, editor, cx| {
-            let snapshot = editor.snapshot(cx);
+        vim.update_editor(window, cx, |_, editor, window, cx| {
+            let snapshot = editor.snapshot(window, cx);
             let mut row = range.start.0;
 
             let point_range = Point::new(range.start.0, 0)
                 ..snapshot
                     .buffer_snapshot
                     .clip_point(Point::new(range.end.0 + 1, 0), Bias::Left);
-            cx.spawn(|editor, mut cx| async move {
+            cx.spawn_in(window, |editor, mut cx| async move {
                 let new_selections = cx
                     .background_executor()
                     .spawn(async move {
@@ -1088,15 +1089,15 @@ impl OnMatchingLines {
                     return;
                 }
                 editor
-                    .update(&mut cx, |editor, cx| {
-                        editor.start_transaction_at(Instant::now(), cx);
-                        editor.change_selections(None, cx, |s| {
+                    .update_in(&mut cx, |editor, window, cx| {
+                        editor.start_transaction_at(Instant::now(), window, cx);
+                        editor.change_selections(None, window, cx, |s| {
                             s.replace_cursors_with(|_| new_selections);
                         });
-                        cx.dispatch_action(action);
-                        cx.defer(move |editor, cx| {
+                        window.dispatch_action(action, cx);
+                        cx.defer_in(window, move |editor, window, cx| {
                             let newest = editor.selections.newest::<Point>(cx).clone();
-                            editor.change_selections(None, cx, |s| {
+                            editor.change_selections(None, window, cx, |s| {
                                 s.select(vec![newest]);
                             });
                             editor.end_transaction_at(Instant::now(), cx);
@@ -1117,17 +1118,22 @@ pub struct ShellExec {
 }
 
 impl Vim {
-    pub fn cancel_running_command(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn cancel_running_command(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if self.running_command.take().is_some() {
-            self.update_editor(cx, |_, editor, cx| {
-                editor.transact(cx, |editor, _| {
+            self.update_editor(window, cx, |_, editor, window, cx| {
+                editor.transact(window, cx, |editor, _window, _cx| {
                     editor.clear_row_highlights::<ShellExec>();
                 })
             });
         }
     }
 
-    fn prepare_shell_command(&mut self, command: &str, cx: &mut ViewContext<Self>) -> String {
+    fn prepare_shell_command(
+        &mut self,
+        command: &str,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> String {
         let mut ret = String::new();
         // N.B. non-standard escaping rules:
         // * !echo % => "echo README.md"
@@ -1145,7 +1151,7 @@ impl Vim {
             }
             match c {
                 '%' => {
-                    self.update_editor(cx, |_, editor, cx| {
+                    self.update_editor(window, cx, |_, editor, _window, cx| {
                         if let Some((_, buffer, _)) = editor.active_excerpt(cx) {
                             if let Some(file) = buffer.read(cx).file() {
                                 if let Some(local) = file.as_local() {
@@ -1173,21 +1179,22 @@ impl Vim {
         &mut self,
         motion: Motion,
         times: Option<usize>,
-        cx: &mut ViewContext<Vim>,
+        window: &mut Window,
+        cx: &mut Context<Vim>,
     ) {
         self.stop_recording(cx);
-        let Some(workspace) = self.workspace(cx) else {
+        let Some(workspace) = self.workspace(window) else {
             return;
         };
-        let command = self.update_editor(cx, |_, editor, cx| {
-            let snapshot = editor.snapshot(cx);
+        let command = self.update_editor(window, cx, |_, editor, window, cx| {
+            let snapshot = editor.snapshot(window, cx);
             let start = editor.selections.newest_display(cx);
-            let text_layout_details = editor.text_layout_details(cx);
+            let text_layout_details = editor.text_layout_details(window);
             let mut range = motion
                 .range(&snapshot, start.clone(), times, false, &text_layout_details)
                 .unwrap_or(start.range());
             if range.start != start.start {
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.select_ranges([
                         range.start.to_point(&snapshot)..range.start.to_point(&snapshot)
                     ]);
@@ -1204,7 +1211,7 @@ impl Vim {
         });
         if let Some(command) = command {
             workspace.update(cx, |workspace, cx| {
-                command_palette::CommandPalette::toggle(workspace, &command, cx);
+                command_palette::CommandPalette::toggle(workspace, &command, window, cx);
             });
         }
     }
@@ -1213,20 +1220,21 @@ impl Vim {
         &mut self,
         object: Object,
         around: bool,
-        cx: &mut ViewContext<Vim>,
+        window: &mut Window,
+        cx: &mut Context<Vim>,
     ) {
         self.stop_recording(cx);
-        let Some(workspace) = self.workspace(cx) else {
+        let Some(workspace) = self.workspace(window) else {
             return;
         };
-        let command = self.update_editor(cx, |_, editor, cx| {
-            let snapshot = editor.snapshot(cx);
+        let command = self.update_editor(window, cx, |_, editor, window, cx| {
+            let snapshot = editor.snapshot(window, cx);
             let start = editor.selections.newest_display(cx);
             let range = object
                 .range(&snapshot, start.clone(), around)
                 .unwrap_or(start.range());
             if range.start != start.start {
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.select_ranges([
                         range.start.to_point(&snapshot)..range.start.to_point(&snapshot)
                     ]);
@@ -1240,7 +1248,7 @@ impl Vim {
         });
         if let Some(command) = command {
             workspace.update(cx, |workspace, cx| {
-                command_palette::CommandPalette::toggle(workspace, &command, cx);
+                command_palette::CommandPalette::toggle(workspace, &command, window, cx);
             });
         }
     }
@@ -1265,13 +1273,13 @@ impl ShellExec {
         )
     }
 
-    pub fn run(&self, vim: &mut Vim, cx: &mut ViewContext<Vim>) {
-        let Some(workspace) = vim.workspace(cx) else {
+    pub fn run(&self, vim: &mut Vim, window: &mut Window, cx: &mut Context<Vim>) {
+        let Some(workspace) = vim.workspace(window) else {
             return;
         };
 
         let project = workspace.read(cx).project().clone();
-        let command = vim.prepare_shell_command(&self.command, cx);
+        let command = vim.prepare_shell_command(&self.command, window, cx);
 
         if self.range.is_none() && !self.is_read {
             workspace.update(cx, |workspace, cx| {
@@ -1305,10 +1313,10 @@ impl ShellExec {
         let mut input_snapshot = None;
         let mut input_range = None;
         let mut needs_newline_prefix = false;
-        vim.update_editor(cx, |vim, editor, cx| {
+        vim.update_editor(window, cx, |vim, editor, window, cx| {
             let snapshot = editor.buffer().read(cx).snapshot(cx);
             let range = if let Some(range) = self.range.clone() {
-                let Some(range) = range.buffer_range(vim, editor, cx).log_err() else {
+                let Some(range) = range.buffer_range(vim, editor, window, cx).log_err() else {
                     return;
                 };
                 Point::new(range.start.0, 0)
@@ -1364,10 +1372,10 @@ impl ShellExec {
         };
         let is_read = self.is_read;
 
-        let task = cx.spawn(|vim, mut cx| async move {
+        let task = cx.spawn_in(window, |vim, mut cx| async move {
             let Some(mut running) = process.spawn().log_err() else {
-                vim.update(&mut cx, |vim, cx| {
-                    vim.cancel_running_command(cx);
+                vim.update_in(&mut cx, |vim, window, cx| {
+                    vim.cancel_running_command(window, cx);
                 })
                 .log_err();
                 return;
@@ -1395,8 +1403,8 @@ impl ShellExec {
                 .await;
 
             let Some(output) = output.log_err() else {
-                vim.update(&mut cx, |vim, cx| {
-                    vim.cancel_running_command(cx);
+                vim.update_in(&mut cx, |vim, window, cx| {
+                    vim.cancel_running_command(window, cx);
                 })
                 .log_err();
                 return;
@@ -1411,12 +1419,12 @@ impl ShellExec {
                 text.push('\n');
             }
 
-            vim.update(&mut cx, |vim, cx| {
-                vim.update_editor(cx, |_, editor, cx| {
-                    editor.transact(cx, |editor, cx| {
+            vim.update_in(&mut cx, |vim, window, cx| {
+                vim.update_editor(window, cx, |_, editor, window, cx| {
+                    editor.transact(window, cx, |editor, window, cx| {
                         editor.edit([(range.clone(), text)], cx);
                         let snapshot = editor.buffer().read(cx).snapshot(cx);
-                        editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                        editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                             let point = if is_read {
                                 let point = range.end.to_point(&snapshot);
                                 Point::new(point.row.saturating_sub(1), 0)
@@ -1428,7 +1436,7 @@ impl ShellExec {
                         })
                     })
                 });
-                vim.cancel_running_command(cx);
+                vim.cancel_running_command(window, cx);
             })
             .log_err();
         });
@@ -1445,9 +1453,8 @@ mod test {
         test::{NeovimBackedTestContext, VimTestContext},
     };
     use editor::Editor;
-    use gpui::TestAppContext;
+    use gpui::{Context, TestAppContext};
     use indoc::indoc;
-    use ui::ViewContext;
     use workspace::Workspace;
 
     #[gpui::test]
@@ -1545,7 +1552,7 @@ mod test {
     async fn test_command_write(cx: &mut TestAppContext) {
         let mut cx = VimTestContext::new(cx, true).await;
         let path = Path::new("/root/dir/file.rs");
-        let fs = cx.workspace(|workspace, cx| workspace.project().read(cx).fs().clone());
+        let fs = cx.workspace(|workspace, _, cx| workspace.project().read(cx).fs().clone());
 
         cx.simulate_keystrokes("i @ escape");
         cx.simulate_keystrokes(": w enter");
@@ -1573,13 +1580,13 @@ mod test {
         let mut cx = VimTestContext::new(cx, true).await;
 
         cx.simulate_keystrokes(": n e w enter");
-        cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 2));
+        cx.workspace(|workspace, _, cx| assert_eq!(workspace.items(cx).count(), 2));
         cx.simulate_keystrokes(": q enter");
-        cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 1));
+        cx.workspace(|workspace, _, cx| assert_eq!(workspace.items(cx).count(), 1));
         cx.simulate_keystrokes(": n e w enter");
-        cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 2));
+        cx.workspace(|workspace, _, cx| assert_eq!(workspace.items(cx).count(), 2));
         cx.simulate_keystrokes(": q a enter");
-        cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 0));
+        cx.workspace(|workspace, _, cx| assert_eq!(workspace.items(cx).count(), 0));
     }
 
     #[gpui::test]
@@ -1641,7 +1648,7 @@ mod test {
         workspace: &mut Workspace,
         expected_path: &str,
         expected_text: &str,
-        cx: &mut ViewContext<Workspace>,
+        cx: &mut Context<Workspace>,
     ) {
         let active_editor = workspace.active_item_as::<Editor>(cx).unwrap();
 
@@ -1665,12 +1672,12 @@ mod test {
         let mut cx = VimTestContext::new(cx, true).await;
 
         // Assert base state, that we're in /root/dir/file.rs
-        cx.workspace(|workspace, cx| {
+        cx.workspace(|workspace, _, cx| {
             assert_active_item(workspace, "/root/dir/file.rs", "", cx);
         });
 
         // Insert a new file
-        let fs = cx.workspace(|workspace, cx| workspace.project().read(cx).fs().clone());
+        let fs = cx.workspace(|workspace, _, cx| workspace.project().read(cx).fs().clone());
         fs.as_fake()
             .insert_file("/root/dir/file2.rs", "This is file2.rs".as_bytes().to_vec())
             .await;
@@ -1685,13 +1692,14 @@ mod test {
         cx.simulate_keystrokes("g f");
 
         // We now have two items
-        cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 2));
-        cx.workspace(|workspace, cx| {
+        cx.workspace(|workspace, _, cx| assert_eq!(workspace.items(cx).count(), 2));
+        cx.workspace(|workspace, _, cx| {
             assert_active_item(workspace, "/root/dir/file2.rs", "This is file2.rs", cx);
         });
 
         // Update editor to point to `file2.rs`
-        cx.editor = cx.workspace(|workspace, cx| workspace.active_item_as::<Editor>(cx).unwrap());
+        cx.editor =
+            cx.workspace(|workspace, _, cx| workspace.active_item_as::<Editor>(cx).unwrap());
 
         // Put the path to the third file into the currently open buffer,
         // but remove its suffix, because we want that lookup to happen automatically.
@@ -1701,8 +1709,8 @@ mod test {
         cx.simulate_keystrokes("g f");
 
         // We now have three items
-        cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 3));
-        cx.workspace(|workspace, cx| {
+        cx.workspace(|workspace, _, cx| assert_eq!(workspace.items(cx).count(), 3));
+        cx.workspace(|workspace, _, cx| {
             assert_active_item(workspace, "/root/dir/file3.rs", "go to file3", cx);
         });
     }

crates/vim/src/digraph.rs 🔗

@@ -2,12 +2,11 @@ use std::sync::Arc;
 
 use collections::HashMap;
 use editor::Editor;
-use gpui::{impl_actions, AppContext, Keystroke, KeystrokeEvent};
+use gpui::{impl_actions, App, Context, Keystroke, KeystrokeEvent, Window};
 use schemars::JsonSchema;
 use serde::Deserialize;
 use settings::Settings;
 use std::sync::LazyLock;
-use ui::ViewContext;
 
 use crate::{state::Operator, Vim, VimSettings};
 
@@ -17,7 +16,7 @@ mod default;
 struct Literal(String, char);
 impl_actions!(vim, [Literal]);
 
-pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
+pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
     Vim::action(editor, cx, Vim::literal)
 }
 
@@ -31,7 +30,7 @@ static DEFAULT_DIGRAPHS_MAP: LazyLock<HashMap<String, Arc<str>>> = LazyLock::new
     map
 });
 
-fn lookup_digraph(a: char, b: char, cx: &AppContext) -> Arc<str> {
+fn lookup_digraph(a: char, b: char, cx: &App) -> Arc<str> {
     let custom_digraphs = &VimSettings::get_global(cx).custom_digraphs;
     let input = format!("{a}{b}");
     let reversed = format!("{b}{a}");
@@ -50,38 +49,42 @@ impl Vim {
         &mut self,
         first_char: char,
         second_char: char,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let text = lookup_digraph(first_char, second_char, cx);
 
-        self.pop_operator(cx);
+        self.pop_operator(window, cx);
         if self.editor_input_enabled() {
-            self.update_editor(cx, |_, editor, cx| editor.insert(&text, cx));
+            self.update_editor(window, cx, |_, editor, window, cx| {
+                editor.insert(&text, window, cx)
+            });
         } else {
-            self.input_ignored(text, cx);
+            self.input_ignored(text, window, cx);
         }
     }
 
-    fn literal(&mut self, action: &Literal, cx: &mut ViewContext<Self>) {
+    fn literal(&mut self, action: &Literal, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(Operator::Literal { prefix }) = self.active_operator() {
             if let Some(prefix) = prefix {
                 if let Some(keystroke) = Keystroke::parse(&action.0).ok() {
-                    cx.window_context().defer(|cx| {
-                        cx.dispatch_keystroke(keystroke);
+                    window.defer(cx, |window, cx| {
+                        window.dispatch_keystroke(keystroke, cx);
                     });
                 }
-                return self.handle_literal_input(prefix, "", cx);
+                return self.handle_literal_input(prefix, "", window, cx);
             }
         }
 
-        self.insert_literal(Some(action.1), "", cx);
+        self.insert_literal(Some(action.1), "", window, cx);
     }
 
     pub fn handle_literal_keystroke(
         &mut self,
         keystroke_event: &KeystrokeEvent,
         prefix: String,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         // handled by handle_literal_input
         if keystroke_event.keystroke.key_char.is_some() {
@@ -89,17 +92,17 @@ impl Vim {
         };
 
         if prefix.len() > 0 {
-            self.handle_literal_input(prefix, "", cx);
+            self.handle_literal_input(prefix, "", window, cx);
         } else {
-            self.pop_operator(cx);
+            self.pop_operator(window, cx);
         }
 
         // give another chance to handle the binding outside
         // of waiting mode.
         if keystroke_event.action.is_none() {
             let keystroke = keystroke_event.keystroke.clone();
-            cx.window_context().defer(|cx| {
-                cx.dispatch_keystroke(keystroke);
+            window.defer(cx, |window, cx| {
+                window.dispatch_keystroke(keystroke, cx);
             });
         }
         return;
@@ -109,7 +112,8 @@ impl Vim {
         &mut self,
         mut prefix: String,
         text: &str,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let first = prefix.chars().next();
         let next = text.chars().next().unwrap_or(' ');
@@ -119,7 +123,7 @@ impl Vim {
                     prefix.push(next);
                     if prefix.len() == 4 {
                         let ch: char = u8::from_str_radix(&prefix[1..], 8).unwrap_or(255).into();
-                        return self.insert_literal(Some(ch), "", cx);
+                        return self.insert_literal(Some(ch), "", window, cx);
                     }
                 } else {
                     let ch = if prefix.len() > 1 {
@@ -127,7 +131,7 @@ impl Vim {
                     } else {
                         None
                     };
-                    return self.insert_literal(ch, text, cx);
+                    return self.insert_literal(ch, text, window, cx);
                 }
             }
             Some('x' | 'X' | 'u' | 'U') => {
@@ -145,7 +149,7 @@ impl Vim {
                             .ok()
                             .and_then(|n| n.try_into().ok())
                             .unwrap_or('\u{FFFD}');
-                        return self.insert_literal(Some(ch), "", cx);
+                        return self.insert_literal(Some(ch), "", window, cx);
                     }
                 } else {
                     let ch = if prefix.len() > 1 {
@@ -158,7 +162,7 @@ impl Vim {
                     } else {
                         None
                     };
-                    return self.insert_literal(ch, text, cx);
+                    return self.insert_literal(ch, text, window, cx);
                 }
             }
             Some('0'..='9') => {
@@ -166,32 +170,39 @@ impl Vim {
                     prefix.push(next);
                     if prefix.len() == 3 {
                         let ch: char = u8::from_str_radix(&prefix, 10).unwrap_or(255).into();
-                        return self.insert_literal(Some(ch), "", cx);
+                        return self.insert_literal(Some(ch), "", window, cx);
                     }
                 } else {
                     let ch: char = u8::from_str_radix(&prefix, 10).unwrap_or(255).into();
-                    return self.insert_literal(Some(ch), "", cx);
+                    return self.insert_literal(Some(ch), "", window, cx);
                 }
             }
             None if matches!(next, 'o' | 'O' | 'x' | 'X' | 'u' | 'U' | '0'..='9') => {
                 prefix.push(next)
             }
             _ => {
-                return self.insert_literal(None, text, cx);
+                return self.insert_literal(None, text, window, cx);
             }
         };
 
-        self.pop_operator(cx);
+        self.pop_operator(window, cx);
         self.push_operator(
             Operator::Literal {
                 prefix: Some(prefix),
             },
+            window,
             cx,
         );
     }
 
-    fn insert_literal(&mut self, ch: Option<char>, suffix: &str, cx: &mut ViewContext<Self>) {
-        self.pop_operator(cx);
+    fn insert_literal(
+        &mut self,
+        ch: Option<char>,
+        suffix: &str,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.pop_operator(window, cx);
         let mut text = String::new();
         if let Some(c) = ch {
             if c == '\n' {
@@ -203,9 +214,11 @@ impl Vim {
         text.push_str(suffix);
 
         if self.editor_input_enabled() {
-            self.update_editor(cx, |_, editor, cx| editor.insert(&text, cx));
+            self.update_editor(window, cx, |_, editor, window, cx| {
+                editor.insert(&text, window, cx)
+            });
         } else {
-            self.input_ignored(text.into(), cx);
+            self.input_ignored(text.into(), window, cx);
         }
     }
 }

crates/vim/src/helix.rs 🔗

@@ -1,26 +1,31 @@
 use editor::{movement, scroll::Autoscroll, DisplayPoint, Editor};
 use gpui::{actions, Action};
+use gpui::{Context, Window};
 use language::{CharClassifier, CharKind};
-use ui::ViewContext;
 
 use crate::{motion::Motion, state::Mode, Vim};
 
 actions!(vim, [HelixNormalAfter, HelixDelete]);
 
-pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
+pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
     Vim::action(editor, cx, Vim::helix_normal_after);
     Vim::action(editor, cx, Vim::helix_delete);
 }
 
 impl Vim {
-    pub fn helix_normal_after(&mut self, action: &HelixNormalAfter, cx: &mut ViewContext<Self>) {
+    pub fn helix_normal_after(
+        &mut self,
+        action: &HelixNormalAfter,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if self.active_operator().is_some() {
             self.operator_stack.clear();
-            self.sync_vim_settings(cx);
+            self.sync_vim_settings(window, cx);
             return;
         }
         self.stop_recording_immediately(action.boxed_clone(), cx);
-        self.switch_mode(Mode::HelixNormal, false, cx);
+        self.switch_mode(Mode::HelixNormal, false, window, cx);
         return;
     }
 
@@ -28,19 +33,21 @@ impl Vim {
         &mut self,
         motion: Motion,
         times: Option<usize>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
-        self.helix_move_cursor(motion, times, cx);
+        self.helix_move_cursor(motion, times, window, cx);
     }
 
     fn helix_find_range_forward(
         &mut self,
         times: Option<usize>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
         mut is_boundary: impl FnMut(char, char, &CharClassifier) -> bool,
     ) {
-        self.update_editor(cx, |_, editor, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                 s.move_with(|map, selection| {
                     let times = times.unwrap_or(1);
 
@@ -88,11 +95,12 @@ impl Vim {
     fn helix_find_range_backward(
         &mut self,
         times: Option<usize>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
         mut is_boundary: impl FnMut(char, char, &CharClassifier) -> bool,
     ) {
-        self.update_editor(cx, |_, editor, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                 s.move_with(|map, selection| {
                     let times = times.unwrap_or(1);
 
@@ -145,11 +153,12 @@ impl Vim {
         &mut self,
         motion: Motion,
         times: Option<usize>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
-        self.update_editor(cx, |_, editor, cx| {
-            let text_layout_details = editor.text_layout_details(cx);
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            let text_layout_details = editor.text_layout_details(window);
+            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                 s.move_with(|map, selection| {
                     let goal = selection.goal;
                     let cursor = if selection.is_empty() || selection.reversed {
@@ -172,11 +181,12 @@ impl Vim {
         &mut self,
         motion: Motion,
         times: Option<usize>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match motion {
             Motion::NextWordStart { ignore_punctuation } => {
-                self.helix_find_range_forward(times, cx, |left, right, classifier| {
+                self.helix_find_range_forward(times, window, cx, |left, right, classifier| {
                     let left_kind = classifier.kind_with(left, ignore_punctuation);
                     let right_kind = classifier.kind_with(right, ignore_punctuation);
                     let at_newline = right == '\n';
@@ -188,7 +198,7 @@ impl Vim {
                 })
             }
             Motion::NextWordEnd { ignore_punctuation } => {
-                self.helix_find_range_forward(times, cx, |left, right, classifier| {
+                self.helix_find_range_forward(times, window, cx, |left, right, classifier| {
                     let left_kind = classifier.kind_with(left, ignore_punctuation);
                     let right_kind = classifier.kind_with(right, ignore_punctuation);
                     let at_newline = right == '\n';
@@ -200,7 +210,7 @@ impl Vim {
                 })
             }
             Motion::PreviousWordStart { ignore_punctuation } => {
-                self.helix_find_range_backward(times, cx, |left, right, classifier| {
+                self.helix_find_range_backward(times, window, cx, |left, right, classifier| {
                     let left_kind = classifier.kind_with(left, ignore_punctuation);
                     let right_kind = classifier.kind_with(right, ignore_punctuation);
                     let at_newline = right == '\n';
@@ -212,7 +222,7 @@ impl Vim {
                 })
             }
             Motion::PreviousWordEnd { ignore_punctuation } => {
-                self.helix_find_range_backward(times, cx, |left, right, classifier| {
+                self.helix_find_range_backward(times, window, cx, |left, right, classifier| {
                     let left_kind = classifier.kind_with(left, ignore_punctuation);
                     let right_kind = classifier.kind_with(right, ignore_punctuation);
                     let at_newline = right == '\n';
@@ -224,18 +234,18 @@ impl Vim {
                     found
                 })
             }
-            _ => self.helix_move_and_collapse(motion, times, cx),
+            _ => self.helix_move_and_collapse(motion, times, window, cx),
         }
     }
 
-    pub fn helix_delete(&mut self, _: &HelixDelete, cx: &mut ViewContext<Self>) {
-        self.store_visual_marks(cx);
-        self.update_editor(cx, |vim, editor, cx| {
+    pub fn helix_delete(&mut self, _: &HelixDelete, window: &mut Window, cx: &mut Context<Self>) {
+        self.store_visual_marks(window, cx);
+        self.update_editor(window, cx, |vim, editor, window, cx| {
             // Fixup selections so they have helix's semantics.
             // Specifically:
             //  - Make sure that each cursor acts as a 1 character wide selection
-            editor.transact(cx, |editor, cx| {
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+            editor.transact(window, cx, |editor, window, cx| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.move_with(|map, selection| {
                         if selection.is_empty() && !selection.reversed {
                             selection.end = movement::right(map, selection.end);
@@ -245,7 +255,7 @@ impl Vim {
             });
 
             vim.copy_selections_content(editor, false, cx);
-            editor.insert("", cx);
+            editor.insert("", window, cx);
         });
     }
 }

crates/vim/src/indent.rs 🔗

@@ -2,8 +2,8 @@ use crate::{motion::Motion, object::Object, state::Mode, Vim};
 use collections::HashMap;
 use editor::{display_map::ToDisplayPoint, Bias, Editor};
 use gpui::actions;
+use gpui::{Context, Window};
 use language::SelectionGoal;
-use ui::ViewContext;
 
 #[derive(PartialEq, Eq)]
 pub(crate) enum IndentDirection {
@@ -14,58 +14,58 @@ pub(crate) enum IndentDirection {
 
 actions!(vim, [Indent, Outdent, AutoIndent]);
 
-pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
-    Vim::action(editor, cx, |vim, _: &Indent, cx| {
+pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
+    Vim::action(editor, cx, |vim, _: &Indent, window, cx| {
         vim.record_current_action(cx);
         let count = Vim::take_count(cx).unwrap_or(1);
-        vim.store_visual_marks(cx);
-        vim.update_editor(cx, |vim, editor, cx| {
-            editor.transact(cx, |editor, cx| {
+        vim.store_visual_marks(window, cx);
+        vim.update_editor(window, cx, |vim, editor, window, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 let original_positions = vim.save_selection_starts(editor, cx);
                 for _ in 0..count {
-                    editor.indent(&Default::default(), cx);
+                    editor.indent(&Default::default(), window, cx);
                 }
-                vim.restore_selection_cursors(editor, cx, original_positions);
+                vim.restore_selection_cursors(editor, window, cx, original_positions);
             });
         });
         if vim.mode.is_visual() {
-            vim.switch_mode(Mode::Normal, true, cx)
+            vim.switch_mode(Mode::Normal, true, window, cx)
         }
     });
 
-    Vim::action(editor, cx, |vim, _: &Outdent, cx| {
+    Vim::action(editor, cx, |vim, _: &Outdent, window, cx| {
         vim.record_current_action(cx);
         let count = Vim::take_count(cx).unwrap_or(1);
-        vim.store_visual_marks(cx);
-        vim.update_editor(cx, |vim, editor, cx| {
-            editor.transact(cx, |editor, cx| {
+        vim.store_visual_marks(window, cx);
+        vim.update_editor(window, cx, |vim, editor, window, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 let original_positions = vim.save_selection_starts(editor, cx);
                 for _ in 0..count {
-                    editor.outdent(&Default::default(), cx);
+                    editor.outdent(&Default::default(), window, cx);
                 }
-                vim.restore_selection_cursors(editor, cx, original_positions);
+                vim.restore_selection_cursors(editor, window, cx, original_positions);
             });
         });
         if vim.mode.is_visual() {
-            vim.switch_mode(Mode::Normal, true, cx)
+            vim.switch_mode(Mode::Normal, true, window, cx)
         }
     });
 
-    Vim::action(editor, cx, |vim, _: &AutoIndent, cx| {
+    Vim::action(editor, cx, |vim, _: &AutoIndent, window, cx| {
         vim.record_current_action(cx);
         let count = Vim::take_count(cx).unwrap_or(1);
-        vim.store_visual_marks(cx);
-        vim.update_editor(cx, |vim, editor, cx| {
-            editor.transact(cx, |editor, cx| {
+        vim.store_visual_marks(window, cx);
+        vim.update_editor(window, cx, |vim, editor, window, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 let original_positions = vim.save_selection_starts(editor, cx);
                 for _ in 0..count {
-                    editor.autoindent(&Default::default(), cx);
+                    editor.autoindent(&Default::default(), window, cx);
                 }
-                vim.restore_selection_cursors(editor, cx, original_positions);
+                vim.restore_selection_cursors(editor, window, cx, original_positions);
             });
         });
         if vim.mode.is_visual() {
-            vim.switch_mode(Mode::Normal, true, cx)
+            vim.switch_mode(Mode::Normal, true, window, cx)
         }
     });
 }
@@ -76,14 +76,15 @@ impl Vim {
         motion: Motion,
         times: Option<usize>,
         dir: IndentDirection,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.stop_recording(cx);
-        self.update_editor(cx, |_, editor, cx| {
-            let text_layout_details = editor.text_layout_details(cx);
-            editor.transact(cx, |editor, cx| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            let text_layout_details = editor.text_layout_details(window);
+            editor.transact(window, cx, |editor, window, cx| {
                 let mut selection_starts: HashMap<_, _> = Default::default();
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
                         selection_starts.insert(selection.id, anchor);
@@ -91,11 +92,11 @@ impl Vim {
                     });
                 });
                 match dir {
-                    IndentDirection::In => editor.indent(&Default::default(), cx),
-                    IndentDirection::Out => editor.outdent(&Default::default(), cx),
-                    IndentDirection::Auto => editor.autoindent(&Default::default(), cx),
+                    IndentDirection::In => editor.indent(&Default::default(), window, cx),
+                    IndentDirection::Out => editor.outdent(&Default::default(), window, cx),
+                    IndentDirection::Auto => editor.autoindent(&Default::default(), window, cx),
                 }
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = selection_starts.remove(&selection.id).unwrap();
                         selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);
@@ -110,13 +111,14 @@ impl Vim {
         object: Object,
         around: bool,
         dir: IndentDirection,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.stop_recording(cx);
-        self.update_editor(cx, |_, editor, cx| {
-            editor.transact(cx, |editor, cx| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 let mut original_positions: HashMap<_, _> = Default::default();
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
                         original_positions.insert(selection.id, anchor);
@@ -124,11 +126,11 @@ impl Vim {
                     });
                 });
                 match dir {
-                    IndentDirection::In => editor.indent(&Default::default(), cx),
-                    IndentDirection::Out => editor.outdent(&Default::default(), cx),
-                    IndentDirection::Auto => editor.autoindent(&Default::default(), cx),
+                    IndentDirection::In => editor.indent(&Default::default(), window, cx),
+                    IndentDirection::Out => editor.outdent(&Default::default(), window, cx),
+                    IndentDirection::Auto => editor.autoindent(&Default::default(), window, cx),
                 }
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = original_positions.remove(&selection.id).unwrap();
                         selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);

crates/vim/src/insert.rs 🔗

@@ -1,44 +1,54 @@
 use crate::{state::Mode, Vim};
 use editor::{scroll::Autoscroll, Bias, Editor};
-use gpui::{actions, Action, ViewContext};
+use gpui::{actions, Action, Context, Window};
 use language::SelectionGoal;
 
 actions!(vim, [NormalBefore, TemporaryNormal]);
 
-pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
+pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
     Vim::action(editor, cx, Vim::normal_before);
     Vim::action(editor, cx, Vim::temporary_normal);
 }
 
 impl Vim {
-    fn normal_before(&mut self, action: &NormalBefore, cx: &mut ViewContext<Self>) {
+    fn normal_before(
+        &mut self,
+        action: &NormalBefore,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if self.active_operator().is_some() {
             self.operator_stack.clear();
-            self.sync_vim_settings(cx);
+            self.sync_vim_settings(window, cx);
             return;
         }
         let count = Vim::take_count(cx).unwrap_or(1);
         self.stop_recording_immediately(action.boxed_clone(), cx);
         if count <= 1 || Vim::globals(cx).dot_replaying {
-            self.create_mark("^".into(), false, cx);
-            self.update_editor(cx, |_, editor, cx| {
-                editor.dismiss_menus_and_popups(false, cx);
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+            self.create_mark("^".into(), false, window, cx);
+            self.update_editor(window, cx, |_, editor, window, cx| {
+                editor.dismiss_menus_and_popups(false, window, cx);
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.move_cursors_with(|map, mut cursor, _| {
                         *cursor.column_mut() = cursor.column().saturating_sub(1);
                         (map.clip_point(cursor, Bias::Left), SelectionGoal::None)
                     });
                 });
             });
-            self.switch_mode(Mode::Normal, false, cx);
+            self.switch_mode(Mode::Normal, false, window, cx);
             return;
         }
 
-        self.repeat(true, cx)
+        self.repeat(true, window, cx)
     }
 
-    fn temporary_normal(&mut self, _: &TemporaryNormal, cx: &mut ViewContext<Self>) {
-        self.switch_mode(Mode::Normal, true, cx);
+    fn temporary_normal(
+        &mut self,
+        _: &TemporaryNormal,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.switch_mode(Mode::Normal, true, window, cx);
         self.temp_mode = true;
     }
 }

crates/vim/src/mode_indicator.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{div, Element, Render, Subscription, View, ViewContext, WeakView};
+use gpui::{div, Context, Element, Entity, Render, Subscription, WeakEntity, Window};
 use itertools::Itertools;
 use workspace::{item::ItemHandle, ui::prelude::*, StatusItemView};
 
@@ -6,27 +6,30 @@ use crate::{Vim, VimEvent, VimGlobals};
 
 /// The ModeIndicator displays the current mode in the status bar.
 pub struct ModeIndicator {
-    vim: Option<WeakView<Vim>>,
+    vim: Option<WeakEntity<Vim>>,
     pending_keys: Option<String>,
     vim_subscription: Option<Subscription>,
 }
 
 impl ModeIndicator {
     /// Construct a new mode indicator in this window.
-    pub fn new(cx: &mut ViewContext<Self>) -> Self {
-        cx.observe_pending_input(|this, cx| {
-            this.update_pending_keys(cx);
+    pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
+        cx.observe_pending_input(window, |this: &mut Self, window, cx| {
+            this.update_pending_keys(window);
             cx.notify();
         })
         .detach();
 
-        let handle = cx.view().clone();
-        let window = cx.window_handle();
-        cx.observe_new_views::<Vim>(move |_, cx| {
-            if cx.window_handle() != window {
+        let handle = cx.model().clone();
+        let window_handle = window.window_handle();
+        cx.observe_new::<Vim>(move |_, window, cx| {
+            let Some(window) = window else {
+                return;
+            };
+            if window.window_handle() != window_handle {
                 return;
             }
-            let vim = cx.view().clone();
+            let vim = cx.model().clone();
             handle.update(cx, |_, cx| {
                 cx.subscribe(&vim, |mode_indicator, vim, event, cx| match event {
                     VimEvent::Focused => {
@@ -47,8 +50,8 @@ impl ModeIndicator {
         }
     }
 
-    fn update_pending_keys(&mut self, cx: &mut ViewContext<Self>) {
-        self.pending_keys = cx.pending_input_keystrokes().map(|keystrokes| {
+    fn update_pending_keys(&mut self, window: &mut Window) {
+        self.pending_keys = window.pending_input_keystrokes().map(|keystrokes| {
             keystrokes
                 .iter()
                 .map(|keystroke| format!("{}", keystroke))
@@ -56,11 +59,11 @@ impl ModeIndicator {
         });
     }
 
-    fn vim(&self) -> Option<View<Vim>> {
+    fn vim(&self) -> Option<Entity<Vim>> {
         self.vim.as_ref().and_then(|vim| vim.upgrade())
     }
 
-    fn current_operators_description(&self, vim: View<Vim>, cx: &mut ViewContext<Self>) -> String {
+    fn current_operators_description(&self, vim: Entity<Vim>, cx: &mut Context<Self>) -> String {
         let recording = Vim::globals(cx)
             .recording_register
             .map(|reg| format!("recording @{reg} "))
@@ -90,7 +93,7 @@ impl ModeIndicator {
 }
 
 impl Render for ModeIndicator {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let vim = self.vim();
         let Some(vim) = vim else {
             return div().into_any();
@@ -125,7 +128,8 @@ impl StatusItemView for ModeIndicator {
     fn set_active_pane_item(
         &mut self,
         _active_pane_item: Option<&dyn ItemHandle>,
-        _cx: &mut ViewContext<Self>,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
     ) {
     }
 }

crates/vim/src/motion.rs 🔗

@@ -6,7 +6,7 @@ use editor::{
     scroll::Autoscroll,
     Anchor, Bias, DisplayPoint, Editor, RowExt, ToOffset, ToPoint,
 };
-use gpui::{actions, impl_actions, px, ViewContext};
+use gpui::{actions, impl_actions, px, Context, Window};
 use language::{CharKind, Point, Selection, SelectionGoal};
 use multi_buffer::MultiBufferRow;
 use schemars::JsonSchema;
@@ -304,223 +304,242 @@ actions!(
     ]
 );
 
-pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
-    Vim::action(editor, cx, |vim, _: &Left, cx| vim.motion(Motion::Left, cx));
-    Vim::action(editor, cx, |vim, _: &Backspace, cx| {
-        vim.motion(Motion::Backspace, cx)
+pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
+    Vim::action(editor, cx, |vim, _: &Left, window, cx| {
+        vim.motion(Motion::Left, window, cx)
     });
-    Vim::action(editor, cx, |vim, action: &Down, cx| {
+    Vim::action(editor, cx, |vim, _: &Backspace, window, cx| {
+        vim.motion(Motion::Backspace, window, cx)
+    });
+    Vim::action(editor, cx, |vim, action: &Down, window, cx| {
         vim.motion(
             Motion::Down {
                 display_lines: action.display_lines,
             },
+            window,
             cx,
         )
     });
-    Vim::action(editor, cx, |vim, action: &Up, cx| {
+    Vim::action(editor, cx, |vim, action: &Up, window, cx| {
         vim.motion(
             Motion::Up {
                 display_lines: action.display_lines,
             },
+            window,
             cx,
         )
     });
-    Vim::action(editor, cx, |vim, _: &Right, cx| {
-        vim.motion(Motion::Right, cx)
-    });
-    Vim::action(editor, cx, |vim, _: &Space, cx| {
-        vim.motion(Motion::Space, cx)
+    Vim::action(editor, cx, |vim, _: &Right, window, cx| {
+        vim.motion(Motion::Right, window, cx)
     });
-    Vim::action(editor, cx, |vim, action: &FirstNonWhitespace, cx| {
-        vim.motion(
-            Motion::FirstNonWhitespace {
-                display_lines: action.display_lines,
-            },
-            cx,
-        )
+    Vim::action(editor, cx, |vim, _: &Space, window, cx| {
+        vim.motion(Motion::Space, window, cx)
     });
-    Vim::action(editor, cx, |vim, action: &StartOfLine, cx| {
+    Vim::action(
+        editor,
+        cx,
+        |vim, action: &FirstNonWhitespace, window, cx| {
+            vim.motion(
+                Motion::FirstNonWhitespace {
+                    display_lines: action.display_lines,
+                },
+                window,
+                cx,
+            )
+        },
+    );
+    Vim::action(editor, cx, |vim, action: &StartOfLine, window, cx| {
         vim.motion(
             Motion::StartOfLine {
                 display_lines: action.display_lines,
             },
+            window,
             cx,
         )
     });
-    Vim::action(editor, cx, |vim, action: &EndOfLine, cx| {
+    Vim::action(editor, cx, |vim, action: &EndOfLine, window, cx| {
         vim.motion(
             Motion::EndOfLine {
                 display_lines: action.display_lines,
             },
+            window,
             cx,
         )
     });
-    Vim::action(editor, cx, |vim, _: &CurrentLine, cx| {
-        vim.motion(Motion::CurrentLine, cx)
+    Vim::action(editor, cx, |vim, _: &CurrentLine, window, cx| {
+        vim.motion(Motion::CurrentLine, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &StartOfParagraph, cx| {
-        vim.motion(Motion::StartOfParagraph, cx)
+    Vim::action(editor, cx, |vim, _: &StartOfParagraph, window, cx| {
+        vim.motion(Motion::StartOfParagraph, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &EndOfParagraph, cx| {
-        vim.motion(Motion::EndOfParagraph, cx)
+    Vim::action(editor, cx, |vim, _: &EndOfParagraph, window, cx| {
+        vim.motion(Motion::EndOfParagraph, window, cx)
     });
 
-    Vim::action(editor, cx, |vim, _: &SentenceForward, cx| {
-        vim.motion(Motion::SentenceForward, cx)
+    Vim::action(editor, cx, |vim, _: &SentenceForward, window, cx| {
+        vim.motion(Motion::SentenceForward, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &SentenceBackward, cx| {
-        vim.motion(Motion::SentenceBackward, cx)
+    Vim::action(editor, cx, |vim, _: &SentenceBackward, window, cx| {
+        vim.motion(Motion::SentenceBackward, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &StartOfDocument, cx| {
-        vim.motion(Motion::StartOfDocument, cx)
+    Vim::action(editor, cx, |vim, _: &StartOfDocument, window, cx| {
+        vim.motion(Motion::StartOfDocument, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &EndOfDocument, cx| {
-        vim.motion(Motion::EndOfDocument, cx)
+    Vim::action(editor, cx, |vim, _: &EndOfDocument, window, cx| {
+        vim.motion(Motion::EndOfDocument, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &Matching, cx| {
-        vim.motion(Motion::Matching, cx)
+    Vim::action(editor, cx, |vim, _: &Matching, window, cx| {
+        vim.motion(Motion::Matching, window, cx)
     });
     Vim::action(
         editor,
         cx,
-        |vim, &UnmatchedForward { char }: &UnmatchedForward, cx| {
-            vim.motion(Motion::UnmatchedForward { char }, cx)
+        |vim, &UnmatchedForward { char }: &UnmatchedForward, window, cx| {
+            vim.motion(Motion::UnmatchedForward { char }, window, cx)
         },
     );
     Vim::action(
         editor,
         cx,
-        |vim, &UnmatchedBackward { char }: &UnmatchedBackward, cx| {
-            vim.motion(Motion::UnmatchedBackward { char }, cx)
+        |vim, &UnmatchedBackward { char }: &UnmatchedBackward, window, cx| {
+            vim.motion(Motion::UnmatchedBackward { char }, window, cx)
         },
     );
     Vim::action(
         editor,
         cx,
-        |vim, &NextWordStart { ignore_punctuation }: &NextWordStart, cx| {
-            vim.motion(Motion::NextWordStart { ignore_punctuation }, cx)
+        |vim, &NextWordStart { ignore_punctuation }: &NextWordStart, window, cx| {
+            vim.motion(Motion::NextWordStart { ignore_punctuation }, window, cx)
         },
     );
     Vim::action(
         editor,
         cx,
-        |vim, &NextWordEnd { ignore_punctuation }: &NextWordEnd, cx| {
-            vim.motion(Motion::NextWordEnd { ignore_punctuation }, cx)
+        |vim, &NextWordEnd { ignore_punctuation }: &NextWordEnd, window, cx| {
+            vim.motion(Motion::NextWordEnd { ignore_punctuation }, window, cx)
         },
     );
     Vim::action(
         editor,
         cx,
-        |vim, &PreviousWordStart { ignore_punctuation }: &PreviousWordStart, cx| {
-            vim.motion(Motion::PreviousWordStart { ignore_punctuation }, cx)
+        |vim, &PreviousWordStart { ignore_punctuation }: &PreviousWordStart, window, cx| {
+            vim.motion(Motion::PreviousWordStart { ignore_punctuation }, window, cx)
         },
     );
     Vim::action(
         editor,
         cx,
-        |vim, &PreviousWordEnd { ignore_punctuation }, cx| {
-            vim.motion(Motion::PreviousWordEnd { ignore_punctuation }, cx)
+        |vim, &PreviousWordEnd { ignore_punctuation }, window, cx| {
+            vim.motion(Motion::PreviousWordEnd { ignore_punctuation }, window, cx)
         },
     );
     Vim::action(
         editor,
         cx,
-        |vim, &NextSubwordStart { ignore_punctuation }: &NextSubwordStart, cx| {
-            vim.motion(Motion::NextSubwordStart { ignore_punctuation }, cx)
+        |vim, &NextSubwordStart { ignore_punctuation }: &NextSubwordStart, window, cx| {
+            vim.motion(Motion::NextSubwordStart { ignore_punctuation }, window, cx)
         },
     );
     Vim::action(
         editor,
         cx,
-        |vim, &NextSubwordEnd { ignore_punctuation }: &NextSubwordEnd, cx| {
-            vim.motion(Motion::NextSubwordEnd { ignore_punctuation }, cx)
+        |vim, &NextSubwordEnd { ignore_punctuation }: &NextSubwordEnd, window, cx| {
+            vim.motion(Motion::NextSubwordEnd { ignore_punctuation }, window, cx)
         },
     );
     Vim::action(
         editor,
         cx,
-        |vim, &PreviousSubwordStart { ignore_punctuation }: &PreviousSubwordStart, cx| {
-            vim.motion(Motion::PreviousSubwordStart { ignore_punctuation }, cx)
+        |vim, &PreviousSubwordStart { ignore_punctuation }: &PreviousSubwordStart, window, cx| {
+            vim.motion(
+                Motion::PreviousSubwordStart { ignore_punctuation },
+                window,
+                cx,
+            )
         },
     );
     Vim::action(
         editor,
         cx,
-        |vim, &PreviousSubwordEnd { ignore_punctuation }, cx| {
-            vim.motion(Motion::PreviousSubwordEnd { ignore_punctuation }, cx)
+        |vim, &PreviousSubwordEnd { ignore_punctuation }, window, cx| {
+            vim.motion(
+                Motion::PreviousSubwordEnd { ignore_punctuation },
+                window,
+                cx,
+            )
         },
     );
-    Vim::action(editor, cx, |vim, &NextLineStart, cx| {
-        vim.motion(Motion::NextLineStart, cx)
+    Vim::action(editor, cx, |vim, &NextLineStart, window, cx| {
+        vim.motion(Motion::NextLineStart, window, cx)
     });
-    Vim::action(editor, cx, |vim, &PreviousLineStart, cx| {
-        vim.motion(Motion::PreviousLineStart, cx)
+    Vim::action(editor, cx, |vim, &PreviousLineStart, window, cx| {
+        vim.motion(Motion::PreviousLineStart, window, cx)
     });
-    Vim::action(editor, cx, |vim, &StartOfLineDownward, cx| {
-        vim.motion(Motion::StartOfLineDownward, cx)
+    Vim::action(editor, cx, |vim, &StartOfLineDownward, window, cx| {
+        vim.motion(Motion::StartOfLineDownward, window, cx)
     });
-    Vim::action(editor, cx, |vim, &EndOfLineDownward, cx| {
-        vim.motion(Motion::EndOfLineDownward, cx)
+    Vim::action(editor, cx, |vim, &EndOfLineDownward, window, cx| {
+        vim.motion(Motion::EndOfLineDownward, window, cx)
     });
-    Vim::action(editor, cx, |vim, &GoToColumn, cx| {
-        vim.motion(Motion::GoToColumn, cx)
+    Vim::action(editor, cx, |vim, &GoToColumn, window, cx| {
+        vim.motion(Motion::GoToColumn, window, cx)
     });
 
-    Vim::action(editor, cx, |vim, _: &RepeatFind, cx| {
+    Vim::action(editor, cx, |vim, _: &RepeatFind, window, cx| {
         if let Some(last_find) = Vim::globals(cx).last_find.clone().map(Box::new) {
-            vim.motion(Motion::RepeatFind { last_find }, cx);
+            vim.motion(Motion::RepeatFind { last_find }, window, cx);
         }
     });
 
-    Vim::action(editor, cx, |vim, _: &RepeatFindReversed, cx| {
+    Vim::action(editor, cx, |vim, _: &RepeatFindReversed, window, cx| {
         if let Some(last_find) = Vim::globals(cx).last_find.clone().map(Box::new) {
-            vim.motion(Motion::RepeatFindReversed { last_find }, cx);
+            vim.motion(Motion::RepeatFindReversed { last_find }, window, cx);
         }
     });
-    Vim::action(editor, cx, |vim, &WindowTop, cx| {
-        vim.motion(Motion::WindowTop, cx)
+    Vim::action(editor, cx, |vim, &WindowTop, window, cx| {
+        vim.motion(Motion::WindowTop, window, cx)
     });
-    Vim::action(editor, cx, |vim, &WindowMiddle, cx| {
-        vim.motion(Motion::WindowMiddle, cx)
+    Vim::action(editor, cx, |vim, &WindowMiddle, window, cx| {
+        vim.motion(Motion::WindowMiddle, window, cx)
     });
-    Vim::action(editor, cx, |vim, &WindowBottom, cx| {
-        vim.motion(Motion::WindowBottom, cx)
+    Vim::action(editor, cx, |vim, &WindowBottom, window, cx| {
+        vim.motion(Motion::WindowBottom, window, cx)
     });
 
-    Vim::action(editor, cx, |vim, &PreviousSectionStart, cx| {
-        vim.motion(Motion::PreviousSectionStart, cx)
+    Vim::action(editor, cx, |vim, &PreviousSectionStart, window, cx| {
+        vim.motion(Motion::PreviousSectionStart, window, cx)
     });
-    Vim::action(editor, cx, |vim, &NextSectionStart, cx| {
-        vim.motion(Motion::NextSectionStart, cx)
+    Vim::action(editor, cx, |vim, &NextSectionStart, window, cx| {
+        vim.motion(Motion::NextSectionStart, window, cx)
     });
-    Vim::action(editor, cx, |vim, &PreviousSectionEnd, cx| {
-        vim.motion(Motion::PreviousSectionEnd, cx)
+    Vim::action(editor, cx, |vim, &PreviousSectionEnd, window, cx| {
+        vim.motion(Motion::PreviousSectionEnd, window, cx)
     });
-    Vim::action(editor, cx, |vim, &NextSectionEnd, cx| {
-        vim.motion(Motion::NextSectionEnd, cx)
+    Vim::action(editor, cx, |vim, &NextSectionEnd, window, cx| {
+        vim.motion(Motion::NextSectionEnd, window, cx)
     });
-    Vim::action(editor, cx, |vim, &PreviousMethodStart, cx| {
-        vim.motion(Motion::PreviousMethodStart, cx)
+    Vim::action(editor, cx, |vim, &PreviousMethodStart, window, cx| {
+        vim.motion(Motion::PreviousMethodStart, window, cx)
     });
-    Vim::action(editor, cx, |vim, &NextMethodStart, cx| {
-        vim.motion(Motion::NextMethodStart, cx)
+    Vim::action(editor, cx, |vim, &NextMethodStart, window, cx| {
+        vim.motion(Motion::NextMethodStart, window, cx)
     });
-    Vim::action(editor, cx, |vim, &PreviousMethodEnd, cx| {
-        vim.motion(Motion::PreviousMethodEnd, cx)
+    Vim::action(editor, cx, |vim, &PreviousMethodEnd, window, cx| {
+        vim.motion(Motion::PreviousMethodEnd, window, cx)
     });
-    Vim::action(editor, cx, |vim, &NextMethodEnd, cx| {
-        vim.motion(Motion::NextMethodEnd, cx)
+    Vim::action(editor, cx, |vim, &NextMethodEnd, window, cx| {
+        vim.motion(Motion::NextMethodEnd, window, cx)
     });
-    Vim::action(editor, cx, |vim, &NextComment, cx| {
-        vim.motion(Motion::NextComment, cx)
+    Vim::action(editor, cx, |vim, &NextComment, window, cx| {
+        vim.motion(Motion::NextComment, window, cx)
     });
-    Vim::action(editor, cx, |vim, &PreviousComment, cx| {
-        vim.motion(Motion::PreviousComment, cx)
+    Vim::action(editor, cx, |vim, &PreviousComment, window, cx| {
+        vim.motion(Motion::PreviousComment, window, cx)
     });
 }
 
 impl Vim {
-    pub(crate) fn search_motion(&mut self, m: Motion, cx: &mut ViewContext<Self>) {
+    pub(crate) fn search_motion(&mut self, m: Motion, window: &mut Window, cx: &mut Context<Self>) {
         if let Motion::ZedSearchResult {
             prior_selections, ..
         } = &m
@@ -528,8 +547,8 @@ impl Vim {
             match self.mode {
                 Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
                     if !prior_selections.is_empty() {
-                        self.update_editor(cx, |_, editor, cx| {
-                            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                        self.update_editor(window, cx, |_, editor, window, cx| {
+                            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                                 s.select_ranges(prior_selections.iter().cloned())
                             })
                         });
@@ -545,16 +564,16 @@ impl Vim {
             }
         }
 
-        self.motion(m, cx)
+        self.motion(m, window, cx)
     }
 
-    pub(crate) fn motion(&mut self, motion: Motion, cx: &mut ViewContext<Self>) {
+    pub(crate) fn motion(&mut self, motion: Motion, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(Operator::FindForward { .. })
         | Some(Operator::Sneak { .. })
         | Some(Operator::SneakBackward { .. })
         | Some(Operator::FindBackward { .. }) = self.active_operator()
         {
-            self.pop_operator(cx);
+            self.pop_operator(window, cx);
         }
 
         let count = Vim::take_count(cx);
@@ -567,18 +586,18 @@ impl Vim {
                         target: Some(SurroundsType::Motion(motion)),
                     });
                 } else {
-                    self.normal_motion(motion.clone(), active_operator.clone(), count, cx)
+                    self.normal_motion(motion.clone(), active_operator.clone(), count, window, cx)
                 }
             }
             Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
-                self.visual_motion(motion.clone(), count, cx)
+                self.visual_motion(motion.clone(), count, window, cx)
             }
 
-            Mode::HelixNormal => self.helix_normal_motion(motion.clone(), count, cx),
+            Mode::HelixNormal => self.helix_normal_motion(motion.clone(), count, window, cx),
         }
-        self.clear_operator(cx);
+        self.clear_operator(window, cx);
         if let Some(operator) = waiting_operator {
-            self.push_operator(operator, cx);
+            self.push_operator(operator, window, cx);
             Vim::globals(cx).pre_count = count
         }
     }
@@ -3415,7 +3434,7 @@ mod test {
             Mode::Normal,
         );
 
-        cx.update_editor(|editor, cx| {
+        cx.update_editor(|editor, _window, cx| {
             let range = editor.selections.newest_anchor().range();
             let inlay_text = "  field: int,\n  field2: string\n  field3: float";
             let inlay = Inlay::inline_completion(1, range.start, inlay_text);

crates/vim/src/normal.rs 🔗

@@ -29,7 +29,7 @@ use editor::Anchor;
 use editor::Bias;
 use editor::Editor;
 use editor::{display_map::ToDisplayPoint, movement};
-use gpui::{actions, ViewContext};
+use gpui::{actions, Context, Window};
 use language::{Point, SelectionGoal, ToPoint};
 use log::error;
 use multi_buffer::MultiBufferRow;
@@ -62,7 +62,7 @@ actions!(
     ]
 );
 
-pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
+pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
     Vim::action(editor, cx, Vim::insert_after);
     Vim::action(editor, cx, Vim::insert_before);
     Vim::action(editor, cx, Vim::insert_first_non_whitespace);
@@ -78,17 +78,17 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
     Vim::action(editor, cx, Vim::paste);
     Vim::action(editor, cx, Vim::show_location);
 
-    Vim::action(editor, cx, |vim, _: &DeleteLeft, cx| {
+    Vim::action(editor, cx, |vim, _: &DeleteLeft, window, cx| {
         vim.record_current_action(cx);
         let times = Vim::take_count(cx);
-        vim.delete_motion(Motion::Left, times, cx);
+        vim.delete_motion(Motion::Left, times, window, cx);
     });
-    Vim::action(editor, cx, |vim, _: &DeleteRight, cx| {
+    Vim::action(editor, cx, |vim, _: &DeleteRight, window, cx| {
         vim.record_current_action(cx);
         let times = Vim::take_count(cx);
-        vim.delete_motion(Motion::Right, times, cx);
+        vim.delete_motion(Motion::Right, times, window, cx);
     });
-    Vim::action(editor, cx, |vim, _: &ChangeToEndOfLine, cx| {
+    Vim::action(editor, cx, |vim, _: &ChangeToEndOfLine, window, cx| {
         vim.start_recording(cx);
         let times = Vim::take_count(cx);
         vim.change_motion(
@@ -96,10 +96,11 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
                 display_lines: false,
             },
             times,
+            window,
             cx,
         );
     });
-    Vim::action(editor, cx, |vim, _: &DeleteToEndOfLine, cx| {
+    Vim::action(editor, cx, |vim, _: &DeleteToEndOfLine, window, cx| {
         vim.record_current_action(cx);
         let times = Vim::take_count(cx);
         vim.delete_motion(
@@ -107,30 +108,31 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
                 display_lines: false,
             },
             times,
+            window,
             cx,
         );
     });
-    Vim::action(editor, cx, |vim, _: &JoinLines, cx| {
-        vim.join_lines_impl(true, cx);
+    Vim::action(editor, cx, |vim, _: &JoinLines, window, cx| {
+        vim.join_lines_impl(true, window, cx);
     });
 
-    Vim::action(editor, cx, |vim, _: &JoinLinesNoWhitespace, cx| {
-        vim.join_lines_impl(false, cx);
+    Vim::action(editor, cx, |vim, _: &JoinLinesNoWhitespace, window, cx| {
+        vim.join_lines_impl(false, window, cx);
     });
 
-    Vim::action(editor, cx, |vim, _: &Undo, cx| {
+    Vim::action(editor, cx, |vim, _: &Undo, window, cx| {
         let times = Vim::take_count(cx);
-        vim.update_editor(cx, |_, editor, cx| {
+        vim.update_editor(window, cx, |_, editor, window, cx| {
             for _ in 0..times.unwrap_or(1) {
-                editor.undo(&editor::actions::Undo, cx);
+                editor.undo(&editor::actions::Undo, window, cx);
             }
         });
     });
-    Vim::action(editor, cx, |vim, _: &Redo, cx| {
+    Vim::action(editor, cx, |vim, _: &Redo, window, cx| {
         let times = Vim::take_count(cx);
-        vim.update_editor(cx, |_, editor, cx| {
+        vim.update_editor(window, cx, |_, editor, window, cx| {
             for _ in 0..times.unwrap_or(1) {
-                editor.redo(&editor::actions::Redo, cx);
+                editor.redo(&editor::actions::Redo, window, cx);
             }
         });
     });
@@ -148,75 +150,84 @@ impl Vim {
         motion: Motion,
         operator: Option<Operator>,
         times: Option<usize>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         match operator {
-            None => self.move_cursor(motion, times, cx),
-            Some(Operator::Change) => self.change_motion(motion, times, cx),
-            Some(Operator::Delete) => self.delete_motion(motion, times, cx),
-            Some(Operator::Yank) => self.yank_motion(motion, times, cx),
+            None => self.move_cursor(motion, times, window, cx),
+            Some(Operator::Change) => self.change_motion(motion, times, window, cx),
+            Some(Operator::Delete) => self.delete_motion(motion, times, window, cx),
+            Some(Operator::Yank) => self.yank_motion(motion, times, window, cx),
             Some(Operator::AddSurrounds { target: None }) => {}
-            Some(Operator::Indent) => self.indent_motion(motion, times, IndentDirection::In, cx),
-            Some(Operator::Rewrap) => self.rewrap_motion(motion, times, cx),
-            Some(Operator::Outdent) => self.indent_motion(motion, times, IndentDirection::Out, cx),
+            Some(Operator::Indent) => {
+                self.indent_motion(motion, times, IndentDirection::In, window, cx)
+            }
+            Some(Operator::Rewrap) => self.rewrap_motion(motion, times, window, cx),
+            Some(Operator::Outdent) => {
+                self.indent_motion(motion, times, IndentDirection::Out, window, cx)
+            }
             Some(Operator::AutoIndent) => {
-                self.indent_motion(motion, times, IndentDirection::Auto, cx)
+                self.indent_motion(motion, times, IndentDirection::Auto, window, cx)
             }
-            Some(Operator::ShellCommand) => self.shell_command_motion(motion, times, cx),
+            Some(Operator::ShellCommand) => self.shell_command_motion(motion, times, window, cx),
             Some(Operator::Lowercase) => {
-                self.change_case_motion(motion, times, CaseTarget::Lowercase, cx)
+                self.change_case_motion(motion, times, CaseTarget::Lowercase, window, cx)
             }
             Some(Operator::Uppercase) => {
-                self.change_case_motion(motion, times, CaseTarget::Uppercase, cx)
+                self.change_case_motion(motion, times, CaseTarget::Uppercase, window, cx)
             }
             Some(Operator::OppositeCase) => {
-                self.change_case_motion(motion, times, CaseTarget::OppositeCase, cx)
+                self.change_case_motion(motion, times, CaseTarget::OppositeCase, window, cx)
+            }
+            Some(Operator::ToggleComments) => {
+                self.toggle_comments_motion(motion, times, window, cx)
             }
-            Some(Operator::ToggleComments) => self.toggle_comments_motion(motion, times, cx),
             Some(operator) => {
                 // Can't do anything for text objects, Ignoring
                 error!("Unexpected normal mode motion operator: {:?}", operator)
             }
         }
         // Exit temporary normal mode (if active).
-        self.exit_temporary_normal(cx);
+        self.exit_temporary_normal(window, cx);
     }
 
-    pub fn normal_object(&mut self, object: Object, cx: &mut ViewContext<Self>) {
+    pub fn normal_object(&mut self, object: Object, window: &mut Window, cx: &mut Context<Self>) {
         let mut waiting_operator: Option<Operator> = None;
         match self.maybe_pop_operator() {
             Some(Operator::Object { around }) => match self.maybe_pop_operator() {
-                Some(Operator::Change) => self.change_object(object, around, cx),
-                Some(Operator::Delete) => self.delete_object(object, around, cx),
-                Some(Operator::Yank) => self.yank_object(object, around, cx),
+                Some(Operator::Change) => self.change_object(object, around, window, cx),
+                Some(Operator::Delete) => self.delete_object(object, around, window, cx),
+                Some(Operator::Yank) => self.yank_object(object, around, window, cx),
                 Some(Operator::Indent) => {
-                    self.indent_object(object, around, IndentDirection::In, cx)
+                    self.indent_object(object, around, IndentDirection::In, window, cx)
                 }
                 Some(Operator::Outdent) => {
-                    self.indent_object(object, around, IndentDirection::Out, cx)
+                    self.indent_object(object, around, IndentDirection::Out, window, cx)
                 }
                 Some(Operator::AutoIndent) => {
-                    self.indent_object(object, around, IndentDirection::Auto, cx)
+                    self.indent_object(object, around, IndentDirection::Auto, window, cx)
                 }
                 Some(Operator::ShellCommand) => {
-                    self.shell_command_object(object, around, cx);
+                    self.shell_command_object(object, around, window, cx);
                 }
-                Some(Operator::Rewrap) => self.rewrap_object(object, around, cx),
+                Some(Operator::Rewrap) => self.rewrap_object(object, around, window, cx),
                 Some(Operator::Lowercase) => {
-                    self.change_case_object(object, around, CaseTarget::Lowercase, cx)
+                    self.change_case_object(object, around, CaseTarget::Lowercase, window, cx)
                 }
                 Some(Operator::Uppercase) => {
-                    self.change_case_object(object, around, CaseTarget::Uppercase, cx)
+                    self.change_case_object(object, around, CaseTarget::Uppercase, window, cx)
                 }
                 Some(Operator::OppositeCase) => {
-                    self.change_case_object(object, around, CaseTarget::OppositeCase, cx)
+                    self.change_case_object(object, around, CaseTarget::OppositeCase, window, cx)
                 }
                 Some(Operator::AddSurrounds { target: None }) => {
                     waiting_operator = Some(Operator::AddSurrounds {
                         target: Some(SurroundsType::Object(object, around)),
                     });
                 }
-                Some(Operator::ToggleComments) => self.toggle_comments_object(object, around, cx),
+                Some(Operator::ToggleComments) => {
+                    self.toggle_comments_object(object, around, window, cx)
+                }
                 _ => {
                     // Can't do anything for namespace operators. Ignoring
                 }
@@ -225,7 +236,7 @@ impl Vim {
                 waiting_operator = Some(Operator::DeleteSurrounds);
             }
             Some(Operator::ChangeSurrounds { target: None }) => {
-                if self.check_and_move_to_valid_bracket_pair(object, cx) {
+                if self.check_and_move_to_valid_bracket_pair(object, window, cx) {
                     waiting_operator = Some(Operator::ChangeSurrounds {
                         target: Some(object),
                     });
@@ -235,9 +246,9 @@ impl Vim {
                 // Can't do anything with change/delete/yank/surrounds and text objects. Ignoring
             }
         }
-        self.clear_operator(cx);
+        self.clear_operator(window, cx);
         if let Some(operator) = waiting_operator {
-            self.push_operator(operator, cx);
+            self.push_operator(operator, window, cx);
         }
     }
 
@@ -245,11 +256,12 @@ impl Vim {
         &mut self,
         motion: Motion,
         times: Option<usize>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
-        self.update_editor(cx, |_, editor, cx| {
-            let text_layout_details = editor.text_layout_details(cx);
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            let text_layout_details = editor.text_layout_details(window);
+            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                 s.move_cursors_with(|map, cursor, goal| {
                     motion
                         .move_point(map, cursor, goal, times, &text_layout_details)
@@ -259,30 +271,31 @@ impl Vim {
         });
     }
 
-    fn insert_after(&mut self, _: &InsertAfter, cx: &mut ViewContext<Self>) {
+    fn insert_after(&mut self, _: &InsertAfter, window: &mut Window, cx: &mut Context<Self>) {
         self.start_recording(cx);
-        self.switch_mode(Mode::Insert, false, cx);
-        self.update_editor(cx, |_, editor, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+        self.switch_mode(Mode::Insert, false, window, cx);
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                 s.move_cursors_with(|map, cursor, _| (right(map, cursor, 1), SelectionGoal::None));
             });
         });
     }
 
-    fn insert_before(&mut self, _: &InsertBefore, cx: &mut ViewContext<Self>) {
+    fn insert_before(&mut self, _: &InsertBefore, window: &mut Window, cx: &mut Context<Self>) {
         self.start_recording(cx);
-        self.switch_mode(Mode::Insert, false, cx);
+        self.switch_mode(Mode::Insert, false, window, cx);
     }
 
     fn insert_first_non_whitespace(
         &mut self,
         _: &InsertFirstNonWhitespace,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.start_recording(cx);
-        self.switch_mode(Mode::Insert, false, cx);
-        self.update_editor(cx, |_, editor, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+        self.switch_mode(Mode::Insert, false, window, cx);
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                 s.move_cursors_with(|map, cursor, _| {
                     (
                         first_non_whitespace(map, false, cursor),
@@ -293,11 +306,16 @@ impl Vim {
         });
     }
 
-    fn insert_end_of_line(&mut self, _: &InsertEndOfLine, cx: &mut ViewContext<Self>) {
+    fn insert_end_of_line(
+        &mut self,
+        _: &InsertEndOfLine,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.start_recording(cx);
-        self.switch_mode(Mode::Insert, false, cx);
-        self.update_editor(cx, |_, editor, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+        self.switch_mode(Mode::Insert, false, window, cx);
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                 s.move_cursors_with(|map, cursor, _| {
                     (next_line_end(map, cursor, 1), SelectionGoal::None)
                 });
@@ -305,23 +323,33 @@ impl Vim {
         });
     }
 
-    fn insert_at_previous(&mut self, _: &InsertAtPrevious, cx: &mut ViewContext<Self>) {
+    fn insert_at_previous(
+        &mut self,
+        _: &InsertAtPrevious,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.start_recording(cx);
-        self.switch_mode(Mode::Insert, false, cx);
-        self.update_editor(cx, |vim, editor, cx| {
+        self.switch_mode(Mode::Insert, false, window, cx);
+        self.update_editor(window, cx, |vim, editor, window, cx| {
             if let Some(marks) = vim.marks.get("^") {
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.select_anchor_ranges(marks.iter().map(|mark| *mark..*mark))
                 });
             }
         });
     }
 
-    fn insert_line_above(&mut self, _: &InsertLineAbove, cx: &mut ViewContext<Self>) {
+    fn insert_line_above(
+        &mut self,
+        _: &InsertLineAbove,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.start_recording(cx);
-        self.switch_mode(Mode::Insert, false, cx);
-        self.update_editor(cx, |_, editor, cx| {
-            editor.transact(cx, |editor, cx| {
+        self.switch_mode(Mode::Insert, false, window, cx);
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 let selections = editor.selections.all::<Point>(cx);
                 let snapshot = editor.buffer().read(cx).snapshot(cx);
 
@@ -342,7 +370,7 @@ impl Vim {
                     })
                     .collect::<Vec<_>>();
                 editor.edit_with_autoindent(edits, cx);
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.move_cursors_with(|map, cursor, _| {
                         let previous_line = motion::start_of_relative_buffer_row(map, cursor, -1);
                         let insert_point = motion::end_of_line(map, false, previous_line, 1);
@@ -353,12 +381,17 @@ impl Vim {
         });
     }
 
-    fn insert_line_below(&mut self, _: &InsertLineBelow, cx: &mut ViewContext<Self>) {
+    fn insert_line_below(
+        &mut self,
+        _: &InsertLineBelow,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.start_recording(cx);
-        self.switch_mode(Mode::Insert, false, cx);
-        self.update_editor(cx, |_, editor, cx| {
-            let text_layout_details = editor.text_layout_details(cx);
-            editor.transact(cx, |editor, cx| {
+        self.switch_mode(Mode::Insert, false, window, cx);
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            let text_layout_details = editor.text_layout_details(window);
+            editor.transact(window, cx, |editor, window, cx| {
                 let selections = editor.selections.all::<Point>(cx);
                 let snapshot = editor.buffer().read(cx).snapshot(cx);
 
@@ -378,7 +411,7 @@ impl Vim {
                         (end_of_line..end_of_line, "\n".to_string() + &indent)
                     })
                     .collect::<Vec<_>>();
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.maybe_move_cursors_with(|map, cursor, goal| {
                         Motion::CurrentLine.move_point(
                             map,
@@ -394,7 +427,12 @@ impl Vim {
         });
     }
 
-    fn join_lines_impl(&mut self, insert_whitespace: bool, cx: &mut ViewContext<Self>) {
+    fn join_lines_impl(
+        &mut self,
+        insert_whitespace: bool,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.record_current_action(cx);
         let mut times = Vim::take_count(cx).unwrap_or(1);
         if self.mode.is_visual() {
@@ -404,26 +442,26 @@ impl Vim {
             times -= 1;
         }
 
-        self.update_editor(cx, |_, editor, cx| {
-            editor.transact(cx, |editor, cx| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 for _ in 0..times {
-                    editor.join_lines_impl(insert_whitespace, cx)
+                    editor.join_lines_impl(insert_whitespace, window, cx)
                 }
             })
         });
         if self.mode.is_visual() {
-            self.switch_mode(Mode::Normal, true, cx)
+            self.switch_mode(Mode::Normal, true, window, cx)
         }
     }
 
-    fn yank_line(&mut self, _: &YankLine, cx: &mut ViewContext<Self>) {
+    fn yank_line(&mut self, _: &YankLine, window: &mut Window, cx: &mut Context<Self>) {
         let count = Vim::take_count(cx);
-        self.yank_motion(motion::Motion::CurrentLine, count, cx)
+        self.yank_motion(motion::Motion::CurrentLine, count, window, cx)
     }
 
-    fn show_location(&mut self, _: &ShowLocation, cx: &mut ViewContext<Self>) {
+    fn show_location(&mut self, _: &ShowLocation, window: &mut Window, cx: &mut Context<Self>) {
         let count = Vim::take_count(cx);
-        self.update_editor(cx, |vim, editor, cx| {
+        self.update_editor(window, cx, |vim, editor, _window, cx| {
             let selection = editor.selections.newest_anchor();
             if let Some((_, buffer, _)) = editor.active_excerpt(cx) {
                 let filename = if let Some(file) = buffer.read(cx).file() {
@@ -460,26 +498,31 @@ impl Vim {
         });
     }
 
-    fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
+    fn toggle_comments(&mut self, _: &ToggleComments, window: &mut Window, cx: &mut Context<Self>) {
         self.record_current_action(cx);
-        self.store_visual_marks(cx);
-        self.update_editor(cx, |vim, editor, cx| {
-            editor.transact(cx, |editor, cx| {
+        self.store_visual_marks(window, cx);
+        self.update_editor(window, cx, |vim, editor, window, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 let original_positions = vim.save_selection_starts(editor, cx);
-                editor.toggle_comments(&Default::default(), cx);
-                vim.restore_selection_cursors(editor, cx, original_positions);
+                editor.toggle_comments(&Default::default(), window, cx);
+                vim.restore_selection_cursors(editor, window, cx, original_positions);
             });
         });
         if self.mode.is_visual() {
-            self.switch_mode(Mode::Normal, true, cx)
+            self.switch_mode(Mode::Normal, true, window, cx)
         }
     }
 
-    pub(crate) fn normal_replace(&mut self, text: Arc<str>, cx: &mut ViewContext<Self>) {
+    pub(crate) fn normal_replace(
+        &mut self,
+        text: Arc<str>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let count = Vim::take_count(cx).unwrap_or(1);
         self.stop_recording(cx);
-        self.update_editor(cx, |_, editor, cx| {
-            editor.transact(cx, |editor, cx| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 editor.set_clip_at_line_ends(false, cx);
                 let (map, display_selections) = editor.selections.all_display(cx);
 
@@ -503,7 +546,7 @@ impl Vim {
 
                 editor.edit(edits, cx);
                 editor.set_clip_at_line_ends(true, cx);
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         let point = movement::saturating_left(map, selection.head());
                         selection.collapse_to(point, SelectionGoal::None)
@@ -511,13 +554,14 @@ impl Vim {
                 });
             });
         });
-        self.pop_operator(cx);
+        self.pop_operator(window, cx);
     }
 
     pub fn save_selection_starts(
         &self,
         editor: &Editor,
-        cx: &mut ViewContext<Editor>,
+
+        cx: &mut Context<Editor>,
     ) -> HashMap<usize, Anchor> {
         let (map, selections) = editor.selections.all_display(cx);
         selections
@@ -534,10 +578,11 @@ impl Vim {
     pub fn restore_selection_cursors(
         &self,
         editor: &mut Editor,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
         mut positions: HashMap<usize, Anchor>,
     ) {
-        editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+        editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
             s.move_with(|map, selection| {
                 if let Some(anchor) = positions.remove(&selection.id) {
                     selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);
@@ -546,9 +591,9 @@ impl Vim {
         });
     }
 
-    fn exit_temporary_normal(&mut self, cx: &mut ViewContext<Self>) {
+    fn exit_temporary_normal(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if self.temp_mode {
-            self.switch_mode(Mode::Insert, true, cx);
+            self.switch_mode(Mode::Insert, true, window, cx);
         }
     }
 }
@@ -1385,7 +1430,7 @@ mod test {
     #[gpui::test]
     async fn test_subword_motions(cx: &mut gpui::TestAppContext) {
         let mut cx = VimTestContext::new(cx, true).await;
-        cx.update(|cx| {
+        cx.update(|_, cx| {
             cx.bind_keys(vec![
                 KeyBinding::new(
                     "w",
@@ -1469,7 +1514,7 @@ mod test {
         let mut cx = NeovimBackedTestContext::new(cx).await;
         cx.set_neovim_option("textwidth=5").await;
 
-        cx.update(|cx| {
+        cx.update(|_, cx| {
             SettingsStore::update_global(cx, |settings, cx| {
                 settings.update_user_settings::<AllLanguageSettings>(cx, |settings| {
                     settings.defaults.preferred_line_length = Some(5);

crates/vim/src/normal/case.rs 🔗

@@ -1,6 +1,6 @@
 use collections::HashMap;
 use editor::{display_map::ToDisplayPoint, scroll::Autoscroll};
-use gpui::ViewContext;
+use gpui::{Context, Window};
 use language::{Bias, Point, SelectionGoal};
 use multi_buffer::MultiBufferRow;
 
@@ -24,15 +24,16 @@ impl Vim {
         motion: Motion,
         times: Option<usize>,
         mode: CaseTarget,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.stop_recording(cx);
-        self.update_editor(cx, |_, editor, cx| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
             editor.set_clip_at_line_ends(false, cx);
-            let text_layout_details = editor.text_layout_details(cx);
-            editor.transact(cx, |editor, cx| {
+            let text_layout_details = editor.text_layout_details(window);
+            editor.transact(window, cx, |editor, window, cx| {
                 let mut selection_starts: HashMap<_, _> = Default::default();
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = map.display_point_to_anchor(selection.head(), Bias::Left);
                         selection_starts.insert(selection.id, anchor);
@@ -40,13 +41,17 @@ impl Vim {
                     });
                 });
                 match mode {
-                    CaseTarget::Lowercase => editor.convert_to_lower_case(&Default::default(), cx),
-                    CaseTarget::Uppercase => editor.convert_to_upper_case(&Default::default(), cx),
+                    CaseTarget::Lowercase => {
+                        editor.convert_to_lower_case(&Default::default(), window, cx)
+                    }
+                    CaseTarget::Uppercase => {
+                        editor.convert_to_upper_case(&Default::default(), window, cx)
+                    }
                     CaseTarget::OppositeCase => {
-                        editor.convert_to_opposite_case(&Default::default(), cx)
+                        editor.convert_to_opposite_case(&Default::default(), window, cx)
                     }
                 }
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = selection_starts.remove(&selection.id).unwrap();
                         selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);
@@ -62,13 +67,14 @@ impl Vim {
         object: Object,
         around: bool,
         mode: CaseTarget,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.stop_recording(cx);
-        self.update_editor(cx, |_, editor, cx| {
-            editor.transact(cx, |editor, cx| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 let mut original_positions: HashMap<_, _> = Default::default();
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         object.expand_selection(map, selection, around);
                         original_positions.insert(
@@ -78,13 +84,17 @@ impl Vim {
                     });
                 });
                 match mode {
-                    CaseTarget::Lowercase => editor.convert_to_lower_case(&Default::default(), cx),
-                    CaseTarget::Uppercase => editor.convert_to_upper_case(&Default::default(), cx),
+                    CaseTarget::Lowercase => {
+                        editor.convert_to_lower_case(&Default::default(), window, cx)
+                    }
+                    CaseTarget::Uppercase => {
+                        editor.convert_to_upper_case(&Default::default(), window, cx)
+                    }
                     CaseTarget::OppositeCase => {
-                        editor.convert_to_opposite_case(&Default::default(), cx)
+                        editor.convert_to_opposite_case(&Default::default(), window, cx)
                     }
                 }
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = original_positions.remove(&selection.id).unwrap();
                         selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);
@@ -94,8 +104,8 @@ impl Vim {
         });
     }
 
-    pub fn change_case(&mut self, _: &ChangeCase, cx: &mut ViewContext<Self>) {
-        self.manipulate_text(cx, |c| {
+    pub fn change_case(&mut self, _: &ChangeCase, window: &mut Window, cx: &mut Context<Self>) {
+        self.manipulate_text(window, cx, |c| {
             if c.is_lowercase() {
                 c.to_uppercase().collect::<Vec<char>>()
             } else {
@@ -104,23 +114,33 @@ impl Vim {
         })
     }
 
-    pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
-        self.manipulate_text(cx, |c| c.to_uppercase().collect::<Vec<char>>())
+    pub fn convert_to_upper_case(
+        &mut self,
+        _: &ConvertToUpperCase,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.manipulate_text(window, cx, |c| c.to_uppercase().collect::<Vec<char>>())
     }
 
-    pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
-        self.manipulate_text(cx, |c| c.to_lowercase().collect::<Vec<char>>())
+    pub fn convert_to_lower_case(
+        &mut self,
+        _: &ConvertToLowerCase,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.manipulate_text(window, cx, |c| c.to_lowercase().collect::<Vec<char>>())
     }
 
-    fn manipulate_text<F>(&mut self, cx: &mut ViewContext<Self>, transform: F)
+    fn manipulate_text<F>(&mut self, window: &mut Window, cx: &mut Context<Self>, transform: F)
     where
         F: Fn(char) -> Vec<char> + Copy,
     {
         self.record_current_action(cx);
-        self.store_visual_marks(cx);
+        self.store_visual_marks(window, cx);
         let count = Vim::take_count(cx).unwrap_or(1) as u32;
 
-        self.update_editor(cx, |vim, editor, cx| {
+        self.update_editor(window, cx, |vim, editor, window, cx| {
             let mut ranges = Vec::new();
             let mut cursor_positions = Vec::new();
             let snapshot = editor.buffer().read(cx).snapshot(cx);
@@ -162,7 +182,7 @@ impl Vim {
                     }
                 }
             }
-            editor.transact(cx, |editor, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 for range in ranges.into_iter().rev() {
                     let snapshot = editor.buffer().read(cx).snapshot(cx);
                     let text = snapshot
@@ -172,12 +192,12 @@ impl Vim {
                         .collect::<String>();
                     editor.edit([(range, text)], cx)
                 }
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.select_ranges(cursor_positions)
                 })
             });
         });
-        self.switch_mode(Mode::Normal, true, cx)
+        self.switch_mode(Mode::Normal, true, window, cx)
     }
 }
 

crates/vim/src/normal/change.rs 🔗

@@ -10,15 +10,16 @@ use editor::{
     scroll::Autoscroll,
     Bias, DisplayPoint,
 };
+use gpui::{Context, Window};
 use language::Selection;
-use ui::ViewContext;
 
 impl Vim {
     pub fn change_motion(
         &mut self,
         motion: Motion,
         times: Option<usize>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         // Some motions ignore failure when switching to normal mode
         let mut motion_succeeded = matches!(
@@ -29,12 +30,12 @@ impl Vim {
                 | Motion::Backspace
                 | Motion::StartOfLine { .. }
         );
-        self.update_editor(cx, |vim, editor, cx| {
-            let text_layout_details = editor.text_layout_details(cx);
-            editor.transact(cx, |editor, cx| {
+        self.update_editor(window, cx, |vim, editor, window, cx| {
+            let text_layout_details = editor.text_layout_details(window);
+            editor.transact(window, cx, |editor, window, cx| {
                 // We are swapping to insert mode anyway. Just set the line end clipping behavior now
                 editor.set_clip_at_line_ends(false, cx);
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.move_with(|map, selection| {
                         motion_succeeded |= match motion {
                             Motion::NextWordStart { ignore_punctuation }
@@ -76,41 +77,47 @@ impl Vim {
                     });
                 });
                 vim.copy_selections_content(editor, motion.linewise(), cx);
-                editor.insert("", cx);
-                editor.refresh_inline_completion(true, false, cx);
+                editor.insert("", window, cx);
+                editor.refresh_inline_completion(true, false, window, cx);
             });
         });
 
         if motion_succeeded {
-            self.switch_mode(Mode::Insert, false, cx)
+            self.switch_mode(Mode::Insert, false, window, cx)
         } else {
-            self.switch_mode(Mode::Normal, false, cx)
+            self.switch_mode(Mode::Normal, false, window, cx)
         }
     }
 
-    pub fn change_object(&mut self, object: Object, around: bool, cx: &mut ViewContext<Self>) {
+    pub fn change_object(
+        &mut self,
+        object: Object,
+        around: bool,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let mut objects_found = false;
-        self.update_editor(cx, |vim, editor, cx| {
+        self.update_editor(window, cx, |vim, editor, window, cx| {
             // We are swapping to insert mode anyway. Just set the line end clipping behavior now
             editor.set_clip_at_line_ends(false, cx);
-            editor.transact(cx, |editor, cx| {
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+            editor.transact(window, cx, |editor, window, cx| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.move_with(|map, selection| {
                         objects_found |= object.expand_selection(map, selection, around);
                     });
                 });
                 if objects_found {
                     vim.copy_selections_content(editor, false, cx);
-                    editor.insert("", cx);
-                    editor.refresh_inline_completion(true, false, cx);
+                    editor.insert("", window, cx);
+                    editor.refresh_inline_completion(true, false, window, cx);
                 }
             });
         });
 
         if objects_found {
-            self.switch_mode(Mode::Insert, false, cx);
+            self.switch_mode(Mode::Insert, false, window, cx);
         } else {
-            self.switch_mode(Mode::Normal, false, cx);
+            self.switch_mode(Mode::Normal, false, window, cx);
         }
     }
 }

crates/vim/src/normal/delete.rs 🔗

@@ -5,24 +5,25 @@ use editor::{
     scroll::Autoscroll,
     Bias, DisplayPoint,
 };
+use gpui::{Context, Window};
 use language::{Point, Selection};
 use multi_buffer::MultiBufferRow;
-use ui::ViewContext;
 
 impl Vim {
     pub fn delete_motion(
         &mut self,
         motion: Motion,
         times: Option<usize>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.stop_recording(cx);
-        self.update_editor(cx, |vim, editor, cx| {
-            let text_layout_details = editor.text_layout_details(cx);
-            editor.transact(cx, |editor, cx| {
+        self.update_editor(window, cx, |vim, editor, window, cx| {
+            let text_layout_details = editor.text_layout_details(window);
+            editor.transact(window, cx, |editor, window, cx| {
                 editor.set_clip_at_line_ends(false, cx);
                 let mut original_columns: HashMap<_, _> = Default::default();
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let original_head = selection.head();
                         original_columns.insert(selection.id, original_head.column());
@@ -60,11 +61,11 @@ impl Vim {
                     });
                 });
                 vim.copy_selections_content(editor, motion.linewise(), cx);
-                editor.insert("", cx);
+                editor.insert("", window, cx);
 
                 // Fixup cursor position after the deletion
                 editor.set_clip_at_line_ends(true, cx);
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let mut cursor = selection.head();
                         if motion.linewise() {
@@ -76,20 +77,26 @@ impl Vim {
                         selection.collapse_to(cursor, selection.goal)
                     });
                 });
-                editor.refresh_inline_completion(true, false, cx);
+                editor.refresh_inline_completion(true, false, window, cx);
             });
         });
     }
 
-    pub fn delete_object(&mut self, object: Object, around: bool, cx: &mut ViewContext<Self>) {
+    pub fn delete_object(
+        &mut self,
+        object: Object,
+        around: bool,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.stop_recording(cx);
-        self.update_editor(cx, |vim, editor, cx| {
-            editor.transact(cx, |editor, cx| {
+        self.update_editor(window, cx, |vim, editor, window, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 editor.set_clip_at_line_ends(false, cx);
                 // Emulates behavior in vim where if we expanded backwards to include a newline
                 // the cursor gets set back to the start of the line
                 let mut should_move_to_start: HashSet<_> = Default::default();
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.move_with(|map, selection| {
                         object.expand_selection(map, selection, around);
                         let offset_range = selection.map(|p| p.to_offset(map, Bias::Left)).range();
@@ -142,11 +149,11 @@ impl Vim {
                     });
                 });
                 vim.copy_selections_content(editor, false, cx);
-                editor.insert("", cx);
+                editor.insert("", window, cx);
 
                 // Fixup cursor position after the deletion
                 editor.set_clip_at_line_ends(true, cx);
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let mut cursor = selection.head();
                         if should_move_to_start.contains(&selection.id) {
@@ -156,7 +163,7 @@ impl Vim {
                         selection.collapse_to(cursor, selection.goal)
                     });
                 });
-                editor.refresh_inline_completion(true, false, cx);
+                editor.refresh_inline_completion(true, false, window, cx);
             });
         });
     }

crates/vim/src/normal/increment.rs 🔗

@@ -1,5 +1,5 @@
 use editor::{scroll::Autoscroll, Editor, MultiBufferSnapshot, ToOffset, ToPoint};
-use gpui::{impl_actions, ViewContext};
+use gpui::{impl_actions, Context, Window};
 use language::{Bias, Point};
 use schemars::JsonSchema;
 use serde::Deserialize;
@@ -23,25 +23,31 @@ struct Decrement {
 
 impl_actions!(vim, [Increment, Decrement]);
 
-pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
-    Vim::action(editor, cx, |vim, action: &Increment, cx| {
+pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
+    Vim::action(editor, cx, |vim, action: &Increment, window, cx| {
         vim.record_current_action(cx);
         let count = Vim::take_count(cx).unwrap_or(1);
         let step = if action.step { 1 } else { 0 };
-        vim.increment(count as i64, step, cx)
+        vim.increment(count as i64, step, window, cx)
     });
-    Vim::action(editor, cx, |vim, action: &Decrement, cx| {
+    Vim::action(editor, cx, |vim, action: &Decrement, window, cx| {
         vim.record_current_action(cx);
         let count = Vim::take_count(cx).unwrap_or(1);
         let step = if action.step { -1 } else { 0 };
-        vim.increment(-(count as i64), step, cx)
+        vim.increment(-(count as i64), step, window, cx)
     });
 }
 
 impl Vim {
-    fn increment(&mut self, mut delta: i64, step: i32, cx: &mut ViewContext<Self>) {
-        self.store_visual_marks(cx);
-        self.update_editor(cx, |vim, editor, cx| {
+    fn increment(
+        &mut self,
+        mut delta: i64,
+        step: i32,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.store_visual_marks(window, cx);
+        self.update_editor(window, cx, |vim, editor, window, cx| {
             let mut edits = Vec::new();
             let mut new_anchors = Vec::new();
 
@@ -76,11 +82,11 @@ impl Vim {
                     }
                 }
             }
-            editor.transact(cx, |editor, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 editor.edit(edits, cx);
 
                 let snapshot = editor.buffer().read(cx).snapshot(cx);
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     let mut new_ranges = Vec::new();
                     for (visual, anchor) in new_anchors.iter() {
                         let mut point = anchor.to_point(&snapshot);
@@ -94,7 +100,7 @@ impl Vim {
                 })
             });
         });
-        self.switch_mode(Mode::Normal, true, cx)
+        self.switch_mode(Mode::Normal, true, window, cx)
     }
 }
 

crates/vim/src/normal/mark.rs 🔗

@@ -6,7 +6,7 @@ use editor::{
     scroll::Autoscroll,
     Anchor, Bias, DisplayPoint,
 };
-use gpui::ViewContext;
+use gpui::{Context, Window};
 use language::SelectionGoal;
 
 use crate::{
@@ -16,8 +16,14 @@ use crate::{
 };
 
 impl Vim {
-    pub fn create_mark(&mut self, text: Arc<str>, tail: bool, cx: &mut ViewContext<Self>) {
-        let Some(anchors) = self.update_editor(cx, |_, editor, _| {
+    pub fn create_mark(
+        &mut self,
+        text: Arc<str>,
+        tail: bool,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        let Some(anchors) = self.update_editor(window, cx, |_, editor, _, _| {
             editor
                 .selections
                 .disjoint_anchors()
@@ -28,23 +34,28 @@ impl Vim {
             return;
         };
         self.marks.insert(text.to_string(), anchors);
-        self.clear_operator(cx);
+        self.clear_operator(window, cx);
     }
 
     // When handling an action, you must create visual marks if you will switch to normal
     // mode without the default selection behavior.
-    pub(crate) fn store_visual_marks(&mut self, cx: &mut ViewContext<Self>) {
+    pub(crate) fn store_visual_marks(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if self.mode.is_visual() {
-            self.create_visual_marks(self.mode, cx);
+            self.create_visual_marks(self.mode, window, cx);
         }
     }
 
-    pub(crate) fn create_visual_marks(&mut self, mode: Mode, cx: &mut ViewContext<Self>) {
+    pub(crate) fn create_visual_marks(
+        &mut self,
+        mode: Mode,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let mut starts = vec![];
         let mut ends = vec![];
         let mut reversed = vec![];
 
-        self.update_editor(cx, |_, editor, cx| {
+        self.update_editor(window, cx, |_, editor, _, cx| {
             let (map, selections) = editor.selections.all_display(cx);
             for selection in selections {
                 let end = movement::saturating_left(&map, selection.end);
@@ -65,11 +76,17 @@ impl Vim {
         self.stored_visual_mode.replace((mode, reversed));
     }
 
-    pub fn jump(&mut self, text: Arc<str>, line: bool, cx: &mut ViewContext<Self>) {
-        self.pop_operator(cx);
+    pub fn jump(
+        &mut self,
+        text: Arc<str>,
+        line: bool,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.pop_operator(window, cx);
 
         let anchors = match &*text {
-            "{" | "}" => self.update_editor(cx, |_, editor, cx| {
+            "{" | "}" => self.update_editor(window, cx, |_, editor, _, cx| {
                 let (map, selections) = editor.selections.all_display(cx);
                 selections
                     .into_iter()
@@ -98,12 +115,13 @@ impl Vim {
                         anchor: *anchor,
                         line,
                     },
+                    window,
                     cx,
                 )
             }
         } else {
-            self.update_editor(cx, |_, editor, cx| {
-                let map = editor.snapshot(cx);
+            self.update_editor(window, cx, |_, editor, window, cx| {
+                let map = editor.snapshot(window, cx);
                 let mut ranges: Vec<Range<Anchor>> = Vec::new();
                 for mut anchor in anchors {
                     if line {
@@ -118,7 +136,7 @@ impl Vim {
                         ranges.push(anchor..anchor);
                     }
                 }
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.select_anchor_ranges(ranges)
                 })
             });

crates/vim/src/normal/paste.rs 🔗

@@ -1,5 +1,5 @@
 use editor::{display_map::ToDisplayPoint, movement, scroll::Autoscroll, DisplayPoint, RowExt};
-use gpui::{impl_actions, ViewContext};
+use gpui::{impl_actions, Context, Window};
 use language::{Bias, SelectionGoal};
 use schemars::JsonSchema;
 use serde::Deserialize;
@@ -22,14 +22,14 @@ pub struct Paste {
 impl_actions!(vim, [Paste]);
 
 impl Vim {
-    pub fn paste(&mut self, action: &Paste, cx: &mut ViewContext<Self>) {
+    pub fn paste(&mut self, action: &Paste, window: &mut Window, cx: &mut Context<Self>) {
         self.record_current_action(cx);
-        self.store_visual_marks(cx);
+        self.store_visual_marks(window, cx);
         let count = Vim::take_count(cx).unwrap_or(1);
 
-        self.update_editor(cx, |vim, editor, cx| {
-            let text_layout_details = editor.text_layout_details(cx);
-            editor.transact(cx, |editor, cx| {
+        self.update_editor(window, cx, |vim, editor, window, cx| {
+            let text_layout_details = editor.text_layout_details(window);
+            editor.transact(window, cx, |editor, window, cx| {
                 editor.set_clip_at_line_ends(false, cx);
 
                 let selected_register = vim.selected_register.take();
@@ -159,7 +159,7 @@ impl Vim {
                 // and put the cursor on the first non-blank character of the first inserted line (or at the end if the first line is blank).
                 // otherwise vim will insert the next text at (or before) the current cursor position,
                 // the cursor will go to the last (or first, if is_multiline) inserted character.
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.replace_cursors_with(|map| {
                         let mut cursors = Vec::new();
                         for (anchor, line_mode, is_multiline) in &new_selections {
@@ -190,7 +190,7 @@ impl Vim {
                 })
             });
         });
-        self.switch_mode(Mode::Normal, true, cx);
+        self.switch_mode(Mode::Normal, true, window, cx);
     }
 }
 

crates/vim/src/normal/repeat.rs 🔗

@@ -8,8 +8,7 @@ use crate::{
     Vim,
 };
 use editor::Editor;
-use gpui::{actions, Action, ViewContext, WindowContext};
-use util::ResultExt;
+use gpui::{actions, Action, App, Context, Window};
 use workspace::Workspace;
 
 actions!(vim, [Repeat, EndRepeat, ToggleRecord, ReplayLastRecording]);
@@ -45,28 +44,30 @@ fn repeatable_insert(action: &ReplayableAction) -> Option<Box<dyn Action>> {
     }
 }
 
-pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
-    Vim::action(editor, cx, |vim, _: &EndRepeat, cx| {
+pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
+    Vim::action(editor, cx, |vim, _: &EndRepeat, window, cx| {
         Vim::globals(cx).dot_replaying = false;
-        vim.switch_mode(Mode::Normal, false, cx)
+        vim.switch_mode(Mode::Normal, false, window, cx)
     });
 
-    Vim::action(editor, cx, |vim, _: &Repeat, cx| vim.repeat(false, cx));
+    Vim::action(editor, cx, |vim, _: &Repeat, window, cx| {
+        vim.repeat(false, window, cx)
+    });
 
-    Vim::action(editor, cx, |vim, _: &ToggleRecord, cx| {
+    Vim::action(editor, cx, |vim, _: &ToggleRecord, window, cx| {
         let globals = Vim::globals(cx);
         if let Some(char) = globals.recording_register.take() {
             globals.last_recorded_register = Some(char)
         } else {
-            vim.push_operator(Operator::RecordRegister, cx);
+            vim.push_operator(Operator::RecordRegister, window, cx);
         }
     });
 
-    Vim::action(editor, cx, |vim, _: &ReplayLastRecording, cx| {
+    Vim::action(editor, cx, |vim, _: &ReplayLastRecording, window, cx| {
         let Some(register) = Vim::globals(cx).last_recorded_register else {
             return;
         };
-        vim.replay_register(register, cx)
+        vim.replay_register(register, window, cx)
     });
 }
 
@@ -88,7 +89,7 @@ impl Replayer {
         })))
     }
 
-    pub fn replay(&mut self, actions: Vec<ReplayableAction>, cx: &mut WindowContext) {
+    pub fn replay(&mut self, actions: Vec<ReplayableAction>, window: &mut Window, cx: &mut App) {
         let mut lock = self.0.borrow_mut();
         let range = lock.ix..lock.ix;
         lock.actions.splice(range, actions);
@@ -97,14 +98,14 @@ impl Replayer {
         }
         lock.running = true;
         let this = self.clone();
-        cx.defer(move |cx| this.next(cx))
+        window.defer(cx, move |window, cx| this.next(window, cx))
     }
 
     pub fn stop(self) {
         self.0.borrow_mut().actions.clear()
     }
 
-    pub fn next(self, cx: &mut WindowContext) {
+    pub fn next(self, window: &mut Window, cx: &mut App) {
         let mut lock = self.0.borrow_mut();
         let action = if lock.ix < 10000 {
             lock.actions.get(lock.ix).cloned()
@@ -121,7 +122,7 @@ impl Replayer {
         match action {
             ReplayableAction::Action(action) => {
                 if should_replay(&*action) {
-                    cx.dispatch_action(action.boxed_clone());
+                    window.dispatch_action(action.boxed_clone(), cx);
                     cx.defer(move |cx| Vim::globals(cx).observe_action(action.boxed_clone()));
                 }
             }
@@ -129,41 +130,47 @@ impl Replayer {
                 text,
                 utf16_range_to_replace,
             } => {
-                cx.window_handle()
-                    .update(cx, |handle, cx| {
-                        let Ok(workspace) = handle.downcast::<Workspace>() else {
-                            return;
-                        };
-                        let Some(editor) = workspace
-                            .read(cx)
-                            .active_item(cx)
-                            .and_then(|item| item.act_as::<Editor>(cx))
-                        else {
-                            return;
-                        };
-                        editor.update(cx, |editor, cx| {
-                            editor.replay_insert_event(&text, utf16_range_to_replace.clone(), cx)
-                        })
-                    })
-                    .log_err();
+                let Some(Some(workspace)) = window.root_model::<Workspace>() else {
+                    return;
+                };
+                let Some(editor) = workspace
+                    .read(cx)
+                    .active_item(cx)
+                    .and_then(|item| item.act_as::<Editor>(cx))
+                else {
+                    return;
+                };
+                editor.update(cx, |editor, cx| {
+                    editor.replay_insert_event(&text, utf16_range_to_replace.clone(), window, cx)
+                })
             }
         }
-        cx.defer(move |cx| self.next(cx));
+        window.defer(cx, move |window, cx| self.next(window, cx));
     }
 }
 
 impl Vim {
-    pub(crate) fn record_register(&mut self, register: char, cx: &mut ViewContext<Self>) {
+    pub(crate) fn record_register(
+        &mut self,
+        register: char,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let globals = Vim::globals(cx);
         globals.recording_register = Some(register);
         globals.recordings.remove(&register);
         globals.ignore_current_insertion = true;
-        self.clear_operator(cx)
+        self.clear_operator(window, cx)
     }
 
-    pub(crate) fn replay_register(&mut self, mut register: char, cx: &mut ViewContext<Self>) {
+    pub(crate) fn replay_register(
+        &mut self,
+        mut register: char,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let mut count = Vim::take_count(cx).unwrap_or(1);
-        self.clear_operator(cx);
+        self.clear_operator(window, cx);
 
         let globals = Vim::globals(cx);
         if register == '@' {
@@ -184,11 +191,17 @@ impl Vim {
 
         globals.last_replayed_register = Some(register);
         let mut replayer = globals.replayer.get_or_insert_with(Replayer::new).clone();
-        replayer.replay(repeated_actions, cx);
+        replayer.replay(repeated_actions, window, cx);
     }
 
-    pub(crate) fn repeat(&mut self, from_insert_mode: bool, cx: &mut ViewContext<Self>) {
+    pub(crate) fn repeat(
+        &mut self,
+        from_insert_mode: bool,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let count = Vim::take_count(cx);
+
         let Some((mut actions, selection, mode)) = Vim::update_globals(cx, |globals, _| {
             let actions = globals.recorded_actions.clone();
             if actions.is_empty() {
@@ -231,13 +244,13 @@ impl Vim {
             return;
         };
         if let Some(mode) = mode {
-            self.switch_mode(mode, false, cx)
+            self.switch_mode(mode, false, window, cx)
         }
 
         match selection {
             RecordedSelection::SingleLine { cols } => {
                 if cols > 1 {
-                    self.visual_motion(Motion::Right, Some(cols as usize - 1), cx)
+                    self.visual_motion(Motion::Right, Some(cols as usize - 1), window, cx)
                 }
             }
             RecordedSelection::Visual { rows, cols } => {
@@ -246,6 +259,7 @@ impl Vim {
                         display_lines: false,
                     },
                     Some(rows as usize),
+                    window,
                     cx,
                 );
                 self.visual_motion(
@@ -253,10 +267,11 @@ impl Vim {
                         display_lines: false,
                     },
                     None,
+                    window,
                     cx,
                 );
                 if cols > 1 {
-                    self.visual_motion(Motion::Right, Some(cols as usize - 1), cx)
+                    self.visual_motion(Motion::Right, Some(cols as usize - 1), window, cx)
                 }
             }
             RecordedSelection::VisualBlock { rows, cols } => {
@@ -265,10 +280,11 @@ impl Vim {
                         display_lines: false,
                     },
                     Some(rows as usize),
+                    window,
                     cx,
                 );
                 if cols > 1 {
-                    self.visual_motion(Motion::Right, Some(cols as usize - 1), cx);
+                    self.visual_motion(Motion::Right, Some(cols as usize - 1), window, cx);
                 }
             }
             RecordedSelection::VisualLine { rows } => {
@@ -277,6 +293,7 @@ impl Vim {
                         display_lines: false,
                     },
                     Some(rows as usize),
+                    window,
                     cx,
                 );
             }
@@ -321,7 +338,8 @@ impl Vim {
         let globals = Vim::globals(cx);
         globals.dot_replaying = true;
         let mut replayer = globals.replayer.get_or_insert_with(Replayer::new).clone();
-        replayer.replay(actions, cx);
+
+        replayer.replay(actions, window, cx);
     }
 }
 
@@ -380,9 +398,9 @@ mod test {
         cx.simulate_keystrokes("i");
 
         // simulate brazilian input for ä.
-        cx.update_editor(|editor, cx| {
-            editor.replace_and_mark_text_in_range(None, "\"", Some(1..1), cx);
-            editor.replace_text_in_range(None, "ä", cx);
+        cx.update_editor(|editor, window, cx| {
+            editor.replace_and_mark_text_in_range(None, "\"", Some(1..1), window, cx);
+            editor.replace_text_in_range(None, "ä", window, cx);
         });
         cx.simulate_keystrokes("escape");
         cx.assert_state("hˇällo", Mode::Normal);

crates/vim/src/normal/scroll.rs 🔗

@@ -4,7 +4,7 @@ use editor::{
     scroll::ScrollAmount,
     DisplayPoint, Editor, EditorSettings,
 };
-use gpui::{actions, ViewContext};
+use gpui::{actions, Context, Window};
 use language::Bias;
 use settings::Settings;
 
@@ -13,21 +13,21 @@ actions!(
     [LineUp, LineDown, ScrollUp, ScrollDown, PageUp, PageDown]
 );
 
-pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
-    Vim::action(editor, cx, |vim, _: &LineDown, cx| {
-        vim.scroll(false, cx, |c| ScrollAmount::Line(c.unwrap_or(1.)))
+pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
+    Vim::action(editor, cx, |vim, _: &LineDown, window, cx| {
+        vim.scroll(false, window, cx, |c| ScrollAmount::Line(c.unwrap_or(1.)))
     });
-    Vim::action(editor, cx, |vim, _: &LineUp, cx| {
-        vim.scroll(false, cx, |c| ScrollAmount::Line(-c.unwrap_or(1.)))
+    Vim::action(editor, cx, |vim, _: &LineUp, window, cx| {
+        vim.scroll(false, window, cx, |c| ScrollAmount::Line(-c.unwrap_or(1.)))
     });
-    Vim::action(editor, cx, |vim, _: &PageDown, cx| {
-        vim.scroll(false, cx, |c| ScrollAmount::Page(c.unwrap_or(1.)))
+    Vim::action(editor, cx, |vim, _: &PageDown, window, cx| {
+        vim.scroll(false, window, cx, |c| ScrollAmount::Page(c.unwrap_or(1.)))
     });
-    Vim::action(editor, cx, |vim, _: &PageUp, cx| {
-        vim.scroll(false, cx, |c| ScrollAmount::Page(-c.unwrap_or(1.)))
+    Vim::action(editor, cx, |vim, _: &PageUp, window, cx| {
+        vim.scroll(false, window, cx, |c| ScrollAmount::Page(-c.unwrap_or(1.)))
     });
-    Vim::action(editor, cx, |vim, _: &ScrollDown, cx| {
-        vim.scroll(true, cx, |c| {
+    Vim::action(editor, cx, |vim, _: &ScrollDown, window, cx| {
+        vim.scroll(true, window, cx, |c| {
             if let Some(c) = c {
                 ScrollAmount::Line(c)
             } else {
@@ -35,8 +35,8 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
             }
         })
     });
-    Vim::action(editor, cx, |vim, _: &ScrollUp, cx| {
-        vim.scroll(true, cx, |c| {
+    Vim::action(editor, cx, |vim, _: &ScrollUp, window, cx| {
+        vim.scroll(true, window, cx, |c| {
             if let Some(c) = c {
                 ScrollAmount::Line(-c)
             } else {
@@ -50,12 +50,13 @@ impl Vim {
     fn scroll(
         &mut self,
         move_cursor: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
         by: fn(c: Option<f32>) -> ScrollAmount,
     ) {
         let amount = by(Vim::take_count(cx).map(|c| c as f32));
-        self.update_editor(cx, |_, editor, cx| {
-            scroll_editor(editor, move_cursor, &amount, cx)
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            scroll_editor(editor, move_cursor, &amount, window, cx)
         });
     }
 }
@@ -64,12 +65,13 @@ fn scroll_editor(
     editor: &mut Editor,
     preserve_cursor_position: bool,
     amount: &ScrollAmount,
-    cx: &mut ViewContext<Editor>,
+    window: &mut Window,
+    cx: &mut Context<Editor>,
 ) {
     let should_move_cursor = editor.newest_selection_on_screen(cx).is_eq();
     let old_top_anchor = editor.scroll_manager.anchor().anchor;
 
-    if editor.scroll_hover(amount, cx) {
+    if editor.scroll_hover(amount, window, cx) {
         return;
     }
 
@@ -85,7 +87,7 @@ fn scroll_editor(
         _ => amount.clone(),
     };
 
-    editor.scroll_screen(&amount, cx);
+    editor.scroll_screen(&amount, window, cx);
     if !should_move_cursor {
         return;
     }
@@ -97,7 +99,7 @@ fn scroll_editor(
     let top_anchor = editor.scroll_manager.anchor().anchor;
     let vertical_scroll_margin = EditorSettings::get_global(cx).vertical_scroll_margin;
 
-    editor.change_selections(None, cx, |s| {
+    editor.change_selections(None, window, cx, |s| {
         s.move_with(|map, selection| {
             let mut head = selection.head();
             let top = top_anchor.to_display_point(map);
@@ -161,7 +163,7 @@ mod test {
         test::{NeovimBackedTestContext, VimTestContext},
     };
     use editor::{EditorSettings, ScrollBeyondLastLine};
-    use gpui::{point, px, size, Context};
+    use gpui::{point, px, size, AppContext as _};
     use indoc::indoc;
     use language::Point;
     use settings::SettingsStore;
@@ -183,21 +185,21 @@ mod test {
     async fn test_scroll(cx: &mut gpui::TestAppContext) {
         let mut cx = VimTestContext::new(cx, true).await;
 
-        let (line_height, visible_line_count) = cx.editor(|editor, cx| {
+        let (line_height, visible_line_count) = cx.editor(|editor, window, _cx| {
             (
                 editor
                     .style()
                     .unwrap()
                     .text
-                    .line_height_in_pixels(cx.rem_size()),
+                    .line_height_in_pixels(window.rem_size()),
                 editor.visible_line_count().unwrap(),
             )
         });
 
         let window = cx.window;
         let margin = cx
-            .update_window(window, |_, cx| {
-                cx.viewport_size().height - line_height * visible_line_count
+            .update_window(window, |_, window, _cx| {
+                window.viewport_size().height - line_height * visible_line_count
             })
             .unwrap();
         cx.simulate_window_resize(
@@ -224,30 +226,33 @@ mod test {
             Mode::Normal,
         );
 
-        cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 0.))
+        cx.update_editor(|editor, window, cx| {
+            assert_eq!(editor.snapshot(window, cx).scroll_position(), point(0., 0.))
         });
         cx.simulate_keystrokes("ctrl-e");
-        cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 1.))
+        cx.update_editor(|editor, window, cx| {
+            assert_eq!(editor.snapshot(window, cx).scroll_position(), point(0., 1.))
         });
         cx.simulate_keystrokes("2 ctrl-e");
-        cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 3.))
+        cx.update_editor(|editor, window, cx| {
+            assert_eq!(editor.snapshot(window, cx).scroll_position(), point(0., 3.))
         });
         cx.simulate_keystrokes("ctrl-y");
-        cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 2.))
+        cx.update_editor(|editor, window, cx| {
+            assert_eq!(editor.snapshot(window, cx).scroll_position(), point(0., 2.))
         });
 
         // does not select in normal mode
         cx.simulate_keystrokes("g g");
-        cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 0.))
+        cx.update_editor(|editor, window, cx| {
+            assert_eq!(editor.snapshot(window, cx).scroll_position(), point(0., 0.))
         });
         cx.simulate_keystrokes("ctrl-d");
-        cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 3.0));
+        cx.update_editor(|editor, window, cx| {
+            assert_eq!(
+                editor.snapshot(window, cx).scroll_position(),
+                point(0., 3.0)
+            );
             assert_eq!(
                 editor.selections.newest(cx).range(),
                 Point::new(6, 0)..Point::new(6, 0)
@@ -256,12 +261,15 @@ mod test {
 
         // does select in visual mode
         cx.simulate_keystrokes("g g");
-        cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 0.))
+        cx.update_editor(|editor, window, cx| {
+            assert_eq!(editor.snapshot(window, cx).scroll_position(), point(0., 0.))
         });
         cx.simulate_keystrokes("v ctrl-d");
-        cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 3.0));
+        cx.update_editor(|editor, window, cx| {
+            assert_eq!(
+                editor.snapshot(window, cx).scroll_position(),
+                point(0., 3.0)
+            );
             assert_eq!(
                 editor.selections.newest(cx).range(),
                 Point::new(0, 0)..Point::new(6, 1)

crates/vim/src/normal/search.rs 🔗

@@ -1,5 +1,5 @@
 use editor::Editor;
-use gpui::{actions, impl_actions, impl_internal_actions, ViewContext};
+use gpui::{actions, impl_actions, impl_internal_actions, Context, Window};
 use language::Point;
 use schemars::JsonSchema;
 use search::{buffer_search, BufferSearchBar, SearchOptions};
@@ -69,7 +69,7 @@ actions!(vim, [SearchSubmit, MoveToNextMatch, MoveToPrevMatch]);
 impl_actions!(vim, [FindCommand, Search, MoveToPrev, MoveToNext]);
 impl_internal_actions!(vim, [ReplaceCommand]);
 
-pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
+pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
     Vim::action(editor, cx, Vim::move_to_next);
     Vim::action(editor, cx, Vim::move_to_prev);
     Vim::action(editor, cx, Vim::move_to_next_match);
@@ -81,36 +81,48 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
 }
 
 impl Vim {
-    fn move_to_next(&mut self, action: &MoveToNext, cx: &mut ViewContext<Self>) {
+    fn move_to_next(&mut self, action: &MoveToNext, window: &mut Window, cx: &mut Context<Self>) {
         self.move_to_internal(
             Direction::Next,
             action.case_sensitive,
             !action.partial_word,
             action.regex,
+            window,
             cx,
         )
     }
 
-    fn move_to_prev(&mut self, action: &MoveToPrev, cx: &mut ViewContext<Self>) {
+    fn move_to_prev(&mut self, action: &MoveToPrev, window: &mut Window, cx: &mut Context<Self>) {
         self.move_to_internal(
             Direction::Prev,
             action.case_sensitive,
             !action.partial_word,
             action.regex,
+            window,
             cx,
         )
     }
 
-    fn move_to_next_match(&mut self, _: &MoveToNextMatch, cx: &mut ViewContext<Self>) {
-        self.move_to_match_internal(self.search.direction, cx)
+    fn move_to_next_match(
+        &mut self,
+        _: &MoveToNextMatch,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.move_to_match_internal(self.search.direction, window, cx)
     }
 
-    fn move_to_prev_match(&mut self, _: &MoveToPrevMatch, cx: &mut ViewContext<Self>) {
-        self.move_to_match_internal(self.search.direction.opposite(), cx)
+    fn move_to_prev_match(
+        &mut self,
+        _: &MoveToPrevMatch,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.move_to_match_internal(self.search.direction.opposite(), window, cx)
     }
 
-    fn search(&mut self, action: &Search, cx: &mut ViewContext<Self>) {
-        let Some(pane) = self.pane(cx) else {
+    fn search(&mut self, action: &Search, window: &mut Window, cx: &mut Context<Self>) {
+        let Some(pane) = self.pane(window, cx) else {
             return;
         };
         let direction = if action.backwards {
@@ -119,17 +131,17 @@ impl Vim {
             Direction::Next
         };
         let count = Vim::take_count(cx).unwrap_or(1);
-        let prior_selections = self.editor_selections(cx);
+        let prior_selections = self.editor_selections(window, cx);
         pane.update(cx, |pane, cx| {
             if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
                 search_bar.update(cx, |search_bar, cx| {
-                    if !search_bar.show(cx) {
+                    if !search_bar.show(window, cx) {
                         return;
                     }
                     let query = search_bar.query(cx);
 
-                    search_bar.select_query(cx);
-                    cx.focus_self();
+                    search_bar.select_query(window, cx);
+                    cx.focus_self(window);
 
                     search_bar.set_replacement(None, cx);
                     let mut options = SearchOptions::NONE;
@@ -157,14 +169,16 @@ impl Vim {
     }
 
     // hook into the existing to clear out any vim search state on cmd+f or edit -> find.
-    fn search_deploy(&mut self, _: &buffer_search::Deploy, cx: &mut ViewContext<Self>) {
+    fn search_deploy(&mut self, _: &buffer_search::Deploy, _: &mut Window, cx: &mut Context<Self>) {
         self.search = Default::default();
         cx.propagate();
     }
 
-    pub fn search_submit(&mut self, cx: &mut ViewContext<Self>) {
-        self.store_visual_marks(cx);
-        let Some(pane) = self.pane(cx) else { return };
+    pub fn search_submit(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        self.store_visual_marks(window, cx);
+        let Some(pane) = self.pane(window, cx) else {
+            return;
+        };
         let result = pane.update(cx, |pane, cx| {
             let search_bar = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>()?;
             search_bar.update(cx, |search_bar, cx| {
@@ -178,8 +192,8 @@ impl Vim {
                     count = count.saturating_sub(1)
                 }
                 self.search.count = 1;
-                search_bar.select_match(direction, count, cx);
-                search_bar.focus_editor(&Default::default(), cx);
+                search_bar.select_match(direction, count, window, cx);
+                search_bar.focus_editor(&Default::default(), window, cx);
 
                 let prior_selections: Vec<_> = self.search.prior_selections.drain(..).collect();
                 let prior_mode = self.search.prior_mode;
@@ -195,12 +209,13 @@ impl Vim {
             return;
         };
 
-        let new_selections = self.editor_selections(cx);
+        let new_selections = self.editor_selections(window, cx);
 
         // If the active editor has changed during a search, don't panic.
         if prior_selections.iter().any(|s| {
-            self.update_editor(cx, |_, editor, cx| {
-                !s.start.is_valid(&editor.snapshot(cx).buffer_snapshot)
+            self.update_editor(window, cx, |_, editor, window, cx| {
+                !s.start
+                    .is_valid(&editor.snapshot(window, cx).buffer_snapshot)
             })
             .unwrap_or(true)
         }) {
@@ -208,34 +223,42 @@ impl Vim {
         }
 
         if prior_mode != self.mode {
-            self.switch_mode(prior_mode, true, cx);
+            self.switch_mode(prior_mode, true, window, cx);
         }
         if let Some(operator) = prior_operator {
-            self.push_operator(operator, cx);
+            self.push_operator(operator, window, cx);
         };
         self.search_motion(
             Motion::ZedSearchResult {
                 prior_selections,
                 new_selections,
             },
+            window,
             cx,
         );
     }
 
-    pub fn move_to_match_internal(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
-        let Some(pane) = self.pane(cx) else { return };
+    pub fn move_to_match_internal(
+        &mut self,
+        direction: Direction,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        let Some(pane) = self.pane(window, cx) else {
+            return;
+        };
         let count = Vim::take_count(cx).unwrap_or(1);
-        let prior_selections = self.editor_selections(cx);
+        let prior_selections = self.editor_selections(window, cx);
 
         let success = pane.update(cx, |pane, cx| {
             let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() else {
                 return false;
             };
             search_bar.update(cx, |search_bar, cx| {
-                if !search_bar.has_active_match() || !search_bar.show(cx) {
+                if !search_bar.has_active_match() || !search_bar.show(window, cx) {
                     return false;
                 }
-                search_bar.select_match(direction, count, cx);
+                search_bar.select_match(direction, count, window, cx);
                 true
             })
         });
@@ -243,12 +266,13 @@ impl Vim {
             return;
         }
 
-        let new_selections = self.editor_selections(cx);
+        let new_selections = self.editor_selections(window, cx);
         self.search_motion(
             Motion::ZedSearchResult {
                 prior_selections,
                 new_selections,
             },
+            window,
             cx,
         );
     }
@@ -259,12 +283,15 @@ impl Vim {
         case_sensitive: bool,
         whole_word: bool,
         regex: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
-        let Some(pane) = self.pane(cx) else { return };
+        let Some(pane) = self.pane(window, cx) else {
+            return;
+        };
         let count = Vim::take_count(cx).unwrap_or(1);
-        let prior_selections = self.editor_selections(cx);
-        let vim = cx.view().clone();
+        let prior_selections = self.editor_selections(window, cx);
+        let vim = cx.model().clone();
 
         let searched = pane.update(cx, |pane, cx| {
             self.search.direction = direction;
@@ -282,32 +309,33 @@ impl Vim {
                 if whole_word {
                     options |= SearchOptions::WHOLE_WORD;
                 }
-                if !search_bar.show(cx) {
+                if !search_bar.show(window, cx) {
                     return None;
                 }
-                let Some(query) = search_bar.query_suggestion(cx) else {
-                    drop(search_bar.search("", None, cx));
+                let Some(query) = search_bar.query_suggestion(window, cx) else {
+                    drop(search_bar.search("", None, window, cx));
                     return None;
                 };
                 let query = regex::escape(&query);
-                Some(search_bar.search(&query, Some(options), cx))
+                Some(search_bar.search(&query, Some(options), window, cx))
             });
 
             let Some(search) = search else { return false };
 
             let search_bar = search_bar.downgrade();
-            cx.spawn(|_, mut cx| async move {
+            cx.spawn_in(window, |_, mut cx| async move {
                 search.await?;
-                search_bar.update(&mut cx, |search_bar, cx| {
-                    search_bar.select_match(direction, count, cx);
+                search_bar.update_in(&mut cx, |search_bar, window, cx| {
+                    search_bar.select_match(direction, count, window, cx);
 
                     vim.update(cx, |vim, cx| {
-                        let new_selections = vim.editor_selections(cx);
+                        let new_selections = vim.editor_selections(window, cx);
                         vim.search_motion(
                             Motion::ZedSearchResult {
                                 prior_selections,
                                 new_selections,
                             },
+                            window,
                             cx,
                         )
                     });
@@ -318,20 +346,22 @@ impl Vim {
             true
         });
         if !searched {
-            self.clear_operator(cx)
+            self.clear_operator(window, cx)
         }
 
         if self.mode.is_visual() {
-            self.switch_mode(Mode::Normal, false, cx)
+            self.switch_mode(Mode::Normal, false, window, cx)
         }
     }
 
-    fn find_command(&mut self, action: &FindCommand, cx: &mut ViewContext<Self>) {
-        let Some(pane) = self.pane(cx) else { return };
+    fn find_command(&mut self, action: &FindCommand, window: &mut Window, cx: &mut Context<Self>) {
+        let Some(pane) = self.pane(window, cx) else {
+            return;
+        };
         pane.update(cx, |pane, cx| {
             if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
                 let search = search_bar.update(cx, |search_bar, cx| {
-                    if !search_bar.show(cx) {
+                    if !search_bar.show(window, cx) {
                         return None;
                     }
                     let mut query = action.query.clone();
@@ -347,7 +377,7 @@ impl Vim {
                         );
                     }
 
-                    Some(search_bar.search(&query, Some(options), cx))
+                    Some(search_bar.search(&query, Some(options), window, cx))
                 });
                 let Some(search) = search else { return };
                 let search_bar = search_bar.downgrade();
@@ -356,10 +386,10 @@ impl Vim {
                 } else {
                     Direction::Next
                 };
-                cx.spawn(|_, mut cx| async move {
+                cx.spawn_in(window, |_, mut cx| async move {
                     search.await?;
-                    search_bar.update(&mut cx, |search_bar, cx| {
-                        search_bar.select_match(direction, 1, cx)
+                    search_bar.update_in(&mut cx, |search_bar, window, cx| {
+                        search_bar.select_match(direction, 1, window, cx)
                     })?;
                     anyhow::Ok(())
                 })
@@ -368,16 +398,23 @@ impl Vim {
         })
     }
 
-    fn replace_command(&mut self, action: &ReplaceCommand, cx: &mut ViewContext<Self>) {
+    fn replace_command(
+        &mut self,
+        action: &ReplaceCommand,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let replacement = action.replacement.clone();
-        let Some(((pane, workspace), editor)) =
-            self.pane(cx).zip(self.workspace(cx)).zip(self.editor())
+        let Some(((pane, workspace), editor)) = self
+            .pane(window, cx)
+            .zip(self.workspace(window))
+            .zip(self.editor())
         else {
             return;
         };
-        if let Some(result) = self.update_editor(cx, |vim, editor, cx| {
-            let range = action.range.buffer_range(vim, editor, cx)?;
-            let snapshot = &editor.snapshot(cx).buffer_snapshot;
+        if let Some(result) = self.update_editor(window, cx, |vim, editor, window, cx| {
+            let range = action.range.buffer_range(vim, editor, window, cx)?;
+            let snapshot = &editor.snapshot(window, cx).buffer_snapshot;
             let end_point = Point::new(range.end.0, snapshot.line_len(range.end));
             let range = snapshot.anchor_before(Point::new(range.start.0, 0))
                 ..snapshot.anchor_after(end_point);
@@ -388,13 +425,13 @@ impl Vim {
                 result.notify_err(workspace, cx);
             })
         }
-        let vim = cx.view().clone();
+        let vim = cx.model().clone();
         pane.update(cx, |pane, cx| {
             let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() else {
                 return;
             };
             let search = search_bar.update(cx, |search_bar, cx| {
-                if !search_bar.show(cx) {
+                if !search_bar.show(window, cx) {
                     return None;
                 }
 
@@ -414,16 +451,16 @@ impl Vim {
                     );
                 }
                 search_bar.set_replacement(Some(&replacement.replacement), cx);
-                Some(search_bar.search(&search, Some(options), cx))
+                Some(search_bar.search(&search, Some(options), window, cx))
             });
             let Some(search) = search else { return };
             let search_bar = search_bar.downgrade();
-            cx.spawn(|_, mut cx| async move {
+            cx.spawn_in(window, |_, mut cx| async move {
                 search.await?;
-                search_bar.update(&mut cx, |search_bar, cx| {
+                search_bar.update_in(&mut cx, |search_bar, window, cx| {
                     if replacement.should_replace_all {
-                        search_bar.select_last_match(cx);
-                        search_bar.replace_all(&Default::default(), cx);
+                        search_bar.select_last_match(window, cx);
+                        search_bar.replace_all(&Default::default(), window, cx);
                         cx.spawn(|_, mut cx| async move {
                             cx.background_executor()
                                 .timer(Duration::from_millis(200))
@@ -439,6 +476,7 @@ impl Vim {
                                     display_lines: false,
                                 },
                                 None,
+                                window,
                                 cx,
                             )
                         });
@@ -621,7 +659,7 @@ mod test {
         cx.set_state("aa\nbˇb\ncc\ncc\ncc\n", Mode::Normal);
         cx.simulate_keystrokes("/ c c");
 
-        let search_bar = cx.workspace(|workspace, cx| {
+        let search_bar = cx.workspace(|workspace, _, cx| {
             workspace
                 .active_pane()
                 .read(cx)
@@ -631,14 +669,14 @@ mod test {
                 .expect("Buffer search bar should be deployed")
         });
 
-        cx.update_view(search_bar, |bar, cx| {
+        cx.update_model(search_bar, |bar, _window, cx| {
             assert_eq!(bar.query(cx), "cc");
         });
 
         cx.run_until_parked();
 
-        cx.update_editor(|editor, cx| {
-            let highlights = editor.all_text_background_highlights(cx);
+        cx.update_editor(|editor, window, cx| {
+            let highlights = editor.all_text_background_highlights(window, cx);
             assert_eq!(3, highlights.len());
             assert_eq!(
                 DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 2),
@@ -679,7 +717,9 @@ mod test {
         cx.simulate_keystrokes("/ d");
         cx.simulate_keystrokes("enter");
         cx.assert_state("aa\nbb\nˇdd\ncc\nbb\n", Mode::Normal);
-        cx.update_editor(|editor, cx| editor.move_to_beginning(&Default::default(), cx));
+        cx.update_editor(|editor, window, cx| {
+            editor.move_to_beginning(&Default::default(), window, cx)
+        });
         cx.assert_state("ˇaa\nbb\ndd\ncc\nbb\n", Mode::Normal);
         cx.simulate_keystrokes("/ b");
         cx.simulate_keystrokes("enter");

crates/vim/src/normal/substitute.rs 🔗

@@ -1,25 +1,25 @@
 use editor::{movement, Editor};
-use gpui::{actions, ViewContext};
+use gpui::{actions, Context, Window};
 use language::Point;
 
 use crate::{motion::Motion, Mode, Vim};
 
 actions!(vim, [Substitute, SubstituteLine]);
 
-pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
-    Vim::action(editor, cx, |vim, _: &Substitute, cx| {
+pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
+    Vim::action(editor, cx, |vim, _: &Substitute, window, cx| {
         vim.start_recording(cx);
         let count = Vim::take_count(cx);
-        vim.substitute(count, vim.mode == Mode::VisualLine, cx);
+        vim.substitute(count, vim.mode == Mode::VisualLine, window, cx);
     });
 
-    Vim::action(editor, cx, |vim, _: &SubstituteLine, cx| {
+    Vim::action(editor, cx, |vim, _: &SubstituteLine, window, cx| {
         vim.start_recording(cx);
         if matches!(vim.mode, Mode::VisualBlock | Mode::Visual) {
-            vim.switch_mode(Mode::VisualLine, false, cx)
+            vim.switch_mode(Mode::VisualLine, false, window, cx)
         }
         let count = Vim::take_count(cx);
-        vim.substitute(count, true, cx)
+        vim.substitute(count, true, window, cx)
     });
 }
 
@@ -28,14 +28,15 @@ impl Vim {
         &mut self,
         count: Option<usize>,
         line_mode: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
-        self.store_visual_marks(cx);
-        self.update_editor(cx, |vim, editor, cx| {
+        self.store_visual_marks(window, cx);
+        self.update_editor(window, cx, |vim, editor, window, cx| {
             editor.set_clip_at_line_ends(false, cx);
-            editor.transact(cx, |editor, cx| {
-                let text_layout_details = editor.text_layout_details(cx);
-                editor.change_selections(None, cx, |s| {
+            editor.transact(window, cx, |editor, window, cx| {
+                let text_layout_details = editor.text_layout_details(window);
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         if selection.start == selection.end {
                             Motion::Right.expand_selection(
@@ -80,7 +81,7 @@ impl Vim {
                 editor.edit(edits, cx);
             });
         });
-        self.switch_mode(Mode::Insert, true, cx);
+        self.switch_mode(Mode::Insert, true, window, cx);
     }
 }
 

crates/vim/src/normal/toggle_comments.rs 🔗

@@ -1,30 +1,31 @@
 use crate::{motion::Motion, object::Object, Vim};
 use collections::HashMap;
 use editor::{display_map::ToDisplayPoint, Bias};
+use gpui::{Context, Window};
 use language::SelectionGoal;
-use ui::ViewContext;
 
 impl Vim {
     pub fn toggle_comments_motion(
         &mut self,
         motion: Motion,
         times: Option<usize>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.stop_recording(cx);
-        self.update_editor(cx, |_, editor, cx| {
-            let text_layout_details = editor.text_layout_details(cx);
-            editor.transact(cx, |editor, cx| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            let text_layout_details = editor.text_layout_details(window);
+            editor.transact(window, cx, |editor, window, cx| {
                 let mut selection_starts: HashMap<_, _> = Default::default();
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
                         selection_starts.insert(selection.id, anchor);
                         motion.expand_selection(map, selection, times, false, &text_layout_details);
                     });
                 });
-                editor.toggle_comments(&Default::default(), cx);
-                editor.change_selections(None, cx, |s| {
+                editor.toggle_comments(&Default::default(), window, cx);
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = selection_starts.remove(&selection.id).unwrap();
                         selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);
@@ -38,21 +39,22 @@ impl Vim {
         &mut self,
         object: Object,
         around: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.stop_recording(cx);
-        self.update_editor(cx, |_, editor, cx| {
-            editor.transact(cx, |editor, cx| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 let mut original_positions: HashMap<_, _> = Default::default();
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
                         original_positions.insert(selection.id, anchor);
                         object.expand_selection(map, selection, around);
                     });
                 });
-                editor.toggle_comments(&Default::default(), cx);
-                editor.change_selections(None, cx, |s| {
+                editor.toggle_comments(&Default::default(), window, cx);
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = original_positions.remove(&selection.id).unwrap();
                         selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);

crates/vim/src/normal/yank.rs 🔗

@@ -8,7 +8,8 @@ use crate::{
 };
 use collections::HashMap;
 use editor::{ClipboardSelection, Editor};
-use gpui::ViewContext;
+use gpui::Context;
+use gpui::Window;
 use language::Point;
 use multi_buffer::MultiBufferRow;
 use settings::Settings;
@@ -20,14 +21,15 @@ impl Vim {
         &mut self,
         motion: Motion,
         times: Option<usize>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
-        self.update_editor(cx, |vim, editor, cx| {
-            let text_layout_details = editor.text_layout_details(cx);
-            editor.transact(cx, |editor, cx| {
+        self.update_editor(window, cx, |vim, editor, window, cx| {
+            let text_layout_details = editor.text_layout_details(window);
+            editor.transact(window, cx, |editor, window, cx| {
                 editor.set_clip_at_line_ends(false, cx);
                 let mut original_positions: HashMap<_, _> = Default::default();
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         let original_position = (selection.head(), selection.goal);
                         original_positions.insert(selection.id, original_position);
@@ -35,7 +37,7 @@ impl Vim {
                     });
                 });
                 vim.yank_selections_content(editor, motion.linewise(), cx);
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|_, selection| {
                         let (head, goal) = original_positions.remove(&selection.id).unwrap();
                         selection.collapse_to(head, goal);
@@ -43,15 +45,21 @@ impl Vim {
                 });
             });
         });
-        self.exit_temporary_normal(cx);
+        self.exit_temporary_normal(window, cx);
     }
 
-    pub fn yank_object(&mut self, object: Object, around: bool, cx: &mut ViewContext<Self>) {
-        self.update_editor(cx, |vim, editor, cx| {
-            editor.transact(cx, |editor, cx| {
+    pub fn yank_object(
+        &mut self,
+        object: Object,
+        around: bool,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.update_editor(window, cx, |vim, editor, window, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 editor.set_clip_at_line_ends(false, cx);
                 let mut original_positions: HashMap<_, _> = Default::default();
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         let original_position = (selection.head(), selection.goal);
                         object.expand_selection(map, selection, around);
@@ -59,7 +67,7 @@ impl Vim {
                     });
                 });
                 vim.yank_selections_content(editor, false, cx);
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|_, selection| {
                         let (head, goal) = original_positions.remove(&selection.id).unwrap();
                         selection.collapse_to(head, goal);
@@ -67,14 +75,14 @@ impl Vim {
                 });
             });
         });
-        self.exit_temporary_normal(cx);
+        self.exit_temporary_normal(window, cx);
     }
 
     pub fn yank_selections_content(
         &mut self,
         editor: &mut Editor,
         linewise: bool,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) {
         self.copy_ranges(
             editor,
@@ -94,7 +102,7 @@ impl Vim {
         &mut self,
         editor: &mut Editor,
         linewise: bool,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) {
         self.copy_ranges(
             editor,
@@ -116,7 +124,7 @@ impl Vim {
         linewise: bool,
         is_yank: bool,
         selections: Vec<Range<Point>>,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) {
         let buffer = editor.buffer().read(cx).snapshot(cx);
         let mut text = String::new();

crates/vim/src/object.rs 🔗

@@ -10,12 +10,13 @@ use editor::{
     movement::{self, FindRange},
     Bias, DisplayPoint, Editor,
 };
-use gpui::{actions, impl_actions, ViewContext};
+use gpui::{actions, impl_actions, Window};
 use itertools::Itertools;
 use language::{BufferSnapshot, CharKind, Point, Selection, TextObject, TreeSitterOptions};
 use multi_buffer::MultiBufferRow;
 use schemars::JsonSchema;
 use serde::Deserialize;
+use ui::Context;
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, JsonSchema)]
 pub enum Object {
@@ -84,84 +85,88 @@ actions!(
     ]
 );
 
-pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
+pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
     Vim::action(
         editor,
         cx,
-        |vim, &Word { ignore_punctuation }: &Word, cx| {
-            vim.object(Object::Word { ignore_punctuation }, cx)
+        |vim, &Word { ignore_punctuation }: &Word, window, cx| {
+            vim.object(Object::Word { ignore_punctuation }, window, cx)
         },
     );
     Vim::action(
         editor,
         cx,
-        |vim, &Subword { ignore_punctuation }: &Subword, cx| {
-            vim.object(Object::Subword { ignore_punctuation }, cx)
+        |vim, &Subword { ignore_punctuation }: &Subword, window, cx| {
+            vim.object(Object::Subword { ignore_punctuation }, window, cx)
         },
     );
-    Vim::action(editor, cx, |vim, _: &Tag, cx| vim.object(Object::Tag, cx));
-    Vim::action(editor, cx, |vim, _: &Sentence, cx| {
-        vim.object(Object::Sentence, cx)
+    Vim::action(editor, cx, |vim, _: &Tag, window, cx| {
+        vim.object(Object::Tag, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &Paragraph, cx| {
-        vim.object(Object::Paragraph, cx)
+    Vim::action(editor, cx, |vim, _: &Sentence, window, cx| {
+        vim.object(Object::Sentence, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &Quotes, cx| {
-        vim.object(Object::Quotes, cx)
+    Vim::action(editor, cx, |vim, _: &Paragraph, window, cx| {
+        vim.object(Object::Paragraph, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &BackQuotes, cx| {
-        vim.object(Object::BackQuotes, cx)
+    Vim::action(editor, cx, |vim, _: &Quotes, window, cx| {
+        vim.object(Object::Quotes, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &AnyQuotes, cx| {
-        vim.object(Object::AnyQuotes, cx)
+    Vim::action(editor, cx, |vim, _: &AnyQuotes, window, cx| {
+        vim.object(Object::AnyQuotes, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &DoubleQuotes, cx| {
-        vim.object(Object::DoubleQuotes, cx)
+    Vim::action(editor, cx, |vim, _: &DoubleQuotes, window, cx| {
+        vim.object(Object::DoubleQuotes, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &Parentheses, cx| {
-        vim.object(Object::Parentheses, cx)
+    Vim::action(editor, cx, |vim, _: &DoubleQuotes, window, cx| {
+        vim.object(Object::DoubleQuotes, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &SquareBrackets, cx| {
-        vim.object(Object::SquareBrackets, cx)
+    Vim::action(editor, cx, |vim, _: &Parentheses, window, cx| {
+        vim.object(Object::Parentheses, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &CurlyBrackets, cx| {
-        vim.object(Object::CurlyBrackets, cx)
+    Vim::action(editor, cx, |vim, _: &SquareBrackets, window, cx| {
+        vim.object(Object::SquareBrackets, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &AngleBrackets, cx| {
-        vim.object(Object::AngleBrackets, cx)
+    Vim::action(editor, cx, |vim, _: &CurlyBrackets, window, cx| {
+        vim.object(Object::CurlyBrackets, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &VerticalBars, cx| {
-        vim.object(Object::VerticalBars, cx)
+    Vim::action(editor, cx, |vim, _: &AngleBrackets, window, cx| {
+        vim.object(Object::AngleBrackets, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &Argument, cx| {
-        vim.object(Object::Argument, cx)
+    Vim::action(editor, cx, |vim, _: &VerticalBars, window, cx| {
+        vim.object(Object::VerticalBars, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &Method, cx| {
-        vim.object(Object::Method, cx)
+    Vim::action(editor, cx, |vim, _: &Argument, window, cx| {
+        vim.object(Object::Argument, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &Class, cx| {
-        vim.object(Object::Class, cx)
+    Vim::action(editor, cx, |vim, _: &Method, window, cx| {
+        vim.object(Object::Method, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &Comment, cx| {
+    Vim::action(editor, cx, |vim, _: &Class, window, cx| {
+        vim.object(Object::Class, window, cx)
+    });
+    Vim::action(editor, cx, |vim, _: &Comment, window, cx| {
         if !matches!(vim.active_operator(), Some(Operator::Object { .. })) {
-            vim.push_operator(Operator::Object { around: true }, cx);
+            vim.push_operator(Operator::Object { around: true }, window, cx);
         }
-        vim.object(Object::Comment, cx)
+        vim.object(Object::Comment, window, cx)
     });
     Vim::action(
         editor,
         cx,
-        |vim, &IndentObj { include_below }: &IndentObj, cx| {
-            vim.object(Object::IndentObj { include_below }, cx)
+        |vim, &IndentObj { include_below }: &IndentObj, window, cx| {
+            vim.object(Object::IndentObj { include_below }, window, cx)
         },
     );
 }
 
 impl Vim {
-    fn object(&mut self, object: Object, cx: &mut ViewContext<Self>) {
+    fn object(&mut self, object: Object, window: &mut Window, cx: &mut Context<Self>) {
         match self.mode {
-            Mode::Normal => self.normal_object(object, cx),
-            Mode::Visual | Mode::VisualLine | Mode::VisualBlock => self.visual_object(object, cx),
+            Mode::Normal => self.normal_object(object, window, cx),
+            Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
+                self.visual_object(object, window, cx)
+            }
             Mode::Insert | Mode::Replace | Mode::HelixNormal => {
                 // Shouldn't execute a text object in insert mode. Ignoring
             }

crates/vim/src/replace.rs 🔗

@@ -4,35 +4,40 @@ use crate::{
     Vim,
 };
 use editor::{display_map::ToDisplayPoint, Bias, Editor, ToPoint};
-use gpui::{actions, ViewContext};
+use gpui::{actions, Context, Window};
 use language::Point;
 use std::ops::Range;
 use std::sync::Arc;
 
 actions!(vim, [ToggleReplace, UndoReplace]);
 
-pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
-    Vim::action(editor, cx, |vim, _: &ToggleReplace, cx| {
+pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
+    Vim::action(editor, cx, |vim, _: &ToggleReplace, window, cx| {
         vim.replacements = vec![];
         vim.start_recording(cx);
-        vim.switch_mode(Mode::Replace, false, cx);
+        vim.switch_mode(Mode::Replace, false, window, cx);
     });
 
-    Vim::action(editor, cx, |vim, _: &UndoReplace, cx| {
+    Vim::action(editor, cx, |vim, _: &UndoReplace, window, cx| {
         if vim.mode != Mode::Replace {
             return;
         }
         let count = Vim::take_count(cx);
-        vim.undo_replace(count, cx)
+        vim.undo_replace(count, window, cx)
     });
 }
 
 impl Vim {
-    pub(crate) fn multi_replace(&mut self, text: Arc<str>, cx: &mut ViewContext<Self>) {
-        self.update_editor(cx, |vim, editor, cx| {
-            editor.transact(cx, |editor, cx| {
+    pub(crate) fn multi_replace(
+        &mut self,
+        text: Arc<str>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.update_editor(window, cx, |vim, editor, window, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 editor.set_clip_at_line_ends(false, cx);
-                let map = editor.snapshot(cx);
+                let map = editor.snapshot(window, cx);
                 let display_selections = editor.selections.all::<Point>(cx);
 
                 // Handles all string that require manipulation, including inserts and replaces
@@ -60,7 +65,7 @@ impl Vim {
 
                 editor.edit_with_block_indent(edits.clone(), Vec::new(), cx);
 
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.select_anchor_ranges(edits.iter().map(|(range, _)| range.end..range.end));
                 });
                 editor.set_clip_at_line_ends(true, cx);
@@ -68,11 +73,16 @@ impl Vim {
         });
     }
 
-    fn undo_replace(&mut self, maybe_times: Option<usize>, cx: &mut ViewContext<Self>) {
-        self.update_editor(cx, |vim, editor, cx| {
-            editor.transact(cx, |editor, cx| {
+    fn undo_replace(
+        &mut self,
+        maybe_times: Option<usize>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.update_editor(window, cx, |vim, editor, window, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 editor.set_clip_at_line_ends(false, cx);
-                let map = editor.snapshot(cx);
+                let map = editor.snapshot(window, cx);
                 let selections = editor.selections.all::<Point>(cx);
                 let mut new_selections = vec![];
                 let edits: Vec<(Range<Point>, String)> = selections
@@ -107,7 +117,7 @@ impl Vim {
 
                 editor.edit(edits, cx);
 
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.select_ranges(new_selections);
                 });
                 editor.set_clip_at_line_ends(true, cx);

crates/vim/src/rewrap.rs 🔗

@@ -1,22 +1,21 @@
 use crate::{motion::Motion, object::Object, state::Mode, Vim};
 use collections::HashMap;
 use editor::{display_map::ToDisplayPoint, scroll::Autoscroll, Bias, Editor, IsVimMode};
-use gpui::actions;
+use gpui::{actions, Context, Window};
 use language::SelectionGoal;
-use ui::ViewContext;
 
 actions!(vim, [Rewrap]);
 
-pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
-    Vim::action(editor, cx, |vim, _: &Rewrap, cx| {
+pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
+    Vim::action(editor, cx, |vim, _: &Rewrap, window, cx| {
         vim.record_current_action(cx);
         Vim::take_count(cx);
-        vim.store_visual_marks(cx);
-        vim.update_editor(cx, |vim, editor, cx| {
-            editor.transact(cx, |editor, cx| {
+        vim.store_visual_marks(window, cx);
+        vim.update_editor(window, cx, |vim, editor, window, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 let mut positions = vim.save_selection_starts(editor, cx);
                 editor.rewrap_impl(IsVimMode::Yes, cx);
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.move_with(|map, selection| {
                         if let Some(anchor) = positions.remove(&selection.id) {
                             let mut point = anchor.to_display_point(map);
@@ -28,7 +27,7 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
             });
         });
         if vim.mode.is_visual() {
-            vim.switch_mode(Mode::Normal, true, cx)
+            vim.switch_mode(Mode::Normal, true, window, cx)
         }
     });
 }
@@ -38,14 +37,15 @@ impl Vim {
         &mut self,
         motion: Motion,
         times: Option<usize>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.stop_recording(cx);
-        self.update_editor(cx, |_, editor, cx| {
-            let text_layout_details = editor.text_layout_details(cx);
-            editor.transact(cx, |editor, cx| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            let text_layout_details = editor.text_layout_details(window);
+            editor.transact(window, cx, |editor, window, cx| {
                 let mut selection_starts: HashMap<_, _> = Default::default();
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
                         selection_starts.insert(selection.id, anchor);
@@ -53,7 +53,7 @@ impl Vim {
                     });
                 });
                 editor.rewrap_impl(IsVimMode::Yes, cx);
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = selection_starts.remove(&selection.id).unwrap();
                         let mut point = anchor.to_display_point(map);
@@ -69,13 +69,14 @@ impl Vim {
         &mut self,
         object: Object,
         around: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.stop_recording(cx);
-        self.update_editor(cx, |_, editor, cx| {
-            editor.transact(cx, |editor, cx| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 let mut original_positions: HashMap<_, _> = Default::default();
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
                         original_positions.insert(selection.id, anchor);
@@ -83,7 +84,7 @@ impl Vim {
                     });
                 });
                 editor.rewrap_impl(IsVimMode::Yes, cx);
-                editor.change_selections(None, cx, |s| {
+                editor.change_selections(None, window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = original_positions.remove(&selection.id).unwrap();
                         let mut point = anchor.to_display_point(map);

crates/vim/src/state.rs 🔗

@@ -7,7 +7,7 @@ use collections::HashMap;
 use command_palette_hooks::{CommandPaletteFilter, CommandPaletteInterceptor};
 use editor::{Anchor, ClipboardSelection, Editor};
 use gpui::{
-    Action, AppContext, BorrowAppContext, ClipboardEntry, ClipboardItem, Global, View, WeakView,
+    Action, App, BorrowAppContext, ClipboardEntry, ClipboardItem, Entity, Global, WeakEntity,
 };
 use language::Point;
 use schemars::JsonSchema;
@@ -15,7 +15,7 @@ use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsStore};
 use std::borrow::BorrowMut;
 use std::{fmt::Display, ops::Range, sync::Arc};
-use ui::{SharedString, ViewContext};
+use ui::{Context, SharedString};
 use workspace::searchable::Direction;
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, JsonSchema, Serialize)]
@@ -199,15 +199,15 @@ pub struct VimGlobals {
     pub registers: HashMap<char, Register>,
     pub recordings: HashMap<char, Vec<ReplayableAction>>,
 
-    pub focused_vim: Option<WeakView<Vim>>,
+    pub focused_vim: Option<WeakEntity<Vim>>,
 }
 impl Global for VimGlobals {}
 
 impl VimGlobals {
-    pub(crate) fn register(cx: &mut AppContext) {
+    pub(crate) fn register(cx: &mut App) {
         cx.set_global(VimGlobals::default());
 
-        cx.observe_keystrokes(|event, cx| {
+        cx.observe_keystrokes(|event, _, cx| {
             let Some(action) = event.action.as_ref().map(|action| action.boxed_clone()) else {
                 return;
             };
@@ -242,7 +242,7 @@ impl VimGlobals {
         register: Option<char>,
         is_yank: bool,
         linewise: bool,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) {
         if let Some(register) = register {
             let lower = register.to_lowercase().next().unwrap_or(register);
@@ -316,7 +316,7 @@ impl VimGlobals {
         &mut self,
         register: Option<char>,
         editor: Option<&mut Editor>,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut Context<Editor>,
     ) -> Option<Register> {
         let Some(register) = register.filter(|reg| *reg != '"') else {
             let setting = VimSettings::get_global(cx).use_system_clipboard;
@@ -361,7 +361,7 @@ impl VimGlobals {
         }
     }
 
-    fn system_clipboard_is_newer(&self, cx: &ViewContext<Editor>) -> bool {
+    fn system_clipboard_is_newer(&self, cx: &mut Context<Editor>) -> bool {
         cx.read_from_clipboard().is_some_and(|item| {
             if let Some(last_state) = &self.last_yank {
                 Some(last_state.as_ref()) != item.text().as_deref()
@@ -418,19 +418,19 @@ impl VimGlobals {
         }
     }
 
-    pub fn focused_vim(&self) -> Option<View<Vim>> {
+    pub fn focused_vim(&self) -> Option<Entity<Vim>> {
         self.focused_vim.as_ref().and_then(|vim| vim.upgrade())
     }
 }
 
 impl Vim {
-    pub fn globals(cx: &mut AppContext) -> &mut VimGlobals {
+    pub fn globals(cx: &mut App) -> &mut VimGlobals {
         cx.global_mut::<VimGlobals>()
     }
 
     pub fn update_globals<C, R>(cx: &mut C, f: impl FnOnce(&mut VimGlobals, &mut C) -> R) -> R
     where
-        C: BorrowMut<AppContext>,
+        C: BorrowMut<App>,
     {
         cx.update_global(f)
     }

crates/vim/src/surrounds.rs 🔗

@@ -5,10 +5,10 @@ use crate::{
     Vim,
 };
 use editor::{movement, scroll::Autoscroll, Bias};
+use gpui::{Context, Window};
 use language::BracketPair;
 
 use std::sync::Arc;
-use ui::ViewContext;
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub enum SurroundsType {
@@ -22,14 +22,15 @@ impl Vim {
         &mut self,
         text: Arc<str>,
         target: SurroundsType,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self.stop_recording(cx);
         let count = Vim::take_count(cx);
         let mode = self.mode;
-        self.update_editor(cx, |_, editor, cx| {
-            let text_layout_details = editor.text_layout_details(cx);
-            editor.transact(cx, |editor, cx| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            let text_layout_details = editor.text_layout_details(window);
+            editor.transact(window, cx, |editor, window, cx| {
                 editor.set_clip_at_line_ends(false, cx);
 
                 let pair = match find_surround_pair(&all_support_surround_pair(), &text) {
@@ -111,7 +112,7 @@ impl Vim {
 
                 editor.edit(edits, cx);
                 editor.set_clip_at_line_ends(true, cx);
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     if mode == Mode::VisualBlock {
                         s.select_anchor_ranges(anchors.into_iter().take(1))
                     } else {
@@ -120,10 +121,15 @@ impl Vim {
                 });
             });
         });
-        self.switch_mode(Mode::Normal, false, cx);
+        self.switch_mode(Mode::Normal, false, window, cx);
     }
 
-    pub fn delete_surrounds(&mut self, text: Arc<str>, cx: &mut ViewContext<Self>) {
+    pub fn delete_surrounds(
+        &mut self,
+        text: Arc<str>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.stop_recording(cx);
 
         // only legitimate surrounds can be removed
@@ -137,8 +143,8 @@ impl Vim {
         };
         let surround = pair.end != *text;
 
-        self.update_editor(cx, |_, editor, cx| {
-            editor.transact(cx, |editor, cx| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 editor.set_clip_at_line_ends(false, cx);
 
                 let (display_map, display_selections) = editor.selections.all_display(cx);
@@ -204,7 +210,7 @@ impl Vim {
                     }
                 }
 
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.select_ranges(anchors);
                 });
                 edits.sort_by_key(|(range, _)| range.start);
@@ -214,11 +220,17 @@ impl Vim {
         });
     }
 
-    pub fn change_surrounds(&mut self, text: Arc<str>, target: Object, cx: &mut ViewContext<Self>) {
+    pub fn change_surrounds(
+        &mut self,
+        text: Arc<str>,
+        target: Object,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(will_replace_pair) = object_to_bracket_pair(target) {
             self.stop_recording(cx);
-            self.update_editor(cx, |_, editor, cx| {
-                editor.transact(cx, |editor, cx| {
+            self.update_editor(window, cx, |_, editor, window, cx| {
+                editor.transact(window, cx, |editor, window, cx| {
                     editor.set_clip_at_line_ends(false, cx);
 
                     let pair = match find_surround_pair(&all_support_surround_pair(), &text) {
@@ -308,7 +320,7 @@ impl Vim {
                     edits.sort_by_key(|(range, _)| range.start);
                     editor.edit(edits, cx);
                     editor.set_clip_at_line_ends(true, cx);
-                    editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                    editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                         s.select_anchor_ranges(stable_anchors);
                     });
                 });
@@ -326,12 +338,13 @@ impl Vim {
     pub fn check_and_move_to_valid_bracket_pair(
         &mut self,
         object: Object,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> bool {
         let mut valid = false;
         if let Some(pair) = object_to_bracket_pair(object) {
-            self.update_editor(cx, |_, editor, cx| {
-                editor.transact(cx, |editor, cx| {
+            self.update_editor(window, cx, |_, editor, window, cx| {
+                editor.transact(window, cx, |editor, window, cx| {
                     editor.set_clip_at_line_ends(false, cx);
                     let (display_map, selections) = editor.selections.all_adjusted_display(cx);
                     let mut anchors = Vec::new();
@@ -365,7 +378,7 @@ impl Vim {
                             anchors.push(start..start)
                         }
                     }
-                    editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                    editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                         s.select_ranges(anchors);
                     });
                     editor.set_clip_at_line_ends(true, cx);
@@ -733,7 +746,7 @@ mod test {
     async fn test_add_surrounds_visual(cx: &mut gpui::TestAppContext) {
         let mut cx = VimTestContext::new(cx, true).await;
 
-        cx.update(|cx| {
+        cx.update(|_, cx| {
             cx.bind_keys([KeyBinding::new(
                 "shift-s",
                 PushOperator(Operator::AddSurrounds { target: None }),

crates/vim/src/test.rs 🔗

@@ -58,9 +58,9 @@ async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) {
     // Selections aren't changed if editor is blurred but vim-mode is still disabled.
     cx.cx.set_state("«hjklˇ»");
     cx.assert_editor_state("«hjklˇ»");
-    cx.update_editor(|_, cx| cx.blur());
+    cx.update_editor(|_, window, _cx| window.blur());
     cx.assert_editor_state("«hjklˇ»");
-    cx.update_editor(|_, cx| cx.focus_self());
+    cx.update_editor(|_, window, cx| cx.focus_self(window));
     cx.assert_editor_state("«hjklˇ»");
 
     // Enabling dynamically sets vim mode again and restores normal mode
@@ -116,7 +116,7 @@ async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
     );
     cx.simulate_keystrokes("/");
 
-    let search_bar = cx.workspace(|workspace, cx| {
+    let search_bar = cx.workspace(|workspace, _, cx| {
         workspace
             .active_pane()
             .read(cx)
@@ -126,7 +126,7 @@ async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
             .expect("Buffer search bar should be deployed")
     });
 
-    cx.update_view(search_bar, |bar, cx| {
+    cx.update_model(search_bar, |bar, _, cx| {
         assert_eq!(bar.query(cx), "");
     })
 }
@@ -229,10 +229,12 @@ async fn test_escape_command_palette(cx: &mut gpui::TestAppContext) {
     cx.set_state("aˇbc\n", Mode::Normal);
     cx.simulate_keystrokes("i cmd-shift-p");
 
-    assert!(cx.workspace(|workspace, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
+    assert!(cx.workspace(|workspace, _, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
     cx.simulate_keystrokes("escape");
     cx.run_until_parked();
-    assert!(!cx.workspace(|workspace, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
+    assert!(
+        !cx.workspace(|workspace, _, cx| workspace.active_modal::<CommandPalette>(cx).is_some())
+    );
     cx.assert_state("aˇbc\n", Mode::Insert);
 }
 
@@ -253,7 +255,7 @@ async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
     cx.set_state(indoc! {"aa\nbˇb\ncc\ncc\ncc\n"}, Mode::Normal);
     cx.simulate_keystrokes("/ c c");
 
-    let search_bar = cx.workspace(|workspace, cx| {
+    let search_bar = cx.workspace(|workspace, _, cx| {
         workspace
             .active_pane()
             .read(cx)
@@ -263,12 +265,12 @@ async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
             .expect("Buffer search bar should be deployed")
     });
 
-    cx.update_view(search_bar, |bar, cx| {
+    cx.update_model(search_bar, |bar, _, cx| {
         assert_eq!(bar.query(cx), "cc");
     });
 
-    cx.update_editor(|editor, cx| {
-        let highlights = editor.all_text_background_highlights(cx);
+    cx.update_editor(|editor, window, cx| {
+        let highlights = editor.all_text_background_highlights(window, cx);
         assert_eq!(3, highlights.len());
         assert_eq!(
             DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 2),
@@ -843,7 +845,7 @@ async fn test_select_all_issue_2170(cx: &mut gpui::TestAppContext) {
 async fn test_jk(cx: &mut gpui::TestAppContext) {
     let mut cx = NeovimBackedTestContext::new(cx).await;
 
-    cx.update(|cx| {
+    cx.update(|_, cx| {
         cx.bind_keys([KeyBinding::new(
             "j k",
             NormalBefore,
@@ -861,7 +863,7 @@ async fn test_jk(cx: &mut gpui::TestAppContext) {
 async fn test_jk_delay(cx: &mut gpui::TestAppContext) {
     let mut cx = VimTestContext::new(cx, true).await;
 
-    cx.update(|cx| {
+    cx.update(|_, cx| {
         cx.bind_keys([KeyBinding::new(
             "j k",
             NormalBefore,
@@ -885,7 +887,7 @@ async fn test_jk_delay(cx: &mut gpui::TestAppContext) {
 async fn test_comma_w(cx: &mut gpui::TestAppContext) {
     let mut cx = NeovimBackedTestContext::new(cx).await;
 
-    cx.update(|cx| {
+    cx.update(|_, cx| {
         cx.bind_keys([KeyBinding::new(
             ", w",
             motion::Down {
@@ -952,7 +954,7 @@ async fn test_remap(cx: &mut gpui::TestAppContext) {
     let mut cx = VimTestContext::new(cx, true).await;
 
     // test moving the cursor
-    cx.update(|cx| {
+    cx.update(|_, cx| {
         cx.bind_keys([KeyBinding::new(
             "g z",
             workspace::SendKeystrokes("l l l l".to_string()),
@@ -964,7 +966,7 @@ async fn test_remap(cx: &mut gpui::TestAppContext) {
     cx.assert_state("1234ˇ56789", Mode::Normal);
 
     // test switching modes
-    cx.update(|cx| {
+    cx.update(|_, cx| {
         cx.bind_keys([KeyBinding::new(
             "g y",
             workspace::SendKeystrokes("i f o o escape l".to_string()),
@@ -976,7 +978,7 @@ async fn test_remap(cx: &mut gpui::TestAppContext) {
     cx.assert_state("fooˇ123456789", Mode::Normal);
 
     // test recursion
-    cx.update(|cx| {
+    cx.update(|_, cx| {
         cx.bind_keys([KeyBinding::new(
             "g x",
             workspace::SendKeystrokes("g z g y".to_string()),
@@ -990,7 +992,7 @@ async fn test_remap(cx: &mut gpui::TestAppContext) {
     cx.executor().allow_parking();
 
     // test command
-    cx.update(|cx| {
+    cx.update(|_, cx| {
         cx.bind_keys([KeyBinding::new(
             "g w",
             workspace::SendKeystrokes(": j enter".to_string()),
@@ -1002,7 +1004,7 @@ async fn test_remap(cx: &mut gpui::TestAppContext) {
     cx.assert_state("1234ˇ 56789", Mode::Normal);
 
     // test leaving command
-    cx.update(|cx| {
+    cx.update(|_, cx| {
         cx.bind_keys([KeyBinding::new(
             "g u",
             workspace::SendKeystrokes("g w g z".to_string()),
@@ -1014,7 +1016,7 @@ async fn test_remap(cx: &mut gpui::TestAppContext) {
     cx.assert_state("1234 567ˇ89", Mode::Normal);
 
     // test leaving command
-    cx.update(|cx| {
+    cx.update(|_, cx| {
         cx.bind_keys([KeyBinding::new(
             "g t",
             workspace::SendKeystrokes("i space escape".to_string()),
@@ -1341,7 +1343,7 @@ async fn test_find_multibyte(cx: &mut gpui::TestAppContext) {
 async fn test_sneak(cx: &mut gpui::TestAppContext) {
     let mut cx = VimTestContext::new(cx, true).await;
 
-    cx.update(|cx| {
+    cx.update(|_window, cx| {
         cx.bind_keys([
             KeyBinding::new(
                 "s",
@@ -1437,7 +1439,7 @@ async fn test_command_alias(cx: &mut gpui::TestAppContext) {
 #[gpui::test]
 async fn test_remap_adjacent_dog_cat(cx: &mut gpui::TestAppContext) {
     let mut cx = NeovimBackedTestContext::new(cx).await;
-    cx.update(|cx| {
+    cx.update(|_, cx| {
         cx.bind_keys([
             KeyBinding::new(
                 "d o g",
@@ -1470,7 +1472,7 @@ async fn test_remap_adjacent_dog_cat(cx: &mut gpui::TestAppContext) {
 #[gpui::test]
 async fn test_remap_nested_pineapple(cx: &mut gpui::TestAppContext) {
     let mut cx = NeovimBackedTestContext::new(cx).await;
-    cx.update(|cx| {
+    cx.update(|_, cx| {
         cx.bind_keys([
             KeyBinding::new(
                 "p i n",
@@ -1513,7 +1515,7 @@ async fn test_remap_nested_pineapple(cx: &mut gpui::TestAppContext) {
 #[gpui::test]
 async fn test_remap_recursion(cx: &mut gpui::TestAppContext) {
     let mut cx = NeovimBackedTestContext::new(cx).await;
-    cx.update(|cx| {
+    cx.update(|_, cx| {
         cx.bind_keys([KeyBinding::new(
             "x",
             workspace::SendKeystrokes("\" _ x".to_string()),
@@ -1547,7 +1549,7 @@ async fn test_escape_while_waiting(cx: &mut gpui::TestAppContext) {
 #[gpui::test]
 async fn test_ctrl_w_override(cx: &mut gpui::TestAppContext) {
     let mut cx = NeovimBackedTestContext::new(cx).await;
-    cx.update(|cx| {
+    cx.update(|_, cx| {
         cx.bind_keys([KeyBinding::new("ctrl-w", DeleteLine, None)]);
     });
     cx.neovim.exec("map <c-w> D").await;

crates/vim/src/test/neovim_backed_test_context.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{px, size, Context, UpdateGlobal};
+use gpui::{px, size, AppContext as _, UpdateGlobal};
 use indoc::indoc;
 use settings::SettingsStore;
 use std::{
@@ -222,7 +222,7 @@ impl NeovimBackedTestContext {
             .set_option(&format!("columns={}", columns))
             .await;
 
-        self.update(|cx| {
+        self.update(|_, cx| {
             SettingsStore::update_global(cx, |settings, cx| {
                 settings.update_user_settings::<AllLanguageSettings>(cx, |settings| {
                     settings.defaults.soft_wrap = Some(SoftWrap::PreferredLineLength);
@@ -237,21 +237,21 @@ impl NeovimBackedTestContext {
         self.neovim.set_option(&format!("scrolloff={}", 3)).await;
         // +2 to account for the vim command UI at the bottom.
         self.neovim.set_option(&format!("lines={}", rows + 2)).await;
-        let (line_height, visible_line_count) = self.editor(|editor, cx| {
+        let (line_height, visible_line_count) = self.editor(|editor, window, _cx| {
             (
                 editor
                     .style()
                     .unwrap()
                     .text
-                    .line_height_in_pixels(cx.rem_size()),
+                    .line_height_in_pixels(window.rem_size()),
                 editor.visible_line_count().unwrap(),
             )
         });
 
         let window = self.window;
         let margin = self
-            .update_window(window, |_, cx| {
-                cx.viewport_size().height - line_height * visible_line_count
+            .update_window(window, |_, window, _cx| {
+                window.viewport_size().height - line_height * visible_line_count
             })
             .unwrap();
 
@@ -286,7 +286,7 @@ impl NeovimBackedTestContext {
             register,
             state: self.shared_state().await,
             neovim: self.neovim.read_register(register).await,
-            editor: self.update(|cx| {
+            editor: self.update(|_, cx| {
                 cx.global::<VimGlobals>()
                     .registers
                     .get(&register)

crates/vim/src/test/vim_test_context.rs 🔗

@@ -2,7 +2,7 @@ use std::ops::{Deref, DerefMut};
 
 use assets::Assets;
 use editor::test::editor_lsp_test_context::EditorLspTestContext;
-use gpui::{Context, SemanticVersion, UpdateGlobal, View, VisualContext};
+use gpui::{Context, Entity, SemanticVersion, UpdateGlobal};
 use search::{project_search::ProjectSearchBar, BufferSearchBar};
 
 use crate::{state::Operator, *};
@@ -57,7 +57,7 @@ impl VimTestContext {
     }
 
     pub fn new_with_lsp(mut cx: EditorLspTestContext, enabled: bool) -> VimTestContext {
-        cx.update(|cx| {
+        cx.update(|_, cx| {
             SettingsStore::update_global(cx, |store, cx| {
                 store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(enabled));
             });
@@ -75,44 +75,46 @@ impl VimTestContext {
         });
 
         // Setup search toolbars and keypress hook
-        cx.update_workspace(|workspace, cx| {
+        cx.update_workspace(|workspace, window, cx| {
             workspace.active_pane().update(cx, |pane, cx| {
                 pane.toolbar().update(cx, |toolbar, cx| {
-                    let buffer_search_bar = cx.new_view(BufferSearchBar::new);
-                    toolbar.add_item(buffer_search_bar, cx);
+                    let buffer_search_bar = cx.new(|cx| BufferSearchBar::new(window, cx));
+                    toolbar.add_item(buffer_search_bar, window, cx);
 
-                    let project_search_bar = cx.new_view(|_| ProjectSearchBar::new());
-                    toolbar.add_item(project_search_bar, cx);
+                    let project_search_bar = cx.new(|_| ProjectSearchBar::new());
+                    toolbar.add_item(project_search_bar, window, cx);
                 })
             });
             workspace.status_bar().update(cx, |status_bar, cx| {
-                let vim_mode_indicator = cx.new_view(ModeIndicator::new);
-                status_bar.add_right_item(vim_mode_indicator, cx);
+                let vim_mode_indicator = cx.new(|cx| ModeIndicator::new(window, cx));
+                status_bar.add_right_item(vim_mode_indicator, window, cx);
             });
         });
 
         Self { cx }
     }
 
-    pub fn update_view<F, T, R>(&mut self, view: View<T>, update: F) -> R
+    pub fn update_model<F, T, R>(&mut self, model: Entity<T>, update: F) -> R
     where
         T: 'static,
-        F: FnOnce(&mut T, &mut ViewContext<T>) -> R + 'static,
+        F: FnOnce(&mut T, &mut Window, &mut Context<T>) -> R + 'static,
     {
         let window = self.window;
-        self.update_window(window, move |_, cx| view.update(cx, update))
-            .unwrap()
+        self.update_window(window, move |_, window, cx| {
+            model.update(cx, |t, cx| update(t, window, cx))
+        })
+        .unwrap()
     }
 
     pub fn workspace<F, T>(&mut self, update: F) -> T
     where
-        F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
+        F: FnOnce(&mut Workspace, &mut Window, &mut Context<Workspace>) -> T,
     {
         self.cx.update_workspace(update)
     }
 
     pub fn enable_vim(&mut self) {
-        self.cx.update(|cx| {
+        self.cx.update(|_, cx| {
             SettingsStore::update_global(cx, |store, cx| {
                 store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(true));
             });
@@ -120,7 +122,7 @@ impl VimTestContext {
     }
 
     pub fn disable_vim(&mut self) {
-        self.cx.update(|cx| {
+        self.cx.update(|_, cx| {
             SettingsStore::update_global(cx, |store, cx| {
                 store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(false));
             });
@@ -128,15 +130,15 @@ impl VimTestContext {
     }
 
     pub fn mode(&mut self) -> Mode {
-        self.update_editor(|editor, cx| editor.addon::<VimAddon>().unwrap().view.read(cx).mode)
+        self.update_editor(|editor, _, cx| editor.addon::<VimAddon>().unwrap().model.read(cx).mode)
     }
 
     pub fn active_operator(&mut self) -> Option<Operator> {
-        self.update_editor(|editor, cx| {
+        self.update_editor(|editor, _, cx| {
             editor
                 .addon::<VimAddon>()
                 .unwrap()
-                .view
+                .model
                 .read(cx)
                 .operator_stack
                 .last()
@@ -146,11 +148,12 @@ impl VimTestContext {
 
     pub fn set_state(&mut self, text: &str, mode: Mode) {
         self.cx.set_state(text);
-        let vim = self.update_editor(|editor, _cx| editor.addon::<VimAddon>().cloned().unwrap());
+        let vim =
+            self.update_editor(|editor, _window, _cx| editor.addon::<VimAddon>().cloned().unwrap());
 
-        self.update(|cx| {
-            vim.view.update(cx, |vim, cx| {
-                vim.switch_mode(mode, true, cx);
+        self.update(|window, cx| {
+            vim.model.update(cx, |vim, cx| {
+                vim.switch_mode(mode, true, window, cx);
             });
         });
         self.cx.cx.cx.run_until_parked();

crates/vim/src/vim.rs 🔗

@@ -26,8 +26,8 @@ use editor::{
     Anchor, Bias, Editor, EditorEvent, EditorMode, ToPoint,
 };
 use gpui::{
-    actions, impl_actions, Action, AppContext, Axis, Entity, EventEmitter, KeyContext,
-    KeystrokeEvent, Render, Subscription, Task, View, ViewContext, WeakView,
+    actions, impl_actions, Action, App, AppContext as _, Axis, Context, Entity, EventEmitter,
+    KeyContext, KeystrokeEvent, Render, Subscription, Task, WeakEntity, Window,
 };
 use insert::{NormalBefore, TemporaryNormal};
 use language::{CursorShape, Point, Selection, SelectionGoal, TransactionId};
@@ -42,7 +42,7 @@ use state::{Mode, Operator, RecordedSelection, SearchState, VimGlobals};
 use std::{mem, ops::Range, sync::Arc};
 use surrounds::SurroundsType;
 use theme::ThemeSettings;
-use ui::{px, IntoElement, SharedString, VisualContext};
+use ui::{px, IntoElement, SharedString};
 use vim_mode_setting::VimModeSetting;
 use workspace::{self, Pane, ResizeIntent, Workspace};
 
@@ -96,16 +96,15 @@ impl_actions!(
 );
 
 /// Initializes the `vim` crate.
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     vim_mode_setting::init(cx);
     VimSettings::register(cx);
     VimGlobals::register(cx);
 
-    cx.observe_new_views(|editor: &mut Editor, cx| Vim::register(editor, cx))
-        .detach();
+    cx.observe_new(Vim::register).detach();
 
-    cx.observe_new_views(|workspace: &mut Workspace, _| {
-        workspace.register_action(|workspace, _: &ToggleVimMode, cx| {
+    cx.observe_new(|workspace: &mut Workspace, _, _| {
+        workspace.register_action(|workspace, _: &ToggleVimMode, _, cx| {
             let fs = workspace.app_state().fs.clone();
             let currently_enabled = Vim::enabled(cx);
             update_settings_file::<VimModeSetting>(fs, cx, move |setting, _| {
@@ -113,7 +112,7 @@ pub fn init(cx: &mut AppContext) {
             })
         });
 
-        workspace.register_action(|_, _: &OpenDefaultKeymap, cx| {
+        workspace.register_action(|_, _: &OpenDefaultKeymap, _, cx| {
             cx.emit(workspace::Event::OpenBundledFile {
                 text: settings::vim_keymap(),
                 title: "Default Vim Bindings",
@@ -121,11 +120,11 @@ pub fn init(cx: &mut AppContext) {
             });
         });
 
-        workspace.register_action(|workspace, _: &ResetPaneSizes, cx| {
+        workspace.register_action(|workspace, _: &ResetPaneSizes, _, cx| {
             workspace.reset_pane_sizes(cx);
         });
 
-        workspace.register_action(|workspace, _: &MaximizePane, cx| {
+        workspace.register_action(|workspace, _: &MaximizePane, _, cx| {
             let pane = workspace.active_pane();
             let Some(size) = workspace.bounding_box_for_pane(&pane) else {
                 return;
@@ -142,13 +141,13 @@ pub fn init(cx: &mut AppContext) {
             workspace.resize_pane(Axis::Vertical, desired_size - size.size.height, cx)
         });
 
-        workspace.register_action(|workspace, action: &ResizePane, cx| {
+        workspace.register_action(|workspace, action: &ResizePane, window, cx| {
             let count = Vim::take_count(cx).unwrap_or(1) as f32;
             let theme = ThemeSettings::get_global(cx);
-            let Ok(font_id) = cx.text_system().font_id(&theme.buffer_font) else {
+            let Ok(font_id) = window.text_system().font_id(&theme.buffer_font) else {
                 return;
             };
-            let Ok(width) = cx
+            let Ok(width) = window
                 .text_system()
                 .advance(font_id, theme.buffer_font_size(), 'm')
             else {
@@ -166,16 +165,17 @@ pub fn init(cx: &mut AppContext) {
             workspace.resize_pane(axis, amount * count, cx);
         });
 
-        workspace.register_action(|workspace, _: &SearchSubmit, cx| {
+        workspace.register_action(|workspace, _: &SearchSubmit, window, cx| {
             let vim = workspace
-                .focused_pane(cx)
+                .focused_pane(window, cx)
                 .read(cx)
                 .active_item()
                 .and_then(|item| item.act_as::<Editor>(cx))
                 .and_then(|editor| editor.read(cx).addon::<VimAddon>().cloned());
             let Some(vim) = vim else { return };
-            vim.view
-                .update(cx, |_, cx| cx.defer(|vim, cx| vim.search_submit(cx)))
+            vim.model.update(cx, |_, cx| {
+                cx.defer_in(window, |vim, window, cx| vim.search_submit(window, cx))
+            })
         });
     })
     .detach();
@@ -183,12 +183,12 @@ pub fn init(cx: &mut AppContext) {
 
 #[derive(Clone)]
 pub(crate) struct VimAddon {
-    pub(crate) view: View<Vim>,
+    pub(crate) model: Entity<Vim>,
 }
 
 impl editor::Addon for VimAddon {
-    fn extend_key_context(&self, key_context: &mut KeyContext, cx: &AppContext) {
-        self.view.read(cx).extend_key_context(key_context, cx)
+    fn extend_key_context(&self, key_context: &mut KeyContext, cx: &App) {
+        self.model.read(cx).extend_key_context(key_context, cx)
     }
 
     fn to_any(&self) -> &dyn std::any::Any {
@@ -219,7 +219,7 @@ pub(crate) struct Vim {
     selected_register: Option<char>,
     pub search: SearchState,
 
-    editor: WeakView<Editor>,
+    editor: WeakEntity<Editor>,
 
     last_command: Option<String>,
     running_command: Option<Task<()>>,
@@ -230,7 +230,7 @@ pub(crate) struct Vim {
 // This means it needs a VisualContext. The easiest way to satisfy that constraint is
 // to make Vim a "View" that is just never actually rendered.
 impl Render for Vim {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         gpui::Empty
     }
 }
@@ -244,10 +244,10 @@ impl Vim {
     /// The namespace for Vim actions.
     const NAMESPACE: &'static str = "vim";
 
-    pub fn new(cx: &mut ViewContext<Editor>) -> View<Self> {
-        let editor = cx.view().clone();
+    pub fn new(window: &mut Window, cx: &mut Context<Editor>) -> Entity<Self> {
+        let editor = cx.model().clone();
 
-        cx.new_view(|cx| Vim {
+        cx.new(|cx| Vim {
             mode: Mode::Normal,
             last_mode: Mode::Normal,
             temp_mode: false,
@@ -273,28 +273,32 @@ impl Vim {
             editor: editor.downgrade(),
             _subscriptions: vec![
                 cx.observe_keystrokes(Self::observe_keystrokes),
-                cx.subscribe(&editor, |this, _, event, cx| {
-                    this.handle_editor_event(event, cx)
+                cx.subscribe_in(&editor, window, |this, _, event, window, cx| {
+                    this.handle_editor_event(event, window, cx)
                 }),
             ],
         })
     }
 
-    fn register(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
+    fn register(editor: &mut Editor, window: Option<&mut Window>, cx: &mut Context<Editor>) {
+        let Some(window) = window else {
+            return;
+        };
+
         if !editor.use_modal_editing() {
             return;
         }
 
         let mut was_enabled = Vim::enabled(cx);
         let mut was_toggle = VimSettings::get_global(cx).toggle_relative_line_numbers;
-        cx.observe_global::<SettingsStore>(move |editor, cx| {
+        cx.observe_global_in::<SettingsStore>(window, move |editor, window, cx| {
             let enabled = Vim::enabled(cx);
             let toggle = VimSettings::get_global(cx).toggle_relative_line_numbers;
             if enabled && was_enabled && (toggle != was_toggle) {
                 if toggle {
                     let is_relative = editor
                         .addon::<VimAddon>()
-                        .map(|vim| vim.view.read(cx).mode != Mode::Insert);
+                        .map(|vim| vim.model.read(cx).mode != Mode::Insert);
                     editor.set_relative_line_number(is_relative, cx)
                 } else {
                     editor.set_relative_line_number(None, cx)
@@ -306,42 +310,42 @@ impl Vim {
             }
             was_enabled = enabled;
             if enabled {
-                Self::activate(editor, cx)
+                Self::activate(editor, window, cx)
             } else {
                 Self::deactivate(editor, cx)
             }
         })
         .detach();
         if was_enabled {
-            Self::activate(editor, cx)
+            Self::activate(editor, window, cx)
         }
     }
 
-    fn activate(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
-        let vim = Vim::new(cx);
+    fn activate(editor: &mut Editor, window: &mut Window, cx: &mut Context<Editor>) {
+        let vim = Vim::new(window, cx);
 
-        editor.register_addon(VimAddon { view: vim.clone() });
+        editor.register_addon(VimAddon { model: vim.clone() });
 
         vim.update(cx, |_, cx| {
-            Vim::action(editor, cx, |vim, action: &SwitchMode, cx| {
-                vim.switch_mode(action.0, false, cx)
+            Vim::action(editor, cx, |vim, action: &SwitchMode, window, cx| {
+                vim.switch_mode(action.0, false, window, cx)
             });
 
-            Vim::action(editor, cx, |vim, action: &PushOperator, cx| {
-                vim.push_operator(action.0.clone(), cx)
+            Vim::action(editor, cx, |vim, action: &PushOperator, window, cx| {
+                vim.push_operator(action.0.clone(), window, cx)
             });
 
-            Vim::action(editor, cx, |vim, _: &ClearOperators, cx| {
-                vim.clear_operator(cx)
+            Vim::action(editor, cx, |vim, _: &ClearOperators, window, cx| {
+                vim.clear_operator(window, cx)
             });
-            Vim::action(editor, cx, |vim, n: &Number, cx| {
-                vim.push_count_digit(n.0, cx);
+            Vim::action(editor, cx, |vim, n: &Number, window, cx| {
+                vim.push_count_digit(n.0, window, cx);
             });
-            Vim::action(editor, cx, |vim, _: &Tab, cx| {
-                vim.input_ignored(" ".into(), cx)
+            Vim::action(editor, cx, |vim, _: &Tab, window, cx| {
+                vim.input_ignored(" ".into(), window, cx)
             });
-            Vim::action(editor, cx, |vim, _: &Enter, cx| {
-                vim.input_ignored("\n".into(), cx)
+            Vim::action(editor, cx, |vim, _: &Enter, window, cx| {
+                vim.input_ignored("\n".into(), window, cx)
             });
 
             normal::register(editor, cx);
@@ -357,13 +361,13 @@ impl Vim {
             change_list::register(editor, cx);
             digraph::register(editor, cx);
 
-            cx.defer(|vim, cx| {
-                vim.focused(false, cx);
+            cx.defer_in(window, |vim, window, cx| {
+                vim.focused(false, window, cx);
             })
         })
     }
 
-    fn deactivate(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
+    fn deactivate(editor: &mut Editor, cx: &mut Context<Editor>) {
         editor.set_cursor_shape(CursorShape::Bar, cx);
         editor.set_clip_at_line_ends(false, cx);
         editor.set_collapse_matches(false);
@@ -373,7 +377,7 @@ impl Vim {
         editor.unregister_addon::<VimAddon>();
         editor.set_relative_line_number(None, cx);
         if let Some(vim) = Vim::globals(cx).focused_vim() {
-            if vim.entity_id() == cx.view().entity_id() {
+            if vim.entity_id() == cx.model().entity_id() {
                 Vim::globals(cx).focused_vim = None;
             }
         }
@@ -382,35 +386,38 @@ impl Vim {
     /// Register an action on the editor.
     pub fn action<A: Action>(
         editor: &mut Editor,
-        cx: &mut ViewContext<Vim>,
-        f: impl Fn(&mut Vim, &A, &mut ViewContext<Vim>) + 'static,
+        cx: &mut Context<Vim>,
+        f: impl Fn(&mut Vim, &A, &mut Window, &mut Context<Vim>) + 'static,
     ) {
         let subscription = editor.register_action(cx.listener(f));
-        cx.on_release(|_, _, _| drop(subscription)).detach();
+        cx.on_release(|_, _| drop(subscription)).detach();
     }
 
-    pub fn editor(&self) -> Option<View<Editor>> {
+    pub fn editor(&self) -> Option<Entity<Editor>> {
         self.editor.upgrade()
     }
 
-    pub fn workspace(&self, cx: &mut ViewContext<Self>) -> Option<View<Workspace>> {
-        cx.window_handle()
-            .downcast::<Workspace>()
-            .and_then(|handle| handle.root(cx).ok())
+    pub fn workspace(&self, window: &mut Window) -> Option<Entity<Workspace>> {
+        window.root_model::<Workspace>().flatten()
     }
 
-    pub fn pane(&self, cx: &mut ViewContext<Self>) -> Option<View<Pane>> {
-        self.workspace(cx)
-            .map(|workspace| workspace.read(cx).focused_pane(cx))
+    pub fn pane(&self, window: &mut Window, cx: &mut Context<Self>) -> Option<Entity<Pane>> {
+        self.workspace(window)
+            .map(|workspace| workspace.read(cx).focused_pane(window, cx))
     }
 
-    pub fn enabled(cx: &mut AppContext) -> bool {
+    pub fn enabled(cx: &mut App) -> bool {
         VimModeSetting::get_global(cx).0
     }
 
     /// Called whenever an keystroke is typed so vim can observe all actions
     /// and keystrokes accordingly.
-    fn observe_keystrokes(&mut self, keystroke_event: &KeystrokeEvent, cx: &mut ViewContext<Self>) {
+    fn observe_keystrokes(
+        &mut self,
+        keystroke_event: &KeystrokeEvent,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if self.exit_temporary_mode {
             self.exit_temporary_mode = false;
             // Don't switch to insert mode if the action is temporary_normal.
@@ -419,24 +426,30 @@ impl Vim {
                     return;
                 }
             }
-            self.switch_mode(Mode::Insert, false, cx)
+            self.switch_mode(Mode::Insert, false, window, cx)
         }
         if let Some(action) = keystroke_event.action.as_ref() {
             // Keystroke is handled by the vim system, so continue forward
             if action.name().starts_with("vim::") {
                 return;
             }
-        } else if cx.has_pending_keystrokes() || keystroke_event.keystroke.is_ime_in_progress() {
+        } else if window.has_pending_keystrokes() || keystroke_event.keystroke.is_ime_in_progress()
+        {
             return;
         }
 
         if let Some(operator) = self.active_operator() {
             match operator {
                 Operator::Literal { prefix } => {
-                    self.handle_literal_keystroke(keystroke_event, prefix.unwrap_or_default(), cx);
+                    self.handle_literal_keystroke(
+                        keystroke_event,
+                        prefix.unwrap_or_default(),
+                        window,
+                        cx,
+                    );
                 }
                 _ if !operator.is_waiting(self.mode) => {
-                    self.clear_operator(cx);
+                    self.clear_operator(window, cx);
                     self.stop_recording_immediately(Box::new(ClearOperators), cx)
                 }
                 _ => {}
@@ -444,15 +457,20 @@ impl Vim {
         }
     }
 
-    fn handle_editor_event(&mut self, event: &EditorEvent, cx: &mut ViewContext<Self>) {
+    fn handle_editor_event(
+        &mut self,
+        event: &EditorEvent,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         match event {
-            EditorEvent::Focused => self.focused(true, cx),
-            EditorEvent::Blurred => self.blurred(cx),
+            EditorEvent::Focused => self.focused(true, window, cx),
+            EditorEvent::Blurred => self.blurred(window, cx),
             EditorEvent::SelectionsChanged { local: true } => {
-                self.local_selections_changed(cx);
+                self.local_selections_changed(window, cx);
             }
             EditorEvent::InputIgnored { text } => {
-                self.input_ignored(text.clone(), cx);
+                self.input_ignored(text.clone(), window, cx);
                 Vim::globals(cx).observe_insertion(text, None)
             }
             EditorEvent::InputHandled {
@@ -460,19 +478,19 @@ impl Vim {
                 utf16_range_to_replace: range_to_replace,
             } => Vim::globals(cx).observe_insertion(text, range_to_replace.clone()),
             EditorEvent::TransactionBegun { transaction_id } => {
-                self.transaction_begun(*transaction_id, cx)
+                self.transaction_begun(*transaction_id, window, cx)
             }
             EditorEvent::TransactionUndone { transaction_id } => {
-                self.transaction_undone(transaction_id, cx)
+                self.transaction_undone(transaction_id, window, cx)
             }
-            EditorEvent::Edited { .. } => self.push_to_change_list(cx),
-            EditorEvent::FocusedIn => self.sync_vim_settings(cx),
-            EditorEvent::CursorShapeChanged => self.cursor_shape_changed(cx),
+            EditorEvent::Edited { .. } => self.push_to_change_list(window, cx),
+            EditorEvent::FocusedIn => self.sync_vim_settings(window, cx),
+            EditorEvent::CursorShapeChanged => self.cursor_shape_changed(window, cx),
             _ => {}
         }
     }
 
-    fn push_operator(&mut self, operator: Operator, cx: &mut ViewContext<Self>) {
+    fn push_operator(&mut self, operator: Operator, window: &mut Window, cx: &mut Context<Self>) {
         if matches!(
             operator,
             Operator::Change
@@ -503,14 +521,20 @@ impl Vim {
             }
         };
         self.operator_stack.push(operator);
-        self.sync_vim_settings(cx);
+        self.sync_vim_settings(window, cx);
     }
 
-    pub fn switch_mode(&mut self, mode: Mode, leave_selections: bool, cx: &mut ViewContext<Self>) {
+    pub fn switch_mode(
+        &mut self,
+        mode: Mode,
+        leave_selections: bool,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if self.temp_mode && mode == Mode::Normal {
             self.temp_mode = false;
-            self.switch_mode(Mode::Normal, leave_selections, cx);
-            self.switch_mode(Mode::Insert, false, cx);
+            self.switch_mode(Mode::Normal, leave_selections, window, cx);
+            self.switch_mode(Mode::Insert, false, window, cx);
             return;
         } else if self.temp_mode
             && !matches!(mode, Mode::Visual | Mode::VisualLine | Mode::VisualBlock)
@@ -526,7 +550,7 @@ impl Vim {
         self.mode = mode;
         self.operator_stack.clear();
         self.selected_register.take();
-        self.cancel_running_command(cx);
+        self.cancel_running_command(window, cx);
         if mode == Mode::Normal || mode != last_mode {
             self.current_tx.take();
             self.current_anchor.take();
@@ -536,13 +560,13 @@ impl Vim {
         }
 
         // Sync editor settings like clip mode
-        self.sync_vim_settings(cx);
+        self.sync_vim_settings(window, cx);
 
         if VimSettings::get_global(cx).toggle_relative_line_numbers
             && self.mode != self.last_mode
             && (self.mode == Mode::Insert || self.last_mode == Mode::Insert)
         {
-            self.update_editor(cx, |vim, editor, cx| {
+            self.update_editor(window, cx, |vim, editor, _, cx| {
                 let is_relative = vim.mode != Mode::Insert;
                 editor.set_relative_line_number(Some(is_relative), cx)
             });
@@ -553,14 +577,16 @@ impl Vim {
         }
 
         if !mode.is_visual() && last_mode.is_visual() {
-            self.create_visual_marks(last_mode, cx);
+            self.create_visual_marks(last_mode, window, cx);
         }
 
         // Adjust selections
-        self.update_editor(cx, |vim, editor, cx| {
+        self.update_editor(window, cx, |vim, editor, window, cx| {
             if last_mode != Mode::VisualBlock && last_mode.is_visual() && mode == Mode::VisualBlock
             {
-                vim.visual_block_motion(true, editor, cx, |_, point, goal| Some((point, goal)))
+                vim.visual_block_motion(true, editor, window, cx, |_, point, goal| {
+                    Some((point, goal))
+                })
             }
             if last_mode == Mode::Insert || last_mode == Mode::Replace {
                 if let Some(prior_tx) = prior_tx {
@@ -568,7 +594,7 @@ impl Vim {
                 }
             }
 
-            editor.change_selections(None, cx, |s| {
+            editor.change_selections(None, window, cx, |s| {
                 // we cheat with visual block mode and use multiple cursors.
                 // the cost of this cheat is we need to convert back to a single
                 // cursor whenever vim would.
@@ -612,7 +638,7 @@ impl Vim {
         });
     }
 
-    pub fn take_count(cx: &mut AppContext) -> Option<usize> {
+    pub fn take_count(cx: &mut App) -> Option<usize> {
         let global_state = cx.global_mut::<VimGlobals>();
         if global_state.dot_replaying {
             return global_state.recorded_count;
@@ -697,7 +723,7 @@ impl Vim {
         }
     }
 
-    pub fn extend_key_context(&self, context: &mut KeyContext, cx: &AppContext) {
+    pub fn extend_key_context(&self, context: &mut KeyContext, cx: &App) {
         let mut mode = match self.mode {
             Mode::Normal => "normal",
             Mode::Visual | Mode::VisualLine | Mode::VisualBlock => "visual",
@@ -736,7 +762,7 @@ impl Vim {
         context.set("vim_operator", operator_id);
     }
 
-    fn focused(&mut self, preserve_selection: bool, cx: &mut ViewContext<Self>) {
+    fn focused(&mut self, preserve_selection: bool, window: &mut Window, cx: &mut Context<Self>) {
         let Some(editor) = self.editor() else {
             return;
         };
@@ -753,11 +779,11 @@ impl Vim {
                 && editor.leader_peer_id().is_none()
         {
             if preserve_selection {
-                self.switch_mode(Mode::Visual, true, cx);
+                self.switch_mode(Mode::Visual, true, window, cx);
             } else {
-                self.update_editor(cx, |_, editor, cx| {
+                self.update_editor(window, cx, |_, editor, window, cx| {
                     editor.set_clip_at_line_ends(false, cx);
-                    editor.change_selections(None, cx, |s| {
+                    editor.change_selections(None, window, cx, |s| {
                         s.move_with(|_, selection| {
                             selection.collapse_to(selection.start, selection.goal)
                         })
@@ -767,58 +793,63 @@ impl Vim {
         }
 
         cx.emit(VimEvent::Focused);
-        self.sync_vim_settings(cx);
+        self.sync_vim_settings(window, cx);
 
         if VimSettings::get_global(cx).toggle_relative_line_numbers {
             if let Some(old_vim) = Vim::globals(cx).focused_vim() {
-                if old_vim.entity_id() != cx.view().entity_id() {
+                if old_vim.entity_id() != cx.model().entity_id() {
                     old_vim.update(cx, |vim, cx| {
-                        vim.update_editor(cx, |_, editor, cx| {
+                        vim.update_editor(window, cx, |_, editor, _, cx| {
                             editor.set_relative_line_number(None, cx)
                         });
                     });
 
-                    self.update_editor(cx, |vim, editor, cx| {
+                    self.update_editor(window, cx, |vim, editor, _, cx| {
                         let is_relative = vim.mode != Mode::Insert;
                         editor.set_relative_line_number(Some(is_relative), cx)
                     });
                 }
             } else {
-                self.update_editor(cx, |vim, editor, cx| {
+                self.update_editor(window, cx, |vim, editor, _, cx| {
                     let is_relative = vim.mode != Mode::Insert;
                     editor.set_relative_line_number(Some(is_relative), cx)
                 });
             }
         }
-        Vim::globals(cx).focused_vim = Some(cx.view().downgrade());
+        Vim::globals(cx).focused_vim = Some(cx.model().downgrade());
     }
 
-    fn blurred(&mut self, cx: &mut ViewContext<Self>) {
+    fn blurred(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.stop_recording_immediately(NormalBefore.boxed_clone(), cx);
-        self.store_visual_marks(cx);
-        self.clear_operator(cx);
-        self.update_editor(cx, |_, editor, cx| {
+        self.store_visual_marks(window, cx);
+        self.clear_operator(window, cx);
+        self.update_editor(window, cx, |_, editor, _, cx| {
             editor.set_cursor_shape(language::CursorShape::Hollow, cx);
         });
     }
 
-    fn cursor_shape_changed(&mut self, cx: &mut ViewContext<Self>) {
-        self.update_editor(cx, |vim, editor, cx| {
+    fn cursor_shape_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        self.update_editor(window, cx, |vim, editor, _, cx| {
             editor.set_cursor_shape(vim.cursor_shape(), cx);
         });
     }
 
     fn update_editor<S>(
         &mut self,
-        cx: &mut ViewContext<Self>,
-        update: impl FnOnce(&mut Self, &mut Editor, &mut ViewContext<Editor>) -> S,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+        update: impl FnOnce(&mut Self, &mut Editor, &mut Window, &mut Context<Editor>) -> S,
     ) -> Option<S> {
         let editor = self.editor.upgrade()?;
-        Some(editor.update(cx, |editor, cx| update(self, editor, cx)))
+        Some(editor.update(cx, |editor, cx| update(self, editor, window, cx)))
     }
 
-    fn editor_selections(&mut self, cx: &mut ViewContext<Self>) -> Vec<Range<Anchor>> {
-        self.update_editor(cx, |_, editor, _| {
+    fn editor_selections(
+        &mut self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Vec<Range<Anchor>> {
+        self.update_editor(window, cx, |_, editor, _, _| {
             editor
                 .selections
                 .disjoint_anchors()
@@ -831,7 +862,7 @@ impl Vim {
 
     /// When doing an action that modifies the buffer, we start recording so that `.`
     /// will replay the action.
-    pub fn start_recording(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn start_recording(&mut self, cx: &mut Context<Self>) {
         Vim::update_globals(cx, |globals, cx| {
             if !globals.dot_replaying {
                 globals.dot_recording = true;
@@ -874,7 +905,7 @@ impl Vim {
         })
     }
 
-    pub fn stop_replaying(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn stop_replaying(&mut self, cx: &mut Context<Self>) {
         let globals = Vim::globals(cx);
         globals.dot_replaying = false;
         if let Some(replayer) = globals.replayer.take() {
@@ -885,7 +916,7 @@ impl Vim {
     /// When finishing an action that modifies the buffer, stop recording.
     /// as you usually call this within a keystroke handler we also ensure that
     /// the current action is recorded.
-    pub fn stop_recording(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn stop_recording(&mut self, cx: &mut Context<Self>) {
         let globals = Vim::globals(cx);
         if globals.dot_recording {
             globals.stop_recording_after_next_action = true;
@@ -897,11 +928,7 @@ impl Vim {
     /// next action to stop recording.
     ///
     /// This doesn't include the current action.
-    pub fn stop_recording_immediately(
-        &mut self,
-        action: Box<dyn Action>,
-        cx: &mut ViewContext<Self>,
-    ) {
+    pub fn stop_recording_immediately(&mut self, action: Box<dyn Action>, cx: &mut Context<Self>) {
         let globals = Vim::globals(cx);
         if globals.dot_recording {
             globals
@@ -915,12 +942,12 @@ impl Vim {
     }
 
     /// Explicitly record one action (equivalents to start_recording and stop_recording)
-    pub fn record_current_action(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn record_current_action(&mut self, cx: &mut Context<Self>) {
         self.start_recording(cx);
         self.stop_recording(cx);
     }
 
-    fn push_count_digit(&mut self, number: usize, cx: &mut ViewContext<Self>) {
+    fn push_count_digit(&mut self, number: usize, window: &mut Window, cx: &mut Context<Self>) {
         if self.active_operator().is_some() {
             let post_count = Vim::globals(cx).post_count.unwrap_or(0);
 
@@ -941,41 +968,46 @@ impl Vim {
             )
         }
         // update the keymap so that 0 works
-        self.sync_vim_settings(cx)
+        self.sync_vim_settings(window, cx)
     }
 
-    fn select_register(&mut self, register: Arc<str>, cx: &mut ViewContext<Self>) {
+    fn select_register(&mut self, register: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
         if register.chars().count() == 1 {
             self.selected_register
                 .replace(register.chars().next().unwrap());
         }
         self.operator_stack.clear();
-        self.sync_vim_settings(cx);
+        self.sync_vim_settings(window, cx);
     }
 
     fn maybe_pop_operator(&mut self) -> Option<Operator> {
         self.operator_stack.pop()
     }
 
-    fn pop_operator(&mut self, cx: &mut ViewContext<Self>) -> Operator {
+    fn pop_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Operator {
         let popped_operator = self.operator_stack.pop()
             .expect("Operator popped when no operator was on the stack. This likely means there is an invalid keymap config");
-        self.sync_vim_settings(cx);
+        self.sync_vim_settings(window, cx);
         popped_operator
     }
 
-    fn clear_operator(&mut self, cx: &mut ViewContext<Self>) {
+    fn clear_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         Vim::take_count(cx);
         self.selected_register.take();
         self.operator_stack.clear();
-        self.sync_vim_settings(cx);
+        self.sync_vim_settings(window, cx);
     }
 
     fn active_operator(&self) -> Option<Operator> {
         self.operator_stack.last().cloned()
     }
 
-    fn transaction_begun(&mut self, transaction_id: TransactionId, _: &mut ViewContext<Self>) {
+    fn transaction_begun(
+        &mut self,
+        transaction_id: TransactionId,
+        _window: &mut Window,
+        _: &mut Context<Self>,
+    ) {
         let mode = if (self.mode == Mode::Insert
             || self.mode == Mode::Replace
             || self.mode == Mode::Normal)
@@ -991,12 +1023,17 @@ impl Vim {
         }
     }
 
-    fn transaction_undone(&mut self, transaction_id: &TransactionId, cx: &mut ViewContext<Self>) {
+    fn transaction_undone(
+        &mut self,
+        transaction_id: &TransactionId,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         match self.mode {
             Mode::VisualLine | Mode::VisualBlock | Mode::Visual => {
-                self.update_editor(cx, |vim, editor, cx| {
+                self.update_editor(window, cx, |vim, editor, window, cx| {
                     let original_mode = vim.undo_modes.get(transaction_id);
-                    editor.change_selections(None, cx, |s| match original_mode {
+                    editor.change_selections(None, window, cx, |s| match original_mode {
                         Some(Mode::VisualLine) => {
                             s.move_with(|map, selection| {
                                 selection.collapse_to(
@@ -1020,11 +1057,11 @@ impl Vim {
                         }
                     });
                 });
-                self.switch_mode(Mode::Normal, true, cx)
+                self.switch_mode(Mode::Normal, true, window, cx)
             }
             Mode::Normal => {
-                self.update_editor(cx, |_, editor, cx| {
-                    editor.change_selections(None, cx, |s| {
+                self.update_editor(window, cx, |_, editor, window, cx| {
+                    editor.change_selections(None, window, cx, |s| {
                         s.move_with(|map, selection| {
                             selection
                                 .collapse_to(map.clip_at_line_end(selection.end), selection.goal)
@@ -1036,7 +1073,7 @@ impl Vim {
         }
     }
 
-    fn local_selections_changed(&mut self, cx: &mut ViewContext<Self>) {
+    fn local_selections_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let Some(editor) = self.editor() else { return };
 
         if editor.read(cx).leader_peer_id().is_some() {
@@ -1050,26 +1087,26 @@ impl Vim {
                 self.current_anchor = Some(newest);
             } else if self.current_anchor.as_ref().unwrap() != &newest {
                 if let Some(tx_id) = self.current_tx.take() {
-                    self.update_editor(cx, |_, editor, cx| {
+                    self.update_editor(window, cx, |_, editor, _, cx| {
                         editor.group_until_transaction(tx_id, cx)
                     });
                 }
             }
         } else if self.mode == Mode::Normal && newest.start != newest.end {
             if matches!(newest.goal, SelectionGoal::HorizontalRange { .. }) {
-                self.switch_mode(Mode::VisualBlock, false, cx);
+                self.switch_mode(Mode::VisualBlock, false, window, cx);
             } else {
-                self.switch_mode(Mode::Visual, false, cx)
+                self.switch_mode(Mode::Visual, false, window, cx)
             }
         } else if newest.start == newest.end
             && !is_multicursor
             && [Mode::Visual, Mode::VisualLine, Mode::VisualBlock].contains(&self.mode)
         {
-            self.switch_mode(Mode::Normal, true, cx);
+            self.switch_mode(Mode::Normal, true, window, cx);
         }
     }
 
-    fn input_ignored(&mut self, text: Arc<str>, cx: &mut ViewContext<Self>) {
+    fn input_ignored(&mut self, text: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
         if text.is_empty() {
             return;
         }
@@ -1087,7 +1124,7 @@ impl Vim {
                     smartcase: VimSettings::get_global(cx).use_smartcase_find,
                 };
                 Vim::globals(cx).last_find = Some(find.clone());
-                self.motion(find, cx)
+                self.motion(find, window, cx)
             }
             Some(Operator::FindBackward { after }) => {
                 let find = Motion::FindBackward {
@@ -1101,7 +1138,7 @@ impl Vim {
                     smartcase: VimSettings::get_global(cx).use_smartcase_find,
                 };
                 Vim::globals(cx).last_find = Some(find.clone());
-                self.motion(find, cx)
+                self.motion(find, window, cx)
             }
             Some(Operator::Sneak { first_char }) => {
                 if let Some(first_char) = first_char {
@@ -1112,12 +1149,12 @@ impl Vim {
                             smartcase: VimSettings::get_global(cx).use_smartcase_find,
                         };
                         Vim::globals(cx).last_find = Some((&sneak).clone());
-                        self.motion(sneak, cx)
+                        self.motion(sneak, window, cx)
                     }
                 } else {
                     let first_char = text.chars().next();
-                    self.pop_operator(cx);
-                    self.push_operator(Operator::Sneak { first_char }, cx);
+                    self.pop_operator(window, cx);
+                    self.push_operator(Operator::Sneak { first_char }, window, cx);
                 }
             }
             Some(Operator::SneakBackward { first_char }) => {
@@ -1129,74 +1166,74 @@ impl Vim {
                             smartcase: VimSettings::get_global(cx).use_smartcase_find,
                         };
                         Vim::globals(cx).last_find = Some((&sneak).clone());
-                        self.motion(sneak, cx)
+                        self.motion(sneak, window, cx)
                     }
                 } else {
                     let first_char = text.chars().next();
-                    self.pop_operator(cx);
-                    self.push_operator(Operator::SneakBackward { first_char }, cx);
+                    self.pop_operator(window, cx);
+                    self.push_operator(Operator::SneakBackward { first_char }, window, cx);
                 }
             }
             Some(Operator::Replace) => match self.mode {
-                Mode::Normal => self.normal_replace(text, cx),
+                Mode::Normal => self.normal_replace(text, window, cx),
                 Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
-                    self.visual_replace(text, cx)
+                    self.visual_replace(text, window, cx)
                 }
-                _ => self.clear_operator(cx),
+                _ => self.clear_operator(window, cx),
             },
             Some(Operator::Digraph { first_char }) => {
                 if let Some(first_char) = first_char {
                     if let Some(second_char) = text.chars().next() {
-                        self.insert_digraph(first_char, second_char, cx);
+                        self.insert_digraph(first_char, second_char, window, cx);
                     }
                 } else {
                     let first_char = text.chars().next();
-                    self.pop_operator(cx);
-                    self.push_operator(Operator::Digraph { first_char }, cx);
+                    self.pop_operator(window, cx);
+                    self.push_operator(Operator::Digraph { first_char }, window, cx);
                 }
             }
             Some(Operator::Literal { prefix }) => {
-                self.handle_literal_input(prefix.unwrap_or_default(), &text, cx)
+                self.handle_literal_input(prefix.unwrap_or_default(), &text, window, cx)
             }
             Some(Operator::AddSurrounds { target }) => match self.mode {
                 Mode::Normal => {
                     if let Some(target) = target {
-                        self.add_surrounds(text, target, cx);
-                        self.clear_operator(cx);
+                        self.add_surrounds(text, target, window, cx);
+                        self.clear_operator(window, cx);
                     }
                 }
                 Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
-                    self.add_surrounds(text, SurroundsType::Selection, cx);
-                    self.clear_operator(cx);
+                    self.add_surrounds(text, SurroundsType::Selection, window, cx);
+                    self.clear_operator(window, cx);
                 }
-                _ => self.clear_operator(cx),
+                _ => self.clear_operator(window, cx),
             },
             Some(Operator::ChangeSurrounds { target }) => match self.mode {
                 Mode::Normal => {
                     if let Some(target) = target {
-                        self.change_surrounds(text, target, cx);
-                        self.clear_operator(cx);
+                        self.change_surrounds(text, target, window, cx);
+                        self.clear_operator(window, cx);
                     }
                 }
-                _ => self.clear_operator(cx),
+                _ => self.clear_operator(window, cx),
             },
             Some(Operator::DeleteSurrounds) => match self.mode {
                 Mode::Normal => {
-                    self.delete_surrounds(text, cx);
-                    self.clear_operator(cx);
+                    self.delete_surrounds(text, window, cx);
+                    self.clear_operator(window, cx);
                 }
-                _ => self.clear_operator(cx),
+                _ => self.clear_operator(window, cx),
             },
-            Some(Operator::Mark) => self.create_mark(text, false, cx),
+            Some(Operator::Mark) => self.create_mark(text, false, window, cx),
             Some(Operator::RecordRegister) => {
-                self.record_register(text.chars().next().unwrap(), cx)
+                self.record_register(text.chars().next().unwrap(), window, cx)
             }
             Some(Operator::ReplayRegister) => {
-                self.replay_register(text.chars().next().unwrap(), cx)
+                self.replay_register(text.chars().next().unwrap(), window, cx)
             }
             Some(Operator::Register) => match self.mode {
                 Mode::Insert => {
-                    self.update_editor(cx, |_, editor, cx| {
+                    self.update_editor(window, cx, |_, editor, window, cx| {
                         if let Some(register) = Vim::update_globals(cx, |globals, cx| {
                             globals.read_register(text.chars().next(), Some(editor), cx)
                         }) {
@@ -1204,26 +1241,28 @@ impl Vim {
                                 &register.text.to_string(),
                                 register.clipboard_selections.clone(),
                                 false,
+                                window,
                                 cx,
                             )
                         }
                     });
-                    self.clear_operator(cx);
+                    self.clear_operator(window, cx);
                 }
                 _ => {
-                    self.select_register(text, cx);
+                    self.select_register(text, window, cx);
                 }
             },
-            Some(Operator::Jump { line }) => self.jump(text, line, cx),
+            Some(Operator::Jump { line }) => self.jump(text, line, window, cx),
             _ => {
                 if self.mode == Mode::Replace {
-                    self.multi_replace(text, cx)
+                    self.multi_replace(text, window, cx)
                 }
 
                 if self.mode == Mode::Normal {
-                    self.update_editor(cx, |_, editor, cx| {
+                    self.update_editor(window, cx, |_, editor, window, cx| {
                         editor.accept_inline_completion(
                             &editor::actions::AcceptInlineCompletion {},
+                            window,
                             cx,
                         );
                     });
@@ -1232,8 +1271,8 @@ impl Vim {
         }
     }
 
-    fn sync_vim_settings(&mut self, cx: &mut ViewContext<Self>) {
-        self.update_editor(cx, |vim, editor, cx| {
+    fn sync_vim_settings(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        self.update_editor(window, cx, |vim, editor, _, cx| {
             editor.set_cursor_shape(vim.cursor_shape(), cx);
             editor.set_clip_at_line_ends(vim.clip_at_line_ends(), cx);
             editor.set_collapse_matches(true);
@@ -1291,7 +1330,7 @@ impl Settings for VimSettings {
 
     type FileContent = VimSettingsContent;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
         sources.json_merge()
     }
 }

crates/vim/src/visual.rs 🔗

@@ -7,7 +7,7 @@ use editor::{
     scroll::Autoscroll,
     Bias, DisplayPoint, Editor, ToOffset,
 };
-use gpui::{actions, ViewContext};
+use gpui::{actions, Context, Window};
 use language::{Point, Selection, SelectionGoal};
 use multi_buffer::MultiBufferRow;
 use search::BufferSearchBar;
@@ -44,62 +44,66 @@ actions!(
     ]
 );
 
-pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
-    Vim::action(editor, cx, |vim, _: &ToggleVisual, cx| {
-        vim.toggle_mode(Mode::Visual, cx)
+pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
+    Vim::action(editor, cx, |vim, _: &ToggleVisual, window, cx| {
+        vim.toggle_mode(Mode::Visual, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &ToggleVisualLine, cx| {
-        vim.toggle_mode(Mode::VisualLine, cx)
+    Vim::action(editor, cx, |vim, _: &ToggleVisualLine, window, cx| {
+        vim.toggle_mode(Mode::VisualLine, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &ToggleVisualBlock, cx| {
-        vim.toggle_mode(Mode::VisualBlock, cx)
+    Vim::action(editor, cx, |vim, _: &ToggleVisualBlock, window, cx| {
+        vim.toggle_mode(Mode::VisualBlock, window, cx)
     });
     Vim::action(editor, cx, Vim::other_end);
     Vim::action(editor, cx, Vim::visual_insert_end_of_line);
     Vim::action(editor, cx, Vim::visual_insert_first_non_white_space);
-    Vim::action(editor, cx, |vim, _: &VisualDelete, cx| {
+    Vim::action(editor, cx, |vim, _: &VisualDelete, window, cx| {
         vim.record_current_action(cx);
-        vim.visual_delete(false, cx);
+        vim.visual_delete(false, window, cx);
     });
-    Vim::action(editor, cx, |vim, _: &VisualDeleteLine, cx| {
+    Vim::action(editor, cx, |vim, _: &VisualDeleteLine, window, cx| {
         vim.record_current_action(cx);
-        vim.visual_delete(true, cx);
+        vim.visual_delete(true, window, cx);
     });
-    Vim::action(editor, cx, |vim, _: &VisualYank, cx| {
-        vim.visual_yank(false, cx)
+    Vim::action(editor, cx, |vim, _: &VisualYank, window, cx| {
+        vim.visual_yank(false, window, cx)
     });
-    Vim::action(editor, cx, |vim, _: &VisualYankLine, cx| {
-        vim.visual_yank(true, cx)
+    Vim::action(editor, cx, |vim, _: &VisualYankLine, window, cx| {
+        vim.visual_yank(true, window, cx)
     });
 
     Vim::action(editor, cx, Vim::select_next);
     Vim::action(editor, cx, Vim::select_previous);
-    Vim::action(editor, cx, |vim, _: &SelectNextMatch, cx| {
-        vim.select_match(Direction::Next, cx);
+    Vim::action(editor, cx, |vim, _: &SelectNextMatch, window, cx| {
+        vim.select_match(Direction::Next, window, cx);
     });
-    Vim::action(editor, cx, |vim, _: &SelectPreviousMatch, cx| {
-        vim.select_match(Direction::Prev, cx);
+    Vim::action(editor, cx, |vim, _: &SelectPreviousMatch, window, cx| {
+        vim.select_match(Direction::Prev, window, cx);
     });
 
-    Vim::action(editor, cx, |vim, _: &SelectLargerSyntaxNode, cx| {
+    Vim::action(editor, cx, |vim, _: &SelectLargerSyntaxNode, window, cx| {
         let count = Vim::take_count(cx).unwrap_or(1);
         for _ in 0..count {
-            vim.update_editor(cx, |_, editor, cx| {
-                editor.select_larger_syntax_node(&Default::default(), cx);
+            vim.update_editor(window, cx, |_, editor, window, cx| {
+                editor.select_larger_syntax_node(&Default::default(), window, cx);
             });
         }
     });
 
-    Vim::action(editor, cx, |vim, _: &SelectSmallerSyntaxNode, cx| {
-        let count = Vim::take_count(cx).unwrap_or(1);
-        for _ in 0..count {
-            vim.update_editor(cx, |_, editor, cx| {
-                editor.select_smaller_syntax_node(&Default::default(), cx);
-            });
-        }
-    });
+    Vim::action(
+        editor,
+        cx,
+        |vim, _: &SelectSmallerSyntaxNode, window, cx| {
+            let count = Vim::take_count(cx).unwrap_or(1);
+            for _ in 0..count {
+                vim.update_editor(window, cx, |_, editor, window, cx| {
+                    editor.select_smaller_syntax_node(&Default::default(), window, cx);
+                });
+            }
+        },
+    );
 
-    Vim::action(editor, cx, |vim, _: &RestoreVisualSelection, cx| {
+    Vim::action(editor, cx, |vim, _: &RestoreVisualSelection, window, cx| {
         let Some((stored_mode, reversed)) = vim.stored_visual_mode.take() else {
             return;
         };
@@ -114,11 +118,11 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
             .collect::<Vec<_>>();
 
         if vim.mode.is_visual() {
-            vim.create_visual_marks(vim.mode, cx);
+            vim.create_visual_marks(vim.mode, window, cx);
         }
 
-        vim.update_editor(cx, |_, editor, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+        vim.update_editor(window, cx, |_, editor, window, cx| {
+            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                 let map = s.display_map();
                 let ranges = ranges
                     .into_iter()
@@ -136,7 +140,7 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
                 s.select(ranges);
             })
         });
-        vim.switch_mode(stored_mode, true, cx)
+        vim.switch_mode(stored_mode, true, window, cx)
     });
 }
 
@@ -145,10 +149,11 @@ impl Vim {
         &mut self,
         motion: Motion,
         times: Option<usize>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
-        self.update_editor(cx, |vim, editor, cx| {
-            let text_layout_details = editor.text_layout_details(cx);
+        self.update_editor(window, cx, |vim, editor, window, cx| {
+            let text_layout_details = editor.text_layout_details(window);
             if vim.mode == Mode::VisualBlock
                 && !matches!(
                     motion,
@@ -158,11 +163,11 @@ impl Vim {
                 )
             {
                 let is_up_or_down = matches!(motion, Motion::Up { .. } | Motion::Down { .. });
-                vim.visual_block_motion(is_up_or_down, editor, cx, |map, point, goal| {
+                vim.visual_block_motion(is_up_or_down, editor, window, cx, |map, point, goal| {
                     motion.move_point(map, point, goal, times, &text_layout_details)
                 })
             } else {
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let was_reversed = selection.reversed;
                         let mut current_head = selection.head();
@@ -225,15 +230,16 @@ impl Vim {
         &mut self,
         preserve_goal: bool,
         editor: &mut Editor,
-        cx: &mut ViewContext<Editor>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
         mut move_selection: impl FnMut(
             &DisplaySnapshot,
             DisplayPoint,
             SelectionGoal,
         ) -> Option<(DisplayPoint, SelectionGoal)>,
     ) {
-        let text_layout_details = editor.text_layout_details(cx);
-        editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+        let text_layout_details = editor.text_layout_details(window);
+        editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
             let map = &s.display_map();
             let mut head = s.newest_anchor().head().to_display_point(map);
             let mut tail = s.oldest_anchor().tail().to_display_point(map);
@@ -329,17 +335,17 @@ impl Vim {
         })
     }
 
-    pub fn visual_object(&mut self, object: Object, cx: &mut ViewContext<Vim>) {
+    pub fn visual_object(&mut self, object: Object, window: &mut Window, cx: &mut Context<Vim>) {
         if let Some(Operator::Object { around }) = self.active_operator() {
-            self.pop_operator(cx);
+            self.pop_operator(window, cx);
             let current_mode = self.mode;
             let target_mode = object.target_visual_mode(current_mode, around);
             if target_mode != current_mode {
-                self.switch_mode(target_mode, true, cx);
+                self.switch_mode(target_mode, true, window, cx);
             }
 
-            self.update_editor(cx, |_, editor, cx| {
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+            self.update_editor(window, cx, |_, editor, window, cx| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let mut mut_selection = selection.clone();
 
@@ -398,27 +404,33 @@ impl Vim {
         }
     }
 
-    fn visual_insert_end_of_line(&mut self, _: &VisualInsertEndOfLine, cx: &mut ViewContext<Self>) {
-        self.update_editor(cx, |_, editor, cx| {
-            editor.split_selection_into_lines(&Default::default(), cx);
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+    fn visual_insert_end_of_line(
+        &mut self,
+        _: &VisualInsertEndOfLine,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            editor.split_selection_into_lines(&Default::default(), window, cx);
+            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                 s.move_cursors_with(|map, cursor, _| {
                     (next_line_end(map, cursor, 1), SelectionGoal::None)
                 });
             });
         });
 
-        self.switch_mode(Mode::Insert, false, cx);
+        self.switch_mode(Mode::Insert, false, window, cx);
     }
 
     fn visual_insert_first_non_white_space(
         &mut self,
         _: &VisualInsertFirstNonWhiteSpace,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
-        self.update_editor(cx, |_, editor, cx| {
-            editor.split_selection_into_lines(&Default::default(), cx);
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            editor.split_selection_into_lines(&Default::default(), window, cx);
+            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                 s.move_cursors_with(|map, cursor, _| {
                     (
                         first_non_whitespace(map, false, cursor),
@@ -428,20 +440,20 @@ impl Vim {
             });
         });
 
-        self.switch_mode(Mode::Insert, false, cx);
+        self.switch_mode(Mode::Insert, false, window, cx);
     }
 
-    fn toggle_mode(&mut self, mode: Mode, cx: &mut ViewContext<Self>) {
+    fn toggle_mode(&mut self, mode: Mode, window: &mut Window, cx: &mut Context<Self>) {
         if self.mode == mode {
-            self.switch_mode(Mode::Normal, false, cx);
+            self.switch_mode(Mode::Normal, false, window, cx);
         } else {
-            self.switch_mode(mode, false, cx);
+            self.switch_mode(mode, false, window, cx);
         }
     }
 
-    pub fn other_end(&mut self, _: &OtherEnd, cx: &mut ViewContext<Self>) {
-        self.update_editor(cx, |_, editor, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+    pub fn other_end(&mut self, _: &OtherEnd, window: &mut Window, cx: &mut Context<Self>) {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                 s.move_with(|_, selection| {
                     selection.reversed = !selection.reversed;
                 })
@@ -449,14 +461,14 @@ impl Vim {
         });
     }
 
-    pub fn visual_delete(&mut self, line_mode: bool, cx: &mut ViewContext<Self>) {
-        self.store_visual_marks(cx);
-        self.update_editor(cx, |vim, editor, cx| {
+    pub fn visual_delete(&mut self, line_mode: bool, window: &mut Window, cx: &mut Context<Self>) {
+        self.store_visual_marks(window, cx);
+        self.update_editor(window, cx, |vim, editor, window, cx| {
             let mut original_columns: HashMap<_, _> = Default::default();
             let line_mode = line_mode || editor.selections.line_mode;
 
-            editor.transact(cx, |editor, cx| {
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+            editor.transact(window, cx, |editor, window, cx| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.move_with(|map, selection| {
                         if line_mode {
                             let mut position = selection.head();
@@ -488,11 +500,11 @@ impl Vim {
                     });
                 });
                 vim.copy_selections_content(editor, line_mode, cx);
-                editor.insert("", cx);
+                editor.insert("", window, cx);
 
                 // Fixup cursor position after the deletion
                 editor.set_clip_at_line_ends(true, cx);
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let mut cursor = selection.head().to_point(map);
 
@@ -508,16 +520,16 @@ impl Vim {
                 });
             })
         });
-        self.switch_mode(Mode::Normal, true, cx);
+        self.switch_mode(Mode::Normal, true, window, cx);
     }
 
-    pub fn visual_yank(&mut self, line_mode: bool, cx: &mut ViewContext<Self>) {
-        self.store_visual_marks(cx);
-        self.update_editor(cx, |vim, editor, cx| {
+    pub fn visual_yank(&mut self, line_mode: bool, window: &mut Window, cx: &mut Context<Self>) {
+        self.store_visual_marks(window, cx);
+        self.update_editor(window, cx, |vim, editor, window, cx| {
             let line_mode = line_mode || editor.selections.line_mode;
             editor.selections.line_mode = line_mode;
             vim.yank_selections_content(editor, line_mode, cx);
-            editor.change_selections(None, cx, |s| {
+            editor.change_selections(None, window, cx, |s| {
                 s.move_with(|map, selection| {
                     if line_mode {
                         selection.start = start_of_line(map, false, selection.start);
@@ -529,13 +541,18 @@ impl Vim {
                 }
             });
         });
-        self.switch_mode(Mode::Normal, true, cx);
+        self.switch_mode(Mode::Normal, true, window, cx);
     }
 
-    pub(crate) fn visual_replace(&mut self, text: Arc<str>, cx: &mut ViewContext<Self>) {
+    pub(crate) fn visual_replace(
+        &mut self,
+        text: Arc<str>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.stop_recording(cx);
-        self.update_editor(cx, |_, editor, cx| {
-            editor.transact(cx, |editor, cx| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            editor.transact(window, cx, |editor, window, cx| {
                 let (display_map, selections) = editor.selections.all_adjusted_display(cx);
 
                 // Selections are biased right at the start. So we need to store
@@ -565,20 +582,20 @@ impl Vim {
                 }
 
                 editor.edit(edits, cx);
-                editor.change_selections(None, cx, |s| s.select_ranges(stable_anchors));
+                editor.change_selections(None, window, cx, |s| s.select_ranges(stable_anchors));
             });
         });
-        self.switch_mode(Mode::Normal, false, cx);
+        self.switch_mode(Mode::Normal, false, window, cx);
     }
 
-    pub fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
+    pub fn select_next(&mut self, _: &SelectNext, window: &mut Window, cx: &mut Context<Self>) {
         let count =
             Vim::take_count(cx).unwrap_or_else(|| if self.mode.is_visual() { 1 } else { 2 });
-        self.update_editor(cx, |_, editor, cx| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
             editor.set_clip_at_line_ends(false, cx);
             for _ in 0..count {
                 if editor
-                    .select_next(&Default::default(), cx)
+                    .select_next(&Default::default(), window, cx)
                     .log_err()
                     .is_none()
                 {
@@ -588,13 +605,18 @@ impl Vim {
         });
     }
 
-    pub fn select_previous(&mut self, _: &SelectPrevious, cx: &mut ViewContext<Self>) {
+    pub fn select_previous(
+        &mut self,
+        _: &SelectPrevious,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let count =
             Vim::take_count(cx).unwrap_or_else(|| if self.mode.is_visual() { 1 } else { 2 });
-        self.update_editor(cx, |_, editor, cx| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
             for _ in 0..count {
                 if editor
-                    .select_previous(&Default::default(), cx)
+                    .select_previous(&Default::default(), window, cx)
                     .log_err()
                     .is_none()
                 {
@@ -604,16 +626,21 @@ impl Vim {
         });
     }
 
-    pub fn select_match(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
+    pub fn select_match(
+        &mut self,
+        direction: Direction,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let count = Vim::take_count(cx).unwrap_or(1);
-        let Some(pane) = self.pane(cx) else {
+        let Some(pane) = self.pane(window, cx) else {
             return;
         };
         let vim_is_normal = self.mode == Mode::Normal;
         let mut start_selection = 0usize;
         let mut end_selection = 0usize;
 
-        self.update_editor(cx, |_, editor, _| {
+        self.update_editor(window, cx, |_, editor, _, _| {
             editor.set_collapse_matches(false);
         });
         if vim_is_normal {
@@ -621,17 +648,17 @@ impl Vim {
                 if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>()
                 {
                     search_bar.update(cx, |search_bar, cx| {
-                        if !search_bar.has_active_match() || !search_bar.show(cx) {
+                        if !search_bar.has_active_match() || !search_bar.show(window, cx) {
                             return;
                         }
                         // without update_match_index there is a bug when the cursor is before the first match
-                        search_bar.update_match_index(cx);
-                        search_bar.select_match(direction.opposite(), 1, cx);
+                        search_bar.update_match_index(window, cx);
+                        search_bar.select_match(direction.opposite(), 1, window, cx);
                     });
                 }
             });
         }
-        self.update_editor(cx, |_, editor, cx| {
+        self.update_editor(window, cx, |_, editor, _, cx| {
             let latest = editor.selections.newest::<usize>(cx);
             start_selection = latest.start;
             end_selection = latest.end;
@@ -641,18 +668,18 @@ impl Vim {
         pane.update(cx, |pane, cx| {
             if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
                 search_bar.update(cx, |search_bar, cx| {
-                    search_bar.update_match_index(cx);
-                    search_bar.select_match(direction, count, cx);
-                    match_exists = search_bar.match_exists(cx);
+                    search_bar.update_match_index(window, cx);
+                    search_bar.select_match(direction, count, window, cx);
+                    match_exists = search_bar.match_exists(window, cx);
                 });
             }
         });
         if !match_exists {
-            self.clear_operator(cx);
+            self.clear_operator(window, cx);
             self.stop_replaying(cx);
             return;
         }
-        self.update_editor(cx, |_, editor, cx| {
+        self.update_editor(window, cx, |_, editor, window, cx| {
             let latest = editor.selections.newest::<usize>(cx);
             if vim_is_normal {
                 start_selection = latest.start;
@@ -664,19 +691,19 @@ impl Vim {
             if direction == Direction::Prev {
                 std::mem::swap(&mut start_selection, &mut end_selection);
             }
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                 s.select_ranges([start_selection..end_selection]);
             });
             editor.set_collapse_matches(true);
         });
 
         match self.maybe_pop_operator() {
-            Some(Operator::Change) => self.substitute(None, false, cx),
+            Some(Operator::Change) => self.substitute(None, false, window, cx),
             Some(Operator::Delete) => {
                 self.stop_recording(cx);
-                self.visual_delete(false, cx)
+                self.visual_delete(false, window, cx)
             }
-            Some(Operator::Yank) => self.visual_yank(false, cx),
+            Some(Operator::Yank) => self.visual_yank(false, window, cx),
             _ => {} // Ignoring other operators
         }
     }
@@ -701,7 +728,7 @@ mod test {
             the lazy dog"
         })
         .await;
-        let cursor = cx.update_editor(|editor, cx| editor.pixel_position_of_cursor(cx));
+        let cursor = cx.update_editor(|editor, _, cx| editor.pixel_position_of_cursor(cx));
 
         // entering visual mode should select the character
         // under cursor
@@ -711,7 +738,7 @@ mod test {
             .assert_eq(indoc! { "The «qˇ»uick brown
             fox jumps over
             the lazy dog"});
-        cx.update_editor(|editor, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx)));
+        cx.update_editor(|editor, _, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx)));
 
         // forwards motions should extend the selection
         cx.simulate_shared_keystrokes("w j").await;
@@ -739,14 +766,14 @@ mod test {
             b
             "})
             .await;
-        let cursor = cx.update_editor(|editor, cx| editor.pixel_position_of_cursor(cx));
+        let cursor = cx.update_editor(|editor, _, cx| editor.pixel_position_of_cursor(cx));
         cx.simulate_shared_keystrokes("v").await;
         cx.shared_state().await.assert_eq(indoc! {"
             a
             «
             ˇ»b
         "});
-        cx.update_editor(|editor, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx)));
+        cx.update_editor(|editor, _, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx)));
 
         // toggles off again
         cx.simulate_shared_keystrokes("v").await;
@@ -858,13 +885,13 @@ mod test {
             b
             ˇ"})
             .await;
-        let cursor = cx.update_editor(|editor, cx| editor.pixel_position_of_cursor(cx));
+        let cursor = cx.update_editor(|editor, _, cx| editor.pixel_position_of_cursor(cx));
         cx.simulate_shared_keystrokes("shift-v").await;
         cx.shared_state().await.assert_eq(indoc! {"
             a
             b
             ˇ"});
-        cx.update_editor(|editor, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx)));
+        cx.update_editor(|editor, _, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx)));
         cx.simulate_shared_keystrokes("x").await;
         cx.shared_state().await.assert_eq(indoc! {"
             a

crates/vim_mode_setting/src/vim_mode_setting.rs 🔗

@@ -5,11 +5,11 @@
 //! entirety.
 
 use anyhow::Result;
-use gpui::AppContext;
+use gpui::App;
 use settings::{Settings, SettingsSources};
 
 /// Initializes the `vim_mode_setting` crate.
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     VimModeSetting::register(cx);
 }
 
@@ -23,7 +23,7 @@ impl Settings for VimModeSetting {
 
     type FileContent = Option<bool>;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
         Ok(Self(
             sources
                 .user

crates/welcome/src/base_keymap_picker.rs 🔗

@@ -1,8 +1,8 @@
 use super::base_keymap_setting::BaseKeymap;
 use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
 use gpui::{
-    actions, AppContext, DismissEvent, EventEmitter, FocusableView, Render, Task, View,
-    ViewContext, VisualContext, WeakView,
+    actions, App, Context, DismissEvent, Entity, EventEmitter, Focusable, Render, Task, WeakEntity,
+    Window,
 };
 use picker::{Picker, PickerDelegate};
 use project::Fs;
@@ -14,8 +14,8 @@ use workspace::{ui::HighlightedLabel, ModalView, Workspace};
 
 actions!(welcome, [ToggleBaseKeymapSelector]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(|workspace: &mut Workspace, _cx| {
+pub fn init(cx: &mut App) {
+    cx.observe_new(|workspace: &mut Workspace, _window, _cx| {
         workspace.register_action(toggle);
     })
     .detach();
@@ -24,23 +24,25 @@ pub fn init(cx: &mut AppContext) {
 pub fn toggle(
     workspace: &mut Workspace,
     _: &ToggleBaseKeymapSelector,
-    cx: &mut ViewContext<Workspace>,
+    window: &mut Window,
+    cx: &mut Context<Workspace>,
 ) {
     let fs = workspace.app_state().fs.clone();
-    workspace.toggle_modal(cx, |cx| {
+    workspace.toggle_modal(window, cx, |window, cx| {
         BaseKeymapSelector::new(
-            BaseKeymapSelectorDelegate::new(cx.view().downgrade(), fs, cx),
+            BaseKeymapSelectorDelegate::new(cx.model().downgrade(), fs, cx),
+            window,
             cx,
         )
     });
 }
 
 pub struct BaseKeymapSelector {
-    picker: View<Picker<BaseKeymapSelectorDelegate>>,
+    picker: Entity<Picker<BaseKeymapSelectorDelegate>>,
 }
 
-impl FocusableView for BaseKeymapSelector {
-    fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
+impl Focusable for BaseKeymapSelector {
+    fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
         self.picker.focus_handle(cx)
     }
 }
@@ -51,21 +53,22 @@ impl ModalView for BaseKeymapSelector {}
 impl BaseKeymapSelector {
     pub fn new(
         delegate: BaseKeymapSelectorDelegate,
-        cx: &mut ViewContext<BaseKeymapSelector>,
+        window: &mut Window,
+        cx: &mut Context<BaseKeymapSelector>,
     ) -> Self {
-        let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
+        let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
         Self { picker }
     }
 }
 
 impl Render for BaseKeymapSelector {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         v_flex().w(rems(34.)).child(self.picker.clone())
     }
 }
 
 pub struct BaseKeymapSelectorDelegate {
-    view: WeakView<BaseKeymapSelector>,
+    selector: WeakEntity<BaseKeymapSelector>,
     matches: Vec<StringMatch>,
     selected_index: usize,
     fs: Arc<dyn Fs>,
@@ -73,9 +76,9 @@ pub struct BaseKeymapSelectorDelegate {
 
 impl BaseKeymapSelectorDelegate {
     fn new(
-        weak_view: WeakView<BaseKeymapSelector>,
+        selector: WeakEntity<BaseKeymapSelector>,
         fs: Arc<dyn Fs>,
-        cx: &mut ViewContext<BaseKeymapSelector>,
+        cx: &mut Context<BaseKeymapSelector>,
     ) -> Self {
         let base = BaseKeymap::get(None, cx);
         let selected_index = BaseKeymap::OPTIONS
@@ -83,7 +86,7 @@ impl BaseKeymapSelectorDelegate {
             .position(|(_, value)| value == base)
             .unwrap_or(0);
         Self {
-            view: weak_view,
+            selector,
             matches: Vec::new(),
             selected_index,
             fs,
@@ -94,7 +97,7 @@ impl BaseKeymapSelectorDelegate {
 impl PickerDelegate for BaseKeymapSelectorDelegate {
     type ListItem = ui::ListItem;
 
-    fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
         "Select a base keymap...".into()
     }
 
@@ -109,7 +112,8 @@ impl PickerDelegate for BaseKeymapSelectorDelegate {
     fn set_selected_index(
         &mut self,
         ix: usize,
-        _: &mut ViewContext<Picker<BaseKeymapSelectorDelegate>>,
+        _window: &mut Window,
+        _: &mut Context<Picker<BaseKeymapSelectorDelegate>>,
     ) {
         self.selected_index = ix;
     }
@@ -117,7 +121,8 @@ impl PickerDelegate for BaseKeymapSelectorDelegate {
     fn update_matches(
         &mut self,
         query: String,
-        cx: &mut ViewContext<Picker<BaseKeymapSelectorDelegate>>,
+        window: &mut Window,
+        cx: &mut Context<Picker<BaseKeymapSelectorDelegate>>,
     ) -> Task<()> {
         let background = cx.background_executor().clone();
         let candidates = BaseKeymap::names()
@@ -125,7 +130,7 @@ impl PickerDelegate for BaseKeymapSelectorDelegate {
             .map(|(id, name)| StringMatchCandidate::new(id, name))
             .collect::<Vec<_>>();
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_in(window, |this, mut cx| async move {
             let matches = if query.is_empty() {
                 candidates
                     .into_iter()
@@ -160,7 +165,12 @@ impl PickerDelegate for BaseKeymapSelectorDelegate {
         })
     }
 
-    fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<BaseKeymapSelectorDelegate>>) {
+    fn confirm(
+        &mut self,
+        _: bool,
+        _: &mut Window,
+        cx: &mut Context<Picker<BaseKeymapSelectorDelegate>>,
+    ) {
         if let Some(selection) = self.matches.get(self.selected_index) {
             let base_keymap = BaseKeymap::from_names(&selection.string);
 
@@ -175,15 +185,15 @@ impl PickerDelegate for BaseKeymapSelectorDelegate {
             });
         }
 
-        self.view
+        self.selector
             .update(cx, |_, cx| {
                 cx.emit(DismissEvent);
             })
             .ok();
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<BaseKeymapSelectorDelegate>>) {
-        self.view
+    fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<BaseKeymapSelectorDelegate>>) {
+        self.selector
             .update(cx, |_, cx| {
                 cx.emit(DismissEvent);
             })
@@ -194,7 +204,8 @@ impl PickerDelegate for BaseKeymapSelectorDelegate {
         &self,
         ix: usize,
         selected: bool,
-        _cx: &mut ViewContext<Picker<Self>>,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let keymap_match = &self.matches[ix];
 

crates/welcome/src/base_keymap_setting.rs 🔗

@@ -97,7 +97,7 @@ impl Settings for BaseKeymap {
 
     fn load(
         sources: SettingsSources<Self::FileContent>,
-        _: &mut gpui::AppContext,
+        _: &mut gpui::App,
     ) -> anyhow::Result<Self> {
         if let Some(Some(user_value)) = sources.user.copied() {
             return Ok(user_value);

crates/welcome/src/multibuffer_hint.rs 🔗

@@ -3,7 +3,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
 use std::sync::OnceLock;
 
 use db::kvp::KEY_VALUE_STORE;
-use gpui::{AppContext, EntityId, EventEmitter, Subscription};
+use gpui::{App, EntityId, EventEmitter, Subscription};
 use ui::{prelude::*, ButtonLike, IconButtonShape, Tooltip};
 use workspace::item::{ItemEvent, ItemHandle};
 use workspace::{ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
@@ -53,11 +53,11 @@ impl MultibufferHint {
         Self::counter().load(Ordering::Relaxed)
     }
 
-    fn increment_count(cx: &mut AppContext) {
+    fn increment_count(cx: &mut App) {
         Self::set_count(Self::shown_count() + 1, cx)
     }
 
-    pub(crate) fn set_count(count: usize, cx: &mut AppContext) {
+    pub(crate) fn set_count(count: usize, cx: &mut App) {
         Self::counter().store(count, Ordering::Relaxed);
 
         db::write_and_log(cx, move || {
@@ -65,12 +65,12 @@ impl MultibufferHint {
         });
     }
 
-    fn dismiss(&mut self, cx: &mut AppContext) {
+    fn dismiss(&mut self, cx: &mut App) {
         Self::set_count(NUMBER_OF_HINTS, cx)
     }
 
     /// Determines the toolbar location for this [`MultibufferHint`].
-    fn determine_toolbar_location(&mut self, cx: &mut ViewContext<Self>) -> ToolbarItemLocation {
+    fn determine_toolbar_location(&mut self, cx: &mut Context<Self>) -> ToolbarItemLocation {
         if Self::shown_count() >= NUMBER_OF_HINTS {
             return ToolbarItemLocation::Hidden;
         }
@@ -99,7 +99,8 @@ impl ToolbarItemView for MultibufferHint {
     fn set_active_pane_item(
         &mut self,
         active_pane_item: Option<&dyn ItemHandle>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> ToolbarItemLocation {
         cx.notify();
         self.active_item = active_pane_item.map(|item| item.boxed_clone());
@@ -108,10 +109,11 @@ impl ToolbarItemView for MultibufferHint {
             return ToolbarItemLocation::Hidden;
         };
 
-        let this = cx.view().downgrade();
+        let this = cx.model().downgrade();
         self.subscription = Some(active_pane_item.subscribe_to_item_events(
+            window,
             cx,
-            Box::new(move |event, cx| {
+            Box::new(move |event, _, cx| {
                 if let ItemEvent::UpdateBreadcrumbs = event {
                     this.update(cx, |this, cx| {
                         cx.notify();
@@ -128,7 +130,7 @@ impl ToolbarItemView for MultibufferHint {
 }
 
 impl Render for MultibufferHint {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         h_flex()
             .px_2()
             .justify_between()
@@ -149,7 +151,7 @@ impl Render for MultibufferHint {
                                     .child(Label::new("Read more…"))
                                     .child(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
                             )
-                            .on_click(move |_event, cx| {
+                            .on_click(move |_event, _, cx| {
                                 cx.open_url("https://zed.dev/docs/multibuffers")
                             }),
                     ),
@@ -159,13 +161,13 @@ impl Render for MultibufferHint {
                     .style(ButtonStyle::Transparent)
                     .shape(IconButtonShape::Square)
                     .icon_size(IconSize::Small)
-                    .on_click(cx.listener(|this, _event, cx| {
+                    .on_click(cx.listener(|this, _event, _, cx| {
                         this.dismiss(cx);
                         cx.emit(ToolbarItemEvent::ChangeLocation(
                             ToolbarItemLocation::Hidden,
                         ))
                     }))
-                    .tooltip(move |cx| Tooltip::text("Dismiss this hint", cx)),
+                    .tooltip(Tooltip::text("Dismiss this hint")),
             )
             .into_any_element()
     }

crates/welcome/src/welcome.rs 🔗

@@ -5,9 +5,8 @@ mod multibuffer_hint;
 use client::{telemetry::Telemetry, TelemetrySettings};
 use db::kvp::KEY_VALUE_STORE;
 use gpui::{
-    actions, svg, Action, AppContext, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
-    ParentElement, Render, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView,
-    WindowContext,
+    actions, svg, Action, App, Context, Entity, EventEmitter, FocusHandle, Focusable,
+    InteractiveElement, ParentElement, Render, Styled, Subscription, Task, WeakEntity, Window,
 };
 use settings::{Settings, SettingsStore};
 use std::sync::Arc;
@@ -28,48 +27,52 @@ pub const FIRST_OPEN: &str = "first_open";
 pub const DOCS_URL: &str = "https://zed.dev/docs/";
 const BOOK_ONBOARDING: &str = "https://dub.sh/zed-c-onboarding";
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     BaseKeymap::register(cx);
 
-    cx.observe_new_views(|workspace: &mut Workspace, _cx| {
-        workspace.register_action(|workspace, _: &Welcome, cx| {
+    cx.observe_new(|workspace: &mut Workspace, _, _cx| {
+        workspace.register_action(|workspace, _: &Welcome, window, cx| {
             let welcome_page = WelcomePage::new(workspace, cx);
-            workspace.add_item_to_active_pane(Box::new(welcome_page), None, true, cx)
+            workspace.add_item_to_active_pane(Box::new(welcome_page), None, true, window, cx)
         });
         workspace
-            .register_action(|_workspace, _: &ResetHints, cx| MultibufferHint::set_count(0, cx));
+            .register_action(|_workspace, _: &ResetHints, _, cx| MultibufferHint::set_count(0, cx));
     })
     .detach();
 
     base_keymap_picker::init(cx);
 }
 
-pub fn show_welcome_view(
-    app_state: Arc<AppState>,
-    cx: &mut AppContext,
-) -> Task<anyhow::Result<()>> {
-    open_new(Default::default(), app_state, cx, |workspace, cx| {
-        workspace.toggle_dock(DockPosition::Left, cx);
-        let welcome_page = WelcomePage::new(workspace, cx);
-        workspace.add_item_to_center(Box::new(welcome_page.clone()), cx);
-        cx.focus_view(&welcome_page);
-        cx.notify();
+pub fn show_welcome_view(app_state: Arc<AppState>, cx: &mut App) -> Task<anyhow::Result<()>> {
+    open_new(
+        Default::default(),
+        app_state,
+        cx,
+        |workspace, window, cx| {
+            workspace.toggle_dock(DockPosition::Left, window, cx);
+            let welcome_page = WelcomePage::new(workspace, cx);
+            workspace.add_item_to_center(Box::new(welcome_page.clone()), window, cx);
 
-        db::write_and_log(cx, || {
-            KEY_VALUE_STORE.write_kvp(FIRST_OPEN.to_string(), "false".to_string())
-        });
-    })
+            window.focus(&welcome_page.focus_handle(cx));
+
+            cx.notify();
+
+            db::write_and_log(cx, || {
+                KEY_VALUE_STORE.write_kvp(FIRST_OPEN.to_string(), "false".to_string())
+            });
+        },
+    )
 }
 
 pub struct WelcomePage {
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     focus_handle: FocusHandle,
     telemetry: Arc<Telemetry>,
     _settings_subscription: Subscription,
 }
 
 impl Render for WelcomePage {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         h_flex()
             .size_full()
             .bg(cx.theme().colors().editor_background)
@@ -116,7 +119,7 @@ impl Render for WelcomePage {
                                     .border_r_1()
                                     .border_color(cx.theme().colors().border_variant)
                                     .child(
-                                        self.section_label(cx).child(
+                                        self.section_label( cx).child(
                                             Label::new("Get Started")
                                                 .size(LabelSize::XSmall)
                                                 .color(Color::Muted),
@@ -128,13 +131,13 @@ impl Render for WelcomePage {
                                             .icon_size(IconSize::XSmall)
                                             .icon_color(Color::Muted)
                                             .icon_position(IconPosition::Start)
-                                            .on_click(cx.listener(|this, _, cx| {
+                                            .on_click(cx.listener(|this, _, window, cx| {
                                                 this.telemetry.report_app_event(
                                                     "welcome page: change theme".to_string(),
                                                 );
                                                 this.workspace
                                                     .update(cx, |_workspace, cx| {
-                                                        cx.dispatch_action(zed_actions::theme_selector::Toggle::default().boxed_clone());
+                                                        window.dispatch_action(zed_actions::theme_selector::Toggle::default().boxed_clone(), cx);
                                                     })
                                                     .ok();
                                             })),
@@ -145,7 +148,7 @@ impl Render for WelcomePage {
                                             .icon_size(IconSize::XSmall)
                                             .icon_color(Color::Muted)
                                             .icon_position(IconPosition::Start)
-                                            .on_click(cx.listener(|this, _, cx| {
+                                            .on_click(cx.listener(|this, _, window, cx| {
                                                 this.telemetry.report_app_event(
                                                     "welcome page: change keymap".to_string(),
                                                 );
@@ -154,7 +157,7 @@ impl Render for WelcomePage {
                                                         base_keymap_picker::toggle(
                                                             workspace,
                                                             &Default::default(),
-                                                            cx,
+                                                            window, cx,
                                                         )
                                                     })
                                                     .ok();
@@ -170,11 +173,11 @@ impl Render for WelcomePage {
                                         .icon_color(Color::Muted)
                                         .icon_position(IconPosition::Start)
                                         .on_click(
-                                            cx.listener(|this, _, cx| {
+                                            cx.listener(|this, _, window, cx| {
                                                 this.telemetry.report_app_event(
                                                     "welcome page: sign in to copilot".to_string(),
                                                 );
-                                                copilot::initiate_sign_in(cx);
+                                                copilot::initiate_sign_in(window, cx);
                                             }),
                                         ),
                                     )
@@ -184,13 +187,13 @@ impl Render for WelcomePage {
                                             .icon_size(IconSize::XSmall)
                                             .icon_color(Color::Muted)
                                             .icon_position(IconPosition::Start)
-                                            .on_click(cx.listener(|this, _, cx| {
+                                            .on_click(cx.listener(|this, _, window, cx| {
                                                 this.telemetry.report_app_event(
                                                     "welcome page: edit settings".to_string(),
                                                 );
-                                                cx.dispatch_action(Box::new(
+                                                window.dispatch_action(Box::new(
                                                     zed_actions::OpenSettings,
-                                                ));
+                                                ), cx);
                                             })),
                                     ),
                             )
@@ -211,12 +214,12 @@ impl Render for WelcomePage {
                                                 .icon_size(IconSize::XSmall)
                                                 .icon_color(Color::Muted)
                                                 .icon_position(IconPosition::Start)
-                                                .on_click(cx.listener(|this, _, cx| {
+                                                .on_click(cx.listener(|this, _, _, cx| {
                                                     this.telemetry.report_app_event(
                                                         "welcome page: install cli".to_string(),
                                                     );
-                                                    cx.app_mut()
-                                                        .spawn(|cx| async move {
+                                                    cx
+                                                        .spawn(|_, cx| async move {
                                                             install_cli::install_cli(&cx).await
                                                         })
                                                         .detach_and_log_err(cx);
@@ -229,7 +232,7 @@ impl Render for WelcomePage {
                                             .icon_size(IconSize::XSmall)
                                             .icon_color(Color::Muted)
                                             .icon_position(IconPosition::Start)
-                                            .on_click(cx.listener(|this, _, cx| {
+                                            .on_click(cx.listener(|this, _, _, cx| {
                                                 this.telemetry.report_app_event(
                                                     "welcome page: view docs".to_string(),
                                                 );
@@ -242,13 +245,13 @@ impl Render for WelcomePage {
                                             .icon_size(IconSize::XSmall)
                                             .icon_color(Color::Muted)
                                             .icon_position(IconPosition::Start)
-                                            .on_click(cx.listener(|this, _, cx| {
+                                            .on_click(cx.listener(|this, _, window, cx| {
                                                 this.telemetry.report_app_event(
                                                     "welcome page: open extensions".to_string(),
                                                 );
-                                                cx.dispatch_action(Box::new(
+                                                window.dispatch_action(Box::new(
                                                     zed_actions::Extensions,
-                                                ));
+                                                ), cx);
                                             })),
                                     )
                                     .child(
@@ -257,7 +260,7 @@ impl Render for WelcomePage {
                                             .icon_size(IconSize::XSmall)
                                             .icon_color(Color::Muted)
                                             .icon_position(IconPosition::Start)
-                                            .on_click(cx.listener(|_, _, cx| {
+                                            .on_click(cx.listener(|_, _, _, cx| {
                                                 cx.open_url(BOOK_ONBOARDING);
                                             })),
                                     ),
@@ -278,7 +281,7 @@ impl Render for WelcomePage {
                                             } else {
                                                 ui::ToggleState::Unselected
                                             },
-                                            cx.listener(move |this, selection, cx| {
+                                            cx.listener(move |this, selection, _window, cx| {
                                                 this.telemetry
                                                     .report_app_event("welcome page: toggle vim".to_string());
                                                 this.update_settings::<VimModeSetting>(
@@ -295,12 +298,10 @@ impl Render for WelcomePage {
                                         IconButton::new("vim-mode", IconName::Info)
                                             .icon_size(IconSize::XSmall)
                                             .icon_color(Color::Muted)
-                                            .tooltip(|cx| {
+                                            .tooltip(
                                                 Tooltip::text(
-                                                    "You can also toggle Vim Mode via the command palette or Editor Controls menu.",
-                                                    cx,
-                                                )
-                                            }),
+                                                    "You can also toggle Vim Mode via the command palette or Editor Controls menu.")
+                                            ),
                                     ),
                             )
                             .child(
@@ -312,7 +313,7 @@ impl Render for WelcomePage {
                                     } else {
                                         ui::ToggleState::Unselected
                                     },
-                                    cx.listener(move |this, selection, cx| {
+                                    cx.listener(move |this, selection, _window, cx| {
                                         this.telemetry.report_app_event(
                                             "welcome page: toggle diagnostic telemetry".to_string(),
                                         );
@@ -340,7 +341,7 @@ impl Render for WelcomePage {
                                     } else {
                                         ui::ToggleState::Unselected
                                     },
-                                    cx.listener(move |this, selection, cx| {
+                                    cx.listener(move |this, selection, _window, cx| {
                                         this.telemetry.report_app_event(
                                             "welcome page: toggle metric telemetry".to_string(),
                                         );
@@ -365,9 +366,9 @@ impl Render for WelcomePage {
 }
 
 impl WelcomePage {
-    pub fn new(workspace: &Workspace, cx: &mut ViewContext<Workspace>) -> View<Self> {
-        let this = cx.new_view(|cx| {
-            cx.on_release(|this: &mut Self, _, _| {
+    pub fn new(workspace: &Workspace, cx: &mut Context<Workspace>) -> Entity<Self> {
+        let this = cx.new(|cx| {
+            cx.on_release(|this: &mut Self, _| {
                 this.telemetry
                     .report_app_event("welcome page: close".to_string());
             })
@@ -385,7 +386,7 @@ impl WelcomePage {
         this
     }
 
-    fn section_label(&self, cx: &WindowContext) -> Div {
+    fn section_label(&self, cx: &mut App) -> Div {
         div()
             .pl_1()
             .font_buffer(cx)
@@ -395,7 +396,7 @@ impl WelcomePage {
     fn update_settings<T: Settings>(
         &mut self,
         selection: &ToggleState,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
         callback: impl 'static + Send + Fn(&mut T::FileContent, bool),
     ) {
         if let Some(workspace) = self.workspace.upgrade() {
@@ -416,8 +417,8 @@ impl WelcomePage {
 
 impl EventEmitter<ItemEvent> for WelcomePage {}
 
-impl FocusableView for WelcomePage {
-    fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
+impl Focusable for WelcomePage {
+    fn focus_handle(&self, _: &App) -> gpui::FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -425,7 +426,7 @@ impl FocusableView for WelcomePage {
 impl Item for WelcomePage {
     type Event = ItemEvent;
 
-    fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
+    fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option<SharedString> {
         Some("Welcome".into())
     }
 
@@ -440,9 +441,10 @@ impl Item for WelcomePage {
     fn clone_on_split(
         &self,
         _workspace_id: Option<WorkspaceId>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<View<Self>> {
-        Some(cx.new_view(|cx| WelcomePage {
+        _: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Entity<Self>> {
+        Some(cx.new(|cx| WelcomePage {
             focus_handle: cx.focus_handle(),
             workspace: self.workspace.clone(),
             telemetry: self.telemetry.clone(),

crates/workspace/src/dock.rs 🔗

@@ -3,10 +3,9 @@ use crate::{status_bar::StatusItemView, Workspace};
 use crate::{DraggedDock, Event, Pane};
 use client::proto;
 use gpui::{
-    deferred, div, px, Action, AnyView, AppContext, Axis, Corner, Entity, EntityId, EventEmitter,
-    FocusHandle, FocusableView, IntoElement, KeyContext, MouseButton, MouseDownEvent, MouseUpEvent,
-    ParentElement, Render, SharedString, StyleRefinement, Styled, Subscription, View, ViewContext,
-    VisualContext, WeakView, WindowContext,
+    deferred, div, px, Action, AnyView, App, Axis, Context, Corner, Entity, EntityId, EventEmitter,
+    FocusHandle, Focusable, IntoElement, KeyContext, MouseButton, MouseDownEvent, MouseUpEvent,
+    ParentElement, Render, SharedString, StyleRefinement, Styled, Subscription, WeakEntity, Window,
 };
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
@@ -26,28 +25,28 @@ pub enum PanelEvent {
 
 pub use proto::PanelId;
 
-pub trait Panel: FocusableView + EventEmitter<PanelEvent> {
+pub trait Panel: Focusable + EventEmitter<PanelEvent> + Render + Sized {
     fn persistent_name() -> &'static str;
-    fn position(&self, cx: &WindowContext) -> DockPosition;
+    fn position(&self, window: &Window, cx: &App) -> DockPosition;
     fn position_is_valid(&self, position: DockPosition) -> bool;
-    fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>);
-    fn size(&self, cx: &WindowContext) -> Pixels;
-    fn set_size(&mut self, size: Option<Pixels>, cx: &mut ViewContext<Self>);
-    fn icon(&self, cx: &WindowContext) -> Option<ui::IconName>;
-    fn icon_tooltip(&self, cx: &WindowContext) -> Option<&'static str>;
+    fn set_position(&mut self, position: DockPosition, window: &mut Window, cx: &mut Context<Self>);
+    fn size(&self, window: &Window, cx: &App) -> Pixels;
+    fn set_size(&mut self, size: Option<Pixels>, window: &mut Window, cx: &mut Context<Self>);
+    fn icon(&self, window: &Window, cx: &App) -> Option<ui::IconName>;
+    fn icon_tooltip(&self, window: &Window, cx: &App) -> Option<&'static str>;
     fn toggle_action(&self) -> Box<dyn Action>;
-    fn icon_label(&self, _: &WindowContext) -> Option<String> {
+    fn icon_label(&self, _window: &Window, _: &App) -> Option<String> {
         None
     }
-    fn is_zoomed(&self, _cx: &WindowContext) -> bool {
+    fn is_zoomed(&self, _window: &Window, _cx: &App) -> bool {
         false
     }
-    fn starts_open(&self, _cx: &WindowContext) -> bool {
+    fn starts_open(&self, _window: &Window, _cx: &App) -> bool {
         false
     }
-    fn set_zoomed(&mut self, _zoomed: bool, _cx: &mut ViewContext<Self>) {}
-    fn set_active(&mut self, _active: bool, _cx: &mut ViewContext<Self>) {}
-    fn pane(&self) -> Option<View<Pane>> {
+    fn set_zoomed(&mut self, _zoomed: bool, _window: &mut Window, _cx: &mut Context<Self>) {}
+    fn set_active(&mut self, _active: bool, _window: &mut Window, _cx: &mut Context<Self>) {}
+    fn pane(&self) -> Option<Entity<Pane>> {
         None
     }
     fn remote_id() -> Option<proto::PanelId> {
@@ -59,25 +58,25 @@ pub trait Panel: FocusableView + EventEmitter<PanelEvent> {
 pub trait PanelHandle: Send + Sync {
     fn panel_id(&self) -> EntityId;
     fn persistent_name(&self) -> &'static str;
-    fn position(&self, cx: &WindowContext) -> DockPosition;
-    fn position_is_valid(&self, position: DockPosition, cx: &WindowContext) -> bool;
-    fn set_position(&self, position: DockPosition, cx: &mut WindowContext);
-    fn is_zoomed(&self, cx: &WindowContext) -> bool;
-    fn set_zoomed(&self, zoomed: bool, cx: &mut WindowContext);
-    fn set_active(&self, active: bool, cx: &mut WindowContext);
+    fn position(&self, window: &Window, cx: &App) -> DockPosition;
+    fn position_is_valid(&self, position: DockPosition, cx: &App) -> bool;
+    fn set_position(&self, position: DockPosition, window: &mut Window, cx: &mut App);
+    fn is_zoomed(&self, window: &Window, cx: &App) -> bool;
+    fn set_zoomed(&self, zoomed: bool, window: &mut Window, cx: &mut App);
+    fn set_active(&self, active: bool, window: &mut Window, cx: &mut App);
     fn remote_id(&self) -> Option<proto::PanelId>;
-    fn pane(&self, cx: &WindowContext) -> Option<View<Pane>>;
-    fn size(&self, cx: &WindowContext) -> Pixels;
-    fn set_size(&self, size: Option<Pixels>, cx: &mut WindowContext);
-    fn icon(&self, cx: &WindowContext) -> Option<ui::IconName>;
-    fn icon_tooltip(&self, cx: &WindowContext) -> Option<&'static str>;
-    fn toggle_action(&self, cx: &WindowContext) -> Box<dyn Action>;
-    fn icon_label(&self, cx: &WindowContext) -> Option<String>;
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle;
+    fn pane(&self, cx: &App) -> Option<Entity<Pane>>;
+    fn size(&self, window: &Window, cx: &App) -> Pixels;
+    fn set_size(&self, size: Option<Pixels>, window: &mut Window, cx: &mut App);
+    fn icon(&self, window: &Window, cx: &App) -> Option<ui::IconName>;
+    fn icon_tooltip(&self, window: &Window, cx: &App) -> Option<&'static str>;
+    fn toggle_action(&self, window: &Window, cx: &App) -> Box<dyn Action>;
+    fn icon_label(&self, window: &Window, cx: &App) -> Option<String>;
+    fn panel_focus_handle(&self, cx: &App) -> FocusHandle;
     fn to_any(&self) -> AnyView;
-    fn activation_priority(&self, cx: &AppContext) -> u32;
-    fn move_to_next_position(&self, cx: &mut WindowContext) {
-        let current_position = self.position(cx);
+    fn activation_priority(&self, cx: &App) -> u32;
+    fn move_to_next_position(&self, window: &mut Window, cx: &mut App) {
+        let current_position = self.position(window, cx);
         let next_position = [
             DockPosition::Left,
             DockPosition::Bottom,
@@ -89,11 +88,11 @@ pub trait PanelHandle: Send + Sync {
         .nth(1)
         .unwrap_or(DockPosition::Left);
 
-        self.set_position(next_position, cx);
+        self.set_position(next_position, window, cx);
     }
 }
 
-impl<T> PanelHandle for View<T>
+impl<T> PanelHandle for Entity<T>
 where
     T: Panel,
 {
@@ -105,31 +104,31 @@ where
         T::persistent_name()
     }
 
-    fn position(&self, cx: &WindowContext) -> DockPosition {
-        self.read(cx).position(cx)
+    fn position(&self, window: &Window, cx: &App) -> DockPosition {
+        self.read(cx).position(window, cx)
     }
 
-    fn position_is_valid(&self, position: DockPosition, cx: &WindowContext) -> bool {
+    fn position_is_valid(&self, position: DockPosition, cx: &App) -> bool {
         self.read(cx).position_is_valid(position)
     }
 
-    fn set_position(&self, position: DockPosition, cx: &mut WindowContext) {
-        self.update(cx, |this, cx| this.set_position(position, cx))
+    fn set_position(&self, position: DockPosition, window: &mut Window, cx: &mut App) {
+        self.update(cx, |this, cx| this.set_position(position, window, cx))
     }
 
-    fn is_zoomed(&self, cx: &WindowContext) -> bool {
-        self.read(cx).is_zoomed(cx)
+    fn is_zoomed(&self, window: &Window, cx: &App) -> bool {
+        self.read(cx).is_zoomed(window, cx)
     }
 
-    fn set_zoomed(&self, zoomed: bool, cx: &mut WindowContext) {
-        self.update(cx, |this, cx| this.set_zoomed(zoomed, cx))
+    fn set_zoomed(&self, zoomed: bool, window: &mut Window, cx: &mut App) {
+        self.update(cx, |this, cx| this.set_zoomed(zoomed, window, cx))
     }
 
-    fn set_active(&self, active: bool, cx: &mut WindowContext) {
-        self.update(cx, |this, cx| this.set_active(active, cx))
+    fn set_active(&self, active: bool, window: &mut Window, cx: &mut App) {
+        self.update(cx, |this, cx| this.set_active(active, window, cx))
     }
 
-    fn pane(&self, cx: &WindowContext) -> Option<View<Pane>> {
+    fn pane(&self, cx: &App) -> Option<Entity<Pane>> {
         self.read(cx).pane()
     }
 
@@ -137,39 +136,39 @@ where
         T::remote_id()
     }
 
-    fn size(&self, cx: &WindowContext) -> Pixels {
-        self.read(cx).size(cx)
+    fn size(&self, window: &Window, cx: &App) -> Pixels {
+        self.read(cx).size(window, cx)
     }
 
-    fn set_size(&self, size: Option<Pixels>, cx: &mut WindowContext) {
-        self.update(cx, |this, cx| this.set_size(size, cx))
+    fn set_size(&self, size: Option<Pixels>, window: &mut Window, cx: &mut App) {
+        self.update(cx, |this, cx| this.set_size(size, window, cx))
     }
 
-    fn icon(&self, cx: &WindowContext) -> Option<ui::IconName> {
-        self.read(cx).icon(cx)
+    fn icon(&self, window: &Window, cx: &App) -> Option<ui::IconName> {
+        self.read(cx).icon(window, cx)
     }
 
-    fn icon_tooltip(&self, cx: &WindowContext) -> Option<&'static str> {
-        self.read(cx).icon_tooltip(cx)
+    fn icon_tooltip(&self, window: &Window, cx: &App) -> Option<&'static str> {
+        self.read(cx).icon_tooltip(window, cx)
     }
 
-    fn toggle_action(&self, cx: &WindowContext) -> Box<dyn Action> {
+    fn toggle_action(&self, _: &Window, cx: &App) -> Box<dyn Action> {
         self.read(cx).toggle_action()
     }
 
-    fn icon_label(&self, cx: &WindowContext) -> Option<String> {
-        self.read(cx).icon_label(cx)
+    fn icon_label(&self, window: &Window, cx: &App) -> Option<String> {
+        self.read(cx).icon_label(window, cx)
     }
 
     fn to_any(&self) -> AnyView {
         self.clone().into()
     }
 
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+    fn panel_focus_handle(&self, cx: &App) -> FocusHandle {
         self.read(cx).focus_handle(cx).clone()
     }
 
-    fn activation_priority(&self, cx: &AppContext) -> u32 {
+    fn activation_priority(&self, cx: &App) -> u32 {
         self.read(cx).activation_priority()
     }
 }
@@ -185,7 +184,7 @@ impl From<&dyn PanelHandle> for AnyView {
 pub struct Dock {
     position: DockPosition,
     panel_entries: Vec<PanelEntry>,
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
     is_open: bool,
     active_panel_index: Option<usize>,
     focus_handle: FocusHandle,
@@ -194,8 +193,8 @@ pub struct Dock {
     _subscriptions: [Subscription; 2],
 }
 
-impl FocusableView for Dock {
-    fn focus_handle(&self, _: &AppContext) -> FocusHandle {
+impl Focusable for Dock {
+    fn focus_handle(&self, _: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -231,19 +230,24 @@ struct PanelEntry {
 }
 
 pub struct PanelButtons {
-    dock: View<Dock>,
+    dock: Entity<Dock>,
 }
 
 impl Dock {
-    pub fn new(position: DockPosition, cx: &mut ViewContext<Workspace>) -> View<Self> {
+    pub fn new(
+        position: DockPosition,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Entity<Self> {
         let focus_handle = cx.focus_handle();
-        let workspace = cx.view().clone();
-        let dock = cx.new_view(|cx: &mut ViewContext<Self>| {
-            let focus_subscription = cx.on_focus(&focus_handle, |dock, cx| {
-                if let Some(active_entry) = dock.active_panel_entry() {
-                    active_entry.panel.focus_handle(cx).focus(cx)
-                }
-            });
+        let workspace = cx.model().clone();
+        let dock = cx.new(|cx| {
+            let focus_subscription =
+                cx.on_focus(&focus_handle, window, |dock: &mut Dock, window, cx| {
+                    if let Some(active_entry) = dock.active_panel_entry() {
+                        active_entry.panel.panel_focus_handle(cx).focus(window)
+                    }
+                });
             let zoom_subscription = cx.subscribe(&workspace, |dock, workspace, e: &Event, cx| {
                 if matches!(e, Event::ZoomChanged) {
                     let is_zoomed = workspace.read(cx).zoomed.is_some();
@@ -263,16 +267,16 @@ impl Dock {
             }
         });
 
-        cx.on_focus_in(&focus_handle, {
+        cx.on_focus_in(&focus_handle, window, {
             let dock = dock.downgrade();
-            move |workspace, cx| {
+            move |workspace, window, cx| {
                 let Some(dock) = dock.upgrade() else {
                     return;
                 };
                 let Some(panel) = dock.read(cx).active_panel() else {
                     return;
                 };
-                if panel.is_zoomed(cx) {
+                if panel.is_zoomed(window, cx) {
                     workspace.zoomed = Some(panel.to_any().downgrade());
                     workspace.zoomed_position = Some(position);
                 } else {
@@ -280,16 +284,16 @@ impl Dock {
                     workspace.zoomed_position = None;
                 }
                 cx.emit(Event::ZoomChanged);
-                workspace.dismiss_zoomed_items_to_reveal(Some(position), cx);
-                workspace.update_active_view_for_followers(cx)
+                workspace.dismiss_zoomed_items_to_reveal(Some(position), window, cx);
+                workspace.update_active_view_for_followers(window, cx)
             }
         })
         .detach();
 
-        cx.observe(&dock, move |workspace, dock, cx| {
+        cx.observe_in(&dock, window, move |workspace, dock, window, cx| {
             if dock.read(cx).is_open() {
                 if let Some(panel) = dock.read(cx).active_panel() {
-                    if panel.is_zoomed(cx) {
+                    if panel.is_zoomed(window, cx) {
                         workspace.zoomed = Some(panel.to_any().downgrade());
                         workspace.zoomed_position = Some(position);
                         cx.emit(Event::ZoomChanged);
@@ -316,7 +320,7 @@ impl Dock {
         self.is_open
     }
 
-    pub fn panel<T: Panel>(&self) -> Option<View<T>> {
+    pub fn panel<T: Panel>(&self) -> Option<Entity<T>> {
         self.panel_entries
             .iter()
             .find_map(|entry| entry.panel.to_any().clone().downcast().ok())
@@ -328,11 +332,7 @@ impl Dock {
             .position(|entry| entry.panel.to_any().downcast::<T>().is_ok())
     }
 
-    pub fn panel_index_for_persistent_name(
-        &self,
-        ui_name: &str,
-        _cx: &AppContext,
-    ) -> Option<usize> {
+    pub fn panel_index_for_persistent_name(&self, ui_name: &str, _cx: &App) -> Option<usize> {
         self.panel_entries
             .iter()
             .position(|entry| entry.panel.persistent_name() == ui_name)
@@ -349,64 +349,71 @@ impl Dock {
             .and_then(|index| self.panel_entries.get(index))
     }
 
-    pub(crate) fn set_open(&mut self, open: bool, cx: &mut ViewContext<Self>) {
+    pub(crate) fn set_open(&mut self, open: bool, window: &mut Window, cx: &mut Context<Self>) {
         if open != self.is_open {
             self.is_open = open;
             if let Some(active_panel) = self.active_panel_entry() {
-                active_panel.panel.set_active(open, cx);
+                active_panel.panel.set_active(open, window, cx);
             }
 
             cx.notify();
         }
     }
 
-    pub fn set_panel_zoomed(&mut self, panel: &AnyView, zoomed: bool, cx: &mut ViewContext<Self>) {
+    pub fn set_panel_zoomed(
+        &mut self,
+        panel: &AnyView,
+        zoomed: bool,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         for entry in &mut self.panel_entries {
             if entry.panel.panel_id() == panel.entity_id() {
-                if zoomed != entry.panel.is_zoomed(cx) {
-                    entry.panel.set_zoomed(zoomed, cx);
+                if zoomed != entry.panel.is_zoomed(window, cx) {
+                    entry.panel.set_zoomed(zoomed, window, cx);
                 }
-            } else if entry.panel.is_zoomed(cx) {
-                entry.panel.set_zoomed(false, cx);
+            } else if entry.panel.is_zoomed(window, cx) {
+                entry.panel.set_zoomed(false, window, cx);
             }
         }
 
         self.workspace
             .update(cx, |workspace, cx| {
-                workspace.serialize_workspace(cx);
+                workspace.serialize_workspace(window, cx);
             })
             .ok();
         cx.notify();
     }
 
-    pub fn zoom_out(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn zoom_out(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         for entry in &mut self.panel_entries {
-            if entry.panel.is_zoomed(cx) {
-                entry.panel.set_zoomed(false, cx);
+            if entry.panel.is_zoomed(window, cx) {
+                entry.panel.set_zoomed(false, window, cx);
             }
         }
     }
 
     pub(crate) fn add_panel<T: Panel>(
         &mut self,
-        panel: View<T>,
-        workspace: WeakView<Workspace>,
-        cx: &mut ViewContext<Self>,
+        panel: Entity<T>,
+        workspace: WeakEntity<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> usize {
         let subscriptions = [
             cx.observe(&panel, |_, _, cx| cx.notify()),
-            cx.observe_global::<SettingsStore>({
+            cx.observe_global_in::<SettingsStore>(window, {
                 let workspace = workspace.clone();
                 let panel = panel.clone();
 
-                move |this, cx| {
-                    let new_position = panel.read(cx).position(cx);
+                move |this, window, cx| {
+                    let new_position = panel.read(cx).position(window, cx);
                     if new_position == this.position {
                         return;
                     }
 
                     let Ok(new_dock) = workspace.update(cx, |workspace, cx| {
-                        if panel.is_zoomed(cx) {
+                        if panel.is_zoomed(window, cx) {
                             workspace.zoomed_position = Some(new_position);
                         }
                         match new_position {
@@ -424,65 +431,72 @@ impl Dock {
                             active_panel.panel_id() == Entity::entity_id(&panel)
                         });
 
-                    this.remove_panel(&panel, cx);
+                    this.remove_panel(&panel, window, cx);
 
                     new_dock.update(cx, |new_dock, cx| {
-                        new_dock.remove_panel(&panel, cx);
-                        let index = new_dock.add_panel(panel.clone(), workspace.clone(), cx);
+                        new_dock.remove_panel(&panel, window, cx);
+                        let index =
+                            new_dock.add_panel(panel.clone(), workspace.clone(), window, cx);
                         if was_visible {
-                            new_dock.set_open(true, cx);
-                            new_dock.activate_panel(index, cx);
+                            new_dock.set_open(true, window, cx);
+                            new_dock.activate_panel(index, window, cx);
                         }
                     });
                 }
             }),
-            cx.subscribe(&panel, move |this, panel, event, cx| match event {
-                PanelEvent::ZoomIn => {
-                    this.set_panel_zoomed(&panel.to_any(), true, cx);
-                    if !panel.focus_handle(cx).contains_focused(cx) {
-                        cx.focus_view(&panel);
-                    }
-                    workspace
-                        .update(cx, |workspace, cx| {
-                            workspace.zoomed = Some(panel.downgrade().into());
-                            workspace.zoomed_position = Some(panel.read(cx).position(cx));
-                            cx.emit(Event::ZoomChanged);
-                        })
-                        .ok();
-                }
-                PanelEvent::ZoomOut => {
-                    this.set_panel_zoomed(&panel.to_any(), false, cx);
-                    workspace
-                        .update(cx, |workspace, cx| {
-                            if workspace.zoomed_position == Some(this.position) {
-                                workspace.zoomed = None;
-                                workspace.zoomed_position = None;
+            cx.subscribe_in(
+                &panel,
+                window,
+                move |this, panel, event, window, cx| match event {
+                    PanelEvent::ZoomIn => {
+                        this.set_panel_zoomed(&panel.to_any(), true, window, cx);
+                        if !PanelHandle::panel_focus_handle(panel, cx).contains_focused(window, cx)
+                        {
+                            window.focus(&panel.focus_handle(cx));
+                        }
+                        workspace
+                            .update(cx, |workspace, cx| {
+                                workspace.zoomed = Some(panel.downgrade().into());
+                                workspace.zoomed_position =
+                                    Some(panel.read(cx).position(window, cx));
                                 cx.emit(Event::ZoomChanged);
-                            }
-                            cx.notify();
-                        })
-                        .ok();
-                }
-                PanelEvent::Activate => {
-                    if let Some(ix) = this
-                        .panel_entries
-                        .iter()
-                        .position(|entry| entry.panel.panel_id() == Entity::entity_id(&panel))
-                    {
-                        this.set_open(true, cx);
-                        this.activate_panel(ix, cx);
-                        cx.focus_view(&panel);
+                            })
+                            .ok();
                     }
-                }
-                PanelEvent::Close => {
-                    if this
-                        .visible_panel()
-                        .map_or(false, |p| p.panel_id() == Entity::entity_id(&panel))
-                    {
-                        this.set_open(false, cx);
+                    PanelEvent::ZoomOut => {
+                        this.set_panel_zoomed(&panel.to_any(), false, window, cx);
+                        workspace
+                            .update(cx, |workspace, cx| {
+                                if workspace.zoomed_position == Some(this.position) {
+                                    workspace.zoomed = None;
+                                    workspace.zoomed_position = None;
+                                    cx.emit(Event::ZoomChanged);
+                                }
+                                cx.notify();
+                            })
+                            .ok();
                     }
-                }
-            }),
+                    PanelEvent::Activate => {
+                        if let Some(ix) = this
+                            .panel_entries
+                            .iter()
+                            .position(|entry| entry.panel.panel_id() == Entity::entity_id(panel))
+                        {
+                            this.set_open(true, window, cx);
+                            this.activate_panel(ix, window, cx);
+                            window.focus(&panel.read(cx).focus_handle(cx));
+                        }
+                    }
+                    PanelEvent::Close => {
+                        if this
+                            .visible_panel()
+                            .map_or(false, |p| p.panel_id() == Entity::entity_id(panel))
+                        {
+                            this.set_open(false, window, cx);
+                        }
+                    }
+                },
+            ),
         ];
 
         let index = match self
@@ -506,36 +520,41 @@ impl Dock {
             },
         );
 
-        self.restore_state(cx);
-        if panel.read(cx).starts_open(cx) {
-            self.activate_panel(index, cx);
-            self.set_open(true, cx);
+        self.restore_state(window, cx);
+        if panel.read(cx).starts_open(window, cx) {
+            self.activate_panel(index, window, cx);
+            self.set_open(true, window, cx);
         }
 
         cx.notify();
         index
     }
 
-    pub fn restore_state(&mut self, cx: &mut ViewContext<Self>) -> bool {
+    pub fn restore_state(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
         if let Some(serialized) = self.serialized_dock.clone() {
             if let Some(active_panel) = serialized.active_panel {
                 if let Some(idx) = self.panel_index_for_persistent_name(active_panel.as_str(), cx) {
-                    self.activate_panel(idx, cx);
+                    self.activate_panel(idx, window, cx);
                 }
             }
 
             if serialized.zoom {
                 if let Some(panel) = self.active_panel() {
-                    panel.set_zoomed(true, cx)
+                    panel.set_zoomed(true, window, cx)
                 }
             }
-            self.set_open(serialized.visible, cx);
+            self.set_open(serialized.visible, window, cx);
             return true;
         }
         false
     }
 
-    pub fn remove_panel<T: Panel>(&mut self, panel: &View<T>, cx: &mut ViewContext<Self>) {
+    pub fn remove_panel<T: Panel>(
+        &mut self,
+        panel: &Entity<T>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(panel_ix) = self
             .panel_entries
             .iter()
@@ -548,7 +567,7 @@ impl Dock {
                     }
                     std::cmp::Ordering::Equal => {
                         self.active_panel_index = None;
-                        self.set_open(false, cx);
+                        self.set_open(false, window, cx);
                     }
                     std::cmp::Ordering::Greater => {}
                 }
@@ -562,15 +581,15 @@ impl Dock {
         self.panel_entries.len()
     }
 
-    pub fn activate_panel(&mut self, panel_ix: usize, cx: &mut ViewContext<Self>) {
+    pub fn activate_panel(&mut self, panel_ix: usize, window: &mut Window, cx: &mut Context<Self>) {
         if Some(panel_ix) != self.active_panel_index {
             if let Some(active_panel) = self.active_panel_entry() {
-                active_panel.panel.set_active(false, cx);
+                active_panel.panel.set_active(false, window, cx);
             }
 
             self.active_panel_index = Some(panel_ix);
             if let Some(active_panel) = self.active_panel_entry() {
-                active_panel.panel.set_active(true, cx);
+                active_panel.panel.set_active(true, window, cx);
             }
 
             cx.notify();
@@ -595,35 +614,41 @@ impl Dock {
         }
     }
 
-    pub fn zoomed_panel(&self, cx: &WindowContext) -> Option<Arc<dyn PanelHandle>> {
+    pub fn zoomed_panel(&self, window: &Window, cx: &App) -> Option<Arc<dyn PanelHandle>> {
         let entry = self.visible_entry()?;
-        if entry.panel.is_zoomed(cx) {
+        if entry.panel.is_zoomed(window, cx) {
             Some(entry.panel.clone())
         } else {
             None
         }
     }
 
-    pub fn panel_size(&self, panel: &dyn PanelHandle, cx: &WindowContext) -> Option<Pixels> {
+    pub fn panel_size(&self, panel: &dyn PanelHandle, window: &Window, cx: &App) -> Option<Pixels> {
         self.panel_entries
             .iter()
             .find(|entry| entry.panel.panel_id() == panel.panel_id())
-            .map(|entry| entry.panel.size(cx))
+            .map(|entry| entry.panel.size(window, cx))
     }
 
-    pub fn active_panel_size(&self, cx: &WindowContext) -> Option<Pixels> {
+    pub fn active_panel_size(&self, window: &Window, cx: &App) -> Option<Pixels> {
         if self.is_open {
-            self.active_panel_entry().map(|entry| entry.panel.size(cx))
+            self.active_panel_entry()
+                .map(|entry| entry.panel.size(window, cx))
         } else {
             None
         }
     }
 
-    pub fn resize_active_panel(&mut self, size: Option<Pixels>, cx: &mut ViewContext<Self>) {
+    pub fn resize_active_panel(
+        &mut self,
+        size: Option<Pixels>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(entry) = self.active_panel_entry() {
             let size = size.map(|size| size.max(RESIZE_HANDLE_SIZE).round());
 
-            entry.panel.set_size(size, cx);
+            entry.panel.set_size(size, window, cx);
             cx.notify();
         }
     }
@@ -643,44 +668,44 @@ impl Dock {
         dispatch_context
     }
 
-    pub fn clamp_panel_size(&mut self, max_size: Pixels, cx: &mut WindowContext) {
+    pub fn clamp_panel_size(&mut self, max_size: Pixels, window: &mut Window, cx: &mut App) {
         let max_size = px((max_size.0 - RESIZE_HANDLE_SIZE.0).abs());
         for panel in self.panel_entries.iter().map(|entry| &entry.panel) {
-            if panel.size(cx) > max_size {
-                panel.set_size(Some(max_size.max(RESIZE_HANDLE_SIZE)), cx);
+            if panel.size(window, cx) > max_size {
+                panel.set_size(Some(max_size.max(RESIZE_HANDLE_SIZE)), window, cx);
             }
         }
     }
 }
 
 impl Render for Dock {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let dispatch_context = Self::dispatch_context();
         if let Some(entry) = self.visible_entry() {
-            let size = entry.panel.size(cx);
+            let size = entry.panel.size(window, cx);
 
             let position = self.position;
             let create_resize_handle = || {
                 let handle = div()
                     .id("resize-handle")
-                    .on_drag(DraggedDock(position), |dock, _, cx| {
+                    .on_drag(DraggedDock(position), |dock, _, _, cx| {
                         cx.stop_propagation();
-                        cx.new_view(|_| dock.clone())
+                        cx.new(|_| dock.clone())
                     })
                     .on_mouse_down(
                         MouseButton::Left,
-                        cx.listener(|_, _: &MouseDownEvent, cx| {
+                        cx.listener(|_, _: &MouseDownEvent, _, cx| {
                             cx.stop_propagation();
                         }),
                     )
                     .on_mouse_up(
                         MouseButton::Left,
-                        cx.listener(|dock, e: &MouseUpEvent, cx| {
+                        cx.listener(|dock, e: &MouseUpEvent, window, cx| {
                             if e.click_count == 2 {
-                                dock.resize_active_panel(None, cx);
+                                dock.resize_active_panel(None, window, cx);
                                 dock.workspace
                                     .update(cx, |workspace, cx| {
-                                        workspace.serialize_workspace(cx);
+                                        workspace.serialize_workspace(window, cx);
                                     })
                                     .ok();
                                 cx.stop_propagation();
@@ -758,14 +783,14 @@ impl Render for Dock {
 }
 
 impl PanelButtons {
-    pub fn new(dock: View<Dock>, cx: &mut ViewContext<Self>) -> Self {
+    pub fn new(dock: Entity<Dock>, cx: &mut Context<Self>) -> Self {
         cx.observe(&dock, |_, _, cx| cx.notify()).detach();
         Self { dock }
     }
 }
 
 impl Render for PanelButtons {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let dock = self.dock.read(cx);
         let active_index = dock.active_panel_index;
         let is_open = dock.is_open;
@@ -781,8 +806,8 @@ impl Render for PanelButtons {
             .iter()
             .enumerate()
             .filter_map(|(i, entry)| {
-                let icon = entry.panel.icon(cx)?;
-                let icon_tooltip = entry.panel.icon_tooltip(cx)?;
+                let icon = entry.panel.icon(window, cx)?;
+                let icon_tooltip = entry.panel.icon_tooltip(window, cx)?;
                 let name = entry.panel.persistent_name();
                 let panel = entry.panel.clone();
 
@@ -795,21 +820,21 @@ impl Render for PanelButtons {
 
                     (action, tooltip)
                 } else {
-                    let action = entry.panel.toggle_action(cx);
+                    let action = entry.panel.toggle_action(window, cx);
 
                     (action, icon_tooltip.into())
                 };
 
                 Some(
                     right_click_menu(name)
-                        .menu(move |cx| {
+                        .menu(move |window, cx| {
                             const POSITIONS: [DockPosition; 3] = [
                                 DockPosition::Left,
                                 DockPosition::Right,
                                 DockPosition::Bottom,
                             ];
 
-                            ContextMenu::build(cx, |mut menu, cx| {
+                            ContextMenu::build(window, cx, |mut menu, _, cx| {
                                 for position in POSITIONS {
                                     if position != dock_position
                                         && panel.position_is_valid(position, cx)
@@ -818,8 +843,8 @@ impl Render for PanelButtons {
                                         menu = menu.entry(
                                             format!("Dock {}", position.label()),
                                             None,
-                                            move |cx| {
-                                                panel.set_position(position, cx);
+                                            move |window, cx| {
+                                                panel.set_position(position, window, cx);
                                             },
                                         )
                                     }
@@ -835,10 +860,12 @@ impl Render for PanelButtons {
                                 .toggle_state(is_active_button)
                                 .on_click({
                                     let action = action.boxed_clone();
-                                    move |_, cx| cx.dispatch_action(action.boxed_clone())
+                                    move |_, window, cx| {
+                                        window.dispatch_action(action.boxed_clone(), cx)
+                                    }
                                 })
-                                .tooltip(move |cx| {
-                                    Tooltip::for_action(tooltip.clone(), &*action, cx)
+                                .tooltip(move |window, cx| {
+                                    Tooltip::for_action(tooltip.clone(), &*action, window, cx)
                                 }),
                         ),
                 )
@@ -852,7 +879,8 @@ impl StatusItemView for PanelButtons {
     fn set_active_pane_item(
         &mut self,
         _active_pane_item: Option<&dyn crate::ItemHandle>,
-        _cx: &mut ViewContext<Self>,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
     ) {
         // Nothing to do, panel buttons don't depend on the active center item
     }
@@ -861,7 +889,7 @@ impl StatusItemView for PanelButtons {
 #[cfg(any(test, feature = "test-support"))]
 pub mod test {
     use super::*;
-    use gpui::{actions, div, ViewContext, WindowContext};
+    use gpui::{actions, div, App, Context, Window};
 
     pub struct TestPanel {
         pub position: DockPosition,
@@ -875,7 +903,7 @@ pub mod test {
     impl EventEmitter<PanelEvent> for TestPanel {}
 
     impl TestPanel {
-        pub fn new(position: DockPosition, cx: &mut WindowContext) -> Self {
+        pub fn new(position: DockPosition, cx: &mut App) -> Self {
             Self {
                 position,
                 zoomed: false,
@@ -887,7 +915,7 @@ pub mod test {
     }
 
     impl Render for TestPanel {
-        fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+        fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
             div().id("test").track_focus(&self.focus_handle(cx))
         }
     }
@@ -897,7 +925,7 @@ pub mod test {
             "TestPanel"
         }
 
-        fn position(&self, _: &WindowContext) -> super::DockPosition {
+        fn position(&self, _window: &Window, _: &App) -> super::DockPosition {
             self.position
         }
 
@@ -905,24 +933,24 @@ pub mod test {
             true
         }
 
-        fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) {
+        fn set_position(&mut self, position: DockPosition, _: &mut Window, cx: &mut Context<Self>) {
             self.position = position;
             cx.update_global::<SettingsStore, _>(|_, _| {});
         }
 
-        fn size(&self, _: &WindowContext) -> Pixels {
+        fn size(&self, _window: &Window, _: &App) -> Pixels {
             self.size
         }
 
-        fn set_size(&mut self, size: Option<Pixels>, _: &mut ViewContext<Self>) {
+        fn set_size(&mut self, size: Option<Pixels>, _window: &mut Window, _: &mut Context<Self>) {
             self.size = size.unwrap_or(px(300.));
         }
 
-        fn icon(&self, _: &WindowContext) -> Option<ui::IconName> {
+        fn icon(&self, _window: &Window, _: &App) -> Option<ui::IconName> {
             None
         }
 
-        fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> {
+        fn icon_tooltip(&self, _window: &Window, _cx: &App) -> Option<&'static str> {
             None
         }
 
@@ -930,15 +958,15 @@ pub mod test {
             ToggleTestPanel.boxed_clone()
         }
 
-        fn is_zoomed(&self, _: &WindowContext) -> bool {
+        fn is_zoomed(&self, _window: &Window, _: &App) -> bool {
             self.zoomed
         }
 
-        fn set_zoomed(&mut self, zoomed: bool, _cx: &mut ViewContext<Self>) {
+        fn set_zoomed(&mut self, zoomed: bool, _window: &mut Window, _cx: &mut Context<Self>) {
             self.zoomed = zoomed;
         }
 
-        fn set_active(&mut self, active: bool, _cx: &mut ViewContext<Self>) {
+        fn set_active(&mut self, active: bool, _window: &mut Window, _cx: &mut Context<Self>) {
             self.active = active;
         }
 
@@ -947,8 +975,8 @@ pub mod test {
         }
     }
 
-    impl FocusableView for TestPanel {
-        fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
+    impl Focusable for TestPanel {
+        fn focus_handle(&self, _cx: &App) -> FocusHandle {
             self.focus_handle.clone()
         }
     }

crates/workspace/src/item.rs 🔗

@@ -13,9 +13,8 @@ use client::{
 };
 use futures::{channel::mpsc, StreamExt};
 use gpui::{
-    AnyElement, AnyView, AppContext, Entity, EntityId, EventEmitter, FocusHandle, FocusableView,
-    Font, HighlightStyle, Model, Pixels, Point, SharedString, Task, View, ViewContext, WeakView,
-    WindowContext,
+    AnyElement, AnyView, App, Context, Entity, EntityId, EventEmitter, FocusHandle, Focusable,
+    Font, HighlightStyle, Pixels, Point, Render, SharedString, Task, WeakEntity, Window,
 };
 use project::{Project, ProjectEntryId, ProjectPath};
 use schemars::JsonSchema;
@@ -130,7 +129,7 @@ impl Settings for ItemSettings {
 
     type FileContent = ItemSettingsContent;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
         sources.json_merge()
     }
 }
@@ -140,7 +139,7 @@ impl Settings for PreviewTabsSettings {
 
     type FileContent = PreviewTabsSettingsContent;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
         sources.json_merge()
     }
 }
@@ -180,18 +179,18 @@ impl TabContentParams {
 
 pub enum TabTooltipContent {
     Text(SharedString),
-    Custom(Box<dyn Fn(&mut WindowContext) -> AnyView>),
+    Custom(Box<dyn Fn(&mut Window, &mut App) -> AnyView>),
 }
 
-pub trait Item: FocusableView + EventEmitter<Self::Event> {
+pub trait Item: Focusable + EventEmitter<Self::Event> + Render + Sized {
     type Event;
 
     /// Returns the tab contents.
     ///
     /// By default this returns a [`Label`] that displays that text from
     /// `tab_content_text`.
-    fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement {
-        let Some(text) = self.tab_content_text(cx) else {
+    fn tab_content(&self, params: TabContentParams, window: &Window, cx: &App) -> AnyElement {
+        let Some(text) = self.tab_content_text(window, cx) else {
             return gpui::Empty.into_any();
         };
 
@@ -203,18 +202,18 @@ pub trait Item: FocusableView + EventEmitter<Self::Event> {
     /// Returns the textual contents of the tab.
     ///
     /// Use this if you don't need to customize the tab contents.
-    fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
+    fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option<SharedString> {
         None
     }
 
-    fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
+    fn tab_icon(&self, _window: &Window, _cx: &App) -> Option<Icon> {
         None
     }
 
     /// Returns the tab tooltip text.
     ///
     /// Use this if you don't need to customize the tab tooltip content.
-    fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
+    fn tab_tooltip_text(&self, _: &App) -> Option<SharedString> {
         None
     }
 
@@ -222,20 +221,20 @@ pub trait Item: FocusableView + EventEmitter<Self::Event> {
     ///
     /// By default this returns a Tooltip text from
     /// `tab_tooltip_text`.
-    fn tab_tooltip_content(&self, cx: &AppContext) -> Option<TabTooltipContent> {
+    fn tab_tooltip_content(&self, cx: &App) -> Option<TabTooltipContent> {
         self.tab_tooltip_text(cx).map(TabTooltipContent::Text)
     }
 
-    fn tab_description(&self, _: usize, _: &AppContext) -> Option<SharedString> {
+    fn tab_description(&self, _: usize, _: &App) -> Option<SharedString> {
         None
     }
 
     fn to_item_events(_event: &Self::Event, _f: impl FnMut(ItemEvent)) {}
 
-    fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
-    fn discarded(&self, _project: Model<Project>, _cx: &mut ViewContext<Self>) {}
-    fn workspace_deactivated(&mut self, _: &mut ViewContext<Self>) {}
-    fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool {
+    fn deactivated(&mut self, _window: &mut Window, _: &mut Context<Self>) {}
+    fn discarded(&self, _project: Entity<Project>, _window: &mut Window, _cx: &mut Context<Self>) {}
+    fn workspace_deactivated(&mut self, _window: &mut Window, _: &mut Context<Self>) {}
+    fn navigate(&mut self, _: Box<dyn Any>, _window: &mut Window, _: &mut Context<Self>) -> bool {
         false
     }
 
@@ -246,56 +245,60 @@ pub trait Item: FocusableView + EventEmitter<Self::Event> {
     /// (model id, Item)
     fn for_each_project_item(
         &self,
-        _: &AppContext,
+        _: &App,
         _: &mut dyn FnMut(EntityId, &dyn project::ProjectItem),
     ) {
     }
-    fn is_singleton(&self, _cx: &AppContext) -> bool {
+    fn is_singleton(&self, _cx: &App) -> bool {
         false
     }
-    fn set_nav_history(&mut self, _: ItemNavHistory, _: &mut ViewContext<Self>) {}
+    fn set_nav_history(&mut self, _: ItemNavHistory, _window: &mut Window, _: &mut Context<Self>) {}
     fn clone_on_split(
         &self,
         _workspace_id: Option<WorkspaceId>,
-        _: &mut ViewContext<Self>,
-    ) -> Option<View<Self>>
+        _window: &mut Window,
+        _: &mut Context<Self>,
+    ) -> Option<Entity<Self>>
     where
         Self: Sized,
     {
         None
     }
-    fn is_dirty(&self, _: &AppContext) -> bool {
+    fn is_dirty(&self, _: &App) -> bool {
         false
     }
-    fn has_deleted_file(&self, _: &AppContext) -> bool {
+    fn has_deleted_file(&self, _: &App) -> bool {
         false
     }
-    fn has_conflict(&self, _: &AppContext) -> bool {
+    fn has_conflict(&self, _: &App) -> bool {
         false
     }
-    fn can_save(&self, _cx: &AppContext) -> bool {
+    fn can_save(&self, _cx: &App) -> bool {
         false
     }
     fn save(
         &mut self,
         _format: bool,
-        _project: Model<Project>,
-        _cx: &mut ViewContext<Self>,
+        _project: Entity<Project>,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         unimplemented!("save() must be implemented if can_save() returns true")
     }
     fn save_as(
         &mut self,
-        _project: Model<Project>,
+        _project: Entity<Project>,
         _path: ProjectPath,
-        _cx: &mut ViewContext<Self>,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         unimplemented!("save_as() must be implemented if can_save() returns true")
     }
     fn reload(
         &mut self,
-        _project: Model<Project>,
-        _cx: &mut ViewContext<Self>,
+        _project: Entity<Project>,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         unimplemented!("reload() must be implemented if can_save() returns true")
     }
@@ -303,8 +306,8 @@ pub trait Item: FocusableView + EventEmitter<Self::Event> {
     fn act_as_type<'a>(
         &'a self,
         type_id: TypeId,
-        self_handle: &'a View<Self>,
-        _: &'a AppContext,
+        self_handle: &'a Entity<Self>,
+        _: &'a App,
     ) -> Option<AnyView> {
         if TypeId::of::<Self>() == type_id {
             Some(self_handle.clone().into())
@@ -313,29 +316,35 @@ pub trait Item: FocusableView + EventEmitter<Self::Event> {
         }
     }
 
-    fn as_searchable(&self, _: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
+    fn as_searchable(&self, _: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {
         None
     }
 
-    fn breadcrumb_location(&self, _: &AppContext) -> ToolbarItemLocation {
+    fn breadcrumb_location(&self, _: &App) -> ToolbarItemLocation {
         ToolbarItemLocation::Hidden
     }
 
-    fn breadcrumbs(&self, _theme: &Theme, _cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
+    fn breadcrumbs(&self, _theme: &Theme, _cx: &App) -> Option<Vec<BreadcrumbText>> {
         None
     }
 
-    fn added_to_workspace(&mut self, _workspace: &mut Workspace, _cx: &mut ViewContext<Self>) {}
+    fn added_to_workspace(
+        &mut self,
+        _workspace: &mut Workspace,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
+    ) {
+    }
 
     fn show_toolbar(&self) -> bool {
         true
     }
 
-    fn pixel_position_of_cursor(&self, _: &AppContext) -> Option<Point<Pixels>> {
+    fn pixel_position_of_cursor(&self, _: &App) -> Option<Point<Pixels>> {
         None
     }
 
-    fn preserve_preview(&self, _cx: &AppContext) -> bool {
+    fn preserve_preview(&self, _cx: &App) -> bool {
         false
     }
 
@@ -350,23 +359,26 @@ pub trait SerializableItem: Item {
     fn cleanup(
         workspace_id: WorkspaceId,
         alive_items: Vec<ItemId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<()>>;
 
     fn deserialize(
-        _project: Model<Project>,
-        _workspace: WeakView<Workspace>,
+        _project: Entity<Project>,
+        _workspace: WeakEntity<Workspace>,
         _workspace_id: WorkspaceId,
         _item_id: ItemId,
-        _cx: &mut WindowContext,
-    ) -> Task<Result<View<Self>>>;
+        _window: &mut Window,
+        _cx: &mut App,
+    ) -> Task<Result<Entity<Self>>>;
 
     fn serialize(
         &mut self,
         workspace: &mut Workspace,
         item_id: ItemId,
         closing: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<Task<Result<()>>>;
 
     fn should_serialize(&self, event: &Self::Event) -> bool;
@@ -378,12 +390,13 @@ pub trait SerializableItemHandle: ItemHandle {
         &self,
         workspace: &mut Workspace,
         closing: bool,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<Task<Result<()>>>;
-    fn should_serialize(&self, event: &dyn Any, cx: &AppContext) -> bool;
+    fn should_serialize(&self, event: &dyn Any, cx: &App) -> bool;
 }
 
-impl<T> SerializableItemHandle for View<T>
+impl<T> SerializableItemHandle for Entity<T>
 where
     T: SerializableItem,
 {
@@ -395,14 +408,15 @@ where
         &self,
         workspace: &mut Workspace,
         closing: bool,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<Task<Result<()>>> {
         self.update(cx, |this, cx| {
-            this.serialize(workspace, cx.entity_id().as_u64(), closing, cx)
+            this.serialize(workspace, cx.entity_id().as_u64(), closing, window, cx)
         })
     }
 
-    fn should_serialize(&self, event: &dyn Any, cx: &AppContext) -> bool {
+    fn should_serialize(&self, event: &dyn Any, cx: &App) -> bool {
         event
             .downcast_ref::<T::Event>()
             .map_or(false, |event| self.read(cx).should_serialize(event))
@@ -410,83 +424,95 @@ where
 }
 
 pub trait ItemHandle: 'static + Send {
+    fn item_focus_handle(&self, cx: &App) -> FocusHandle;
     fn subscribe_to_item_events(
         &self,
-        cx: &mut WindowContext,
-        handler: Box<dyn Fn(ItemEvent, &mut WindowContext)>,
+        window: &mut Window,
+        cx: &mut App,
+        handler: Box<dyn Fn(ItemEvent, &mut Window, &mut App)>,
     ) -> gpui::Subscription;
-    fn focus_handle(&self, cx: &WindowContext) -> FocusHandle;
-    fn tab_description(&self, detail: usize, cx: &AppContext) -> Option<SharedString>;
-    fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement;
-    fn tab_icon(&self, cx: &WindowContext) -> Option<Icon>;
-    fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString>;
-    fn tab_tooltip_content(&self, cx: &AppContext) -> Option<TabTooltipContent>;
-    fn telemetry_event_text(&self, cx: &WindowContext) -> Option<&'static str>;
-    fn dragged_tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement;
-    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
-    fn project_entry_ids(&self, cx: &AppContext) -> SmallVec<[ProjectEntryId; 3]>;
-    fn project_paths(&self, cx: &AppContext) -> SmallVec<[ProjectPath; 3]>;
-    fn project_item_model_ids(&self, cx: &AppContext) -> SmallVec<[EntityId; 3]>;
+    fn tab_description(&self, detail: usize, cx: &App) -> Option<SharedString>;
+    fn tab_content(&self, params: TabContentParams, window: &Window, cx: &App) -> AnyElement;
+    fn tab_icon(&self, window: &Window, cx: &App) -> Option<Icon>;
+    fn tab_tooltip_text(&self, cx: &App) -> Option<SharedString>;
+    fn tab_tooltip_content(&self, cx: &App) -> Option<TabTooltipContent>;
+    fn telemetry_event_text(&self, cx: &App) -> Option<&'static str>;
+    fn dragged_tab_content(
+        &self,
+        params: TabContentParams,
+        window: &Window,
+        cx: &App,
+    ) -> AnyElement;
+    fn project_path(&self, cx: &App) -> Option<ProjectPath>;
+    fn project_entry_ids(&self, cx: &App) -> SmallVec<[ProjectEntryId; 3]>;
+    fn project_paths(&self, cx: &App) -> SmallVec<[ProjectPath; 3]>;
+    fn project_item_model_ids(&self, cx: &App) -> SmallVec<[EntityId; 3]>;
     fn for_each_project_item(
         &self,
-        _: &AppContext,
+        _: &App,
         _: &mut dyn FnMut(EntityId, &dyn project::ProjectItem),
     );
-    fn is_singleton(&self, cx: &AppContext) -> bool;
+    fn is_singleton(&self, cx: &App) -> bool;
     fn boxed_clone(&self) -> Box<dyn ItemHandle>;
     fn clone_on_split(
         &self,
         workspace_id: Option<WorkspaceId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<Box<dyn ItemHandle>>;
     fn added_to_pane(
         &self,
         workspace: &mut Workspace,
-        pane: View<Pane>,
-        cx: &mut ViewContext<Workspace>,
+        pane: Entity<Pane>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     );
-    fn deactivated(&self, cx: &mut WindowContext);
-    fn discarded(&self, project: Model<Project>, cx: &mut WindowContext);
-    fn workspace_deactivated(&self, cx: &mut WindowContext);
-    fn navigate(&self, data: Box<dyn Any>, cx: &mut WindowContext) -> bool;
+    fn deactivated(&self, window: &mut Window, cx: &mut App);
+    fn discarded(&self, project: Entity<Project>, window: &mut Window, cx: &mut App);
+    fn workspace_deactivated(&self, window: &mut Window, cx: &mut App);
+    fn navigate(&self, data: Box<dyn Any>, window: &mut Window, cx: &mut App) -> bool;
     fn item_id(&self) -> EntityId;
     fn to_any(&self) -> AnyView;
-    fn is_dirty(&self, cx: &AppContext) -> bool;
-    fn has_deleted_file(&self, cx: &AppContext) -> bool;
-    fn has_conflict(&self, cx: &AppContext) -> bool;
-    fn can_save(&self, cx: &AppContext) -> bool;
+    fn is_dirty(&self, cx: &App) -> bool;
+    fn has_deleted_file(&self, cx: &App) -> bool;
+    fn has_conflict(&self, cx: &App) -> bool;
+    fn can_save(&self, cx: &App) -> bool;
     fn save(
         &self,
         format: bool,
-        project: Model<Project>,
-        cx: &mut WindowContext,
+        project: Entity<Project>,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<()>>;
     fn save_as(
         &self,
-        project: Model<Project>,
+        project: Entity<Project>,
         path: ProjectPath,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<()>>;
-    fn reload(&self, project: Model<Project>, cx: &mut WindowContext) -> Task<Result<()>>;
-    fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option<AnyView>;
-    fn to_followable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn FollowableItemHandle>>;
-    fn to_serializable_item_handle(
+    fn reload(
         &self,
-        cx: &AppContext,
-    ) -> Option<Box<dyn SerializableItemHandle>>;
+        project: Entity<Project>,
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Task<Result<()>>;
+    fn act_as_type(&self, type_id: TypeId, cx: &App) -> Option<AnyView>;
+    fn to_followable_item_handle(&self, cx: &App) -> Option<Box<dyn FollowableItemHandle>>;
+    fn to_serializable_item_handle(&self, cx: &App) -> Option<Box<dyn SerializableItemHandle>>;
     fn on_release(
         &self,
-        cx: &mut AppContext,
-        callback: Box<dyn FnOnce(&mut AppContext) + Send>,
+        cx: &mut App,
+        callback: Box<dyn FnOnce(&mut App) + Send>,
     ) -> gpui::Subscription;
-    fn to_searchable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>>;
-    fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation;
-    fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>>;
-    fn show_toolbar(&self, cx: &AppContext) -> bool;
-    fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Point<Pixels>>;
+    fn to_searchable_item_handle(&self, cx: &App) -> Option<Box<dyn SearchableItemHandle>>;
+    fn breadcrumb_location(&self, cx: &App) -> ToolbarItemLocation;
+    fn breadcrumbs(&self, theme: &Theme, cx: &App) -> Option<Vec<BreadcrumbText>>;
+    fn show_toolbar(&self, cx: &App) -> bool;
+    fn pixel_position_of_cursor(&self, cx: &App) -> Option<Point<Pixels>>;
     fn downgrade_item(&self) -> Box<dyn WeakItemHandle>;
-    fn workspace_settings<'a>(&self, cx: &'a AppContext) -> &'a WorkspaceSettings;
-    fn preserve_preview(&self, cx: &AppContext) -> bool;
+    fn workspace_settings<'a>(&self, cx: &'a App) -> &'a WorkspaceSettings;
+    fn preserve_preview(&self, cx: &App) -> bool;
     fn include_in_nav_history(&self) -> bool;
 }
 
@@ -497,66 +523,73 @@ pub trait WeakItemHandle: Send + Sync {
 }
 
 impl dyn ItemHandle {
-    pub fn downcast<V: 'static>(&self) -> Option<View<V>> {
+    pub fn downcast<V: 'static>(&self) -> Option<Entity<V>> {
         self.to_any().downcast().ok()
     }
 
-    pub fn act_as<V: 'static>(&self, cx: &AppContext) -> Option<View<V>> {
+    pub fn act_as<V: 'static>(&self, cx: &App) -> Option<Entity<V>> {
         self.act_as_type(TypeId::of::<V>(), cx)
             .and_then(|t| t.downcast().ok())
     }
 }
 
-impl<T: Item> ItemHandle for View<T> {
+impl<T: Item> ItemHandle for Entity<T> {
     fn subscribe_to_item_events(
         &self,
-        cx: &mut WindowContext,
-        handler: Box<dyn Fn(ItemEvent, &mut WindowContext)>,
+        window: &mut Window,
+        cx: &mut App,
+        handler: Box<dyn Fn(ItemEvent, &mut Window, &mut App)>,
     ) -> gpui::Subscription {
-        cx.subscribe(self, move |_, event, cx| {
-            T::to_item_events(event, |item_event| handler(item_event, cx));
+        window.subscribe(self, cx, move |_, event, window, cx| {
+            T::to_item_events(event, |item_event| handler(item_event, window, cx));
         })
     }
 
-    fn focus_handle(&self, cx: &WindowContext) -> FocusHandle {
-        self.focus_handle(cx)
+    fn item_focus_handle(&self, cx: &App) -> FocusHandle {
+        self.read(cx).focus_handle(cx)
     }
 
-    fn telemetry_event_text(&self, cx: &WindowContext) -> Option<&'static str> {
+    fn telemetry_event_text(&self, cx: &App) -> Option<&'static str> {
         self.read(cx).telemetry_event_text()
     }
 
-    fn tab_description(&self, detail: usize, cx: &AppContext) -> Option<SharedString> {
+    fn tab_description(&self, detail: usize, cx: &App) -> Option<SharedString> {
         self.read(cx).tab_description(detail, cx)
     }
 
-    fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement {
-        self.read(cx).tab_content(params, cx)
+    fn tab_content(&self, params: TabContentParams, window: &Window, cx: &App) -> AnyElement {
+        self.read(cx).tab_content(params, window, cx)
     }
 
-    fn tab_icon(&self, cx: &WindowContext) -> Option<Icon> {
-        self.read(cx).tab_icon(cx)
+    fn tab_icon(&self, window: &Window, cx: &App) -> Option<Icon> {
+        self.read(cx).tab_icon(window, cx)
     }
 
-    fn tab_tooltip_content(&self, cx: &AppContext) -> Option<TabTooltipContent> {
+    fn tab_tooltip_content(&self, cx: &App) -> Option<TabTooltipContent> {
         self.read(cx).tab_tooltip_content(cx)
     }
 
-    fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString> {
+    fn tab_tooltip_text(&self, cx: &App) -> Option<SharedString> {
         self.read(cx).tab_tooltip_text(cx)
     }
 
-    fn dragged_tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement {
+    fn dragged_tab_content(
+        &self,
+        params: TabContentParams,
+        window: &Window,
+        cx: &App,
+    ) -> AnyElement {
         self.read(cx).tab_content(
             TabContentParams {
                 selected: true,
                 ..params
             },
+            window,
             cx,
         )
     }
 
-    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
+    fn project_path(&self, cx: &App) -> Option<ProjectPath> {
         let this = self.read(cx);
         let mut result = None;
         if this.is_singleton(cx) {
@@ -567,7 +600,7 @@ impl<T: Item> ItemHandle for View<T> {
         result
     }
 
-    fn workspace_settings<'a>(&self, cx: &'a AppContext) -> &'a WorkspaceSettings {
+    fn workspace_settings<'a>(&self, cx: &'a App) -> &'a WorkspaceSettings {
         if let Some(project_path) = self.project_path(cx) {
             WorkspaceSettings::get(
                 Some(SettingsLocation {
@@ -581,7 +614,7 @@ impl<T: Item> ItemHandle for View<T> {
         }
     }
 
-    fn project_entry_ids(&self, cx: &AppContext) -> SmallVec<[ProjectEntryId; 3]> {
+    fn project_entry_ids(&self, cx: &App) -> SmallVec<[ProjectEntryId; 3]> {
         let mut result = SmallVec::new();
         self.read(cx).for_each_project_item(cx, &mut |_, item| {
             if let Some(id) = item.entry_id(cx) {
@@ -591,7 +624,7 @@ impl<T: Item> ItemHandle for View<T> {
         result
     }
 
-    fn project_paths(&self, cx: &AppContext) -> SmallVec<[ProjectPath; 3]> {
+    fn project_paths(&self, cx: &App) -> SmallVec<[ProjectPath; 3]> {
         let mut result = SmallVec::new();
         self.read(cx).for_each_project_item(cx, &mut |_, item| {
             if let Some(id) = item.project_path(cx) {
@@ -601,7 +634,7 @@ impl<T: Item> ItemHandle for View<T> {
         result
     }
 
-    fn project_item_model_ids(&self, cx: &AppContext) -> SmallVec<[EntityId; 3]> {
+    fn project_item_model_ids(&self, cx: &App) -> SmallVec<[EntityId; 3]> {
         let mut result = SmallVec::new();
         self.read(cx).for_each_project_item(cx, &mut |id, _| {
             result.push(id);
@@ -611,13 +644,13 @@ impl<T: Item> ItemHandle for View<T> {
 
     fn for_each_project_item(
         &self,
-        cx: &AppContext,
+        cx: &App,
         f: &mut dyn FnMut(EntityId, &dyn project::ProjectItem),
     ) {
         self.read(cx).for_each_project_item(cx, f)
     }
 
-    fn is_singleton(&self, cx: &AppContext) -> bool {
+    fn is_singleton(&self, cx: &App) -> bool {
         self.read(cx).is_singleton(cx)
     }
 
@@ -628,23 +661,25 @@ impl<T: Item> ItemHandle for View<T> {
     fn clone_on_split(
         &self,
         workspace_id: Option<WorkspaceId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<Box<dyn ItemHandle>> {
-        self.update(cx, |item, cx| item.clone_on_split(workspace_id, cx))
+        self.update(cx, |item, cx| item.clone_on_split(workspace_id, window, cx))
             .map(|handle| Box::new(handle) as Box<dyn ItemHandle>)
     }
 
     fn added_to_pane(
         &self,
         workspace: &mut Workspace,
-        pane: View<Pane>,
-        cx: &mut ViewContext<Workspace>,
+        pane: Entity<Pane>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) {
         let weak_item = self.downgrade();
         let history = pane.read(cx).nav_history_for_item(self);
         self.update(cx, |this, cx| {
-            this.set_nav_history(history, cx);
-            this.added_to_workspace(workspace, cx);
+            this.set_nav_history(history, window, cx);
+            this.added_to_workspace(workspace, window, cx);
         });
 
         if let Some(serializable_item) = self.to_serializable_item_handle(cx) {
@@ -664,10 +699,10 @@ impl<T: Item> ItemHandle for View<T> {
 
             let mut send_follower_updates = None;
             if let Some(item) = self.to_followable_item_handle(cx) {
-                let is_project_item = item.is_project_item(cx);
+                let is_project_item = item.is_project_item(window, cx);
                 let item = item.downgrade();
 
-                send_follower_updates = Some(cx.spawn({
+                send_follower_updates = Some(cx.spawn_in(window, {
                     let pending_update = pending_update.clone();
                     |workspace, mut cx| async move {
                         while let Some(mut leader_id) = pending_update_rx.next().await {
@@ -675,19 +710,20 @@ impl<T: Item> ItemHandle for View<T> {
                                 leader_id = id;
                             }
 
-                            workspace.update(&mut cx, |workspace, cx| {
+                            workspace.update_in(&mut cx, |workspace, window, cx| {
                                 let Some(item) = item.upgrade() else { return };
                                 workspace.update_followers(
                                     is_project_item,
                                     proto::update_followers::Variant::UpdateView(
                                         proto::UpdateView {
                                             id: item
-                                                .remote_id(workspace.client(), cx)
+                                                .remote_id(workspace.client(), window, cx)
                                                 .map(|id| id.to_proto()),
                                             variant: pending_update.borrow_mut().take(),
                                             leader_id,
                                         },
                                     ),
+                                    window,
                                     cx,
                                 );
                             })?;
@@ -698,9 +734,10 @@ impl<T: Item> ItemHandle for View<T> {
                 }));
             }
 
-            let mut event_subscription = Some(cx.subscribe(
+            let mut event_subscription = Some(cx.subscribe_in(
                 self,
-                move |workspace, item: View<T>, event, cx| {
+                window,
+                move |workspace, item: &Entity<T>, event, window, cx| {
                     let pane = if let Some(pane) = workspace
                         .panes_by_item
                         .get(&item.item_id())
@@ -716,14 +753,15 @@ impl<T: Item> ItemHandle for View<T> {
 
                         if let Some(leader_id) = leader_id {
                             if let Some(FollowEvent::Unfollow) = item.to_follow_event(event) {
-                                workspace.unfollow(leader_id, cx);
+                                workspace.unfollow(leader_id, window, cx);
                             }
                         }
 
-                        if item.focus_handle(cx).contains_focused(cx) {
+                        if item.item_focus_handle(cx).contains_focused(window, cx) {
                             item.add_event_to_update_proto(
                                 event,
                                 &mut pending_update.borrow_mut(),
+                                window,
                                 cx,
                             );
                             pending_update_tx.unbounded_send(leader_id).ok();
@@ -739,7 +777,12 @@ impl<T: Item> ItemHandle for View<T> {
                     T::to_item_events(event, |event| match event {
                         ItemEvent::CloseItem => {
                             pane.update(cx, |pane, cx| {
-                                pane.close_item_by_id(item.item_id(), crate::SaveIntent::Close, cx)
+                                pane.close_item_by_id(
+                                    item.item_id(),
+                                    crate::SaveIntent::Close,
+                                    window,
+                                    cx,
+                                )
                             })
                             .detach_and_log_err(cx);
                         }
@@ -757,9 +800,19 @@ impl<T: Item> ItemHandle for View<T> {
                             if let AutosaveSetting::AfterDelay { milliseconds } = autosave {
                                 let delay = Duration::from_millis(milliseconds);
                                 let item = item.clone();
-                                pending_autosave.fire_new(delay, cx, move |workspace, cx| {
-                                    Pane::autosave_item(&item, workspace.project().clone(), cx)
-                                });
+                                pending_autosave.fire_new(
+                                    delay,
+                                    window,
+                                    cx,
+                                    move |workspace, window, cx| {
+                                        Pane::autosave_item(
+                                            &item,
+                                            workspace.project().clone(),
+                                            window,
+                                            cx,
+                                        )
+                                    },
+                                );
                             }
                             pane.update(cx, |pane, cx| pane.handle_item_edit(item.item_id(), cx));
                         }
@@ -769,18 +822,22 @@ impl<T: Item> ItemHandle for View<T> {
                 },
             ));
 
-            cx.on_blur(&self.focus_handle(cx), move |workspace, cx| {
-                if let Some(item) = weak_item.upgrade() {
-                    if item.workspace_settings(cx).autosave == AutosaveSetting::OnFocusChange {
-                        Pane::autosave_item(&item, workspace.project.clone(), cx)
-                            .detach_and_log_err(cx);
+            cx.on_blur(
+                &self.read(cx).focus_handle(cx),
+                window,
+                move |workspace, window, cx| {
+                    if let Some(item) = weak_item.upgrade() {
+                        if item.workspace_settings(cx).autosave == AutosaveSetting::OnFocusChange {
+                            Pane::autosave_item(&item, workspace.project.clone(), window, cx)
+                                .detach_and_log_err(cx);
+                        }
                     }
-                }
-            })
+                },
+            )
             .detach();
 
             let item_id = self.item_id();
-            cx.observe_release(self, move |workspace, _, _| {
+            cx.observe_release_in(self, window, move |workspace, _, _, _| {
                 workspace.panes_by_item.remove(&item_id);
                 event_subscription.take();
                 send_follower_updates.take();
@@ -788,25 +845,25 @@ impl<T: Item> ItemHandle for View<T> {
             .detach();
         }
 
-        cx.defer(|workspace, cx| {
-            workspace.serialize_workspace(cx);
+        cx.defer_in(window, |workspace, window, cx| {
+            workspace.serialize_workspace(window, cx);
         });
     }
 
-    fn discarded(&self, project: Model<Project>, cx: &mut WindowContext) {
-        self.update(cx, |this, cx| this.discarded(project, cx));
+    fn discarded(&self, project: Entity<Project>, window: &mut Window, cx: &mut App) {
+        self.update(cx, |this, cx| this.discarded(project, window, cx));
     }
 
-    fn deactivated(&self, cx: &mut WindowContext) {
-        self.update(cx, |this, cx| this.deactivated(cx));
+    fn deactivated(&self, window: &mut Window, cx: &mut App) {
+        self.update(cx, |this, cx| this.deactivated(window, cx));
     }
 
-    fn workspace_deactivated(&self, cx: &mut WindowContext) {
-        self.update(cx, |this, cx| this.workspace_deactivated(cx));
+    fn workspace_deactivated(&self, window: &mut Window, cx: &mut App) {
+        self.update(cx, |this, cx| this.workspace_deactivated(window, cx));
     }
 
-    fn navigate(&self, data: Box<dyn Any>, cx: &mut WindowContext) -> bool {
-        self.update(cx, |this, cx| this.navigate(data, cx))
+    fn navigate(&self, data: Box<dyn Any>, window: &mut Window, cx: &mut App) -> bool {
+        self.update(cx, |this, cx| this.navigate(data, window, cx))
     }
 
     fn item_id(&self) -> EntityId {
@@ -817,77 +874,84 @@ impl<T: Item> ItemHandle for View<T> {
         self.clone().into()
     }
 
-    fn is_dirty(&self, cx: &AppContext) -> bool {
+    fn is_dirty(&self, cx: &App) -> bool {
         self.read(cx).is_dirty(cx)
     }
 
-    fn has_deleted_file(&self, cx: &AppContext) -> bool {
+    fn has_deleted_file(&self, cx: &App) -> bool {
         self.read(cx).has_deleted_file(cx)
     }
 
-    fn has_conflict(&self, cx: &AppContext) -> bool {
+    fn has_conflict(&self, cx: &App) -> bool {
         self.read(cx).has_conflict(cx)
     }
 
-    fn can_save(&self, cx: &AppContext) -> bool {
+    fn can_save(&self, cx: &App) -> bool {
         self.read(cx).can_save(cx)
     }
 
     fn save(
         &self,
         format: bool,
-        project: Model<Project>,
-        cx: &mut WindowContext,
+        project: Entity<Project>,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<()>> {
-        self.update(cx, |item, cx| item.save(format, project, cx))
+        self.update(cx, |item, cx| item.save(format, project, window, cx))
     }
 
     fn save_as(
         &self,
-        project: Model<Project>,
+        project: Entity<Project>,
         path: ProjectPath,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<anyhow::Result<()>> {
-        self.update(cx, |item, cx| item.save_as(project, path, cx))
+        self.update(cx, |item, cx| item.save_as(project, path, window, cx))
     }
 
-    fn reload(&self, project: Model<Project>, cx: &mut WindowContext) -> Task<Result<()>> {
-        self.update(cx, |item, cx| item.reload(project, cx))
+    fn reload(
+        &self,
+        project: Entity<Project>,
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Task<Result<()>> {
+        self.update(cx, |item, cx| item.reload(project, window, cx))
     }
 
-    fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a AppContext) -> Option<AnyView> {
+    fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a App) -> Option<AnyView> {
         self.read(cx).act_as_type(type_id, self, cx)
     }
 
-    fn to_followable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn FollowableItemHandle>> {
+    fn to_followable_item_handle(&self, cx: &App) -> Option<Box<dyn FollowableItemHandle>> {
         FollowableViewRegistry::to_followable_view(self.clone(), cx)
     }
 
     fn on_release(
         &self,
-        cx: &mut AppContext,
-        callback: Box<dyn FnOnce(&mut AppContext) + Send>,
+        cx: &mut App,
+        callback: Box<dyn FnOnce(&mut App) + Send>,
     ) -> gpui::Subscription {
         cx.observe_release(self, move |_, cx| callback(cx))
     }
 
-    fn to_searchable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
+    fn to_searchable_item_handle(&self, cx: &App) -> Option<Box<dyn SearchableItemHandle>> {
         self.read(cx).as_searchable(self)
     }
 
-    fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation {
+    fn breadcrumb_location(&self, cx: &App) -> ToolbarItemLocation {
         self.read(cx).breadcrumb_location(cx)
     }
 
-    fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
+    fn breadcrumbs(&self, theme: &Theme, cx: &App) -> Option<Vec<BreadcrumbText>> {
         self.read(cx).breadcrumbs(theme, cx)
     }
 
-    fn show_toolbar(&self, cx: &AppContext) -> bool {
+    fn show_toolbar(&self, cx: &App) -> bool {
         self.read(cx).show_toolbar()
     }
 
-    fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Point<Pixels>> {
+    fn pixel_position_of_cursor(&self, cx: &App) -> Option<Point<Pixels>> {
         self.read(cx).pixel_position_of_cursor(cx)
     }
 
@@ -895,14 +959,11 @@ impl<T: Item> ItemHandle for View<T> {
         Box::new(self.downgrade())
     }
 
-    fn to_serializable_item_handle(
-        &self,
-        cx: &AppContext,
-    ) -> Option<Box<dyn SerializableItemHandle>> {
+    fn to_serializable_item_handle(&self, cx: &App) -> Option<Box<dyn SerializableItemHandle>> {
         SerializableItemRegistry::view_to_serializable_item_handle(self.to_any(), cx)
     }
 
-    fn preserve_preview(&self, cx: &AppContext) -> bool {
+    fn preserve_preview(&self, cx: &App) -> bool {
         self.read(cx).preserve_preview(cx)
     }
 
@@ -929,7 +990,7 @@ impl Clone for Box<dyn ItemHandle> {
     }
 }
 
-impl<T: Item> WeakItemHandle for WeakView<T> {
+impl<T: Item> WeakItemHandle for WeakEntity<T> {
     fn id(&self) -> EntityId {
         self.entity_id()
     }
@@ -947,9 +1008,10 @@ pub trait ProjectItem: Item {
     type Item: project::ProjectItem;
 
     fn for_project_item(
-        project: Model<Project>,
-        item: Model<Self::Item>,
-        cx: &mut ViewContext<Self>,
+        project: Entity<Project>,
+        item: Entity<Self::Item>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self
     where
         Self: Sized;
@@ -967,55 +1029,70 @@ pub enum Dedup {
 
 pub trait FollowableItem: Item {
     fn remote_id(&self) -> Option<ViewId>;
-    fn to_state_proto(&self, cx: &WindowContext) -> Option<proto::view::Variant>;
+    fn to_state_proto(&self, window: &Window, cx: &App) -> Option<proto::view::Variant>;
     fn from_state_proto(
-        project: View<Workspace>,
+        project: Entity<Workspace>,
         id: ViewId,
         state: &mut Option<proto::view::Variant>,
-        cx: &mut WindowContext,
-    ) -> Option<Task<Result<View<Self>>>>;
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Option<Task<Result<Entity<Self>>>>;
     fn to_follow_event(event: &Self::Event) -> Option<FollowEvent>;
     fn add_event_to_update_proto(
         &self,
         event: &Self::Event,
         update: &mut Option<proto::update_view::Variant>,
-        cx: &WindowContext,
+        window: &Window,
+        cx: &App,
     ) -> bool;
     fn apply_update_proto(
         &mut self,
-        project: &Model<Project>,
+        project: &Entity<Project>,
         message: proto::update_view::Variant,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>>;
-    fn is_project_item(&self, cx: &WindowContext) -> bool;
-    fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>);
-    fn dedup(&self, existing: &Self, cx: &WindowContext) -> Option<Dedup>;
+    fn is_project_item(&self, window: &Window, cx: &App) -> bool;
+    fn set_leader_peer_id(
+        &mut self,
+        leader_peer_id: Option<PeerId>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    );
+    fn dedup(&self, existing: &Self, window: &Window, cx: &App) -> Option<Dedup>;
 }
 
 pub trait FollowableItemHandle: ItemHandle {
-    fn remote_id(&self, client: &Arc<Client>, cx: &WindowContext) -> Option<ViewId>;
+    fn remote_id(&self, client: &Arc<Client>, window: &mut Window, cx: &mut App) -> Option<ViewId>;
     fn downgrade(&self) -> Box<dyn WeakFollowableItemHandle>;
-    fn set_leader_peer_id(&self, leader_peer_id: Option<PeerId>, cx: &mut WindowContext);
-    fn to_state_proto(&self, cx: &WindowContext) -> Option<proto::view::Variant>;
+    fn set_leader_peer_id(&self, leader_peer_id: Option<PeerId>, window: &mut Window, cx: &mut App);
+    fn to_state_proto(&self, window: &mut Window, cx: &mut App) -> Option<proto::view::Variant>;
     fn add_event_to_update_proto(
         &self,
         event: &dyn Any,
         update: &mut Option<proto::update_view::Variant>,
-        cx: &WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> bool;
     fn to_follow_event(&self, event: &dyn Any) -> Option<FollowEvent>;
     fn apply_update_proto(
         &self,
-        project: &Model<Project>,
+        project: &Entity<Project>,
         message: proto::update_view::Variant,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<()>>;
-    fn is_project_item(&self, cx: &WindowContext) -> bool;
-    fn dedup(&self, existing: &dyn FollowableItemHandle, cx: &WindowContext) -> Option<Dedup>;
+    fn is_project_item(&self, window: &mut Window, cx: &mut App) -> bool;
+    fn dedup(
+        &self,
+        existing: &dyn FollowableItemHandle,
+        window: &mut Window,
+        cx: &mut App,
+    ) -> Option<Dedup>;
 }
 
-impl<T: FollowableItem> FollowableItemHandle for View<T> {
-    fn remote_id(&self, client: &Arc<Client>, cx: &WindowContext) -> Option<ViewId> {
+impl<T: FollowableItem> FollowableItemHandle for Entity<T> {
+    fn remote_id(&self, client: &Arc<Client>, _: &mut Window, cx: &mut App) -> Option<ViewId> {
         self.read(cx).remote_id().or_else(|| {
             client.peer_id().map(|creator| ViewId {
                 creator,

crates/workspace/src/modal_layer.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{AnyView, DismissEvent, FocusHandle, ManagedView, Subscription, View};
+use gpui::{AnyView, DismissEvent, Entity, FocusHandle, Focusable as _, ManagedView, Subscription};
 use ui::prelude::*;
 
 pub enum DismissDecision {
@@ -7,7 +7,11 @@ pub enum DismissDecision {
 }
 
 pub trait ModalView: ManagedView {
-    fn on_before_dismiss(&mut self, _: &mut ViewContext<Self>) -> DismissDecision {
+    fn on_before_dismiss(
+        &mut self,
+        _window: &mut Window,
+        _: &mut Context<Self>,
+    ) -> DismissDecision {
         DismissDecision::Dismiss(true)
     }
 
@@ -17,21 +21,21 @@ pub trait ModalView: ManagedView {
 }
 
 trait ModalViewHandle {
-    fn on_before_dismiss(&mut self, cx: &mut WindowContext) -> DismissDecision;
+    fn on_before_dismiss(&mut self, window: &mut Window, cx: &mut App) -> DismissDecision;
     fn view(&self) -> AnyView;
-    fn fade_out_background(&self, cx: &WindowContext) -> bool;
+    fn fade_out_background(&self, cx: &mut App) -> bool;
 }
 
-impl<V: ModalView> ModalViewHandle for View<V> {
-    fn on_before_dismiss(&mut self, cx: &mut WindowContext) -> DismissDecision {
-        self.update(cx, |this, cx| this.on_before_dismiss(cx))
+impl<V: ModalView> ModalViewHandle for Entity<V> {
+    fn on_before_dismiss(&mut self, window: &mut Window, cx: &mut App) -> DismissDecision {
+        self.update(cx, |this, cx| this.on_before_dismiss(window, cx))
     }
 
     fn view(&self) -> AnyView {
         self.clone().into()
     }
 
-    fn fade_out_background(&self, cx: &WindowContext) -> bool {
+    fn fade_out_background(&self, cx: &mut App) -> bool {
         self.read(cx).fade_out_background()
     }
 }
@@ -62,23 +66,23 @@ impl ModalLayer {
         }
     }
 
-    pub fn toggle_modal<V, B>(&mut self, cx: &mut ViewContext<Self>, build_view: B)
+    pub fn toggle_modal<V, B>(&mut self, window: &mut Window, cx: &mut Context<Self>, build_view: B)
     where
         V: ModalView,
-        B: FnOnce(&mut ViewContext<V>) -> V,
+        B: FnOnce(&mut Window, &mut Context<V>) -> V,
     {
         if let Some(active_modal) = &self.active_modal {
             let is_close = active_modal.modal.view().downcast::<V>().is_ok();
-            let did_close = self.hide_modal(cx);
+            let did_close = self.hide_modal(window, cx);
             if is_close || !did_close {
                 return;
             }
         }
-        let new_modal = cx.new_view(build_view);
-        self.show_modal(new_modal, cx);
+        let new_modal = cx.new(|cx| build_view(window, cx));
+        self.show_modal(new_modal, window, cx);
     }
 
-    fn show_modal<V>(&mut self, new_modal: View<V>, cx: &mut ViewContext<Self>)
+    fn show_modal<V>(&mut self, new_modal: Entity<V>, window: &mut Window, cx: &mut Context<Self>)
     where
         V: ModalView,
     {
@@ -86,31 +90,35 @@ impl ModalLayer {
         self.active_modal = Some(ActiveModal {
             modal: Box::new(new_modal.clone()),
             _subscriptions: [
-                cx.subscribe(&new_modal, |this, _, _: &DismissEvent, cx| {
-                    this.hide_modal(cx);
-                }),
-                cx.on_focus_out(&focus_handle, |this, _event, cx| {
+                cx.subscribe_in(
+                    &new_modal,
+                    window,
+                    |this, _, _: &DismissEvent, window, cx| {
+                        this.hide_modal(window, cx);
+                    },
+                ),
+                cx.on_focus_out(&focus_handle, window, |this, _event, window, cx| {
                     if this.dismiss_on_focus_lost {
-                        this.hide_modal(cx);
+                        this.hide_modal(window, cx);
                     }
                 }),
             ],
-            previous_focus_handle: cx.focused(),
+            previous_focus_handle: window.focused(cx),
             focus_handle,
         });
-        cx.defer(move |_, cx| {
-            cx.focus_view(&new_modal);
+        cx.defer_in(window, move |_, window, cx| {
+            window.focus(&new_modal.focus_handle(cx));
         });
         cx.notify();
     }
 
-    fn hide_modal(&mut self, cx: &mut ViewContext<Self>) -> bool {
+    fn hide_modal(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
         let Some(active_modal) = self.active_modal.as_mut() else {
             self.dismiss_on_focus_lost = false;
             return false;
         };
 
-        match active_modal.modal.on_before_dismiss(cx) {
+        match active_modal.modal.on_before_dismiss(window, cx) {
             DismissDecision::Dismiss(dismiss) => {
                 self.dismiss_on_focus_lost = !dismiss;
                 if !dismiss {
@@ -125,8 +133,8 @@ impl ModalLayer {
 
         if let Some(active_modal) = self.active_modal.take() {
             if let Some(previous_focus) = active_modal.previous_focus_handle {
-                if active_modal.focus_handle.contains_focused(cx) {
-                    previous_focus.focus(cx);
+                if active_modal.focus_handle.contains_focused(window, cx) {
+                    previous_focus.focus(window);
                 }
             }
             cx.notify();
@@ -134,7 +142,7 @@ impl ModalLayer {
         true
     }
 
-    pub fn active_modal<V>(&self) -> Option<View<V>>
+    pub fn active_modal<V>(&self) -> Option<Entity<V>>
     where
         V: 'static,
     {
@@ -148,7 +156,7 @@ impl ModalLayer {
 }
 
 impl Render for ModalLayer {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let Some(active_modal) = &self.active_modal else {
             return div();
         };
@@ -163,8 +171,8 @@ impl Render for ModalLayer {
                 background.fade_out(0.2);
                 el.bg(background)
                     .occlude()
-                    .on_mouse_down_out(cx.listener(|this, _, cx| {
-                        this.hide_modal(cx);
+                    .on_mouse_down_out(cx.listener(|this, _, window, cx| {
+                        this.hide_modal(window, cx);
                     }))
             })
             .child(

crates/workspace/src/notifications.rs 🔗

@@ -1,17 +1,16 @@
 use crate::{Toast, Workspace};
-use anyhow::Context;
+use anyhow::Context as _;
 use anyhow::{anyhow, Result};
 use gpui::{
-    svg, AnyView, AppContext, AsyncWindowContext, ClipboardItem, DismissEvent, EventEmitter,
-    Global, PromptLevel, Render, ScrollHandle, Task, View, ViewContext, VisualContext,
-    WindowContext,
+    svg, AnyView, App, AppContext as _, AsyncWindowContext, ClipboardItem, Context, DismissEvent,
+    Entity, EventEmitter, Global, PromptLevel, Render, ScrollHandle, Task,
 };
 use std::rc::Rc;
 use std::{any::TypeId, time::Duration};
 use ui::{prelude::*, Tooltip};
 use util::ResultExt;
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     cx.set_global(GlobalAppNotifications {
         app_notifications: Vec::new(),
     })
@@ -59,8 +58,8 @@ impl Workspace {
     pub fn show_notification<V: Notification>(
         &mut self,
         id: NotificationId,
-        cx: &mut ViewContext<Self>,
-        build_notification: impl FnOnce(&mut ViewContext<Self>) -> View<V>,
+        cx: &mut Context<Self>,
+        build_notification: impl FnOnce(&mut Context<Self>) -> Entity<V>,
     ) {
         self.show_notification_without_handling_dismiss_events(&id, cx, |cx| {
             let notification = build_notification(cx);
@@ -83,8 +82,8 @@ impl Workspace {
     pub(crate) fn show_notification_without_handling_dismiss_events(
         &mut self,
         id: &NotificationId,
-        cx: &mut ViewContext<Self>,
-        build_notification: impl FnOnce(&mut ViewContext<Self>) -> AnyView,
+        cx: &mut Context<Self>,
+        build_notification: impl FnOnce(&mut Context<Self>) -> AnyView,
     ) {
         self.dismiss_notification(id, cx);
         self.notifications
@@ -92,20 +91,20 @@ impl Workspace {
         cx.notify();
     }
 
-    pub fn show_error<E>(&mut self, err: &E, cx: &mut ViewContext<Self>)
+    pub fn show_error<E>(&mut self, err: &E, cx: &mut Context<Self>)
     where
         E: std::fmt::Debug + std::fmt::Display,
     {
         self.show_notification(workspace_error_notification_id(), cx, |cx| {
-            cx.new_view(|_cx| ErrorMessagePrompt::new(format!("Error: {err}")))
+            cx.new(|_| ErrorMessagePrompt::new(format!("Error: {err}")))
         });
     }
 
-    pub fn show_portal_error(&mut self, err: String, cx: &mut ViewContext<Self>) {
+    pub fn show_portal_error(&mut self, err: String, cx: &mut Context<Self>) {
         struct PortalError;
 
         self.show_notification(NotificationId::unique::<PortalError>(), cx, |cx| {
-            cx.new_view(|_cx| {
+            cx.new(|_| {
                 ErrorMessagePrompt::new(err.to_string()).with_link_button(
                     "See docs",
                     "https://zed.dev/docs/linux#i-cant-open-any-files",
@@ -114,7 +113,7 @@ impl Workspace {
         });
     }
 
-    pub fn dismiss_notification(&mut self, id: &NotificationId, cx: &mut ViewContext<Self>) {
+    pub fn dismiss_notification(&mut self, id: &NotificationId, cx: &mut Context<Self>) {
         self.notifications.retain(|(existing_id, _)| {
             if existing_id == id {
                 cx.notify();
@@ -125,15 +124,15 @@ impl Workspace {
         });
     }
 
-    pub fn show_toast(&mut self, toast: Toast, cx: &mut ViewContext<Self>) {
+    pub fn show_toast(&mut self, toast: Toast, cx: &mut Context<Self>) {
         self.dismiss_notification(&toast.id, cx);
         self.show_notification(toast.id.clone(), cx, |cx| {
-            cx.new_view(|_cx| match toast.on_click.as_ref() {
+            cx.new(|_| match toast.on_click.as_ref() {
                 Some((click_msg, on_click)) => {
                     let on_click = on_click.clone();
                     simple_message_notification::MessageNotification::new(toast.msg.clone())
                         .with_click_message(click_msg.clone())
-                        .on_click(move |cx| on_click(cx))
+                        .on_click(move |window, cx| on_click(window, cx))
                 }
                 None => simple_message_notification::MessageNotification::new(toast.msg.clone()),
             })
@@ -153,16 +152,16 @@ impl Workspace {
         }
     }
 
-    pub fn dismiss_toast(&mut self, id: &NotificationId, cx: &mut ViewContext<Self>) {
+    pub fn dismiss_toast(&mut self, id: &NotificationId, cx: &mut Context<Self>) {
         self.dismiss_notification(id, cx);
     }
 
-    pub fn clear_all_notifications(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn clear_all_notifications(&mut self, cx: &mut Context<Self>) {
         self.notifications.clear();
         cx.notify();
     }
 
-    pub fn show_initial_notifications(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn show_initial_notifications(&mut self, cx: &mut Context<Self>) {
         // Allow absence of the global so that tests don't need to initialize it.
         let app_notifications = cx
             .try_global::<GlobalAppNotifications>()
@@ -190,7 +189,7 @@ impl LanguageServerPrompt {
         }
     }
 
-    async fn select_option(this: View<Self>, ix: usize, mut cx: AsyncWindowContext) {
+    async fn select_option(this: Entity<Self>, ix: usize, mut cx: AsyncWindowContext) {
         util::maybe!(async move {
             let potential_future = this.update(&mut cx, |this, _| {
                 this.request.take().map(|request| request.respond(ix))
@@ -211,7 +210,7 @@ impl LanguageServerPrompt {
 }
 
 impl Render for LanguageServerPrompt {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let Some(request) = &self.request else {
             return div().id("language_server_prompt_notification");
         };
@@ -227,7 +226,7 @@ impl Render for LanguageServerPrompt {
             .group("language_server_prompt_notification")
             .occlude()
             .w_full()
-            .max_h(vh(0.8, cx))
+            .max_h(vh(0.8, window))
             .elevation_3(cx)
             .overflow_y_scroll()
             .track_scroll(&self.scroll_handle)
@@ -251,30 +250,32 @@ impl Render for LanguageServerPrompt {
                                         IconButton::new("copy", IconName::Copy)
                                             .on_click({
                                                 let message = request.message.clone();
-                                                move |_, cx| {
+                                                move |_, _, cx| {
                                                     cx.write_to_clipboard(
                                                         ClipboardItem::new_string(message.clone()),
                                                     )
                                                 }
                                             })
-                                            .tooltip(|cx| Tooltip::text("Copy Description", cx)),
+                                            .tooltip(Tooltip::text("Copy Description")),
                                     )
                                     .child(IconButton::new("close", IconName::Close).on_click(
-                                        cx.listener(|_, _, cx| cx.emit(gpui::DismissEvent)),
+                                        cx.listener(|_, _, _, cx| cx.emit(gpui::DismissEvent)),
                                     )),
                             ),
                     )
                     .child(Label::new(request.message.to_string()).size(LabelSize::Small))
                     .children(request.actions.iter().enumerate().map(|(ix, action)| {
-                        let this_handle = cx.view().clone();
+                        let this_handle = cx.model().clone();
                         Button::new(ix, action.title.clone())
                             .size(ButtonSize::Large)
-                            .on_click(move |_, cx| {
+                            .on_click(move |_, window, cx| {
                                 let this_handle = this_handle.clone();
-                                cx.spawn(|cx| async move {
-                                    LanguageServerPrompt::select_option(this_handle, ix, cx).await
-                                })
-                                .detach()
+                                window
+                                    .spawn(cx, |cx| async move {
+                                        LanguageServerPrompt::select_option(this_handle, ix, cx)
+                                            .await
+                                    })
+                                    .detach()
                             })
                     })),
             )
@@ -315,7 +316,7 @@ impl ErrorMessagePrompt {
 }
 
 impl Render for ErrorMessagePrompt {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         h_flex()
             .id("error_message_prompt_notification")
             .occlude()
@@ -334,7 +335,7 @@ impl Render for ErrorMessagePrompt {
                             .justify_between()
                             .child(
                                 svg()
-                                    .size(cx.text_style().font_size)
+                                    .size(window.text_style().font_size)
                                     .flex_none()
                                     .mr_2()
                                     .mt(px(-2.0))
@@ -344,8 +345,9 @@ impl Render for ErrorMessagePrompt {
                                     }),
                             )
                             .child(
-                                ui::IconButton::new("close", ui::IconName::Close)
-                                    .on_click(cx.listener(|_, _, cx| cx.emit(gpui::DismissEvent))),
+                                ui::IconButton::new("close", ui::IconName::Close).on_click(
+                                    cx.listener(|_, _, _, cx| cx.emit(gpui::DismissEvent)),
+                                ),
                             ),
                     )
                     .child(
@@ -360,7 +362,7 @@ impl Render for ErrorMessagePrompt {
                         elm.child(
                             div().mt_2().child(
                                 ui::Button::new("error_message_prompt_notification_button", label)
-                                    .on_click(move |_, cx| cx.open_url(&url)),
+                                    .on_click(move |_, _, cx| cx.open_url(&url)),
                             ),
                         )
                     }),
@@ -375,16 +377,15 @@ pub mod simple_message_notification {
 
     use gpui::{
         div, AnyElement, DismissEvent, EventEmitter, ParentElement, Render, SharedString, Styled,
-        ViewContext,
     };
     use ui::prelude::*;
 
     pub struct MessageNotification {
-        content: Box<dyn Fn(&mut ViewContext<Self>) -> AnyElement>,
-        on_click: Option<Arc<dyn Fn(&mut ViewContext<Self>)>>,
+        build_content: Box<dyn Fn(&mut Window, &mut Context<Self>) -> AnyElement>,
+        on_click: Option<Arc<dyn Fn(&mut Window, &mut Context<Self>)>>,
         click_message: Option<SharedString>,
         secondary_click_message: Option<SharedString>,
-        secondary_on_click: Option<Arc<dyn Fn(&mut ViewContext<Self>)>>,
+        secondary_on_click: Option<Arc<dyn Fn(&mut Window, &mut Context<Self>)>>,
     }
 
     impl EventEmitter<DismissEvent> for MessageNotification {}
@@ -395,15 +396,15 @@ pub mod simple_message_notification {
             S: Into<SharedString>,
         {
             let message = message.into();
-            Self::new_from_builder(move |_| Label::new(message.clone()).into_any_element())
+            Self::new_from_builder(move |_, _| Label::new(message.clone()).into_any_element())
         }
 
         pub fn new_from_builder<F>(content: F) -> MessageNotification
         where
-            F: 'static + Fn(&mut ViewContext<Self>) -> AnyElement,
+            F: 'static + Fn(&mut Window, &mut Context<Self>) -> AnyElement,
         {
             Self {
-                content: Box::new(content),
+                build_content: Box::new(content),
                 on_click: None,
                 click_message: None,
                 secondary_on_click: None,
@@ -421,7 +422,7 @@ pub mod simple_message_notification {
 
         pub fn on_click<F>(mut self, on_click: F) -> Self
         where
-            F: 'static + Fn(&mut ViewContext<Self>),
+            F: 'static + Fn(&mut Window, &mut Context<Self>),
         {
             self.on_click = Some(Arc::new(on_click));
             self
@@ -437,19 +438,19 @@ pub mod simple_message_notification {
 
         pub fn on_secondary_click<F>(mut self, on_click: F) -> Self
         where
-            F: 'static + Fn(&mut ViewContext<Self>),
+            F: 'static + Fn(&mut Window, &mut Context<Self>),
         {
             self.secondary_on_click = Some(Arc::new(on_click));
             self
         }
 
-        pub fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
+        pub fn dismiss(&mut self, cx: &mut Context<Self>) {
             cx.emit(DismissEvent);
         }
     }
 
     impl Render for MessageNotification {
-        fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+        fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
             v_flex()
                 .p_3()
                 .gap_2()
@@ -459,10 +460,10 @@ pub mod simple_message_notification {
                         .gap_4()
                         .justify_between()
                         .items_start()
-                        .child(div().max_w_96().child((self.content)(cx)))
+                        .child(div().max_w_96().child((self.build_content)(window, cx)))
                         .child(
                             IconButton::new("close", IconName::Close)
-                                .on_click(cx.listener(|this, _, cx| this.dismiss(cx))),
+                                .on_click(cx.listener(|this, _, _, cx| this.dismiss(cx))),
                         ),
                 )
                 .child(
@@ -475,9 +476,9 @@ pub mod simple_message_notification {
                                 .icon_position(IconPosition::Start)
                                 .icon_size(IconSize::Small)
                                 .icon_color(Color::Success)
-                                .on_click(cx.listener(|this, _, cx| {
+                                .on_click(cx.listener(|this, _, window, cx| {
                                     if let Some(on_click) = this.on_click.as_ref() {
-                                        (on_click)(cx)
+                                        (on_click)(window, cx)
                                     };
                                     this.dismiss(cx)
                                 }))
@@ -489,9 +490,9 @@ pub mod simple_message_notification {
                                 .icon_position(IconPosition::Start)
                                 .icon_size(IconSize::Small)
                                 .icon_color(Color::Error)
-                                .on_click(cx.listener(|this, _, cx| {
+                                .on_click(cx.listener(|this, _, window, cx| {
                                     if let Some(on_click) = this.secondary_on_click.as_ref() {
-                                        (on_click)(cx)
+                                        (on_click)(window, cx)
                                     };
                                     this.dismiss(cx)
                                 }))
@@ -505,7 +506,7 @@ pub mod simple_message_notification {
 struct GlobalAppNotifications {
     app_notifications: Vec<(
         NotificationId,
-        Rc<dyn Fn(&mut ViewContext<Workspace>) -> AnyView>,
+        Rc<dyn Fn(&mut Context<Workspace>) -> AnyView>,
     )>,
 }
 
@@ -515,7 +516,7 @@ impl GlobalAppNotifications {
     pub fn insert(
         &mut self,
         id: NotificationId,
-        build_notification: Rc<dyn Fn(&mut ViewContext<Workspace>) -> AnyView>,
+        build_notification: Rc<dyn Fn(&mut Context<Workspace>) -> AnyView>,
     ) {
         self.remove(&id);
         self.app_notifications.push((id, build_notification))
@@ -532,11 +533,11 @@ impl GlobalAppNotifications {
 /// exist. If the notification is dismissed within any workspace, it will be removed from all.
 pub fn show_app_notification<V: Notification + 'static>(
     id: NotificationId,
-    cx: &mut AppContext,
-    build_notification: impl Fn(&mut ViewContext<Workspace>) -> View<V> + 'static,
+    cx: &mut App,
+    build_notification: impl Fn(&mut Context<Workspace>) -> Entity<V> + 'static,
 ) -> Result<()> {
     // Handle dismiss events by removing the notification from all workspaces.
-    let build_notification: Rc<dyn Fn(&mut ViewContext<Workspace>) -> AnyView> = Rc::new({
+    let build_notification: Rc<dyn Fn(&mut Context<Workspace>) -> AnyView> = Rc::new({
         let id = id.clone();
         move |cx| {
             let notification = build_notification(cx);
@@ -559,7 +560,7 @@ pub fn show_app_notification<V: Notification + 'static>(
 
     for window in cx.windows() {
         if let Some(workspace_window) = window.downcast::<Workspace>() {
-            let notify_result = workspace_window.update(cx, |workspace, cx| {
+            let notify_result = workspace_window.update(cx, |workspace, _window, cx| {
                 workspace.show_notification_without_handling_dismiss_events(&id, cx, |cx| {
                     build_notification(cx)
                 });
@@ -585,7 +586,7 @@ pub fn show_app_notification<V: Notification + 'static>(
     }
 }
 
-pub fn dismiss_app_notification(id: &NotificationId, cx: &mut AppContext) {
+pub fn dismiss_app_notification(id: &NotificationId, cx: &mut App) {
     cx.global_mut::<GlobalAppNotifications>().remove(id);
     for window in cx.windows() {
         if let Some(workspace_window) = window.downcast::<Workspace>() {
@@ -593,7 +594,7 @@ pub fn dismiss_app_notification(id: &NotificationId, cx: &mut AppContext) {
             // This spawn is necessary in order to dismiss the notification on which the click
             // occurred, because in that case we're already in the middle of an update.
             cx.spawn(move |mut cx| async move {
-                workspace_window.update(&mut cx, |workspace, cx| {
+                workspace_window.update(&mut cx, |workspace, _window, cx| {
                     workspace.dismiss_notification(&id, cx)
                 })
             })
@@ -605,16 +606,13 @@ pub fn dismiss_app_notification(id: &NotificationId, cx: &mut AppContext) {
 pub trait NotifyResultExt {
     type Ok;
 
-    fn notify_err(
-        self,
-        workspace: &mut Workspace,
-        cx: &mut ViewContext<Workspace>,
-    ) -> Option<Self::Ok>;
+    fn notify_err(self, workspace: &mut Workspace, cx: &mut Context<Workspace>)
+        -> Option<Self::Ok>;
 
     fn notify_async_err(self, cx: &mut AsyncWindowContext) -> Option<Self::Ok>;
 
     /// Notifies the active workspace if there is one, otherwise notifies all workspaces.
-    fn notify_app_err(self, cx: &mut AppContext) -> Option<Self::Ok>;
+    fn notify_app_err(self, cx: &mut App) -> Option<Self::Ok>;
 }
 
 impl<T, E> NotifyResultExt for std::result::Result<T, E>
@@ -623,7 +621,7 @@ where
 {
     type Ok = T;
 
-    fn notify_err(self, workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) -> Option<T> {
+    fn notify_err(self, workspace: &mut Workspace, cx: &mut Context<Workspace>) -> Option<T> {
         match self {
             Ok(value) => Some(value),
             Err(err) => {
@@ -639,7 +637,7 @@ where
             Ok(value) => Some(value),
             Err(err) => {
                 log::error!("{err:?}");
-                cx.update_root(|view, cx| {
+                cx.update_root(|view, _, cx| {
                     if let Ok(workspace) = view.downcast::<Workspace>() {
                         workspace.update(cx, |workspace, cx| workspace.show_error(&err, cx))
                     }
@@ -650,7 +648,7 @@ where
         }
     }
 
-    fn notify_app_err(self, cx: &mut AppContext) -> Option<T> {
+    fn notify_app_err(self, cx: &mut App) -> Option<T> {
         match self {
             Ok(value) => Some(value),
             Err(err) => {
@@ -659,7 +657,7 @@ where
                 show_app_notification(workspace_error_notification_id(), cx, {
                     let message = message.clone();
                     move |cx| {
-                        cx.new_view({
+                        cx.new({
                             let message = message.clone();
                             move |_cx| ErrorMessagePrompt::new(message)
                         })
@@ -674,7 +672,7 @@ where
 }
 
 pub trait NotifyTaskExt {
-    fn detach_and_notify_err(self, cx: &mut WindowContext);
+    fn detach_and_notify_err(self, window: &mut Window, cx: &mut App);
 }
 
 impl<R, E> NotifyTaskExt for Task<std::result::Result<R, E>>
@@ -682,8 +680,12 @@ where
     E: std::fmt::Debug + std::fmt::Display + Sized + 'static,
     R: 'static,
 {
-    fn detach_and_notify_err(self, cx: &mut WindowContext) {
-        cx.spawn(|mut cx| async move { self.await.notify_async_err(&mut cx) })
+    fn detach_and_notify_err(self, window: &mut Window, cx: &mut App) {
+        window
+            .spawn(
+                cx,
+                |mut cx| async move { self.await.notify_async_err(&mut cx) },
+            )
             .detach();
     }
 }
@@ -692,15 +694,17 @@ pub trait DetachAndPromptErr<R> {
     fn prompt_err(
         self,
         msg: &str,
-        cx: &mut WindowContext,
-        f: impl FnOnce(&anyhow::Error, &mut WindowContext) -> Option<String> + 'static,
+        window: &Window,
+        cx: &App,
+        f: impl FnOnce(&anyhow::Error, &mut Window, &mut App) -> Option<String> + 'static,
     ) -> Task<Option<R>>;
 
     fn detach_and_prompt_err(
         self,
         msg: &str,
-        cx: &mut WindowContext,
-        f: impl FnOnce(&anyhow::Error, &mut WindowContext) -> Option<String> + 'static,
+        window: &Window,
+        cx: &App,
+        f: impl FnOnce(&anyhow::Error, &mut Window, &mut App) -> Option<String> + 'static,
     );
 }
 
@@ -711,17 +715,19 @@ where
     fn prompt_err(
         self,
         msg: &str,
-        cx: &mut WindowContext,
-        f: impl FnOnce(&anyhow::Error, &mut WindowContext) -> Option<String> + 'static,
+        window: &Window,
+        cx: &App,
+        f: impl FnOnce(&anyhow::Error, &mut Window, &mut App) -> Option<String> + 'static,
     ) -> Task<Option<R>> {
         let msg = msg.to_owned();
-        cx.spawn(|mut cx| async move {
+        window.spawn(cx, |mut cx| async move {
             let result = self.await;
             if let Err(err) = result.as_ref() {
                 log::error!("{err:?}");
-                if let Ok(prompt) = cx.update(|cx| {
-                    let detail = f(err, cx).unwrap_or_else(|| format!("{err}. Please try again."));
-                    cx.prompt(PromptLevel::Critical, &msg, Some(&detail), &["Ok"])
+                if let Ok(prompt) = cx.update(|window, cx| {
+                    let detail =
+                        f(err, window, cx).unwrap_or_else(|| format!("{err}. Please try again."));
+                    window.prompt(PromptLevel::Critical, &msg, Some(&detail), &["Ok"], cx)
                 }) {
                     prompt.await.ok();
                 }
@@ -734,9 +740,10 @@ where
     fn detach_and_prompt_err(
         self,
         msg: &str,
-        cx: &mut WindowContext,
-        f: impl FnOnce(&anyhow::Error, &mut WindowContext) -> Option<String> + 'static,
+        window: &Window,
+        cx: &App,
+        f: impl FnOnce(&anyhow::Error, &mut Window, &mut App) -> Option<String> + 'static,
     ) {
-        self.prompt_err(msg, cx, f).detach();
+        self.prompt_err(msg, window, cx, f).detach();
     }
 }

crates/workspace/src/pane.rs 🔗

@@ -14,12 +14,11 @@ use anyhow::Result;
 use collections::{BTreeSet, HashMap, HashSet, VecDeque};
 use futures::{stream::FuturesUnordered, StreamExt};
 use gpui::{
-    actions, anchored, deferred, impl_actions, prelude::*, Action, AnyElement, AppContext,
-    AsyncWindowContext, ClickEvent, ClipboardItem, Corner, Div, DragMoveEvent, EntityId,
-    EventEmitter, ExternalPaths, FocusHandle, FocusOutEvent, FocusableView, KeyContext, Model,
+    actions, anchored, deferred, impl_actions, prelude::*, Action, AnyElement, App,
+    AsyncWindowContext, ClickEvent, ClipboardItem, Context, Corner, Div, DragMoveEvent, Entity,
+    EntityId, EventEmitter, ExternalPaths, FocusHandle, FocusOutEvent, Focusable, KeyContext,
     MouseButton, MouseDownEvent, NavigationDirection, Pixels, Point, PromptLevel, Render,
-    ScrollHandle, Subscription, Task, View, ViewContext, VisualContext, WeakFocusHandle, WeakModel,
-    WeakView, WindowContext,
+    ScrollHandle, Subscription, Task, WeakEntity, WeakFocusHandle, Window,
 };
 use itertools::Itertools;
 use language::DiagnosticSeverity;
@@ -211,7 +210,7 @@ pub enum Event {
         focus_changed: bool,
     },
     Remove {
-        focus_on_pane: Option<View<Pane>>,
+        focus_on_pane: Option<Entity<Pane>>,
     },
     RemoveItem {
         idx: usize,
@@ -287,17 +286,24 @@ pub struct Pane {
     preview_item_id: Option<EntityId>,
     last_focus_handle_by_item: HashMap<EntityId, WeakFocusHandle>,
     nav_history: NavHistory,
-    toolbar: View<Toolbar>,
-    pub(crate) workspace: WeakView<Workspace>,
-    project: WeakModel<Project>,
+    toolbar: Entity<Toolbar>,
+    pub(crate) workspace: WeakEntity<Workspace>,
+    project: WeakEntity<Project>,
     drag_split_direction: Option<SplitDirection>,
-    can_drop_predicate: Option<Arc<dyn Fn(&dyn Any, &mut WindowContext) -> bool>>,
-    custom_drop_handle:
-        Option<Arc<dyn Fn(&mut Pane, &dyn Any, &mut ViewContext<Pane>) -> ControlFlow<(), ()>>>,
-    can_split_predicate: Option<Arc<dyn Fn(&mut Self, &dyn Any, &mut ViewContext<Self>) -> bool>>,
-    should_display_tab_bar: Rc<dyn Fn(&ViewContext<Pane>) -> bool>,
-    render_tab_bar_buttons:
-        Rc<dyn Fn(&mut Pane, &mut ViewContext<Pane>) -> (Option<AnyElement>, Option<AnyElement>)>,
+    can_drop_predicate: Option<Arc<dyn Fn(&dyn Any, &mut Window, &mut App) -> bool>>,
+    custom_drop_handle: Option<
+        Arc<dyn Fn(&mut Pane, &dyn Any, &mut Window, &mut Context<Pane>) -> ControlFlow<(), ()>>,
+    >,
+    can_split_predicate:
+        Option<Arc<dyn Fn(&mut Self, &dyn Any, &mut Window, &mut Context<Self>) -> bool>>,
+    should_display_tab_bar: Rc<dyn Fn(&Window, &mut Context<Pane>) -> bool>,
+    render_tab_bar_buttons: Rc<
+        dyn Fn(
+            &mut Pane,
+            &mut Window,
+            &mut Context<Pane>,
+        ) -> (Option<AnyElement>, Option<AnyElement>),
+    >,
     _subscriptions: Vec<Subscription>,
     tab_bar_scroll_handle: ScrollHandle,
     /// Is None if navigation buttons are permanently turned off (and should not react to setting changes).
@@ -332,7 +338,7 @@ struct NavHistoryState {
     forward_stack: VecDeque<NavigationEntry>,
     closed_stack: VecDeque<NavigationEntry>,
     paths_by_item: HashMap<EntityId, (ProjectPath, Option<PathBuf>)>,
-    pane: WeakView<Pane>,
+    pane: WeakEntity<Pane>,
     next_timestamp: Arc<AtomicUsize>,
 }
 
@@ -361,7 +367,7 @@ pub struct NavigationEntry {
 
 #[derive(Clone)]
 pub struct DraggedTab {
-    pub pane: View<Pane>,
+    pub pane: Entity<Pane>,
     pub item: Box<dyn ItemHandle>,
     pub ix: usize,
     pub detail: usize,
@@ -372,24 +378,25 @@ impl EventEmitter<Event> for Pane {}
 
 impl Pane {
     pub fn new(
-        workspace: WeakView<Workspace>,
-        project: Model<Project>,
+        workspace: WeakEntity<Workspace>,
+        project: Entity<Project>,
         next_timestamp: Arc<AtomicUsize>,
-        can_drop_predicate: Option<Arc<dyn Fn(&dyn Any, &mut WindowContext) -> bool + 'static>>,
+        can_drop_predicate: Option<Arc<dyn Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static>>,
         double_click_dispatch_action: Box<dyn Action>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         let focus_handle = cx.focus_handle();
 
         let subscriptions = vec![
-            cx.on_focus(&focus_handle, Pane::focus_in),
-            cx.on_focus_in(&focus_handle, Pane::focus_in),
-            cx.on_focus_out(&focus_handle, Pane::focus_out),
+            cx.on_focus(&focus_handle, window, Pane::focus_in),
+            cx.on_focus_in(&focus_handle, window, Pane::focus_in),
+            cx.on_focus_out(&focus_handle, window, Pane::focus_out),
             cx.observe_global::<SettingsStore>(Self::settings_changed),
             cx.subscribe(&project, Self::project_events),
         ];
 
-        let handle = cx.view().downgrade();
+        let handle = cx.model().downgrade();
         Self {
             alternate_file_items: (None, None),
             focus_handle,
@@ -410,7 +417,7 @@ impl Pane {
                 pane: handle.clone(),
                 next_timestamp,
             }))),
-            toolbar: cx.new_view(|_| Toolbar::new()),
+            toolbar: cx.new(|_| Toolbar::new()),
             tab_bar_scroll_handle: ScrollHandle::new(),
             drag_split_direction: None,
             workspace,
@@ -418,9 +425,9 @@ impl Pane {
             can_drop_predicate,
             custom_drop_handle: None,
             can_split_predicate: None,
-            should_display_tab_bar: Rc::new(|cx| TabBarSettings::get_global(cx).show),
-            render_tab_bar_buttons: Rc::new(move |pane, cx| {
-                if !pane.has_focus(cx) && !pane.context_menu_focused(cx) {
+            should_display_tab_bar: Rc::new(|_, cx| TabBarSettings::get_global(cx).show),
+            render_tab_bar_buttons: Rc::new(move |pane, window, cx| {
+                if !pane.has_focus(window, cx) && !pane.context_menu_focused(window, cx) {
                     return (None, None);
                 }
                 // Ideally we would return a vec of elements here to pass directly to the [TabBar]'s
@@ -433,12 +440,12 @@ impl Pane {
                             .trigger(
                                 IconButton::new("plus", IconName::Plus)
                                     .icon_size(IconSize::Small)
-                                    .tooltip(|cx| Tooltip::text("New...", cx)),
+                                    .tooltip(Tooltip::text("New...")),
                             )
                             .anchor(Corner::TopRight)
                             .with_handle(pane.new_item_context_menu_handle.clone())
-                            .menu(move |cx| {
-                                Some(ContextMenu::build(cx, |menu, _| {
+                            .menu(move |window, cx| {
+                                Some(ContextMenu::build(window, cx, |menu, _, _| {
                                     menu.action("New File", NewFile.boxed_clone())
                                         .action(
                                             "Open File",
@@ -466,12 +473,12 @@ impl Pane {
                             .trigger(
                                 IconButton::new("split", IconName::Split)
                                     .icon_size(IconSize::Small)
-                                    .tooltip(|cx| Tooltip::text("Split Pane", cx)),
+                                    .tooltip(Tooltip::text("Split Pane")),
                             )
                             .anchor(Corner::TopRight)
                             .with_handle(pane.split_item_context_menu_handle.clone())
-                            .menu(move |cx| {
-                                ContextMenu::build(cx, |menu, _| {
+                            .menu(move |window, cx| {
+                                ContextMenu::build(window, cx, |menu, _, _| {
                                     menu.action("Split Right", SplitRight.boxed_clone())
                                         .action("Split Left", SplitLeft.boxed_clone())
                                         .action("Split Up", SplitUp.boxed_clone())
@@ -486,13 +493,14 @@ impl Pane {
                             .icon_size(IconSize::Small)
                             .toggle_state(zoomed)
                             .selected_icon(IconName::Minimize)
-                            .on_click(cx.listener(|pane, _, cx| {
-                                pane.toggle_zoom(&crate::ToggleZoom, cx);
+                            .on_click(cx.listener(|pane, _, window, cx| {
+                                pane.toggle_zoom(&crate::ToggleZoom, window, cx);
                             }))
-                            .tooltip(move |cx| {
+                            .tooltip(move |window, cx| {
                                 Tooltip::for_action(
                                     if zoomed { "Zoom Out" } else { "Zoom In" },
                                     &ToggleZoom,
+                                    window,
                                     cx,
                                 )
                             })
@@ -515,16 +523,16 @@ impl Pane {
         }
     }
 
-    fn alternate_file(&mut self, cx: &mut ViewContext<Pane>) {
+    fn alternate_file(&mut self, window: &mut Window, cx: &mut Context<Pane>) {
         let (_, alternative) = &self.alternate_file_items;
         if let Some(alternative) = alternative {
             let existing = self
                 .items()
                 .find_position(|item| item.item_id() == alternative.id());
             if let Some((ix, _)) = existing {
-                self.activate_item(ix, true, true, cx);
+                self.activate_item(ix, true, true, window, cx);
             } else if let Some(upgraded) = alternative.upgrade() {
-                self.add_item(upgraded, true, true, None, cx);
+                self.add_item(upgraded, true, true, None, window, cx);
             }
         }
     }
@@ -546,20 +554,20 @@ impl Pane {
         }
     }
 
-    pub fn has_focus(&self, cx: &WindowContext) -> bool {
+    pub fn has_focus(&self, window: &Window, cx: &App) -> bool {
         // We not only check whether our focus handle contains focus, but also
         // whether the active item might have focus, because we might have just activated an item
         // that hasn't rendered yet.
         // Before the next render, we might transfer focus
         // to the item, and `focus_handle.contains_focus` returns false because the `active_item`
         // is not hooked up to us in the dispatch tree.
-        self.focus_handle.contains_focused(cx)
-            || self
-                .active_item()
-                .map_or(false, |item| item.focus_handle(cx).contains_focused(cx))
+        self.focus_handle.contains_focused(window, cx)
+            || self.active_item().map_or(false, |item| {
+                item.item_focus_handle(cx).contains_focused(window, cx)
+            })
     }
 
-    fn focus_in(&mut self, cx: &mut ViewContext<Self>) {
+    fn focus_in(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if !self.was_focused {
             self.was_focused = true;
             cx.emit(Event::Focus);
@@ -567,25 +575,25 @@ impl Pane {
         }
 
         self.toolbar.update(cx, |toolbar, cx| {
-            toolbar.focus_changed(true, cx);
+            toolbar.focus_changed(true, window, cx);
         });
 
         if let Some(active_item) = self.active_item() {
-            if self.focus_handle.is_focused(cx) {
+            if self.focus_handle.is_focused(window) {
                 // Pane was focused directly. We need to either focus a view inside the active item,
                 // or focus the active item itself
                 if let Some(weak_last_focus_handle) =
                     self.last_focus_handle_by_item.get(&active_item.item_id())
                 {
                     if let Some(focus_handle) = weak_last_focus_handle.upgrade() {
-                        focus_handle.focus(cx);
+                        focus_handle.focus(window);
                         return;
                     }
                 }
 
-                active_item.focus_handle(cx).focus(cx);
-            } else if let Some(focused) = cx.focused() {
-                if !self.context_menu_focused(cx) {
+                active_item.item_focus_handle(cx).focus(window);
+            } else if let Some(focused) = window.focused(cx) {
+                if !self.context_menu_focused(window, cx) {
                     self.last_focus_handle_by_item
                         .insert(active_item.item_id(), focused.downgrade());
                 }
@@ -593,30 +601,30 @@ impl Pane {
         }
     }
 
-    pub fn context_menu_focused(&self, cx: &mut ViewContext<Self>) -> bool {
-        self.new_item_context_menu_handle.is_focused(cx)
-            || self.split_item_context_menu_handle.is_focused(cx)
+    pub fn context_menu_focused(&self, window: &mut Window, cx: &mut Context<Self>) -> bool {
+        self.new_item_context_menu_handle.is_focused(window, cx)
+            || self.split_item_context_menu_handle.is_focused(window, cx)
     }
 
-    fn focus_out(&mut self, _event: FocusOutEvent, cx: &mut ViewContext<Self>) {
+    fn focus_out(&mut self, _event: FocusOutEvent, window: &mut Window, cx: &mut Context<Self>) {
         self.was_focused = false;
         self.toolbar.update(cx, |toolbar, cx| {
-            toolbar.focus_changed(false, cx);
+            toolbar.focus_changed(false, window, cx);
         });
         cx.notify();
     }
 
     fn project_events(
-        this: &mut Pane,
-        _project: Model<Project>,
+        &mut self,
+        _project: Entity<Project>,
         event: &project::Event,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match event {
             project::Event::DiskBasedDiagnosticsFinished { .. }
             | project::Event::DiagnosticsUpdated { .. } => {
                 if ItemSettings::get_global(cx).show_diagnostics != ShowDiagnostics::Off {
-                    this.update_diagnostics(cx);
+                    self.update_diagnostics(cx);
                     cx.notify();
                 }
             }
@@ -624,7 +632,7 @@ impl Pane {
         }
     }
 
-    fn update_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
+    fn update_diagnostics(&mut self, cx: &mut Context<Self>) {
         let Some(project) = self.project.upgrade() else {
             return;
         };
@@ -650,7 +658,7 @@ impl Pane {
         }
     }
 
-    fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
+    fn settings_changed(&mut self, cx: &mut Context<Self>) {
         if let Some(display_nav_history_buttons) = self.display_nav_history_buttons.as_mut() {
             *display_nav_history_buttons = TabBarSettings::get_global(cx).show_nav_history_buttons;
         }
@@ -671,7 +679,7 @@ impl Pane {
 
     pub fn set_should_display_tab_bar<F>(&mut self, should_display_tab_bar: F)
     where
-        F: 'static + Fn(&ViewContext<Pane>) -> bool,
+        F: 'static + Fn(&Window, &mut Context<Pane>) -> bool,
     {
         self.should_display_tab_bar = Rc::new(should_display_tab_bar);
     }
@@ -679,37 +687,42 @@ impl Pane {
     pub fn set_can_split(
         &mut self,
         can_split_predicate: Option<
-            Arc<dyn Fn(&mut Self, &dyn Any, &mut ViewContext<Self>) -> bool + 'static>,
+            Arc<dyn Fn(&mut Self, &dyn Any, &mut Window, &mut Context<Self>) -> bool + 'static>,
         >,
     ) {
         self.can_split_predicate = can_split_predicate;
     }
 
-    pub fn set_can_navigate(&mut self, can_navigate: bool, cx: &mut ViewContext<Self>) {
+    pub fn set_can_navigate(&mut self, can_navigate: bool, cx: &mut Context<Self>) {
         self.toolbar.update(cx, |toolbar, cx| {
             toolbar.set_can_navigate(can_navigate, cx);
         });
         cx.notify();
     }
 
-    pub fn set_render_tab_bar_buttons<F>(&mut self, cx: &mut ViewContext<Self>, render: F)
+    pub fn set_render_tab_bar_buttons<F>(&mut self, cx: &mut Context<Self>, render: F)
     where
         F: 'static
-            + Fn(&mut Pane, &mut ViewContext<Pane>) -> (Option<AnyElement>, Option<AnyElement>),
+            + Fn(
+                &mut Pane,
+                &mut Window,
+                &mut Context<Pane>,
+            ) -> (Option<AnyElement>, Option<AnyElement>),
     {
         self.render_tab_bar_buttons = Rc::new(render);
         cx.notify();
     }
 
-    pub fn set_custom_drop_handle<F>(&mut self, cx: &mut ViewContext<Self>, handle: F)
+    pub fn set_custom_drop_handle<F>(&mut self, cx: &mut Context<Self>, handle: F)
     where
-        F: 'static + Fn(&mut Pane, &dyn Any, &mut ViewContext<Pane>) -> ControlFlow<(), ()>,
+        F: 'static
+            + Fn(&mut Pane, &dyn Any, &mut Window, &mut Context<Pane>) -> ControlFlow<(), ()>,
     {
         self.custom_drop_handle = Some(Arc::new(handle));
         cx.notify();
     }
 
-    pub fn nav_history_for_item<T: Item>(&self, item: &View<T>) -> ItemNavHistory {
+    pub fn nav_history_for_item<T: Item>(&self, item: &Entity<T>) -> ItemNavHistory {
         ItemNavHistory {
             history: self.nav_history.clone(),
             item: Arc::new(item.downgrade()),
@@ -741,37 +754,31 @@ impl Pane {
         !self.nav_history.0.lock().forward_stack.is_empty()
     }
 
-    fn navigate_backward(&mut self, cx: &mut ViewContext<Self>) {
+    fn navigate_backward(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(workspace) = self.workspace.upgrade() {
-            let pane = cx.view().downgrade();
-            cx.window_context().defer(move |cx| {
+            let pane = cx.model().downgrade();
+            window.defer(cx, move |window, cx| {
                 workspace.update(cx, |workspace, cx| {
-                    workspace.go_back(pane, cx).detach_and_log_err(cx)
+                    workspace.go_back(pane, window, cx).detach_and_log_err(cx)
                 })
             })
         }
     }
 
-    fn navigate_forward(&mut self, cx: &mut ViewContext<Self>) {
+    fn navigate_forward(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(workspace) = self.workspace.upgrade() {
-            let pane = cx.view().downgrade();
-            cx.window_context().defer(move |cx| {
+            let pane = cx.model().downgrade();
+            window.defer(cx, move |window, cx| {
                 workspace.update(cx, |workspace, cx| {
-                    workspace.go_forward(pane, cx).detach_and_log_err(cx)
+                    workspace
+                        .go_forward(pane, window, cx)
+                        .detach_and_log_err(cx)
                 })
             })
         }
     }
 
-    fn join_into_next(&mut self, cx: &mut ViewContext<Self>) {
-        cx.emit(Event::JoinIntoNext);
-    }
-
-    fn join_all(&mut self, cx: &mut ViewContext<Self>) {
-        cx.emit(Event::JoinAll);
-    }
-
-    fn history_updated(&mut self, cx: &mut ViewContext<Self>) {
+    fn history_updated(&mut self, cx: &mut Context<Self>) {
         self.toolbar.update(cx, |_, cx| cx.notify());
     }
 
@@ -801,7 +808,7 @@ impl Pane {
 
     /// Marks the item with the given ID as the preview item.
     /// This will be ignored if the global setting `preview_tabs` is disabled.
-    pub fn set_preview_item_id(&mut self, item_id: Option<EntityId>, cx: &AppContext) {
+    pub fn set_preview_item_id(&mut self, item_id: Option<EntityId>, cx: &App) {
         if PreviewTabsSettings::get_global(cx).enabled {
             self.preview_item_id = item_id;
         }
@@ -815,7 +822,7 @@ impl Pane {
         self.pinned_tab_count
     }
 
-    pub fn handle_item_edit(&mut self, item_id: EntityId, cx: &AppContext) {
+    pub fn handle_item_edit(&mut self, item_id: EntityId, cx: &App) {
         if let Some(preview_item) = self.preview_item() {
             if preview_item.item_id() == item_id && !preview_item.preserve_preview(cx) {
                 self.set_preview_item_id(None, cx);
@@ -823,14 +830,16 @@ impl Pane {
         }
     }
 
+    #[allow(clippy::too_many_arguments)]
     pub(crate) fn open_item(
         &mut self,
         project_entry_id: Option<ProjectEntryId>,
         focus_item: bool,
         allow_preview: bool,
         suggested_position: Option<usize>,
-        cx: &mut ViewContext<Self>,
-        build_item: impl FnOnce(&mut ViewContext<Pane>) -> Box<dyn ItemHandle>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+        build_item: impl FnOnce(&mut Window, &mut Context<Pane>) -> Box<dyn ItemHandle>,
     ) -> Box<dyn ItemHandle> {
         let mut existing_item = None;
         if let Some(project_entry_id) = project_entry_id {
@@ -844,7 +853,6 @@ impl Pane {
                 }
             }
         }
-
         if let Some((index, existing_item)) = existing_item {
             // If the item is already open, and the item is a preview item
             // and we are not allowing items to open as preview, mark the item as persistent.
@@ -855,36 +863,45 @@ impl Pane {
                     }
                 }
             }
-
-            self.activate_item(index, focus_item, focus_item, cx);
+            self.activate_item(index, focus_item, focus_item, window, cx);
             existing_item
         } else {
             // If the item is being opened as preview and we have an existing preview tab,
             // open the new item in the position of the existing preview tab.
             let destination_index = if allow_preview {
-                self.close_current_preview_item(cx)
+                self.close_current_preview_item(window, cx)
             } else {
                 suggested_position
             };
 
-            let new_item = build_item(cx);
+            let new_item = build_item(window, cx);
 
             if allow_preview {
                 self.set_preview_item_id(Some(new_item.item_id()), cx);
             }
-
-            self.add_item(new_item.clone(), true, focus_item, destination_index, cx);
+            self.add_item(
+                new_item.clone(),
+                true,
+                focus_item,
+                destination_index,
+                window,
+                cx,
+            );
 
             new_item
         }
     }
 
-    pub fn close_current_preview_item(&mut self, cx: &mut ViewContext<Self>) -> Option<usize> {
+    pub fn close_current_preview_item(
+        &mut self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<usize> {
         let item_idx = self.preview_item_idx()?;
         let id = self.preview_item_id()?;
 
         let prev_active_item_index = self.active_item_index;
-        self.remove_item(id, false, false, cx);
+        self.remove_item(id, false, false, window, cx);
         self.active_item_index = prev_active_item_index;
 
         if item_idx < self.items.len() {
@@ -900,9 +917,10 @@ impl Pane {
         activate_pane: bool,
         focus_item: bool,
         destination_index: Option<usize>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
-        self.close_items_over_max_tabs(cx);
+        self.close_items_over_max_tabs(window, cx);
 
         if item.is_singleton(cx) {
             if let Some(&entry_id) = item.project_entry_ids(cx).first() {
@@ -984,7 +1002,7 @@ impl Pane {
                 cx.notify();
             }
 
-            self.activate_item(insertion_index, activate_pane, focus_item, cx);
+            self.activate_item(insertion_index, activate_pane, focus_item, window, cx);
         } else {
             self.items.insert(insertion_index, item.clone());
 
@@ -994,7 +1012,7 @@ impl Pane {
                 self.active_item_index += 1;
             }
 
-            self.activate_item(insertion_index, activate_pane, focus_item, cx);
+            self.activate_item(insertion_index, activate_pane, focus_item, window, cx);
             cx.notify();
         }
 
@@ -1009,7 +1027,7 @@ impl Pane {
         self.items.iter()
     }
 
-    pub fn items_of_type<T: Render>(&self) -> impl '_ + Iterator<Item = View<T>> {
+    pub fn items_of_type<T: Render>(&self) -> impl '_ + Iterator<Item = Entity<T>> {
         self.items
             .iter()
             .filter_map(|item| item.to_any().downcast().ok())
@@ -1019,7 +1037,7 @@ impl Pane {
         self.items.get(self.active_item_index).cloned()
     }
 
-    pub fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Point<Pixels>> {
+    pub fn pixel_position_of_cursor(&self, cx: &App) -> Option<Point<Pixels>> {
         self.items
             .get(self.active_item_index)?
             .pixel_position_of_cursor(cx)
@@ -1028,7 +1046,7 @@ impl Pane {
     pub fn item_for_entry(
         &self,
         entry_id: ProjectEntryId,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<Box<dyn ItemHandle>> {
         self.items.iter().find_map(|item| {
             if item.is_singleton(cx) && (item.project_entry_ids(cx).as_slice() == [entry_id]) {
@@ -1042,7 +1060,7 @@ impl Pane {
     pub fn item_for_path(
         &self,
         project_path: ProjectPath,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<Box<dyn ItemHandle>> {
         self.items.iter().find_map(move |item| {
             if item.is_singleton(cx) && (item.project_path(cx).as_slice() == [project_path.clone()])
@@ -1066,12 +1084,12 @@ impl Pane {
         self.items.get(ix).map(|i| i.as_ref())
     }
 
-    pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext<Self>) {
+    pub fn toggle_zoom(&mut self, _: &ToggleZoom, window: &mut Window, cx: &mut Context<Self>) {
         if self.zoomed {
             cx.emit(Event::ZoomOut);
         } else if !self.items.is_empty() {
-            if !self.focus_handle.contains_focused(cx) {
-                cx.focus_self();
+            if !self.focus_handle.contains_focused(window, cx) {
+                cx.focus_self(window);
             }
             cx.emit(Event::ZoomIn);
         }
@@ -1082,20 +1100,19 @@ impl Pane {
         index: usize,
         activate_pane: bool,
         focus_item: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         use NavigationMode::{GoingBack, GoingForward};
-
         if index < self.items.len() {
             let prev_active_item_ix = mem::replace(&mut self.active_item_index, index);
             if prev_active_item_ix != self.active_item_index
                 || matches!(self.nav_history.mode(), GoingBack | GoingForward)
             {
                 if let Some(prev_item) = self.items.get(prev_active_item_ix) {
-                    prev_item.deactivated(cx);
+                    prev_item.deactivated(window, cx);
                 }
             }
-
             if let Some(newly_active_item) = self.items.get(index) {
                 self.activation_history
                     .retain(|entry| entry.entity_id != newly_active_item.item_id());
@@ -1107,11 +1124,11 @@ impl Pane {
                 });
             }
 
-            self.update_toolbar(cx);
-            self.update_status_bar(cx);
+            self.update_toolbar(window, cx);
+            self.update_status_bar(window, cx);
 
             if focus_item {
-                self.focus_active_item(cx);
+                self.focus_active_item(window, cx);
             }
 
             cx.emit(Event::ActivateItem {
@@ -1128,50 +1145,61 @@ impl Pane {
         }
     }
 
-    pub fn activate_prev_item(&mut self, activate_pane: bool, cx: &mut ViewContext<Self>) {
+    pub fn activate_prev_item(
+        &mut self,
+        activate_pane: bool,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let mut index = self.active_item_index;
         if index > 0 {
             index -= 1;
         } else if !self.items.is_empty() {
             index = self.items.len() - 1;
         }
-        self.activate_item(index, activate_pane, activate_pane, cx);
+        self.activate_item(index, activate_pane, activate_pane, window, cx);
     }
 
-    pub fn activate_next_item(&mut self, activate_pane: bool, cx: &mut ViewContext<Self>) {
+    pub fn activate_next_item(
+        &mut self,
+        activate_pane: bool,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let mut index = self.active_item_index;
         if index + 1 < self.items.len() {
             index += 1;
         } else {
             index = 0;
         }
-        self.activate_item(index, activate_pane, activate_pane, cx);
+        self.activate_item(index, activate_pane, activate_pane, window, cx);
     }
 
-    pub fn swap_item_left(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn swap_item_left(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let index = self.active_item_index;
         if index == 0 {
             return;
         }
 
         self.items.swap(index, index - 1);
-        self.activate_item(index - 1, true, true, cx);
+        self.activate_item(index - 1, true, true, window, cx);
     }
 
-    pub fn swap_item_right(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn swap_item_right(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let index = self.active_item_index;
         if index + 1 == self.items.len() {
             return;
         }
 
         self.items.swap(index, index + 1);
-        self.activate_item(index + 1, true, true, cx);
+        self.activate_item(index + 1, true, true, window, cx);
     }
 
     pub fn close_active_item(
         &mut self,
         action: &CloseActiveItem,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<Task<Result<()>>> {
         if self.items.is_empty() {
             // Close the window when there's no active items to close, if configured
@@ -1179,7 +1207,7 @@ impl Pane {
                 .when_closing_with_no_tabs
                 .should_close()
             {
-                cx.dispatch_action(Box::new(CloseWindow));
+                window.dispatch_action(Box::new(CloseWindow), cx);
             }
 
             return None;
@@ -1188,6 +1216,7 @@ impl Pane {
         Some(self.close_item_by_id(
             active_item_id,
             action.save_intent.unwrap_or(SaveIntent::Close),
+            window,
             cx,
         ))
     }
@@ -1196,15 +1225,19 @@ impl Pane {
         &mut self,
         item_id_to_close: EntityId,
         save_intent: SaveIntent,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
-        self.close_items(cx, save_intent, move |view_id| view_id == item_id_to_close)
+        self.close_items(window, cx, save_intent, move |view_id| {
+            view_id == item_id_to_close
+        })
     }
 
     pub fn close_inactive_items(
         &mut self,
         action: &CloseInactiveItems,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<Task<Result<()>>> {
         if self.items.is_empty() {
             return None;
@@ -1213,6 +1246,7 @@ impl Pane {
         let active_item_id = self.items[self.active_item_index].item_id();
         let non_closeable_items = self.get_non_closeable_item_ids(action.close_pinned);
         Some(self.close_items(
+            window,
             cx,
             action.save_intent.unwrap_or(SaveIntent::Close),
             move |item_id| item_id != active_item_id && !non_closeable_items.contains(&item_id),
@@ -1222,7 +1256,8 @@ impl Pane {
     pub fn close_clean_items(
         &mut self,
         action: &CloseCleanItems,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<Task<Result<()>>> {
         let item_ids: Vec<_> = self
             .items()
@@ -1230,22 +1265,31 @@ impl Pane {
             .map(|item| item.item_id())
             .collect();
         let non_closeable_items = self.get_non_closeable_item_ids(action.close_pinned);
-        Some(self.close_items(cx, SaveIntent::Close, move |item_id| {
-            item_ids.contains(&item_id) && !non_closeable_items.contains(&item_id)
-        }))
+        Some(
+            self.close_items(window, cx, SaveIntent::Close, move |item_id| {
+                item_ids.contains(&item_id) && !non_closeable_items.contains(&item_id)
+            }),
+        )
     }
 
     pub fn close_items_to_the_left(
         &mut self,
         action: &CloseItemsToTheLeft,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<Task<Result<()>>> {
         if self.items.is_empty() {
             return None;
         }
         let active_item_id = self.items[self.active_item_index].item_id();
         let non_closeable_items = self.get_non_closeable_item_ids(action.close_pinned);
-        Some(self.close_items_to_the_left_by_id(active_item_id, action, non_closeable_items, cx))
+        Some(self.close_items_to_the_left_by_id(
+            active_item_id,
+            action,
+            non_closeable_items,
+            window,
+            cx,
+        ))
     }
 
     pub fn close_items_to_the_left_by_id(
@@ -1253,14 +1297,15 @@ impl Pane {
         item_id: EntityId,
         action: &CloseItemsToTheLeft,
         non_closeable_items: Vec<EntityId>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let item_ids: Vec<_> = self
             .items()
             .take_while(|item| item.item_id() != item_id)
             .map(|item| item.item_id())
             .collect();
-        self.close_items(cx, SaveIntent::Close, move |item_id| {
+        self.close_items(window, cx, SaveIntent::Close, move |item_id| {
             item_ids.contains(&item_id)
                 && !action.close_pinned
                 && !non_closeable_items.contains(&item_id)
@@ -1270,14 +1315,21 @@ impl Pane {
     pub fn close_items_to_the_right(
         &mut self,
         action: &CloseItemsToTheRight,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<Task<Result<()>>> {
         if self.items.is_empty() {
             return None;
         }
         let active_item_id = self.items[self.active_item_index].item_id();
         let non_closeable_items = self.get_non_closeable_item_ids(action.close_pinned);
-        Some(self.close_items_to_the_right_by_id(active_item_id, action, non_closeable_items, cx))
+        Some(self.close_items_to_the_right_by_id(
+            active_item_id,
+            action,
+            non_closeable_items,
+            window,
+            cx,
+        ))
     }
 
     pub fn close_items_to_the_right_by_id(
@@ -1285,7 +1337,8 @@ impl Pane {
         item_id: EntityId,
         action: &CloseItemsToTheRight,
         non_closeable_items: Vec<EntityId>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
         let item_ids: Vec<_> = self
             .items()
@@ -1293,7 +1346,7 @@ impl Pane {
             .take_while(|item| item.item_id() != item_id)
             .map(|item| item.item_id())
             .collect();
-        self.close_items(cx, SaveIntent::Close, move |item_id| {
+        self.close_items(window, cx, SaveIntent::Close, move |item_id| {
             item_ids.contains(&item_id)
                 && !action.close_pinned
                 && !non_closeable_items.contains(&item_id)
@@ -1303,7 +1356,8 @@ impl Pane {
     pub fn close_all_items(
         &mut self,
         action: &CloseAllItems,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<Task<Result<()>>> {
         if self.items.is_empty() {
             return None;
@@ -1311,13 +1365,14 @@ impl Pane {
 
         let non_closeable_items = self.get_non_closeable_item_ids(action.close_pinned);
         Some(self.close_items(
+            window,
             cx,
             action.save_intent.unwrap_or(SaveIntent::Close),
             |item_id| !non_closeable_items.contains(&item_id),
         ))
     }
 
-    pub fn close_items_over_max_tabs(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn close_items_over_max_tabs(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let Some(max_tabs) = WorkspaceSettings::get_global(cx).max_tabs.map(|i| i.get()) else {
             return;
         };
@@ -1351,13 +1406,13 @@ impl Pane {
         index_list
             .iter()
             .rev()
-            .for_each(|&index| self._remove_item(index, false, false, None, cx));
+            .for_each(|&index| self._remove_item(index, false, false, None, window, cx));
     }
 
     pub(super) fn file_names_for_prompt(
         items: &mut dyn Iterator<Item = &Box<dyn ItemHandle>>,
         all_dirty_items: usize,
-        cx: &AppContext,
+        cx: &App,
     ) -> (String, String) {
         /// Quantity of item paths displayed in prompt prior to cutoff..
         const FILE_NAMES_CUTOFF_POINT: usize = 10;
@@ -1393,7 +1448,8 @@ impl Pane {
 
     pub fn close_items(
         &mut self,
-        cx: &mut ViewContext<Pane>,
+        window: &mut Window,
+        cx: &mut Context<Pane>,
         mut save_intent: SaveIntent,
         should_close: impl Fn(EntityId) -> bool,
     ) -> Task<Result<()>> {
@@ -1426,16 +1482,17 @@ impl Pane {
         });
 
         let workspace = self.workspace.clone();
-        cx.spawn(|pane, mut cx| async move {
+        cx.spawn_in(window, |pane, mut cx| async move {
             if save_intent == SaveIntent::Close && dirty_items.len() > 1 {
-                let answer = pane.update(&mut cx, |_, cx| {
+                let answer = pane.update_in(&mut cx, |_, window, cx| {
                     let (prompt, detail) =
                         Self::file_names_for_prompt(&mut dirty_items.iter(), dirty_items.len(), cx);
-                    cx.prompt(
+                    window.prompt(
                         PromptLevel::Warning,
                         &prompt,
                         Some(&detail),
                         &["Save all", "Discard all", "Cancel"],
+                        cx,
                     )
                 })?;
                 match answer.await {
@@ -1484,7 +1541,7 @@ impl Pane {
                     .any(|id| saved_project_items_ids.insert(*id))
                     // Always propose to save singleton files without any project paths: those cannot be saved via multibuffer, as require a file path selection modal.
                     || cx
-                        .update(|cx| {
+                        .update(|_window, cx| {
                             item_to_close.can_save(cx) && item_to_close.is_dirty(cx)
                                 && item_to_close.is_singleton(cx)
                                 && item_to_close.project_path(cx).is_none()
@@ -1506,8 +1563,8 @@ impl Pane {
                 }
 
                 // Remove the item from the pane.
-                pane.update(&mut cx, |pane, cx| {
-                    pane.remove_item(item_to_close.item_id(), false, true, cx);
+                pane.update_in(&mut cx, |pane, window, cx| {
+                    pane.remove_item(item_to_close.item_id(), false, true, window, cx);
                 })
                 .ok();
             }
@@ -1522,26 +1579,36 @@ impl Pane {
         item_id: EntityId,
         activate_pane: bool,
         close_pane_if_empty: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         let Some(item_index) = self.index_for_item_id(item_id) else {
             return;
         };
-        self._remove_item(item_index, activate_pane, close_pane_if_empty, None, cx)
+        self._remove_item(
+            item_index,
+            activate_pane,
+            close_pane_if_empty,
+            None,
+            window,
+            cx,
+        )
     }
 
     pub fn remove_item_and_focus_on_pane(
         &mut self,
         item_index: usize,
         activate_pane: bool,
-        focus_on_pane_if_closed: View<Pane>,
-        cx: &mut ViewContext<Self>,
+        focus_on_pane_if_closed: Entity<Pane>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         self._remove_item(
             item_index,
             activate_pane,
             true,
             Some(focus_on_pane_if_closed),
+            window,
             cx,
         )
     }

crates/workspace/src/pane_group.rs 🔗

@@ -8,8 +8,8 @@ use call::{ActiveCall, ParticipantLocation};
 use client::proto::PeerId;
 use collections::HashMap;
 use gpui::{
-    point, size, Along, AnyView, AnyWeakView, Axis, Bounds, IntoElement, Model, MouseButton,
-    Pixels, Point, StyleRefinement, View, ViewContext,
+    point, size, Along, AnyView, AnyWeakView, Axis, Bounds, Context, Entity, IntoElement,
+    MouseButton, Pixels, Point, StyleRefinement, Window,
 };
 use parking_lot::Mutex;
 use project::Project;
@@ -36,7 +36,7 @@ impl PaneGroup {
         Self { root }
     }
 
-    pub fn new(pane: View<Pane>) -> Self {
+    pub fn new(pane: Entity<Pane>) -> Self {
         Self {
             root: Member::Pane(pane),
         }
@@ -44,8 +44,8 @@ impl PaneGroup {
 
     pub fn split(
         &mut self,
-        old_pane: &View<Pane>,
-        new_pane: &View<Pane>,
+        old_pane: &Entity<Pane>,
+        new_pane: &Entity<Pane>,
         direction: SplitDirection,
     ) -> Result<()> {
         match &mut self.root {
@@ -61,14 +61,14 @@ impl PaneGroup {
         }
     }
 
-    pub fn bounding_box_for_pane(&self, pane: &View<Pane>) -> Option<Bounds<Pixels>> {
+    pub fn bounding_box_for_pane(&self, pane: &Entity<Pane>) -> Option<Bounds<Pixels>> {
         match &self.root {
             Member::Pane(_) => None,
             Member::Axis(axis) => axis.bounding_box_for_pane(pane),
         }
     }
 
-    pub fn pane_at_pixel_position(&self, coordinate: Point<Pixels>) -> Option<&View<Pane>> {
+    pub fn pane_at_pixel_position(&self, coordinate: Point<Pixels>) -> Option<&Entity<Pane>> {
         match &self.root {
             Member::Pane(pane) => Some(pane),
             Member::Axis(axis) => axis.pane_at_pixel_position(coordinate),
@@ -79,7 +79,7 @@ impl PaneGroup {
     /// - Ok(true) if it found and removed a pane
     /// - Ok(false) if it found but did not remove the pane
     /// - Err(_) if it did not find the pane
-    pub fn remove(&mut self, pane: &View<Pane>) -> Result<bool> {
+    pub fn remove(&mut self, pane: &Entity<Pane>) -> Result<bool> {
         match &mut self.root {
             Member::Pane(_) => Ok(false),
             Member::Axis(axis) => {
@@ -93,7 +93,7 @@ impl PaneGroup {
 
     pub fn resize(
         &mut self,
-        pane: &View<Pane>,
+        pane: &Entity<Pane>,
         direction: Axis,
         amount: Pixels,
         bounds: &Bounds<Pixels>,
@@ -115,7 +115,7 @@ impl PaneGroup {
         };
     }
 
-    pub fn swap(&mut self, from: &View<Pane>, to: &View<Pane>) {
+    pub fn swap(&mut self, from: &Entity<Pane>, to: &Entity<Pane>) {
         match &mut self.root {
             Member::Pane(_) => {}
             Member::Axis(axis) => axis.swap(from, to),
@@ -125,13 +125,14 @@ impl PaneGroup {
     #[allow(clippy::too_many_arguments)]
     pub fn render(
         &self,
-        project: &Model<Project>,
+        project: &Entity<Project>,
         follower_states: &HashMap<PeerId, FollowerState>,
-        active_call: Option<&Model<ActiveCall>>,
-        active_pane: &View<Pane>,
+        active_call: Option<&Entity<ActiveCall>>,
+        active_pane: &Entity<Pane>,
         zoomed: Option<&AnyWeakView>,
         app_state: &Arc<AppState>,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) -> impl IntoElement {
         self.root.render(
             project,
@@ -141,26 +142,27 @@ impl PaneGroup {
             active_pane,
             zoomed,
             app_state,
+            window,
             cx,
         )
     }
 
-    pub fn panes(&self) -> Vec<&View<Pane>> {
+    pub fn panes(&self) -> Vec<&Entity<Pane>> {
         let mut panes = Vec::new();
         self.root.collect_panes(&mut panes);
         panes
     }
 
-    pub fn first_pane(&self) -> View<Pane> {
+    pub fn first_pane(&self) -> Entity<Pane> {
         self.root.first_pane()
     }
 
     pub fn find_pane_in_direction(
         &mut self,
-        active_pane: &View<Pane>,
+        active_pane: &Entity<Pane>,
         direction: SplitDirection,
-        cx: &WindowContext,
-    ) -> Option<&View<Pane>> {
+        cx: &App,
+    ) -> Option<&Entity<Pane>> {
         let bounding_box = self.bounding_box_for_pane(active_pane)?;
         let cursor = active_pane.read(cx).pixel_position_of_cursor(cx);
         let center = match cursor {
@@ -191,11 +193,11 @@ impl PaneGroup {
 #[derive(Debug, Clone)]
 pub enum Member {
     Axis(PaneAxis),
-    Pane(View<Pane>),
+    Pane(Entity<Pane>),
 }
 
 impl Member {
-    fn new_axis(old_pane: View<Pane>, new_pane: View<Pane>, direction: SplitDirection) -> Self {
+    fn new_axis(old_pane: Entity<Pane>, new_pane: Entity<Pane>, direction: SplitDirection) -> Self {
         use Axis::*;
         use SplitDirection::*;
 
@@ -212,14 +214,14 @@ impl Member {
         Member::Axis(PaneAxis::new(axis, members))
     }
 
-    fn contains(&self, needle: &View<Pane>) -> bool {
+    fn contains(&self, needle: &Entity<Pane>) -> bool {
         match self {
             Member::Axis(axis) => axis.members.iter().any(|member| member.contains(needle)),
             Member::Pane(pane) => pane == needle,
         }
     }
 
-    fn first_pane(&self) -> View<Pane> {
+    fn first_pane(&self) -> Entity<Pane> {
         match self {
             Member::Axis(axis) => axis.members[0].first_pane(),
             Member::Pane(pane) => pane.clone(),
@@ -229,14 +231,15 @@ impl Member {
     #[allow(clippy::too_many_arguments)]
     pub fn render(
         &self,
-        project: &Model<Project>,
+        project: &Entity<Project>,
         basis: usize,
         follower_states: &HashMap<PeerId, FollowerState>,
-        active_call: Option<&Model<ActiveCall>>,
-        active_pane: &View<Pane>,
+        active_call: Option<&Entity<ActiveCall>>,
+        active_pane: &Entity<Pane>,
         zoomed: Option<&AnyWeakView>,
         app_state: &Arc<AppState>,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) -> impl IntoElement {
         match self {
             Member::Pane(pane) => {
@@ -349,7 +352,7 @@ impl Member {
                                     |this, (leader_project_id, leader_user_id)| {
                                         this.cursor_pointer().on_mouse_down(
                                             MouseButton::Left,
-                                            cx.listener(move |this, _, cx| {
+                                            cx.listener(move |this, _, _, cx| {
                                                 crate::join_in_room_project(
                                                     leader_project_id,
                                                     leader_user_id,
@@ -374,13 +377,14 @@ impl Member {
                     active_pane,
                     zoomed,
                     app_state,
+                    window,
                     cx,
                 )
                 .into_any(),
         }
     }
 
-    fn collect_panes<'a>(&'a self, panes: &mut Vec<&'a View<Pane>>) {
+    fn collect_panes<'a>(&'a self, panes: &mut Vec<&'a Entity<Pane>>) {
         match self {
             Member::Axis(axis) => {
                 for member in &axis.members {
@@ -428,8 +432,8 @@ impl PaneAxis {
 
     fn split(
         &mut self,
-        old_pane: &View<Pane>,
-        new_pane: &View<Pane>,
+        old_pane: &Entity<Pane>,
+        new_pane: &Entity<Pane>,
         direction: SplitDirection,
     ) -> Result<()> {
         for (mut idx, member) in self.members.iter_mut().enumerate() {
@@ -460,7 +464,7 @@ impl PaneAxis {
         Err(anyhow!("Pane not found"))
     }
 
-    fn remove(&mut self, pane_to_remove: &View<Pane>) -> Result<Option<Member>> {
+    fn remove(&mut self, pane_to_remove: &Entity<Pane>) -> Result<Option<Member>> {
         let mut found_pane = false;
         let mut remove_member = None;
         for (idx, member) in self.members.iter_mut().enumerate() {
@@ -513,7 +517,7 @@ impl PaneAxis {
 
     fn resize(
         &mut self,
-        pane: &View<Pane>,
+        pane: &Entity<Pane>,
         axis: Axis,
         amount: Pixels,
         bounds: &Bounds<Pixels>,
@@ -621,7 +625,7 @@ impl PaneAxis {
         Some(true)
     }
 
-    fn swap(&mut self, from: &View<Pane>, to: &View<Pane>) {
+    fn swap(&mut self, from: &Entity<Pane>, to: &Entity<Pane>) {
         for member in self.members.iter_mut() {
             match member {
                 Member::Axis(axis) => axis.swap(from, to),
@@ -636,7 +640,7 @@ impl PaneAxis {
         }
     }
 
-    fn bounding_box_for_pane(&self, pane: &View<Pane>) -> Option<Bounds<Pixels>> {
+    fn bounding_box_for_pane(&self, pane: &Entity<Pane>) -> Option<Bounds<Pixels>> {
         debug_assert!(self.members.len() == self.bounding_boxes.lock().len());
 
         for (idx, member) in self.members.iter().enumerate() {
@@ -656,7 +660,7 @@ impl PaneAxis {
         None
     }
 
-    fn pane_at_pixel_position(&self, coordinate: Point<Pixels>) -> Option<&View<Pane>> {
+    fn pane_at_pixel_position(&self, coordinate: Point<Pixels>) -> Option<&Entity<Pane>> {
         debug_assert!(self.members.len() == self.bounding_boxes.lock().len());
 
         let bounding_boxes = self.bounding_boxes.lock();
@@ -677,14 +681,15 @@ impl PaneAxis {
     #[allow(clippy::too_many_arguments)]
     fn render(
         &self,
-        project: &Model<Project>,
+        project: &Entity<Project>,
         basis: usize,
         follower_states: &HashMap<PeerId, FollowerState>,
-        active_call: Option<&Model<ActiveCall>>,
-        active_pane: &View<Pane>,
+        active_call: Option<&Entity<ActiveCall>>,
+        active_pane: &Entity<Pane>,
         zoomed: Option<&AnyWeakView>,
         app_state: &Arc<AppState>,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) -> gpui::AnyElement {
         debug_assert!(self.members.len() == self.flexes.lock().len());
         let mut active_pane_ix = None;
@@ -694,7 +699,7 @@ impl PaneAxis {
             basis,
             self.flexes.clone(),
             self.bounding_boxes.clone(),
-            cx.view().downgrade(),
+            cx.model().downgrade(),
         )
         .children(self.members.iter().enumerate().map(|(ix, member)| {
             if member.contains(active_pane) {
@@ -709,6 +714,7 @@ impl PaneAxis {
                     active_pane,
                     zoomed,
                     app_state,
+                    window,
                     cx,
                 )
                 .into_any_element()
@@ -742,14 +748,14 @@ impl SplitDirection {
         [Self::Up, Self::Down, Self::Left, Self::Right]
     }
 
-    pub fn vertical(cx: &WindowContext) -> Self {
+    pub fn vertical(cx: &mut App) -> Self {
         match WorkspaceSettings::get_global(cx).pane_split_direction_vertical {
             PaneSplitDirectionVertical::Left => SplitDirection::Left,
             PaneSplitDirectionVertical::Right => SplitDirection::Right,
         }
     }
 
-    pub fn horizontal(cx: &WindowContext) -> Self {
+    pub fn horizontal(cx: &mut App) -> Self {
         match WorkspaceSettings::get_global(cx).pane_split_direction_horizontal {
             PaneSplitDirectionHorizontal::Down => SplitDirection::Down,
             PaneSplitDirectionHorizontal::Up => SplitDirection::Up,
@@ -814,9 +820,9 @@ mod element {
     use std::{cell::RefCell, iter, rc::Rc, sync::Arc};
 
     use gpui::{
-        px, relative, size, Along, AnyElement, Axis, Bounds, Element, GlobalElementId, IntoElement,
-        MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Size, Style,
-        WeakView, WindowContext,
+        px, relative, size, Along, AnyElement, App, Axis, Bounds, Element, GlobalElementId,
+        IntoElement, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point,
+        Size, Style, WeakEntity, Window,
     };
     use gpui::{CursorStyle, Hitbox};
     use parking_lot::Mutex;
@@ -838,7 +844,7 @@ mod element {
         basis: usize,
         flexes: Arc<Mutex<Vec<f32>>>,
         bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
     ) -> PaneAxisElement {
         PaneAxisElement {
             axis,
@@ -858,7 +864,7 @@ mod element {
         bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
         children: SmallVec<[AnyElement; 2]>,
         active_pane_ix: Option<usize>,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
     }
 
     pub struct PaneAxisLayout {
@@ -891,8 +897,9 @@ mod element {
             axis: Axis,
             child_start: Point<Pixels>,
             container_size: Size<Pixels>,
-            workspace: WeakView<Workspace>,
-            cx: &mut WindowContext,
+            workspace: WeakEntity<Workspace>,
+            window: &mut Window,
+            cx: &mut App,
         ) {
             let min_size = match axis {
                 Axis::Horizontal => px(HORIZONTAL_MIN_SIZE),
@@ -966,17 +973,18 @@ mod element {
             }
 
             workspace
-                .update(cx, |this, cx| this.serialize_workspace(cx))
+                .update(cx, |this, cx| this.serialize_workspace(window, cx))
                 .log_err();
             cx.stop_propagation();
-            cx.refresh();
+            window.refresh();
         }
 
         #[allow(clippy::too_many_arguments)]
         fn layout_handle(
             axis: Axis,
             pane_bounds: Bounds<Pixels>,
-            cx: &mut WindowContext,
+            window: &mut Window,
+            _cx: &mut App,
         ) -> PaneAxisHandleLayout {
             let handle_bounds = Bounds {
                 origin: pane_bounds.origin.apply_along(axis, |origin| {
@@ -994,7 +1002,7 @@ mod element {
             };
 
             PaneAxisHandleLayout {
-                hitbox: cx.insert_hitbox(handle_bounds, true),
+                hitbox: window.insert_hitbox(handle_bounds, true),
                 divider_bounds,
             }
         }
@@ -1019,7 +1027,8 @@ mod element {
         fn request_layout(
             &mut self,
             _global_id: Option<&GlobalElementId>,
-            cx: &mut WindowContext,
+            window: &mut Window,
+            cx: &mut App,
         ) -> (gpui::LayoutId, Self::RequestLayoutState) {
             let style = Style {
                 flex_grow: 1.,
@@ -1028,7 +1037,7 @@ mod element {
                 size: size(relative(1.).into(), relative(1.).into()),
                 ..Style::default()
             };
-            (cx.request_layout(style, None), ())
+            (window.request_layout(style, None, cx), ())
         }
 
         fn prepaint(
@@ -1036,9 +1045,10 @@ mod element {
             global_id: Option<&GlobalElementId>,
             bounds: Bounds<Pixels>,
             _state: &mut Self::RequestLayoutState,
-            cx: &mut WindowContext,
+            window: &mut Window,
+            cx: &mut App,
         ) -> PaneAxisLayout {
-            let dragged_handle = cx.with_element_state::<Rc<RefCell<Option<usize>>>, _>(
+            let dragged_handle = window.with_element_state::<Rc<RefCell<Option<usize>>>, _>(
                 global_id.unwrap(),
                 |state, _cx| {
                     let state = state.unwrap_or_else(|| Rc::new(RefCell::new(None)));
@@ -1093,8 +1103,8 @@ mod element {
                 };
 
                 bounding_boxes.push(Some(child_bounds));
-                child.layout_as_root(child_size.into(), cx);
-                child.prepaint_at(origin, cx);
+                child.layout_as_root(child_size.into(), window, cx);
+                child.prepaint_at(origin, window, cx);
 
                 origin = origin.apply_along(self.axis, |val| val + child_size.along(self.axis));
                 layout.children.push(PaneAxisChildLayout {
@@ -1106,8 +1116,12 @@ mod element {
 
             for (ix, child_layout) in layout.children.iter_mut().enumerate() {
                 if active_pane_magnification.is_none() && ix < len - 1 {
-                    child_layout.handle =
-                        Some(Self::layout_handle(self.axis, child_layout.bounds, cx));
+                    child_layout.handle = Some(Self::layout_handle(
+                        self.axis,
+                        child_layout.bounds,
+                        window,
+                        cx,
+                    ));
                 }
             }
 
@@ -1120,10 +1134,11 @@ mod element {
             bounds: gpui::Bounds<ui::prelude::Pixels>,
             _: &mut Self::RequestLayoutState,
             layout: &mut Self::PrepaintState,
-            cx: &mut WindowContext,
+            window: &mut Window,
+            cx: &mut App,
         ) {
             for child in &mut layout.children {
-                child.element.paint(cx);
+                child.element.paint(window, cx);
             }
 
             let overlay_opacity = WorkspaceSettings::get(None, cx)
@@ -1158,12 +1173,12 @@ mod element {
                     };
 
                     if overlay_opacity.is_some() && self.active_pane_ix != Some(ix) {
-                        cx.paint_quad(gpui::fill(overlay_bounds, overlay_background));
+                        window.paint_quad(gpui::fill(overlay_bounds, overlay_background));
                     }
 
                     if let Some(border) = overlay_border {
                         if self.active_pane_ix == Some(ix) {
-                            cx.paint_quad(gpui::quad(
+                            window.paint_quad(gpui::quad(
                                 overlay_bounds,
                                 0.,
                                 gpui::transparent_black(),
@@ -1179,40 +1194,40 @@ mod element {
                         Axis::Vertical => CursorStyle::ResizeRow,
                         Axis::Horizontal => CursorStyle::ResizeColumn,
                     };
-                    cx.set_cursor_style(cursor_style, &handle.hitbox);
-                    cx.paint_quad(gpui::fill(
+                    window.set_cursor_style(cursor_style, &handle.hitbox);
+                    window.paint_quad(gpui::fill(
                         handle.divider_bounds,
                         cx.theme().colors().pane_group_border,
                     ));
 
-                    cx.on_mouse_event({
+                    window.on_mouse_event({
                         let dragged_handle = layout.dragged_handle.clone();
                         let flexes = self.flexes.clone();
                         let workspace = self.workspace.clone();
                         let handle_hitbox = handle.hitbox.clone();
-                        move |e: &MouseDownEvent, phase, cx| {
-                            if phase.bubble() && handle_hitbox.is_hovered(cx) {
+                        move |e: &MouseDownEvent, phase, window, cx| {
+                            if phase.bubble() && handle_hitbox.is_hovered(window) {
                                 dragged_handle.replace(Some(ix));
                                 if e.click_count >= 2 {
                                     let mut borrow = flexes.lock();
                                     *borrow = vec![1.; borrow.len()];
                                     workspace
-                                        .update(cx, |this, cx| this.serialize_workspace(cx))
+                                        .update(cx, |this, cx| this.serialize_workspace(window, cx))
                                         .log_err();
 
-                                    cx.refresh();
+                                    window.refresh();
                                 }
                                 cx.stop_propagation();
                             }
                         }
                     });
-                    cx.on_mouse_event({
+                    window.on_mouse_event({
                         let workspace = self.workspace.clone();
                         let dragged_handle = layout.dragged_handle.clone();
                         let flexes = self.flexes.clone();
                         let child_bounds = child.bounds;
                         let axis = self.axis;
-                        move |e: &MouseMoveEvent, phase, cx| {
+                        move |e: &MouseMoveEvent, phase, window, cx| {
                             let dragged_handle = dragged_handle.borrow();
                             if phase.bubble() && *dragged_handle == Some(ix) {
                                 Self::compute_resize(
@@ -1223,6 +1238,7 @@ mod element {
                                     child_bounds.origin,
                                     bounds.size,
                                     workspace.clone(),
+                                    window,
                                     cx,
                                 )
                             }
@@ -1231,9 +1247,9 @@ mod element {
                 }
             }
 
-            cx.on_mouse_event({
+            window.on_mouse_event({
                 let dragged_handle = layout.dragged_handle.clone();
-                move |_: &MouseUpEvent, phase, _cx| {
+                move |_: &MouseUpEvent, phase, _window, _cx| {
                     if phase.bubble() {
                         dragged_handle.replace(None);
                     }

crates/workspace/src/persistence/model.rs 🔗

@@ -2,13 +2,13 @@ use super::{SerializedAxis, SerializedWindowBounds};
 use crate::{
     item::ItemHandle, Member, Pane, PaneAxis, SerializableItemRegistry, Workspace, WorkspaceId,
 };
-use anyhow::{Context, Result};
+use anyhow::{Context as _, Result};
 use async_recursion::async_recursion;
 use db::sqlez::{
     bindable::{Bind, Column, StaticColumnCount},
     statement::Statement,
 };
-use gpui::{AsyncWindowContext, Model, View, WeakView};
+use gpui::{AsyncWindowContext, Entity, WeakEntity};
 use itertools::Itertools as _;
 use project::Project;
 use remote::ssh_session::SshProjectId;
@@ -353,11 +353,15 @@ impl SerializedPaneGroup {
     #[async_recursion(?Send)]
     pub(crate) async fn deserialize(
         self,
-        project: &Model<Project>,
+        project: &Entity<Project>,
         workspace_id: WorkspaceId,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         cx: &mut AsyncWindowContext,
-    ) -> Option<(Member, Option<View<Pane>>, Vec<Option<Box<dyn ItemHandle>>>)> {
+    ) -> Option<(
+        Member,
+        Option<Entity<Pane>>,
+        Vec<Option<Box<dyn ItemHandle>>>,
+    )> {
         match self {
             SerializedPaneGroup::Group {
                 axis,
@@ -394,7 +398,9 @@ impl SerializedPaneGroup {
             }
             SerializedPaneGroup::Pane(serialized_pane) => {
                 let pane = workspace
-                    .update(cx, |workspace, cx| workspace.add_pane(cx).downgrade())
+                    .update_in(cx, |workspace, window, cx| {
+                        workspace.add_pane(window, cx).downgrade()
+                    })
                     .log_err()?;
                 let active = serialized_pane.active;
                 let new_items = serialized_pane
@@ -412,8 +418,8 @@ impl SerializedPaneGroup {
                 } else {
                     let pane = pane.upgrade()?;
                     workspace
-                        .update(cx, |workspace, cx| {
-                            workspace.force_remove_pane(&pane, &None, cx)
+                        .update_in(cx, |workspace, window, cx| {
+                            workspace.force_remove_pane(&pane, &None, window, cx)
                         })
                         .log_err()?;
                     None
@@ -441,10 +447,10 @@ impl SerializedPane {
 
     pub async fn deserialize_to(
         &self,
-        project: &Model<Project>,
-        pane: &WeakView<Pane>,
+        project: &Entity<Project>,
+        pane: &WeakEntity<Pane>,
         workspace_id: WorkspaceId,
-        workspace: WeakView<Workspace>,
+        workspace: WeakEntity<Workspace>,
         cx: &mut AsyncWindowContext,
     ) -> Result<Vec<Option<Box<dyn ItemHandle>>>> {
         let mut item_tasks = Vec::new();
@@ -452,13 +458,14 @@ impl SerializedPane {
         let mut preview_item_index = None;
         for (index, item) in self.children.iter().enumerate() {
             let project = project.clone();
-            item_tasks.push(pane.update(cx, |_, cx| {
+            item_tasks.push(pane.update_in(cx, |_, window, cx| {
                 SerializableItemRegistry::deserialize(
                     &item.kind,
                     project,
                     workspace.clone(),
                     workspace_id,
                     item.item_id,
+                    window,
                     cx,
                 )
             })?);
@@ -476,15 +483,15 @@ impl SerializedPane {
             items.push(item_handle.clone());
 
             if let Some(item_handle) = item_handle {
-                pane.update(cx, |pane, cx| {
-                    pane.add_item(item_handle.clone(), true, true, None, cx);
+                pane.update_in(cx, |pane, window, cx| {
+                    pane.add_item(item_handle.clone(), true, true, None, window, cx);
                 })?;
             }
         }
 
         if let Some(active_item_index) = active_item_index {
-            pane.update(cx, |pane, cx| {
-                pane.activate_item(active_item_index, false, false, cx);
+            pane.update_in(cx, |pane, window, cx| {
+                pane.activate_item(active_item_index, false, false, window, cx);
             })?;
         }
 

crates/workspace/src/searchable.rs 🔗

@@ -2,8 +2,8 @@ use std::{any::Any, sync::Arc};
 
 use any_vec::AnyVec;
 use gpui::{
-    AnyView, AnyWeakView, AppContext, EventEmitter, Subscription, Task, View, ViewContext,
-    WeakView, WindowContext,
+    AnyView, AnyWeakEntity, App, Context, Entity, EventEmitter, Subscription, Task, WeakEntity,
+    Window,
 };
 use project::search::SearchQuery;
 
@@ -57,31 +57,66 @@ pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
         }
     }
 
-    fn search_bar_visibility_changed(&mut self, _visible: bool, _cx: &mut ViewContext<Self>) {}
+    fn search_bar_visibility_changed(
+        &mut self,
+        _visible: bool,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
+    ) {
+    }
 
     fn has_filtered_search_ranges(&mut self) -> bool {
         Self::supported_options().selection
     }
 
-    fn toggle_filtered_search_ranges(&mut self, _enabled: bool, _cx: &mut ViewContext<Self>) {}
+    fn toggle_filtered_search_ranges(
+        &mut self,
+        _enabled: bool,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
+    ) {
+    }
 
-    fn get_matches(&self, _: &mut WindowContext) -> Vec<Self::Match> {
+    fn get_matches(&self, _window: &mut Window, _: &mut App) -> Vec<Self::Match> {
         Vec::new()
     }
-    fn clear_matches(&mut self, cx: &mut ViewContext<Self>);
-    fn update_matches(&mut self, matches: &[Self::Match], cx: &mut ViewContext<Self>);
-    fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String;
-    fn activate_match(&mut self, index: usize, matches: &[Self::Match], cx: &mut ViewContext<Self>);
-    fn select_matches(&mut self, matches: &[Self::Match], cx: &mut ViewContext<Self>);
-    fn replace(&mut self, _: &Self::Match, _: &SearchQuery, _: &mut ViewContext<Self>);
+    fn clear_matches(&mut self, window: &mut Window, cx: &mut Context<Self>);
+    fn update_matches(
+        &mut self,
+        matches: &[Self::Match],
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    );
+    fn query_suggestion(&mut self, window: &mut Window, cx: &mut Context<Self>) -> String;
+    fn activate_match(
+        &mut self,
+        index: usize,
+        matches: &[Self::Match],
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    );
+    fn select_matches(
+        &mut self,
+        matches: &[Self::Match],
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    );
+    fn replace(
+        &mut self,
+        _: &Self::Match,
+        _: &SearchQuery,
+        _window: &mut Window,
+        _: &mut Context<Self>,
+    );
     fn replace_all(
         &mut self,
         matches: &mut dyn Iterator<Item = &Self::Match>,
         query: &SearchQuery,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         for item in matches {
-            self.replace(item, query, cx);
+            self.replace(item, query, window, cx);
         }
     }
     fn match_index_for_direction(
@@ -90,7 +125,8 @@ pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
         current_index: usize,
         direction: Direction,
         count: usize,
-        _: &mut ViewContext<Self>,
+        _window: &mut Window,
+        _: &mut Context<Self>,
     ) -> usize {
         match direction {
             Direction::Prev => {
@@ -107,12 +143,14 @@ pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
     fn find_matches(
         &mut self,
         query: Arc<SearchQuery>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Task<Vec<Self::Match>>;
     fn active_match_index(
         &mut self,
         matches: &[Self::Match],
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Option<usize>;
 }
 
@@ -122,25 +160,34 @@ pub trait SearchableItemHandle: ItemHandle {
     fn supported_options(&self) -> SearchOptions;
     fn subscribe_to_search_events(
         &self,
-        cx: &mut WindowContext,
-        handler: Box<dyn Fn(&SearchEvent, &mut WindowContext) + Send>,
+        window: &mut Window,
+        cx: &mut App,
+        handler: Box<dyn Fn(&SearchEvent, &mut Window, &mut App) + Send>,
     ) -> Subscription;
-    fn clear_matches(&self, cx: &mut WindowContext);
-    fn update_matches(&self, matches: &AnyVec<dyn Send>, cx: &mut WindowContext);
-    fn query_suggestion(&self, cx: &mut WindowContext) -> String;
-    fn activate_match(&self, index: usize, matches: &AnyVec<dyn Send>, cx: &mut WindowContext);
-    fn select_matches(&self, matches: &AnyVec<dyn Send>, cx: &mut WindowContext);
+    fn clear_matches(&self, window: &mut Window, cx: &mut App);
+    fn update_matches(&self, matches: &AnyVec<dyn Send>, window: &mut Window, cx: &mut App);
+    fn query_suggestion(&self, window: &mut Window, cx: &mut App) -> String;
+    fn activate_match(
+        &self,
+        index: usize,
+        matches: &AnyVec<dyn Send>,
+        window: &mut Window,
+        cx: &mut App,
+    );
+    fn select_matches(&self, matches: &AnyVec<dyn Send>, window: &mut Window, cx: &mut App);
     fn replace(
         &self,
         _: any_vec::element::ElementRef<'_, dyn Send>,
         _: &SearchQuery,
-        _: &mut WindowContext,
+        _window: &mut Window,
+        _: &mut App,
     );
     fn replace_all(
         &self,
         matches: &mut dyn Iterator<Item = any_vec::element::ElementRef<'_, dyn Send>>,
         query: &SearchQuery,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     );
     fn match_index_for_direction(
         &self,
@@ -148,24 +195,27 @@ pub trait SearchableItemHandle: ItemHandle {
         current_index: usize,
         direction: Direction,
         count: usize,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> usize;
     fn find_matches(
         &self,
         query: Arc<SearchQuery>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<AnyVec<dyn Send>>;
     fn active_match_index(
         &self,
         matches: &AnyVec<dyn Send>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<usize>;
-    fn search_bar_visibility_changed(&self, visible: bool, cx: &mut WindowContext);
+    fn search_bar_visibility_changed(&self, visible: bool, window: &mut Window, cx: &mut App);
 
-    fn toggle_filtered_search_ranges(&mut self, enabled: bool, cx: &mut WindowContext);
+    fn toggle_filtered_search_ranges(&mut self, enabled: bool, window: &mut Window, cx: &mut App);
 }
 
-impl<T: SearchableItem> SearchableItemHandle for View<T> {
+impl<T: SearchableItem> SearchableItemHandle for Entity<T> {
     fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle> {
         Box::new(self.downgrade())
     }
@@ -180,32 +230,45 @@ impl<T: SearchableItem> SearchableItemHandle for View<T> {
 
     fn subscribe_to_search_events(
         &self,
-        cx: &mut WindowContext,
-        handler: Box<dyn Fn(&SearchEvent, &mut WindowContext) + Send>,
+        window: &mut Window,
+        cx: &mut App,
+        handler: Box<dyn Fn(&SearchEvent, &mut Window, &mut App) + Send>,
     ) -> Subscription {
-        cx.subscribe(self, move |_, event: &SearchEvent, cx| handler(event, cx))
+        window.subscribe(self, cx, move |_, event: &SearchEvent, window, cx| {
+            handler(event, window, cx)
+        })
     }
 
-    fn clear_matches(&self, cx: &mut WindowContext) {
-        self.update(cx, |this, cx| this.clear_matches(cx));
+    fn clear_matches(&self, window: &mut Window, cx: &mut App) {
+        self.update(cx, |this, cx| this.clear_matches(window, cx));
     }
-    fn update_matches(&self, matches: &AnyVec<dyn Send>, cx: &mut WindowContext) {
+    fn update_matches(&self, matches: &AnyVec<dyn Send>, window: &mut Window, cx: &mut App) {
         let matches = matches.downcast_ref().unwrap();
-        self.update(cx, |this, cx| this.update_matches(matches.as_slice(), cx));
+        self.update(cx, |this, cx| {
+            this.update_matches(matches.as_slice(), window, cx)
+        });
     }
-    fn query_suggestion(&self, cx: &mut WindowContext) -> String {
-        self.update(cx, |this, cx| this.query_suggestion(cx))
+    fn query_suggestion(&self, window: &mut Window, cx: &mut App) -> String {
+        self.update(cx, |this, cx| this.query_suggestion(window, cx))
     }
-    fn activate_match(&self, index: usize, matches: &AnyVec<dyn Send>, cx: &mut WindowContext) {
+    fn activate_match(
+        &self,
+        index: usize,
+        matches: &AnyVec<dyn Send>,
+        window: &mut Window,
+        cx: &mut App,
+    ) {
         let matches = matches.downcast_ref().unwrap();
         self.update(cx, |this, cx| {
-            this.activate_match(index, matches.as_slice(), cx)
+            this.activate_match(index, matches.as_slice(), window, cx)
         });
     }
 
-    fn select_matches(&self, matches: &AnyVec<dyn Send>, cx: &mut WindowContext) {
+    fn select_matches(&self, matches: &AnyVec<dyn Send>, window: &mut Window, cx: &mut App) {
         let matches = matches.downcast_ref().unwrap();
-        self.update(cx, |this, cx| this.select_matches(matches.as_slice(), cx));
+        self.update(cx, |this, cx| {
+            this.select_matches(matches.as_slice(), window, cx)
+        });
     }
 
     fn match_index_for_direction(
@@ -214,20 +277,29 @@ impl<T: SearchableItem> SearchableItemHandle for View<T> {
         current_index: usize,
         direction: Direction,
         count: usize,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> usize {
         let matches = matches.downcast_ref().unwrap();
         self.update(cx, |this, cx| {
-            this.match_index_for_direction(matches.as_slice(), current_index, direction, count, cx)
+            this.match_index_for_direction(
+                matches.as_slice(),
+                current_index,
+                direction,
+                count,
+                window,
+                cx,
+            )
         })
     }
     fn find_matches(
         &self,
         query: Arc<SearchQuery>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<AnyVec<dyn Send>> {
-        let matches = self.update(cx, |this, cx| this.find_matches(query, cx));
-        cx.spawn(|_| async {
+        let matches = self.update(cx, |this, cx| this.find_matches(query, window, cx));
+        window.spawn(cx, |_| async {
             let matches = matches.await;
             let mut any_matches = AnyVec::with_capacity::<T::Match>(matches.len());
             {
@@ -242,11 +314,12 @@ impl<T: SearchableItem> SearchableItemHandle for View<T> {
     fn active_match_index(
         &self,
         matches: &AnyVec<dyn Send>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<usize> {
         let matches = matches.downcast_ref()?;
         self.update(cx, |this, cx| {
-            this.active_match_index(matches.as_slice(), cx)
+            this.active_match_index(matches.as_slice(), window, cx)
         })
     }
 
@@ -254,32 +327,39 @@ impl<T: SearchableItem> SearchableItemHandle for View<T> {
         &self,
         mat: any_vec::element::ElementRef<'_, dyn Send>,
         query: &SearchQuery,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         let mat = mat.downcast_ref().unwrap();
-        self.update(cx, |this, cx| this.replace(mat, query, cx))
+        self.update(cx, |this, cx| this.replace(mat, query, window, cx))
     }
 
     fn replace_all(
         &self,
         matches: &mut dyn Iterator<Item = any_vec::element::ElementRef<'_, dyn Send>>,
         query: &SearchQuery,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         self.update(cx, |this, cx| {
-            this.replace_all(&mut matches.map(|m| m.downcast_ref().unwrap()), query, cx);
+            this.replace_all(
+                &mut matches.map(|m| m.downcast_ref().unwrap()),
+                query,
+                window,
+                cx,
+            );
         })
     }
 
-    fn search_bar_visibility_changed(&self, visible: bool, cx: &mut WindowContext) {
+    fn search_bar_visibility_changed(&self, visible: bool, window: &mut Window, cx: &mut App) {
         self.update(cx, |this, cx| {
-            this.search_bar_visibility_changed(visible, cx)
+            this.search_bar_visibility_changed(visible, window, cx)
         });
     }
 
-    fn toggle_filtered_search_ranges(&mut self, enabled: bool, cx: &mut WindowContext) {
+    fn toggle_filtered_search_ranges(&mut self, enabled: bool, window: &mut Window, cx: &mut App) {
         self.update(cx, |this, cx| {
-            this.toggle_filtered_search_ranges(enabled, cx)
+            this.toggle_filtered_search_ranges(enabled, window, cx)
         });
     }
 }
@@ -305,17 +385,17 @@ impl PartialEq for Box<dyn SearchableItemHandle> {
 impl Eq for Box<dyn SearchableItemHandle> {}
 
 pub trait WeakSearchableItemHandle: WeakItemHandle {
-    fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>>;
+    fn upgrade(&self, cx: &App) -> Option<Box<dyn SearchableItemHandle>>;
 
-    fn into_any(self) -> AnyWeakView;
+    fn into_any(self) -> AnyWeakEntity;
 }
 
-impl<T: SearchableItem> WeakSearchableItemHandle for WeakView<T> {
-    fn upgrade(&self, _cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
+impl<T: SearchableItem> WeakSearchableItemHandle for WeakEntity<T> {
+    fn upgrade(&self, _cx: &App) -> Option<Box<dyn SearchableItemHandle>> {
         Some(Box::new(self.upgrade()?))
     }
 
-    fn into_any(self) -> AnyWeakView {
+    fn into_any(self) -> AnyWeakEntity {
         self.into()
     }
 }

crates/workspace/src/shared_screen/cross_platform.rs 🔗

@@ -5,8 +5,8 @@ use crate::{
 use call::{RemoteVideoTrack, RemoteVideoTrackView};
 use client::{proto::PeerId, User};
 use gpui::{
-    div, AppContext, EventEmitter, FocusHandle, FocusableView, InteractiveElement, ParentElement,
-    Render, SharedString, Styled, View, ViewContext, VisualContext, WindowContext,
+    div, AppContext, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement,
+    ParentElement, Render, SharedString, Styled,
 };
 use std::sync::Arc;
 use ui::{prelude::*, Icon, IconName};
@@ -19,7 +19,7 @@ pub struct SharedScreen {
     pub peer_id: PeerId,
     user: Arc<User>,
     nav_history: Option<ItemNavHistory>,
-    view: View<RemoteVideoTrackView>,
+    view: Entity<RemoteVideoTrackView>,
     focus: FocusHandle,
 }
 
@@ -28,9 +28,10 @@ impl SharedScreen {
         track: RemoteVideoTrack,
         peer_id: PeerId,
         user: Arc<User>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
-        let view = cx.new_view(|cx| RemoteVideoTrackView::new(track.clone(), cx));
+        let view = cx.new(|cx| RemoteVideoTrackView::new(track.clone(), window, cx));
         cx.subscribe(&view, |_, _, ev, cx| match ev {
             call::RemoteVideoTrackViewEvent::Close => cx.emit(Event::Close),
         })
@@ -47,13 +48,13 @@ impl SharedScreen {
 
 impl EventEmitter<Event> for SharedScreen {}
 
-impl FocusableView for SharedScreen {
-    fn focus_handle(&self, _: &AppContext) -> FocusHandle {
+impl Focusable for SharedScreen {
+    fn focus_handle(&self, _: &App) -> FocusHandle {
         self.focus.clone()
     }
 }
 impl Render for SharedScreen {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         div()
             .bg(cx.theme().colors().editor_background)
             .track_focus(&self.focus)
@@ -66,21 +67,21 @@ impl Render for SharedScreen {
 impl Item for SharedScreen {
     type Event = Event;
 
-    fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
+    fn tab_tooltip_text(&self, _: &App) -> Option<SharedString> {
         Some(format!("{}'s screen", self.user.github_login).into())
     }
 
-    fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
+    fn deactivated(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
         if let Some(nav_history) = self.nav_history.as_mut() {
             nav_history.push::<()>(None, cx);
         }
     }
 
-    fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
+    fn tab_icon(&self, _window: &Window, _cx: &App) -> Option<Icon> {
         Some(Icon::new(IconName::Screen))
     }
 
-    fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
+    fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option<SharedString> {
         Some(format!("{}'s screen", self.user.github_login).into())
     }
 
@@ -88,17 +89,23 @@ impl Item for SharedScreen {
         None
     }
 
-    fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
+    fn set_nav_history(
+        &mut self,
+        history: ItemNavHistory,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
+    ) {
         self.nav_history = Some(history);
     }
 
     fn clone_on_split(
         &self,
         _workspace_id: Option<WorkspaceId>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<View<Self>> {
-        Some(cx.new_view(|cx| Self {
-            view: self.view.update(cx, |view, cx| view.clone(cx)),
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Entity<Self>> {
+        Some(cx.new(|cx| Self {
+            view: self.view.update(cx, |view, cx| view.clone(window, cx)),
             peer_id: self.peer_id,
             user: self.user.clone(),
             nav_history: Default::default(),

crates/workspace/src/shared_screen/macos.rs 🔗

@@ -7,9 +7,8 @@ use call::participant::{Frame, RemoteVideoTrack};
 use client::{proto::PeerId, User};
 use futures::StreamExt;
 use gpui::{
-    div, surface, AppContext, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
-    ParentElement, Render, SharedString, Styled, Task, View, ViewContext, VisualContext,
-    WindowContext,
+    div, surface, App, Context, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement,
+    ParentElement, Render, SharedString, Styled, Task, Window,
 };
 use std::sync::{Arc, Weak};
 use ui::{prelude::*, Icon, IconName};
@@ -33,7 +32,8 @@ impl SharedScreen {
         track: Arc<RemoteVideoTrack>,
         peer_id: PeerId,
         user: Arc<User>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
         cx.focus_handle();
         let mut frames = track.frames();
@@ -43,7 +43,7 @@ impl SharedScreen {
             peer_id,
             user,
             nav_history: Default::default(),
-            _maintain_frame: cx.spawn(|this, mut cx| async move {
+            _maintain_frame: cx.spawn_in(window, |this, mut cx| async move {
                 while let Some(frame) = frames.next().await {
                     this.update(&mut cx, |this, cx| {
                         this.frame = Some(frame);
@@ -60,13 +60,13 @@ impl SharedScreen {
 
 impl EventEmitter<Event> for SharedScreen {}
 
-impl FocusableView for SharedScreen {
-    fn focus_handle(&self, _: &AppContext) -> FocusHandle {
+impl Focusable for SharedScreen {
+    fn focus_handle(&self, _: &App) -> FocusHandle {
         self.focus.clone()
     }
 }
 impl Render for SharedScreen {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         div()
             .bg(cx.theme().colors().editor_background)
             .track_focus(&self.focus)
@@ -83,21 +83,21 @@ impl Render for SharedScreen {
 impl Item for SharedScreen {
     type Event = Event;
 
-    fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
+    fn tab_tooltip_text(&self, _: &App) -> Option<SharedString> {
         Some(format!("{}'s screen", self.user.github_login).into())
     }
 
-    fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
+    fn deactivated(&mut self, _: &mut Window, cx: &mut Context<Self>) {
         if let Some(nav_history) = self.nav_history.as_mut() {
             nav_history.push::<()>(None, cx);
         }
     }
 
-    fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
+    fn tab_icon(&self, _window: &Window, _cx: &App) -> Option<Icon> {
         Some(Icon::new(IconName::Screen))
     }
 
-    fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
+    fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option<SharedString> {
         Some(format!("{}'s screen", self.user.github_login).into())
     }
 
@@ -105,17 +105,23 @@ impl Item for SharedScreen {
         None
     }
 
-    fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
+    fn set_nav_history(
+        &mut self,
+        history: ItemNavHistory,
+        _window: &mut Window,
+        _: &mut Context<Self>,
+    ) {
         self.nav_history = Some(history);
     }
 
     fn clone_on_split(
         &self,
         _workspace_id: Option<WorkspaceId>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<View<Self>> {
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Entity<Self>> {
         let track = self.track.upgrade()?;
-        Some(cx.new_view(|cx| Self::new(track, self.peer_id, self.user.clone(), cx)))
+        Some(cx.new(|cx| Self::new(track, self.peer_id, self.user.clone(), window, cx)))
     }
 
     fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {

crates/workspace/src/status_bar.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{ItemHandle, Pane};
 use gpui::{
-    AnyView, Decorations, IntoElement, ParentElement, Render, Styled, Subscription, View,
-    ViewContext, WindowContext,
+    AnyView, App, Context, Decorations, Entity, IntoElement, ParentElement, Render, Styled,
+    Subscription, Window,
 };
 use std::any::TypeId;
 use theme::CLIENT_SIDE_DECORATION_ROUNDING;
@@ -12,7 +12,8 @@ pub trait StatusItemView: Render {
     fn set_active_pane_item(
         &mut self,
         active_pane_item: Option<&dyn crate::ItemHandle>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     );
 }
 
@@ -21,7 +22,8 @@ trait StatusItemViewHandle: Send {
     fn set_active_pane_item(
         &self,
         active_pane_item: Option<&dyn ItemHandle>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     );
     fn item_type(&self) -> TypeId;
 }
@@ -29,12 +31,12 @@ trait StatusItemViewHandle: Send {
 pub struct StatusBar {
     left_items: Vec<Box<dyn StatusItemViewHandle>>,
     right_items: Vec<Box<dyn StatusItemViewHandle>>,
-    active_pane: View<Pane>,
+    active_pane: Entity<Pane>,
     _observe_active_pane: Subscription,
 }
 
 impl Render for StatusBar {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         h_flex()
             .w_full()
             .justify_between()
@@ -42,7 +44,7 @@ impl Render for StatusBar {
             .py(DynamicSpacing::Base04.rems(cx))
             .px(DynamicSpacing::Base08.rems(cx))
             .bg(cx.theme().colors().status_bar_background)
-            .map(|el| match cx.window_decorations() {
+            .map(|el| match window.window_decorations() {
                 Decorations::Server => el,
                 Decorations::Client { tiling, .. } => el
                     .when(!(tiling.bottom || tiling.right), |el| {
@@ -62,14 +64,14 @@ impl Render for StatusBar {
 }
 
 impl StatusBar {
-    fn render_left_tools(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render_left_tools(&self, cx: &mut Context<Self>) -> impl IntoElement {
         h_flex()
             .gap(DynamicSpacing::Base04.rems(cx))
             .overflow_x_hidden()
             .children(self.left_items.iter().map(|item| item.to_any()))
     }
 
-    fn render_right_tools(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render_right_tools(&self, cx: &mut Context<Self>) -> impl IntoElement {
         h_flex()
             .gap(DynamicSpacing::Base04.rems(cx))
             .children(self.right_items.iter().rev().map(|item| item.to_any()))
@@ -77,30 +79,31 @@ impl StatusBar {
 }
 
 impl StatusBar {
-    pub fn new(active_pane: &View<Pane>, cx: &mut ViewContext<Self>) -> Self {
+    pub fn new(active_pane: &Entity<Pane>, window: &mut Window, cx: &mut Context<Self>) -> Self {
         let mut this = Self {
             left_items: Default::default(),
             right_items: Default::default(),
             active_pane: active_pane.clone(),
-            _observe_active_pane: cx
-                .observe(active_pane, |this, _, cx| this.update_active_pane_item(cx)),
+            _observe_active_pane: cx.observe_in(active_pane, window, |this, _, window, cx| {
+                this.update_active_pane_item(window, cx)
+            }),
         };
-        this.update_active_pane_item(cx);
+        this.update_active_pane_item(window, cx);
         this
     }
 
-    pub fn add_left_item<T>(&mut self, item: View<T>, cx: &mut ViewContext<Self>)
+    pub fn add_left_item<T>(&mut self, item: Entity<T>, window: &mut Window, cx: &mut Context<Self>)
     where
         T: 'static + StatusItemView,
     {
         let active_pane_item = self.active_pane.read(cx).active_item();
-        item.set_active_pane_item(active_pane_item.as_deref(), cx);
+        item.set_active_pane_item(active_pane_item.as_deref(), window, cx);
 
         self.left_items.push(Box::new(item));
         cx.notify();
     }
 
-    pub fn item_of_type<T: StatusItemView>(&self) -> Option<View<T>> {
+    pub fn item_of_type<T: StatusItemView>(&self) -> Option<Entity<T>> {
         self.left_items
             .iter()
             .chain(self.right_items.iter())
@@ -127,13 +130,14 @@ impl StatusBar {
     pub fn insert_item_after<T>(
         &mut self,
         position: usize,
-        item: View<T>,
-        cx: &mut ViewContext<Self>,
+        item: Entity<T>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) where
         T: 'static + StatusItemView,
     {
         let active_pane_item = self.active_pane.read(cx).active_item();
-        item.set_active_pane_item(active_pane_item.as_deref(), cx);
+        item.set_active_pane_item(active_pane_item.as_deref(), window, cx);
 
         if position < self.left_items.len() {
             self.left_items.insert(position + 1, Box::new(item))
@@ -144,7 +148,7 @@ impl StatusBar {
         cx.notify()
     }
 
-    pub fn remove_item_at(&mut self, position: usize, cx: &mut ViewContext<Self>) {
+    pub fn remove_item_at(&mut self, position: usize, cx: &mut Context<Self>) {
         if position < self.left_items.len() {
             self.left_items.remove(position);
         } else {
@@ -153,33 +157,43 @@ impl StatusBar {
         cx.notify();
     }
 
-    pub fn add_right_item<T>(&mut self, item: View<T>, cx: &mut ViewContext<Self>)
-    where
+    pub fn add_right_item<T>(
+        &mut self,
+        item: Entity<T>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) where
         T: 'static + StatusItemView,
     {
         let active_pane_item = self.active_pane.read(cx).active_item();
-        item.set_active_pane_item(active_pane_item.as_deref(), cx);
+        item.set_active_pane_item(active_pane_item.as_deref(), window, cx);
 
         self.right_items.push(Box::new(item));
         cx.notify();
     }
 
-    pub fn set_active_pane(&mut self, active_pane: &View<Pane>, cx: &mut ViewContext<Self>) {
+    pub fn set_active_pane(
+        &mut self,
+        active_pane: &Entity<Pane>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.active_pane = active_pane.clone();
-        self._observe_active_pane =
-            cx.observe(active_pane, |this, _, cx| this.update_active_pane_item(cx));
-        self.update_active_pane_item(cx);
+        self._observe_active_pane = cx.observe_in(active_pane, window, |this, _, window, cx| {
+            this.update_active_pane_item(window, cx)
+        });
+        self.update_active_pane_item(window, cx);
     }
 
-    fn update_active_pane_item(&mut self, cx: &mut ViewContext<Self>) {
+    fn update_active_pane_item(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let active_pane_item = self.active_pane.read(cx).active_item();
         for item in self.left_items.iter().chain(&self.right_items) {
-            item.set_active_pane_item(active_pane_item.as_deref(), cx);
+            item.set_active_pane_item(active_pane_item.as_deref(), window, cx);
         }
     }
 }
 
-impl<T: StatusItemView> StatusItemViewHandle for View<T> {
+impl<T: StatusItemView> StatusItemViewHandle for Entity<T> {
     fn to_any(&self) -> AnyView {
         self.clone().into()
     }
@@ -187,10 +201,11 @@ impl<T: StatusItemView> StatusItemViewHandle for View<T> {
     fn set_active_pane_item(
         &self,
         active_pane_item: Option<&dyn ItemHandle>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         self.update(cx, |this, cx| {
-            this.set_active_pane_item(active_pane_item, cx)
+            this.set_active_pane_item(active_pane_item, window, cx)
         });
     }
 

crates/workspace/src/tasks.rs 🔗

@@ -1,7 +1,7 @@
+use gpui::Context;
 use project::TaskSourceKind;
 use remote::ConnectionState;
 use task::{ResolvedTask, TaskContext, TaskTemplate};
-use ui::ViewContext;
 
 use crate::Workspace;
 
@@ -11,7 +11,7 @@ pub fn schedule_task(
     task_to_resolve: &TaskTemplate,
     task_cx: &TaskContext,
     omit_history: bool,
-    cx: &mut ViewContext<Workspace>,
+    cx: &mut Context<Workspace>,
 ) {
     match workspace.project.read(cx).ssh_connection_state(cx) {
         None | Some(ConnectionState::Connected) => {}
@@ -44,7 +44,7 @@ pub fn schedule_resolved_task(
     task_source_kind: TaskSourceKind,
     mut resolved_task: ResolvedTask,
     omit_history: bool,
-    cx: &mut ViewContext<Workspace>,
+    cx: &mut Context<Workspace>,
 ) {
     if let Some(spawn_in_terminal) = resolved_task.resolved.take() {
         if !omit_history {

crates/workspace/src/theme_preview.rs 🔗

@@ -1,5 +1,5 @@
 #![allow(unused, dead_code)]
-use gpui::{actions, hsla, AnyElement, AppContext, EventEmitter, FocusHandle, FocusableView, Hsla};
+use gpui::{actions, hsla, AnyElement, App, Entity, EventEmitter, FocusHandle, Focusable, Hsla};
 use strum::IntoEnumIterator;
 use theme::all_theme_colors;
 use ui::{
@@ -13,11 +13,11 @@ use crate::{Item, Workspace};
 
 actions!(debug, [OpenThemePreview]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(|workspace: &mut Workspace, _| {
-        workspace.register_action(|workspace, _: &OpenThemePreview, cx| {
-            let theme_preview = cx.new_view(ThemePreview::new);
-            workspace.add_item_to_active_pane(Box::new(theme_preview), None, true, cx)
+pub fn init(cx: &mut App) {
+    cx.observe_new(|workspace: &mut Workspace, _, _| {
+        workspace.register_action(|workspace, _: &OpenThemePreview, window, cx| {
+            let theme_preview = cx.new(|cx| ThemePreview::new(window, cx));
+            workspace.add_item_to_active_pane(Box::new(theme_preview), None, true, window, cx)
         });
     })
     .detach();
@@ -46,7 +46,7 @@ struct ThemePreview {
 }
 
 impl ThemePreview {
-    pub fn new(cx: &mut ViewContext<Self>) -> Self {
+    pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
         Self {
             current_page: ThemePreviewPage::Overview,
             focus_handle: cx.focus_handle(),
@@ -56,20 +56,25 @@ impl ThemePreview {
     pub fn view(
         &self,
         page: ThemePreviewPage,
-        cx: &mut ViewContext<ThemePreview>,
+        window: &mut Window,
+        cx: &mut Context<ThemePreview>,
     ) -> impl IntoElement {
         match page {
-            ThemePreviewPage::Overview => self.render_overview_page(cx).into_any_element(),
-            ThemePreviewPage::Typography => self.render_typography_page(cx).into_any_element(),
-            ThemePreviewPage::Components => self.render_components_page(cx).into_any_element(),
+            ThemePreviewPage::Overview => self.render_overview_page(window, cx).into_any_element(),
+            ThemePreviewPage::Typography => {
+                self.render_typography_page(window, cx).into_any_element()
+            }
+            ThemePreviewPage::Components => {
+                self.render_components_page(window, cx).into_any_element()
+            }
         }
     }
 }
 
 impl EventEmitter<()> for ThemePreview {}
 
-impl FocusableView for ThemePreview {
-    fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
+impl Focusable for ThemePreview {
+    fn focus_handle(&self, _: &App) -> gpui::FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -80,7 +85,7 @@ impl Item for ThemePreview {
 
     fn to_item_events(_: &Self::Event, _: impl FnMut(crate::item::ItemEvent)) {}
 
-    fn tab_content_text(&self, cx: &WindowContext) -> Option<SharedString> {
+    fn tab_content_text(&self, window: &Window, cx: &App) -> Option<SharedString> {
         let name = cx.theme().name.clone();
         Some(format!("{} Preview", name).into())
     }
@@ -92,23 +97,29 @@ impl Item for ThemePreview {
     fn clone_on_split(
         &self,
         _workspace_id: Option<crate::WorkspaceId>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<gpui::View<Self>>
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Entity<Self>>
     where
         Self: Sized,
     {
-        Some(cx.new_view(Self::new))
+        Some(cx.new(|cx| Self::new(window, cx)))
     }
 }
 
 const AVATAR_URL: &str = "https://avatars.githubusercontent.com/u/1714999?v=4";
 
 impl ThemePreview {
-    fn preview_bg(cx: &WindowContext) -> Hsla {
+    fn preview_bg(window: &mut Window, cx: &mut App) -> Hsla {
         cx.theme().colors().editor_background
     }
 
-    fn render_text(&self, layer: ElevationIndex, cx: &ViewContext<Self>) -> impl IntoElement {
+    fn render_text(
+        &self,
+        layer: ElevationIndex,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> impl IntoElement {
         let bg = layer.bg(cx);
 
         let label_with_contrast = |label: &str, fg: Hsla| {
@@ -269,7 +280,12 @@ impl ThemePreview {
             )
     }
 
-    fn render_colors(&self, layer: ElevationIndex, cx: &ViewContext<Self>) -> impl IntoElement {
+    fn render_colors(
+        &self,
+        layer: ElevationIndex,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> impl IntoElement {
         let bg = layer.bg(cx);
         let all_colors = all_theme_colors(cx);
 
@@ -299,9 +315,15 @@ impl ThemePreview {
                                 )
                                 .size(ButtonSize::None)
                                 .style(ButtonStyle::Transparent)
-                                .tooltip(move |cx| {
+                                .tooltip(move |window, cx| {
                                     let name = name.clone();
-                                    Tooltip::with_meta(name, None, format!("{:?}", color), cx)
+                                    Tooltip::with_meta(
+                                        name,
+                                        None,
+                                        format!("{:?}", color),
+                                        window,
+                                        cx,
+                                    )
                                 }),
                         )
                     })),
@@ -311,7 +333,8 @@ impl ThemePreview {
     fn render_theme_layer(
         &self,
         layer: ElevationIndex,
-        cx: &ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> impl IntoElement {
         v_flex()
             .p_4()
@@ -319,11 +342,15 @@ impl ThemePreview {
             .text_color(cx.theme().colors().text)
             .gap_2()
             .child(Headline::new(layer.clone().to_string()).size(HeadlineSize::Medium))
-            .child(self.render_text(layer, cx))
-            .child(self.render_colors(layer, cx))
+            .child(self.render_text(layer, window, cx))
+            .child(self.render_colors(layer, window, cx))
     }
 
-    fn render_overview_page(&self, cx: &ViewContext<Self>) -> impl IntoElement {
+    fn render_overview_page(
+        &self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> impl IntoElement {
         v_flex()
             .id("theme-preview-overview")
             .overflow_scroll()
@@ -333,13 +360,17 @@ impl ThemePreview {
                     .child(Headline::new("Theme Preview").size(HeadlineSize::Large))
                     .child(div().w_full().text_color(cx.theme().colors().text_muted).child("This view lets you preview a range of UI elements across a theme. Use it for testing out changes to the theme."))
                     )
-            .child(self.render_theme_layer(ElevationIndex::Background, cx))
-            .child(self.render_theme_layer(ElevationIndex::Surface, cx))
-            .child(self.render_theme_layer(ElevationIndex::EditorSurface, cx))
-            .child(self.render_theme_layer(ElevationIndex::ElevatedSurface, cx))
+            .child(self.render_theme_layer(ElevationIndex::Background, window, cx))
+            .child(self.render_theme_layer(ElevationIndex::Surface, window, cx))
+            .child(self.render_theme_layer(ElevationIndex::EditorSurface, window, cx))
+            .child(self.render_theme_layer(ElevationIndex::ElevatedSurface, window, cx))
     }
 
-    fn render_typography_page(&self, cx: &ViewContext<Self>) -> impl IntoElement {
+    fn render_typography_page(
+        &self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> impl IntoElement {
         v_flex()
             .id("theme-preview-typography")
             .overflow_scroll()
@@ -361,7 +392,7 @@ impl ThemePreview {
             )
     }
 
-    fn render_components_page(&self, cx: &mut WindowContext) -> impl IntoElement {
+    fn render_components_page(&self, window: &mut Window, cx: &mut App) -> impl IntoElement {
         let layer = ElevationIndex::Surface;
 
         v_flex()
@@ -369,29 +400,29 @@ impl ThemePreview {
             .overflow_scroll()
             .size_full()
             .gap_2()
-            .child(Button::render_component_previews(cx))
-            .child(Checkbox::render_component_previews(cx))
-            .child(CheckboxWithLabel::render_component_previews(cx))
-            .child(ContentGroup::render_component_previews(cx))
-            .child(DecoratedIcon::render_component_previews(cx))
-            .child(Facepile::render_component_previews(cx))
-            .child(Icon::render_component_previews(cx))
-            .child(IconDecoration::render_component_previews(cx))
-            .child(Indicator::render_component_previews(cx))
-            .child(Switch::render_component_previews(cx))
-            .child(Table::render_component_previews(cx))
+            .child(Button::render_component_previews(window, cx))
+            .child(Checkbox::render_component_previews(window, cx))
+            .child(CheckboxWithLabel::render_component_previews(window, cx))
+            .child(ContentGroup::render_component_previews(window, cx))
+            .child(DecoratedIcon::render_component_previews(window, cx))
+            .child(Facepile::render_component_previews(window, cx))
+            .child(Icon::render_component_previews(window, cx))
+            .child(IconDecoration::render_component_previews(window, cx))
+            .child(Indicator::render_component_previews(window, cx))
+            .child(Switch::render_component_previews(window, cx))
+            .child(Table::render_component_previews(window, cx))
     }
 
-    fn render_page_nav(&self, cx: &ViewContext<Self>) -> impl IntoElement {
+    fn render_page_nav(&self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         h_flex()
             .id("theme-preview-nav")
             .items_center()
             .gap_4()
             .py_2()
-            .bg(Self::preview_bg(cx))
+            .bg(Self::preview_bg(window, cx))
             .children(ThemePreviewPage::iter().map(|p| {
                 Button::new(ElementId::Name(p.name().into()), p.name())
-                    .on_click(cx.listener(move |this, _, cx| {
+                    .on_click(cx.listener(move |this, _, window, cx| {
                         this.current_page = p;
                         cx.notify();
                     }))
@@ -402,7 +433,7 @@ impl ThemePreview {
 }
 
 impl Render for ThemePreview {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl ui::IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement {
         v_flex()
             .id("theme-preview")
             .key_context("ThemePreview")
@@ -412,8 +443,8 @@ impl Render for ThemePreview {
             .max_h_full()
             .track_focus(&self.focus_handle)
             .px_2()
-            .bg(Self::preview_bg(cx))
-            .child(self.render_page_nav(cx))
-            .child(self.view(self.current_page, cx))
+            .bg(Self::preview_bg(window, cx))
+            .child(self.render_page_nav(window, cx))
+            .child(self.view(self.current_page, window, cx))
     }
 }

crates/workspace/src/toolbar.rs 🔗

@@ -1,7 +1,7 @@
 use crate::ItemHandle;
 use gpui::{
-    AnyView, Entity, EntityId, EventEmitter, ParentElement as _, Render, Styled, View, ViewContext,
-    WindowContext,
+    AnyView, App, Context, Entity, EntityId, EventEmitter, ParentElement as _, Render, Styled,
+    Window,
 };
 use ui::prelude::*;
 use ui::{h_flex, v_flex};
@@ -14,10 +14,17 @@ pub trait ToolbarItemView: Render + EventEmitter<ToolbarItemEvent> {
     fn set_active_pane_item(
         &mut self,
         active_pane_item: Option<&dyn crate::ItemHandle>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> ToolbarItemLocation;
 
-    fn pane_focus_update(&mut self, _pane_focused: bool, _cx: &mut ViewContext<Self>) {}
+    fn pane_focus_update(
+        &mut self,
+        _pane_focused: bool,
+        _window: &mut Window,
+        _cx: &mut Context<Self>,
+    ) {
+    }
 }
 
 trait ToolbarItemViewHandle: Send {
@@ -26,9 +33,10 @@ trait ToolbarItemViewHandle: Send {
     fn set_active_pane_item(
         &self,
         active_pane_item: Option<&dyn ItemHandle>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> ToolbarItemLocation;
-    fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext);
+    fn focus_changed(&mut self, pane_focused: bool, window: &mut Window, cx: &mut App);
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
@@ -85,7 +93,7 @@ impl Toolbar {
 }
 
 impl Render for Toolbar {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         if !self.has_any_visible_items() {
             return div();
         }
@@ -157,16 +165,16 @@ impl Toolbar {
         }
     }
 
-    pub fn set_can_navigate(&mut self, can_navigate: bool, cx: &mut ViewContext<Self>) {
+    pub fn set_can_navigate(&mut self, can_navigate: bool, cx: &mut Context<Self>) {
         self.can_navigate = can_navigate;
         cx.notify();
     }
 
-    pub fn add_item<T>(&mut self, item: View<T>, cx: &mut ViewContext<Self>)
+    pub fn add_item<T>(&mut self, item: Entity<T>, window: &mut Window, cx: &mut Context<Self>)
     where
         T: 'static + ToolbarItemView,
     {
-        let location = item.set_active_pane_item(self.active_item.as_deref(), cx);
+        let location = item.set_active_pane_item(self.active_item.as_deref(), window, cx);
         cx.subscribe(&item, |this, item, event, cx| {
             if let Some((_, current_location)) = this
                 .items
@@ -188,7 +196,12 @@ impl Toolbar {
         cx.notify();
     }
 
-    pub fn set_active_item(&mut self, item: Option<&dyn ItemHandle>, cx: &mut ViewContext<Self>) {
+    pub fn set_active_item(
+        &mut self,
+        item: Option<&dyn ItemHandle>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.active_item = item.map(|item| item.boxed_clone());
         self.hidden = self
             .active_item
@@ -197,7 +210,7 @@ impl Toolbar {
             .unwrap_or(false);
 
         for (toolbar_item, current_location) in self.items.iter_mut() {
-            let new_location = toolbar_item.set_active_pane_item(item, cx);
+            let new_location = toolbar_item.set_active_pane_item(item, window, cx);
             if new_location != *current_location {
                 *current_location = new_location;
                 cx.notify();
@@ -205,13 +218,13 @@ impl Toolbar {
         }
     }
 
-    pub fn focus_changed(&mut self, focused: bool, cx: &mut ViewContext<Self>) {
+    pub fn focus_changed(&mut self, focused: bool, window: &mut Window, cx: &mut Context<Self>) {
         for (toolbar_item, _) in self.items.iter_mut() {
-            toolbar_item.focus_changed(focused, cx);
+            toolbar_item.focus_changed(focused, window, cx);
         }
     }
 
-    pub fn item_of_type<T: ToolbarItemView>(&self) -> Option<View<T>> {
+    pub fn item_of_type<T: ToolbarItemView>(&self) -> Option<Entity<T>> {
         self.items
             .iter()
             .find_map(|(item, _)| item.to_any().downcast().ok())
@@ -222,7 +235,7 @@ impl Toolbar {
     }
 }
 
-impl<T: ToolbarItemView> ToolbarItemViewHandle for View<T> {
+impl<T: ToolbarItemView> ToolbarItemViewHandle for Entity<T> {
     fn id(&self) -> EntityId {
         self.entity_id()
     }
@@ -234,16 +247,17 @@ impl<T: ToolbarItemView> ToolbarItemViewHandle for View<T> {
     fn set_active_pane_item(
         &self,
         active_pane_item: Option<&dyn ItemHandle>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> ToolbarItemLocation {
         self.update(cx, |this, cx| {
-            this.set_active_pane_item(active_pane_item, cx)
+            this.set_active_pane_item(active_pane_item, window, cx)
         })
     }
 
-    fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext) {
+    fn focus_changed(&mut self, pane_focused: bool, window: &mut Window, cx: &mut App) {
         self.update(cx, |this, cx| {
-            this.pane_focus_update(pane_focused, cx);
+            this.pane_focus_update(pane_focused, window, cx);
             cx.notify();
         });
     }

crates/workspace/src/workspace.rs 🔗

@@ -32,12 +32,11 @@ use futures::{
 };
 use gpui::{
     action_as, actions, canvas, impl_action_as, impl_actions, point, relative, size,
-    transparent_black, Action, AnyView, AnyWeakView, AppContext, AsyncAppContext,
-    AsyncWindowContext, Bounds, CursorStyle, Decorations, DragMoveEvent, Entity as _, EntityId,
-    EventEmitter, FocusHandle, FocusableView, Global, Hsla, KeyContext, Keystroke, ManagedView,
-    Model, ModelContext, MouseButton, PathPromptOptions, Point, PromptLevel, Render, ResizeEdge,
-    Size, Stateful, Subscription, Task, Tiling, View, WeakView, WindowBounds, WindowHandle,
-    WindowId, WindowOptions,
+    transparent_black, Action, AnyView, AnyWeakView, App, AsyncAppContext, AsyncWindowContext,
+    Bounds, Context, CursorStyle, Decorations, DragMoveEvent, Entity, EntityId, EventEmitter,
+    FocusHandle, Focusable, Global, Hsla, KeyContext, Keystroke, ManagedView, MouseButton,
+    PathPromptOptions, Point, PromptLevel, Render, ResizeEdge, Size, Stateful, Subscription, Task,
+    Tiling, WeakEntity, WindowBounds, WindowHandle, WindowId, WindowOptions,
 };
 pub use item::{
     FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, PreviewTabsSettings,
@@ -262,7 +261,7 @@ pub struct Toast {
     id: NotificationId,
     msg: Cow<'static, str>,
     autohide: bool,
-    on_click: Option<(Cow<'static, str>, Arc<dyn Fn(&mut WindowContext)>)>,
+    on_click: Option<(Cow<'static, str>, Arc<dyn Fn(&mut Window, &mut App)>)>,
 }
 
 impl Toast {
@@ -278,7 +277,7 @@ impl Toast {
     pub fn on_click<F, M>(mut self, message: M, on_click: F) -> Self
     where
         M: Into<Cow<'static, str>>,
-        F: Fn(&mut WindowContext) + 'static,
+        F: Fn(&mut Window, &mut App) + 'static,
     {
         self.on_click = Some((message.into(), Arc::new(on_click)));
         self
@@ -325,18 +324,14 @@ impl From<WorkspaceId> for i64 {
     }
 }
 
-pub fn init_settings(cx: &mut AppContext) {
+pub fn init_settings(cx: &mut App) {
     WorkspaceSettings::register(cx);
     ItemSettings::register(cx);
     PreviewTabsSettings::register(cx);
     TabBarSettings::register(cx);
 }
 
-fn prompt_and_open_paths(
-    app_state: Arc<AppState>,
-    options: PathPromptOptions,
-    cx: &mut AppContext,
-) {
+fn prompt_and_open_paths(app_state: Arc<AppState>, options: PathPromptOptions, cx: &mut App) {
     let paths = cx.prompt_for_paths(options);
     cx.spawn(|cx| async move {
         match paths.await.anyhow().and_then(|res| res) {
@@ -355,7 +350,7 @@ fn prompt_and_open_paths(
                         .and_then(|window| window.downcast::<Workspace>())
                     {
                         workspace_window
-                            .update(cx, |workspace, cx| {
+                            .update(cx, |workspace, _, cx| {
                                 workspace.show_portal_error(err.to_string(), cx);
                             })
                             .ok();
@@ -368,7 +363,7 @@ fn prompt_and_open_paths(
     .detach();
 }
 
-pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
+pub fn init(app_state: Arc<AppState>, cx: &mut App) {
     init_settings(cx);
     notifications::init(cx);
     theme_preview::init(cx);
@@ -378,7 +373,7 @@ pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
 
     cx.on_action({
         let app_state = Arc::downgrade(&app_state);
-        move |_: &Open, cx: &mut AppContext| {
+        move |_: &Open, cx: &mut App| {
             if let Some(app_state) = app_state.upgrade() {
                 prompt_and_open_paths(
                     app_state,
@@ -394,7 +389,7 @@ pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
     });
     cx.on_action({
         let app_state = Arc::downgrade(&app_state);
-        move |_: &OpenFiles, cx: &mut AppContext| {
+        move |_: &OpenFiles, cx: &mut App| {
             let directories = cx.can_select_mixed_files_and_dirs();
             if let Some(app_state) = app_state.upgrade() {
                 prompt_and_open_paths(
@@ -415,30 +410,31 @@ pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
 struct ProjectItemOpeners(Vec<ProjectItemOpener>);
 
 type ProjectItemOpener = fn(
-    &Model<Project>,
+    &Entity<Project>,
     &ProjectPath,
-    &mut WindowContext,
+    &mut Window,
+    &mut App,
 )
     -> Option<Task<Result<(Option<ProjectEntryId>, WorkspaceItemBuilder)>>>;
 
-type WorkspaceItemBuilder = Box<dyn FnOnce(&mut ViewContext<Pane>) -> Box<dyn ItemHandle>>;
+type WorkspaceItemBuilder = Box<dyn FnOnce(&mut Window, &mut Context<Pane>) -> Box<dyn ItemHandle>>;
 
 impl Global for ProjectItemOpeners {}
 
 /// Registers a [ProjectItem] for the app. When opening a file, all the registered
 /// items will get a chance to open the file, starting from the project item that
 /// was added last.
-pub fn register_project_item<I: ProjectItem>(cx: &mut AppContext) {
+pub fn register_project_item<I: ProjectItem>(cx: &mut App) {
     let builders = cx.default_global::<ProjectItemOpeners>();
-    builders.push(|project, project_path, cx| {
+    builders.push(|project, project_path, window, cx| {
         let project_item = <I::Item as project::ProjectItem>::try_open(project, project_path, cx)?;
         let project = project.clone();
-        Some(cx.spawn(|cx| async move {
+        Some(window.spawn(cx, |cx| async move {
             let project_item = project_item.await?;
             let project_entry_id: Option<ProjectEntryId> =
                 project_item.read_with(&cx, project::ProjectItem::entry_id)?;
-            let build_workspace_item = Box::new(|cx: &mut ViewContext<Pane>| {
-                Box::new(cx.new_view(|cx| I::for_project_item(project, project_item, cx)))
+            let build_workspace_item = Box::new(|window: &mut Window, cx: &mut Context<Pane>| {
+                Box::new(cx.new(|cx| I::for_project_item(project, project_item, window, cx)))
                     as Box<dyn ItemHandle>
             }) as Box<_>;
             Ok((project_entry_id, build_workspace_item))
@@ -451,10 +447,11 @@ pub struct FollowableViewRegistry(HashMap<TypeId, FollowableViewDescriptor>);
 
 struct FollowableViewDescriptor {
     from_state_proto: fn(
-        View<Workspace>,
+        Entity<Workspace>,
         ViewId,
         &mut Option<proto::view::Variant>,
-        &mut WindowContext,
+        &mut Window,
+        &mut App,
     ) -> Option<Task<Result<Box<dyn FollowableItemHandle>>>>,
     to_followable_view: fn(&AnyView) -> Box<dyn FollowableItemHandle>,
 }
@@ -462,12 +459,12 @@ struct FollowableViewDescriptor {
 impl Global for FollowableViewRegistry {}
 
 impl FollowableViewRegistry {
-    pub fn register<I: FollowableItem>(cx: &mut AppContext) {
+    pub fn register<I: FollowableItem>(cx: &mut App) {
         cx.default_global::<Self>().0.insert(
             TypeId::of::<I>(),
             FollowableViewDescriptor {
-                from_state_proto: |workspace, id, state, cx| {
-                    I::from_state_proto(workspace, id, state, cx).map(|task| {
+                from_state_proto: |workspace, id, state, window, cx| {
+                    I::from_state_proto(workspace, id, state, window, cx).map(|task| {
                         cx.foreground_executor()
                             .spawn(async move { Ok(Box::new(task.await?) as Box<_>) })
                     })
@@ -478,21 +475,22 @@ impl FollowableViewRegistry {
     }
 
     pub fn from_state_proto(
-        workspace: View<Workspace>,
+        workspace: Entity<Workspace>,
         view_id: ViewId,
         mut state: Option<proto::view::Variant>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Option<Task<Result<Box<dyn FollowableItemHandle>>>> {
         cx.update_default_global(|this: &mut Self, cx| {
             this.0.values().find_map(|descriptor| {
-                (descriptor.from_state_proto)(workspace.clone(), view_id, &mut state, cx)
+                (descriptor.from_state_proto)(workspace.clone(), view_id, &mut state, window, cx)
             })
         })
     }
 
     pub fn to_followable_view(
         view: impl Into<AnyView>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<Box<dyn FollowableItemHandle>> {
         let this = cx.try_global::<Self>()?;
         let view = view.into();
@@ -504,13 +502,14 @@ impl FollowableViewRegistry {
 #[derive(Copy, Clone)]
 struct SerializableItemDescriptor {
     deserialize: fn(
-        Model<Project>,
-        WeakView<Workspace>,
+        Entity<Project>,
+        WeakEntity<Workspace>,
         WorkspaceId,
         ItemId,
-        &mut ViewContext<Pane>,
+        &mut Window,
+        &mut Context<Pane>,
     ) -> Task<Result<Box<dyn ItemHandle>>>,
-    cleanup: fn(WorkspaceId, Vec<ItemId>, &mut WindowContext) -> Task<Result<()>>,
+    cleanup: fn(WorkspaceId, Vec<ItemId>, &mut Window, &mut App) -> Task<Result<()>>,
     view_to_serializable_item: fn(AnyView) -> Box<dyn SerializableItemHandle>,
 }
 
@@ -525,11 +524,12 @@ impl Global for SerializableItemRegistry {}
 impl SerializableItemRegistry {
     fn deserialize(
         item_kind: &str,
-        project: Model<Project>,
-        workspace: WeakView<Workspace>,
+        project: Entity<Project>,
+        workspace: WeakEntity<Workspace>,
         workspace_id: WorkspaceId,
         item_item: ItemId,
-        cx: &mut ViewContext<Pane>,
+        window: &mut Window,
+        cx: &mut Context<Pane>,
     ) -> Task<Result<Box<dyn ItemHandle>>> {
         let Some(descriptor) = Self::descriptor(item_kind, cx) else {
             return Task::ready(Err(anyhow!(
@@ -538,14 +538,15 @@ impl SerializableItemRegistry {
             )));
         };
 
-        (descriptor.deserialize)(project, workspace, workspace_id, item_item, cx)
+        (descriptor.deserialize)(project, workspace, workspace_id, item_item, window, cx)
     }
 
     fn cleanup(
         item_kind: &str,
         workspace_id: WorkspaceId,
         loaded_items: Vec<ItemId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Task<Result<()>> {
         let Some(descriptor) = Self::descriptor(item_kind, cx) else {
             return Task::ready(Err(anyhow!(
@@ -554,35 +555,37 @@ impl SerializableItemRegistry {
             )));
         };
 
-        (descriptor.cleanup)(workspace_id, loaded_items, cx)
+        (descriptor.cleanup)(workspace_id, loaded_items, window, cx)
     }
 
     fn view_to_serializable_item_handle(
         view: AnyView,
-        cx: &AppContext,
+        cx: &App,
     ) -> Option<Box<dyn SerializableItemHandle>> {
         let this = cx.try_global::<Self>()?;
         let descriptor = this.descriptors_by_type.get(&view.entity_type())?;
         Some((descriptor.view_to_serializable_item)(view))
     }
 
-    fn descriptor(item_kind: &str, cx: &AppContext) -> Option<SerializableItemDescriptor> {
+    fn descriptor(item_kind: &str, cx: &App) -> Option<SerializableItemDescriptor> {
         let this = cx.try_global::<Self>()?;
         this.descriptors_by_kind.get(item_kind).copied()
     }
 }
 
-pub fn register_serializable_item<I: SerializableItem>(cx: &mut AppContext) {
+pub fn register_serializable_item<I: SerializableItem>(cx: &mut App) {
     let serialized_item_kind = I::serialized_item_kind();
 
     let registry = cx.default_global::<SerializableItemRegistry>();
     let descriptor = SerializableItemDescriptor {
-        deserialize: |project, workspace, workspace_id, item_id, cx| {
-            let task = I::deserialize(project, workspace, workspace_id, item_id, cx);
+        deserialize: |project, workspace, workspace_id, item_id, window, cx| {
+            let task = I::deserialize(project, workspace, workspace_id, item_id, window, cx);
             cx.foreground_executor()
                 .spawn(async { Ok(Box::new(task.await?) as Box<_>) })
         },
-        cleanup: |workspace_id, loaded_items, cx| I::cleanup(workspace_id, loaded_items, cx),
+        cleanup: |workspace_id, loaded_items, window, cx| {
+            I::cleanup(workspace_id, loaded_items, window, cx)
+        },
         view_to_serializable_item: |view| Box::new(view.downcast::<I>().unwrap()),
     };
     registry
@@ -596,12 +599,12 @@ pub fn register_serializable_item<I: SerializableItem>(cx: &mut AppContext) {
 pub struct AppState {
     pub languages: Arc<LanguageRegistry>,
     pub client: Arc<Client>,
-    pub user_store: Model<UserStore>,
-    pub workspace_store: Model<WorkspaceStore>,
+    pub user_store: Entity<UserStore>,
+    pub workspace_store: Entity<WorkspaceStore>,
     pub fs: Arc<dyn fs::Fs>,
-    pub build_window_options: fn(Option<Uuid>, &mut AppContext) -> WindowOptions,
+    pub build_window_options: fn(Option<Uuid>, &mut App) -> WindowOptions,
     pub node_runtime: NodeRuntime,
-    pub session: Model<AppSession>,
+    pub session: Entity<AppSession>,
 }
 
 struct GlobalAppState(Weak<AppState>);
@@ -621,19 +624,20 @@ struct Follower {
 }
 
 impl AppState {
-    pub fn global(cx: &AppContext) -> Weak<Self> {
+    #[track_caller]
+    pub fn global(cx: &App) -> Weak<Self> {
         cx.global::<GlobalAppState>().0.clone()
     }
-    pub fn try_global(cx: &AppContext) -> Option<Weak<Self>> {
+    pub fn try_global(cx: &App) -> Option<Weak<Self>> {
         cx.try_global::<GlobalAppState>()
             .map(|state| state.0.clone())
     }
-    pub fn set_global(state: Weak<AppState>, cx: &mut AppContext) {
+    pub fn set_global(state: Weak<AppState>, cx: &mut App) {
         cx.set_global(GlobalAppState(state));
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn test(cx: &mut AppContext) -> Arc<Self> {
+    pub fn test(cx: &mut App) -> Arc<Self> {
         use node_runtime::NodeRuntime;
         use session::Session;
         use settings::SettingsStore;
@@ -648,9 +652,9 @@ impl AppState {
         let clock = Arc::new(clock::FakeSystemClock::new());
         let http_client = http_client::FakeHttpClient::with_404_response();
         let client = Client::new(clock, http_client.clone(), cx);
-        let session = cx.new_model(|cx| AppSession::new(Session::test(), cx));
-        let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
-        let workspace_store = cx.new_model(|cx| WorkspaceStore::new(client.clone(), cx));
+        let session = cx.new(|cx| AppSession::new(Session::test(), cx));
+        let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
+        let workspace_store = cx.new(|cx| WorkspaceStore::new(client.clone(), cx));
 
         theme::init(theme::LoadThemes::JustBase, cx);
         client::init(&client, cx);
@@ -682,9 +686,16 @@ impl DelayedDebouncedEditAction {
         }
     }
 
-    fn fire_new<F>(&mut self, delay: Duration, cx: &mut ViewContext<Workspace>, func: F)
-    where
-        F: 'static + Send + FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> Task<Result<()>>,
+    fn fire_new<F>(
+        &mut self,
+        delay: Duration,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+        func: F,
+    ) where
+        F: 'static
+            + Send
+            + FnOnce(&mut Workspace, &mut Window, &mut Context<Workspace>) -> Task<Result<()>>,
     {
         if let Some(channel) = self.cancel_channel.take() {
             _ = channel.send(());
@@ -694,7 +705,7 @@ impl DelayedDebouncedEditAction {
         self.cancel_channel = Some(sender);
 
         let previous_task = self.task.take();
-        self.task = Some(cx.spawn(move |workspace, mut cx| async move {
+        self.task = Some(cx.spawn_in(window, move |workspace, mut cx| async move {
             let mut timer = cx.background_executor().timer(delay).fuse();
             if let Some(previous_task) = previous_task {
                 previous_task.await;
@@ -706,7 +717,9 @@ impl DelayedDebouncedEditAction {
             }
 
             if let Some(result) = workspace
-                .update(&mut cx, |workspace, cx| (func)(workspace, cx))
+                .update_in(&mut cx, |workspace, window, cx| {
+                    (func)(workspace, window, cx)
+                })
                 .log_err()
             {
                 result.await.log_err();
@@ -716,7 +729,7 @@ impl DelayedDebouncedEditAction {
 }
 
 pub enum Event {
-    PaneAdded(View<Pane>),
+    PaneAdded(Entity<Pane>),
     PaneRemoved,
     ItemAdded {
         item: Box<dyn ItemHandle>,
@@ -724,12 +737,12 @@ pub enum Event {
     ItemRemoved,
     ActiveItemChanged,
     UserSavedItem {
-        pane: WeakView<Pane>,
+        pane: WeakEntity<Pane>,
         item: Box<dyn WeakItemHandle>,
         save_intent: SaveIntent,
     },
     ContactRequestedJoin(u64),
-    WorkspaceCreated(WeakView<Workspace>),
+    WorkspaceCreated(WeakEntity<Workspace>),
     SpawnTask {
         action: Box<SpawnInTerminal>,
     },
@@ -750,14 +763,19 @@ pub enum OpenVisible {
 }
 
 type PromptForNewPath = Box<
-    dyn Fn(&mut Workspace, &mut ViewContext<Workspace>) -> oneshot::Receiver<Option<ProjectPath>>,
+    dyn Fn(
+        &mut Workspace,
+        &mut Window,
+        &mut Context<Workspace>,
+    ) -> oneshot::Receiver<Option<ProjectPath>>,
 >;
 
 type PromptForOpenPath = Box<
     dyn Fn(
         &mut Workspace,
         DirectoryLister,
-        &mut ViewContext<Workspace>,
+        &mut Window,
+        &mut Context<Workspace>,
     ) -> oneshot::Receiver<Option<Vec<PathBuf>>>,
 >;
 
@@ -768,29 +786,29 @@ type PromptForOpenPath = Box<
 /// The `Workspace` owns everybody's state and serves as a default, "global context",
 /// that can be used to register a global action to be triggered from any place in the window.
 pub struct Workspace {
-    weak_self: WeakView<Self>,
-    workspace_actions: Vec<Box<dyn Fn(Div, &mut ViewContext<Self>) -> Div>>,
+    weak_self: WeakEntity<Self>,
+    workspace_actions: Vec<Box<dyn Fn(Div, &mut Window, &mut Context<Self>) -> Div>>,
     zoomed: Option<AnyWeakView>,
     previous_dock_drag_coordinates: Option<Point<Pixels>>,
     zoomed_position: Option<DockPosition>,
     center: PaneGroup,
-    left_dock: View<Dock>,
-    bottom_dock: View<Dock>,
-    right_dock: View<Dock>,
-    panes: Vec<View<Pane>>,
-    panes_by_item: HashMap<EntityId, WeakView<Pane>>,
-    active_pane: View<Pane>,
-    last_active_center_pane: Option<WeakView<Pane>>,
+    left_dock: Entity<Dock>,
+    bottom_dock: Entity<Dock>,
+    right_dock: Entity<Dock>,
+    panes: Vec<Entity<Pane>>,
+    panes_by_item: HashMap<EntityId, WeakEntity<Pane>>,
+    active_pane: Entity<Pane>,
+    last_active_center_pane: Option<WeakEntity<Pane>>,
     last_active_view_id: Option<proto::ViewId>,
-    status_bar: View<StatusBar>,
-    modal_layer: View<ModalLayer>,
+    status_bar: Entity<StatusBar>,
+    modal_layer: Entity<ModalLayer>,
     titlebar_item: Option<AnyView>,
     notifications: Vec<(NotificationId, AnyView)>,
-    project: Model<Project>,
+    project: Entity<Project>,
     follower_states: HashMap<PeerId, FollowerState>,
-    last_leaders_by_pane: HashMap<WeakView<Pane>, PeerId>,
+    last_leaders_by_pane: HashMap<WeakEntity<Pane>, PeerId>,
     window_edited: bool,
-    active_call: Option<(Model<ActiveCall>, Vec<Subscription>)>,
+    active_call: Option<(Entity<ActiveCall>, Vec<Subscription>)>,
     leader_updates_tx: mpsc::UnboundedSender<(PeerId, proto::UpdateFollowers)>,
     database_id: Option<WorkspaceId>,
     app_state: Arc<AppState>,
@@ -820,8 +838,8 @@ pub struct ViewId {
 }
 
 pub struct FollowerState {
-    center_pane: View<Pane>,
-    dock_pane: Option<View<Pane>>,
+    center_pane: Entity<Pane>,
+    dock_pane: Option<Entity<Pane>>,
     active_view_id: Option<ViewId>,
     items_by_leader_view_id: HashMap<ViewId, FollowerView>,
 }
@@ -837,47 +855,49 @@ impl Workspace {
 
     pub fn new(
         workspace_id: Option<WorkspaceId>,
-        project: Model<Project>,
+        project: Entity<Project>,
         app_state: Arc<AppState>,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) -> Self {
-        cx.observe(&project, |_, _, cx| cx.notify()).detach();
-        cx.subscribe(&project, move |this, _, event, cx| {
+        cx.observe_in(&project, window, |_, _, _, cx| cx.notify())
+            .detach();
+        cx.subscribe_in(&project, window, move |this, _, event, window, cx| {
             match event {
                 project::Event::RemoteIdChanged(_) => {
-                    this.update_window_title(cx);
+                    this.update_window_title(window, cx);
                 }
 
                 project::Event::CollaboratorLeft(peer_id) => {
-                    this.collaborator_left(*peer_id, cx);
+                    this.collaborator_left(*peer_id, window, cx);
                 }
 
                 project::Event::WorktreeRemoved(_) | project::Event::WorktreeAdded(_) => {
-                    this.update_window_title(cx);
-                    this.serialize_workspace(cx);
+                    this.update_window_title(window, cx);
+                    this.serialize_workspace(window, cx);
                 }
 
                 project::Event::DisconnectedFromHost => {
-                    this.update_window_edited(cx);
+                    this.update_window_edited(window, cx);
                     let leaders_to_unfollow =
                         this.follower_states.keys().copied().collect::<Vec<_>>();
                     for leader_id in leaders_to_unfollow {
-                        this.unfollow(leader_id, cx);
+                        this.unfollow(leader_id, window, cx);
                     }
                 }
 
                 project::Event::DisconnectedFromSshRemote => {
-                    this.update_window_edited(cx);
+                    this.update_window_edited(window, cx);
                 }
 
                 project::Event::Closed => {
-                    cx.remove_window();
+                    window.remove_window();
                 }
 
                 project::Event::DeletedEntry(_, entry_id) => {
                     for pane in this.panes.iter() {
                         pane.update(cx, |pane, cx| {
-                            pane.handle_deleted_project_item(*entry_id, cx)
+                            pane.handle_deleted_project_item(*entry_id, window, cx)
                         });
                     }
                 }
@@ -888,7 +908,7 @@ impl Workspace {
                 } => this.show_notification(
                     NotificationId::named(notification_id.clone()),
                     cx,
-                    |cx| cx.new_view(|_| MessageNotification::new(message.clone())),
+                    |cx| cx.new(|_| MessageNotification::new(message.clone())),
                 ),
 
                 project::Event::HideToast { notification_id } => {
@@ -905,11 +925,7 @@ impl Workspace {
                     this.show_notification(
                         NotificationId::composite::<LanguageServerPrompt>(id as usize),
                         cx,
-                        |cx| {
-                            cx.new_view(|_| {
-                                notifications::LanguageServerPrompt::new(request.clone())
-                            })
-                        },
+                        |cx| cx.new(|_| notifications::LanguageServerPrompt::new(request.clone())),
                     );
                 }
 
@@ -919,40 +935,43 @@ impl Workspace {
         })
         .detach();
 
-        cx.on_focus_lost(|this, cx| {
+        cx.on_focus_lost(window, |this, window, cx| {
             let focus_handle = this.focus_handle(cx);
-            cx.focus(&focus_handle);
+            window.focus(&focus_handle);
         })
         .detach();
 
-        let weak_handle = cx.view().downgrade();
+        let weak_handle = cx.model().downgrade();
         let pane_history_timestamp = Arc::new(AtomicUsize::new(0));
 
-        let center_pane = cx.new_view(|cx| {
+        let center_pane = cx.new(|cx| {
             let mut center_pane = Pane::new(
                 weak_handle.clone(),
                 project.clone(),
                 pane_history_timestamp.clone(),
                 None,
                 NewFile.boxed_clone(),
+                window,
                 cx,
             );
-            center_pane.set_can_split(Some(Arc::new(|_, _, _| true)));
+            center_pane.set_can_split(Some(Arc::new(|_, _, _, _| true)));
             center_pane
         });
-        cx.subscribe(&center_pane, Self::handle_pane_event).detach();
+        cx.subscribe_in(&center_pane, window, Self::handle_pane_event)
+            .detach();
+
+        window.focus(&center_pane.focus_handle(cx));
 
-        cx.focus_view(&center_pane);
         cx.emit(Event::PaneAdded(center_pane.clone()));
 
-        let window_handle = cx.window_handle().downcast::<Workspace>().unwrap();
+        let window_handle = window.window_handle().downcast::<Workspace>().unwrap();
         app_state.workspace_store.update(cx, |store, _| {
             store.workspaces.insert(window_handle);
         });
 
         let mut current_user = app_state.user_store.read(cx).watch_current_user();
         let mut connection_status = app_state.client.status();
-        let _observe_current_user = cx.spawn(|this, mut cx| async move {
+        let _observe_current_user = cx.spawn_in(window, |this, mut cx| async move {
             current_user.next().await;
             connection_status.next().await;
             let mut stream =
@@ -968,7 +987,7 @@ impl Workspace {
         // that each asynchronous operation can be run in order.
         let (leader_updates_tx, mut leader_updates_rx) =
             mpsc::unbounded::<(PeerId, proto::UpdateFollowers)>();
-        let _apply_leader_updates = cx.spawn(|this, mut cx| async move {
+        let _apply_leader_updates = cx.spawn_in(window, |this, mut cx| async move {
             while let Some((leader_id, update)) = leader_updates_rx.next().await {
                 Self::process_leader_update(&this, leader_id, update, &mut cx)
                     .await
@@ -980,85 +999,85 @@ impl Workspace {
 
         cx.emit(Event::WorkspaceCreated(weak_handle.clone()));
 
-        let left_dock = Dock::new(DockPosition::Left, cx);
-        let bottom_dock = Dock::new(DockPosition::Bottom, cx);
-        let right_dock = Dock::new(DockPosition::Right, cx);
-        let left_dock_buttons = cx.new_view(|cx| PanelButtons::new(left_dock.clone(), cx));
-        let bottom_dock_buttons = cx.new_view(|cx| PanelButtons::new(bottom_dock.clone(), cx));
-        let right_dock_buttons = cx.new_view(|cx| PanelButtons::new(right_dock.clone(), cx));
-        let status_bar = cx.new_view(|cx| {
-            let mut status_bar = StatusBar::new(&center_pane.clone(), cx);
-            status_bar.add_left_item(left_dock_buttons, cx);
-            status_bar.add_right_item(right_dock_buttons, cx);
-            status_bar.add_right_item(bottom_dock_buttons, cx);
+        let left_dock = Dock::new(DockPosition::Left, window, cx);
+        let bottom_dock = Dock::new(DockPosition::Bottom, window, cx);
+        let right_dock = Dock::new(DockPosition::Right, window, cx);
+        let left_dock_buttons = cx.new(|cx| PanelButtons::new(left_dock.clone(), cx));
+        let bottom_dock_buttons = cx.new(|cx| PanelButtons::new(bottom_dock.clone(), cx));
+        let right_dock_buttons = cx.new(|cx| PanelButtons::new(right_dock.clone(), cx));
+        let status_bar = cx.new(|cx| {
+            let mut status_bar = StatusBar::new(&center_pane.clone(), window, cx);
+            status_bar.add_left_item(left_dock_buttons, window, cx);
+            status_bar.add_right_item(right_dock_buttons, window, cx);
+            status_bar.add_right_item(bottom_dock_buttons, window, cx);
             status_bar
         });
 
-        let modal_layer = cx.new_view(|_| ModalLayer::new());
+        let modal_layer = cx.new(|_| ModalLayer::new());
 
         let session_id = app_state.session.read(cx).id().to_owned();
 
         let mut active_call = None;
         if let Some(call) = ActiveCall::try_global(cx) {
             let call = call.clone();
-            let subscriptions = vec![cx.subscribe(&call, Self::on_active_call_event)];
+            let subscriptions = vec![cx.subscribe_in(&call, window, Self::on_active_call_event)];
             active_call = Some((call, subscriptions));
         }
 
         let (serializable_items_tx, serializable_items_rx) =
             mpsc::unbounded::<Box<dyn SerializableItemHandle>>();
-        let _items_serializer = cx.spawn(|this, mut cx| async move {
+        let _items_serializer = cx.spawn_in(window, |this, mut cx| async move {
             Self::serialize_items(&this, serializable_items_rx, &mut cx).await
         });
 
         let subscriptions = vec![
-            cx.observe_window_activation(Self::on_window_activation_changed),
-            cx.observe_window_bounds(move |this, cx| {
+            cx.observe_window_activation(window, Self::on_window_activation_changed),
+            cx.observe_window_bounds(window, move |this, window, cx| {
                 if this.bounds_save_task_queued.is_some() {
                     return;
                 }
-                this.bounds_save_task_queued = Some(cx.spawn(|this, mut cx| async move {
-                    cx.background_executor()
-                        .timer(Duration::from_millis(100))
-                        .await;
-                    this.update(&mut cx, |this, cx| {
-                        if let Some(display) = cx.display() {
-                            if let Ok(display_uuid) = display.uuid() {
-                                let window_bounds = cx.inner_window_bounds();
-                                if let Some(database_id) = workspace_id {
-                                    cx.background_executor()
-                                        .spawn(DB.set_window_open_status(
-                                            database_id,
-                                            SerializedWindowBounds(window_bounds),
-                                            display_uuid,
-                                        ))
-                                        .detach_and_log_err(cx);
+                this.bounds_save_task_queued =
+                    Some(cx.spawn_in(window, |this, mut cx| async move {
+                        cx.background_executor()
+                            .timer(Duration::from_millis(100))
+                            .await;
+                        this.update_in(&mut cx, |this, window, cx| {
+                            if let Some(display) = window.display(cx) {
+                                if let Ok(display_uuid) = display.uuid() {
+                                    let window_bounds = window.inner_window_bounds();
+                                    if let Some(database_id) = workspace_id {
+                                        cx.background_executor()
+                                            .spawn(DB.set_window_open_status(
+                                                database_id,
+                                                SerializedWindowBounds(window_bounds),
+                                                display_uuid,
+                                            ))
+                                            .detach_and_log_err(cx);
+                                    }
                                 }
                             }
-                        }
-                        this.bounds_save_task_queued.take();
-                    })
-                    .ok();
-                }));
+                            this.bounds_save_task_queued.take();
+                        })
+                        .ok();
+                    }));
                 cx.notify();
             }),
-            cx.observe_window_appearance(|_, cx| {
-                let window_appearance = cx.appearance();
+            cx.observe_window_appearance(window, |_, window, cx| {
+                let window_appearance = window.appearance();
 
                 *SystemAppearance::global_mut(cx) = SystemAppearance(window_appearance.into());
 
                 ThemeSettings::reload_current_theme(cx);
             }),
-            cx.on_release(|this, window, cx| {
-                this.app_state.workspace_store.update(cx, |store, _| {
-                    let window = window.downcast::<Self>().unwrap();
-                    store.workspaces.remove(&window);
+            cx.on_release(move |this, cx| {
+                this.app_state.workspace_store.update(cx, move |store, _| {
+                    store.workspaces.remove(&window_handle.clone());
                 })
             }),
         ];
 
-        cx.defer(|this, cx| {
-            this.update_window_title(cx);
+        cx.defer_in(window, |this, window, cx| {
+            this.update_window_title(window, cx);
             this.show_initial_notifications(cx);
         });
         Workspace {
@@ -1112,7 +1131,7 @@ impl Workspace {
         app_state: Arc<AppState>,
         requesting_window: Option<WindowHandle<Workspace>>,
         env: Option<HashMap<String, String>>,
-        cx: &mut AppContext,
+        cx: &mut App,
     ) -> Task<
         anyhow::Result<(
             WindowHandle<Workspace>,
@@ -1196,12 +1215,13 @@ impl Workspace {
                     .await;
             }
             let window = if let Some(window) = requesting_window {
-                cx.update_window(window.into(), |_, cx| {
-                    cx.replace_root_view(|cx| {
+                cx.update_window(window.into(), |_, window, cx| {
+                    window.replace_root_model(cx, |window, cx| {
                         Workspace::new(
                             Some(workspace_id),
                             project_handle.clone(),
                             app_state.clone(),
+                            window,
                             cx,
                         )
                     });
@@ -1238,10 +1258,15 @@ impl Workspace {
                 cx.open_window(options, {
                     let app_state = app_state.clone();
                     let project_handle = project_handle.clone();
-                    move |cx| {
-                        cx.new_view(|cx| {
-                            let mut workspace =
-                                Workspace::new(Some(workspace_id), project_handle, app_state, cx);
+                    move |window, cx| {
+                        cx.new(|cx| {
+                            let mut workspace = Workspace::new(
+                                Some(workspace_id),
+                                project_handle,
+                                app_state,
+                                window,
+                                cx,
+                            );
                             workspace.centered_layout = centered_layout;
                             workspace
                         })
@@ -1251,32 +1276,32 @@ impl Workspace {
 
             notify_if_database_failed(window, &mut cx);
             let opened_items = window
-                .update(&mut cx, |_workspace, cx| {
-                    open_items(serialized_workspace, project_paths, cx)
+                .update(&mut cx, |_workspace, window, cx| {
+                    open_items(serialized_workspace, project_paths, window, cx)
                 })?
                 .await
                 .unwrap_or_default();
 
             window
-                .update(&mut cx, |_, cx| cx.activate_window())
+                .update(&mut cx, |_, window, _| window.activate_window())
                 .log_err();
             Ok((window, opened_items))
         })
     }
 
-    pub fn weak_handle(&self) -> WeakView<Self> {
+    pub fn weak_handle(&self) -> WeakEntity<Self> {
         self.weak_self.clone()
     }
 
-    pub fn left_dock(&self) -> &View<Dock> {
+    pub fn left_dock(&self) -> &Entity<Dock> {
         &self.left_dock
     }
 
-    pub fn bottom_dock(&self) -> &View<Dock> {
+    pub fn bottom_dock(&self) -> &Entity<Dock> {
         &self.bottom_dock
     }
 
-    pub fn right_dock(&self) -> &View<Dock> {
+    pub fn right_dock(&self) -> &Entity<Dock> {
         &self.right_dock
     }
 
@@ -1284,23 +1309,28 @@ impl Workspace {
         self.window_edited
     }
 
-    pub fn add_panel<T: Panel>(&mut self, panel: View<T>, cx: &mut ViewContext<Self>) {
-        let focus_handle = panel.focus_handle(cx);
-        cx.on_focus_in(&focus_handle, Self::handle_panel_focused)
+    pub fn add_panel<T: Panel>(
+        &mut self,
+        panel: Entity<T>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        let focus_handle = panel.panel_focus_handle(cx);
+        cx.on_focus_in(&focus_handle, window, Self::handle_panel_focused)
             .detach();
 
-        let dock = match panel.position(cx) {
+        let dock = match panel.position(window, cx) {
             DockPosition::Left => &self.left_dock,
             DockPosition::Bottom => &self.bottom_dock,
             DockPosition::Right => &self.right_dock,
         };
 
         dock.update(cx, |dock, cx| {
-            dock.add_panel(panel, self.weak_self.clone(), cx)
+            dock.add_panel(panel, self.weak_self.clone(), window, cx)
         });
     }
 
-    pub fn status_bar(&self) -> &View<StatusBar> {
+    pub fn status_bar(&self) -> &Entity<StatusBar> {
         &self.status_bar
     }
 
@@ -1308,17 +1338,17 @@ impl Workspace {
         &self.app_state
     }
 
-    pub fn user_store(&self) -> &Model<UserStore> {
+    pub fn user_store(&self) -> &Entity<UserStore> {
         &self.app_state.user_store
     }
 
-    pub fn project(&self) -> &Model<Project> {
+    pub fn project(&self) -> &Entity<Project> {
         &self.project
     }
 
     pub fn recent_navigation_history_iter(
         &self,
-        cx: &AppContext,
+        cx: &App,
     ) -> impl Iterator<Item = (ProjectPath, Option<PathBuf>)> {
         let mut abs_paths_opened: HashMap<PathBuf, HashSet<ProjectPath>> = HashMap::default();
         let mut history: HashMap<ProjectPath, (Option<PathBuf>, usize)> = HashMap::default();
@@ -1372,7 +1402,7 @@ impl Workspace {
     pub fn recent_navigation_history(
         &self,
         limit: Option<usize>,
-        cx: &AppContext,
+        cx: &App,
     ) -> Vec<(ProjectPath, Option<PathBuf>)> {
         self.recent_navigation_history_iter(cx)
             .take(limit.unwrap_or(usize::MAX))
@@ -1381,13 +1411,14 @@ impl Workspace {
 
     fn navigate_history(
         &mut self,
-        pane: WeakView<Pane>,
+        pane: WeakEntity<Pane>,
         mode: NavigationMode,
-        cx: &mut ViewContext<Workspace>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
     ) -> Task<Result<()>> {
         let to_load = if let Some(pane) = pane.upgrade() {
             pane.update(cx, |pane, cx| {
-                pane.focus(cx);
+                window.focus(&pane.focus_handle(cx));
                 loop {
                     // Retrieve the weak item handle from the history.
                     let entry = pane.nav_history_mut().pop(mode, cx)?;
@@ -1400,12 +1431,12 @@ impl Workspace {
                     {
                         let prev_active_item_index = pane.active_item_index();
                         pane.nav_history_mut().set_mode(mode);
-                        pane.activate_item(index, true, true, cx);
+                        pane.activate_item(index, true, true, window, cx);
                         pane.nav_history_mut().set_mode(NavigationMode::Normal);
 
                         let mut navigated = prev_active_item_index != pane.active_item_index();
                         if let Some(data) = entry.data {
-                            navigated |= pane.active_item()?.navigate(data, cx);
+                            navigated |= pane.active_item()?.navigate(data, window, cx);
                         }
 
                         if navigated {
@@ -1427,9 +1458,9 @@ impl Workspace {
 
         if let Some((project_path, abs_path, entry)) = to_load {
             // If the item was no longer present, then load it again from its previous path, first try the local path
-            let open_by_project_path = self.load_path(project_path.clone(), cx);
+            let open_by_project_path = self.load_path(project_path.clone(), window, cx);
 
-            cx.spawn(|workspace, mut cx| async move {
+            cx.spawn_in(window, |workspace, mut cx| async move {
                 let open_by_project_path = open_by_project_path.await;
                 let mut navigated = false;
                 match open_by_project_path
@@ -1441,19 +1472,19 @@ impl Workspace {
                             pane.active_item().map(|p| p.item_id())
                         })?;
 
-                        pane.update(&mut cx, |pane, cx| {
+                        pane.update_in(&mut cx, |pane, window, cx| {
                             let item = pane.open_item(
                                 project_entry_id,
                                 true,
                                 entry.is_preview,
                                 None,
-                                cx,
+                                window, cx,
                                 build_item,
                             );
                             navigated |= Some(item.item_id()) != prev_active_item_id;
                             pane.nav_history_mut().set_mode(NavigationMode::Normal);
                             if let Some(data) = entry.data {
-                                navigated |= item.navigate(data, cx);
+                                navigated |= item.navigate(data, window, cx);
                             }
                         })?;
                     }
@@ -1465,19 +1496,19 @@ impl Workspace {
                                 pane.nav_history_mut().set_mode(mode);
                                 pane.active_item().map(|p| p.item_id())
                             })?;
-                            let open_by_abs_path = workspace.update(&mut cx, |workspace, cx| {
-                                workspace.open_abs_path(abs_path.clone(), false, cx)
+                            let open_by_abs_path = workspace.update_in(&mut cx, |workspace, window, cx| {
+                                workspace.open_abs_path(abs_path.clone(), false, window, cx)
                             })?;
                             match open_by_abs_path
                                 .await
                                 .with_context(|| format!("Navigating to {abs_path:?}"))
                             {
                                 Ok(item) => {
-                                    pane.update(&mut cx, |pane, cx| {
+                                    pane.update_in(&mut cx, |pane, window, cx| {
                                         navigated |= Some(item.item_id()) != prev_active_item_id;
                                         pane.nav_history_mut().set_mode(NavigationMode::Normal);
                                         if let Some(data) = entry.data {
-                                            navigated |= item.navigate(data, cx);
+                                            navigated |= item.navigate(data, window, cx);
                                         }
                                     })?;
                                 }

crates/workspace/src/workspace_settings.rs 🔗

@@ -2,7 +2,7 @@ use std::num::NonZeroUsize;
 
 use anyhow::Result;
 use collections::HashMap;
-use gpui::AppContext;
+use gpui::App;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsSources};
@@ -208,7 +208,7 @@ impl Settings for WorkspaceSettings {
 
     type FileContent = WorkspaceSettingsContent;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
         sources.json_merge()
     }
 }
@@ -218,7 +218,7 @@ impl Settings for TabBarSettings {
 
     type FileContent = TabBarSettingsContent;
 
-    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
         sources.json_merge()
     }
 }

crates/worktree/src/worktree.rs 🔗

@@ -26,8 +26,7 @@ use git::{
     GitHostingProviderRegistry, COOKIES, DOT_GIT, FSMONITOR_DAEMON, GITIGNORE,
 };
 use gpui::{
-    AppContext, AsyncAppContext, BackgroundExecutor, Context, EventEmitter, Model, ModelContext,
-    Task,
+    App, AppContext as _, AsyncAppContext, BackgroundExecutor, Context, Entity, EventEmitter, Task,
 };
 use ignore::IgnoreStack;
 use language::DiskState;
@@ -546,7 +545,7 @@ impl Worktree {
         fs: Arc<dyn Fs>,
         next_entry_id: Arc<AtomicUsize>,
         cx: &mut AsyncAppContext,
-    ) -> Result<Model<Self>> {
+    ) -> Result<Entity<Self>> {
         let abs_path = path.into();
         let metadata = fs
             .metadata(&abs_path)
@@ -562,7 +561,7 @@ impl Worktree {
 
         let root_file_handle = fs.open_handle(&abs_path).await.log_err();
 
-        cx.new_model(move |cx: &mut ModelContext<Worktree>| {
+        cx.new(move |cx: &mut Context<Worktree>| {
             let mut snapshot = LocalSnapshot {
                 ignores_by_parent_abs_path: Default::default(),
                 git_repositories: Default::default(),
@@ -636,9 +635,9 @@ impl Worktree {
         replica_id: ReplicaId,
         worktree: proto::WorktreeMetadata,
         client: AnyProtoClient,
-        cx: &mut AppContext,
-    ) -> Model<Self> {
-        cx.new_model(|cx: &mut ModelContext<Self>| {
+        cx: &mut App,
+    ) -> Entity<Self> {
+        cx.new(|cx: &mut Context<Self>| {
             let snapshot = Snapshot::new(
                 worktree.id,
                 worktree.root_name,
@@ -765,7 +764,7 @@ impl Worktree {
         !self.is_local()
     }
 
-    pub fn settings_location(&self, _: &ModelContext<Self>) -> SettingsLocation<'static> {
+    pub fn settings_location(&self, _: &Context<Self>) -> SettingsLocation<'static> {
         SettingsLocation {
             worktree_id: self.id(),
             path: Path::new(EMPTY_PATH),
@@ -823,17 +822,13 @@ impl Worktree {
         }
     }
 
-    pub fn root_file(&self, cx: &ModelContext<Self>) -> Option<Arc<File>> {
+    pub fn root_file(&self, cx: &Context<Self>) -> Option<Arc<File>> {
         let entry = self.root_entry()?;
-        Some(File::for_entry(entry.clone(), cx.handle()))
+        Some(File::for_entry(entry.clone(), cx.model()))
     }
 
-    pub fn observe_updates<F, Fut>(
-        &mut self,
-        project_id: u64,
-        cx: &ModelContext<Worktree>,
-        callback: F,
-    ) where
+    pub fn observe_updates<F, Fut>(&mut self, project_id: u64, cx: &Context<Worktree>, callback: F)
+    where
         F: 'static + Send + Fn(proto::UpdateWorktree) -> Fut,
         Fut: 'static + Send + Future<Output = bool>,
     {
@@ -862,7 +857,7 @@ impl Worktree {
         }
     }
 
-    pub fn load_file(&self, path: &Path, cx: &ModelContext<Worktree>) -> Task<Result<LoadedFile>> {
+    pub fn load_file(&self, path: &Path, cx: &Context<Worktree>) -> Task<Result<LoadedFile>> {
         match self {
             Worktree::Local(this) => this.load_file(path, cx),
             Worktree::Remote(_) => {
@@ -871,7 +866,7 @@ impl Worktree {
         }
     }
 
-    pub fn load_staged_file(&self, path: &Path, cx: &AppContext) -> Task<Result<Option<String>>> {
+    pub fn load_staged_file(&self, path: &Path, cx: &App) -> Task<Result<Option<String>>> {
         match self {
             Worktree::Local(this) => {
                 let path = Arc::from(path);
@@ -898,7 +893,7 @@ impl Worktree {
     pub fn load_binary_file(
         &self,
         path: &Path,
-        cx: &ModelContext<Worktree>,
+        cx: &Context<Worktree>,
     ) -> Task<Result<LoadedBinaryFile>> {
         match self {
             Worktree::Local(this) => this.load_binary_file(path, cx),
@@ -913,7 +908,7 @@ impl Worktree {
         path: &Path,
         text: Rope,
         line_ending: LineEnding,
-        cx: &ModelContext<Worktree>,
+        cx: &Context<Worktree>,
     ) -> Task<Result<Arc<File>>> {
         match self {
             Worktree::Local(this) => this.write_file(path, text, line_ending, cx),
@@ -927,7 +922,7 @@ impl Worktree {
         &mut self,
         path: impl Into<Arc<Path>>,
         is_directory: bool,
-        cx: &ModelContext<Worktree>,
+        cx: &Context<Worktree>,
     ) -> Task<Result<CreatedEntry>> {
         let path = path.into();
         let worktree_id = self.id();
@@ -972,7 +967,7 @@ impl Worktree {
         &mut self,
         entry_id: ProjectEntryId,
         trash: bool,
-        cx: &mut ModelContext<Worktree>,
+        cx: &mut Context<Worktree>,
     ) -> Option<Task<Result<()>>> {
         let task = match self {
             Worktree::Local(this) => this.delete_entry(entry_id, trash, cx),
@@ -1007,7 +1002,7 @@ impl Worktree {
         &mut self,
         entry_id: ProjectEntryId,
         new_path: impl Into<Arc<Path>>,
-        cx: &ModelContext<Self>,
+        cx: &Context<Self>,
     ) -> Task<Result<CreatedEntry>> {
         let new_path = new_path.into();
         match self {
@@ -1021,7 +1016,7 @@ impl Worktree {
         entry_id: ProjectEntryId,
         relative_worktree_source_path: Option<PathBuf>,
         new_path: impl Into<Arc<Path>>,
-        cx: &ModelContext<Self>,
+        cx: &Context<Self>,
     ) -> Task<Result<Option<Entry>>> {
         let new_path = new_path.into();
         match self {
@@ -1064,7 +1059,7 @@ impl Worktree {
         target_directory: PathBuf,
         paths: Vec<Arc<Path>>,
         overwrite_existing_files: bool,
-        cx: &ModelContext<Worktree>,
+        cx: &Context<Worktree>,
     ) -> Task<Result<Vec<ProjectEntryId>>> {
         match self {
             Worktree::Local(this) => {
@@ -1079,7 +1074,7 @@ impl Worktree {
     pub fn expand_entry(
         &mut self,
         entry_id: ProjectEntryId,
-        cx: &ModelContext<Worktree>,
+        cx: &Context<Worktree>,
     ) -> Option<Task<Result<()>>> {
         match self {
             Worktree::Local(this) => this.expand_entry(entry_id, cx),
@@ -1103,7 +1098,7 @@ impl Worktree {
     }
 
     pub async fn handle_create_entry(
-        this: Model<Self>,
+        this: Entity<Self>,
         request: proto::CreateProjectEntry,
         mut cx: AsyncAppContext,
     ) -> Result<proto::ProjectEntryResponse> {
@@ -1123,7 +1118,7 @@ impl Worktree {
     }
 
     pub async fn handle_delete_entry(
-        this: Model<Self>,
+        this: Entity<Self>,
         request: proto::DeleteProjectEntry,
         mut cx: AsyncAppContext,
     ) -> Result<proto::ProjectEntryResponse> {
@@ -1145,7 +1140,7 @@ impl Worktree {
     }
 
     pub async fn handle_expand_entry(
-        this: Model<Self>,
+        this: Entity<Self>,
         request: proto::ExpandProjectEntry,
         mut cx: AsyncAppContext,
     ) -> Result<proto::ExpandProjectEntryResponse> {
@@ -1160,7 +1155,7 @@ impl Worktree {
     }
 
     pub async fn handle_rename_entry(
-        this: Model<Self>,
+        this: Entity<Self>,
         request: proto::RenameProjectEntry,
         mut cx: AsyncAppContext,
     ) -> Result<proto::ProjectEntryResponse> {
@@ -1184,7 +1179,7 @@ impl Worktree {
     }
 
     pub async fn handle_copy_entry(
-        this: Model<Self>,
+        this: Entity<Self>,
         request: proto::CopyProjectEntry,
         mut cx: AsyncAppContext,
     ) -> Result<proto::ProjectEntryResponse> {
@@ -1222,7 +1217,7 @@ impl LocalWorktree {
         !self.share_private_files && self.settings.is_path_private(path)
     }
 
-    fn restart_background_scanners(&mut self, cx: &ModelContext<Worktree>) {
+    fn restart_background_scanners(&mut self, cx: &Context<Worktree>) {
         let (scan_requests_tx, scan_requests_rx) = channel::unbounded();
         let (path_prefixes_to_scan_tx, path_prefixes_to_scan_rx) = channel::unbounded();
         self.scan_requests_tx = scan_requests_tx;
@@ -1244,7 +1239,7 @@ impl LocalWorktree {
         &mut self,
         scan_requests_rx: channel::Receiver<ScanRequest>,
         path_prefixes_to_scan_rx: channel::Receiver<Arc<Path>>,
-        cx: &ModelContext<Worktree>,
+        cx: &Context<Worktree>,
     ) {
         let snapshot = self.snapshot();
         let share_private_files = self.share_private_files;
@@ -1345,7 +1340,7 @@ impl LocalWorktree {
         &mut self,
         new_snapshot: LocalSnapshot,
         entry_changes: UpdatedEntriesSet,
-        cx: &mut ModelContext<Worktree>,
+        cx: &mut Context<Worktree>,
     ) {
         let repo_changes = self.changed_repos(&self.snapshot, &new_snapshot);
         self.snapshot = new_snapshot;
@@ -1501,7 +1496,7 @@ impl LocalWorktree {
     fn load_binary_file(
         &self,
         path: &Path,
-        cx: &ModelContext<Worktree>,
+        cx: &Context<Worktree>,
     ) -> Task<Result<LoadedBinaryFile>> {
         let path = Arc::from(path);
         let abs_path = self.absolutize(&path);
@@ -1546,7 +1541,7 @@ impl LocalWorktree {
         })
     }
 
-    fn load_file(&self, path: &Path, cx: &ModelContext<Worktree>) -> Task<Result<LoadedFile>> {
+    fn load_file(&self, path: &Path, cx: &Context<Worktree>) -> Task<Result<LoadedFile>> {
         let path = Arc::from(path);
         let abs_path = self.absolutize(&path);
         let fs = self.fs.clone();
@@ -1606,7 +1601,7 @@ impl LocalWorktree {
         &self,
         path: impl Into<Arc<Path>>,
         is_dir: bool,
-        cx: &ModelContext<Worktree>,
+        cx: &Context<Worktree>,
     ) -> Task<Result<CreatedEntry>> {
         let path = path.into();
         let abs_path = match self.absolutize(&path) {
@@ -1671,7 +1666,7 @@ impl LocalWorktree {
         path: impl Into<Arc<Path>>,
         text: Rope,
         line_ending: LineEnding,
-        cx: &ModelContext<Worktree>,
+        cx: &Context<Worktree>,
     ) -> Task<Result<Arc<File>>> {
         let path = path.into();
         let fs = self.fs.clone();
@@ -1726,7 +1721,7 @@ impl LocalWorktree {
         &self,
         entry_id: ProjectEntryId,
         trash: bool,
-        cx: &ModelContext<Worktree>,
+        cx: &Context<Worktree>,
     ) -> Option<Task<Result<()>>> {
         let entry = self.entry_for_id(entry_id)?.clone();
         let abs_path = self.absolutize(&entry.path);
@@ -1778,7 +1773,7 @@ impl LocalWorktree {
         &self,
         entry_id: ProjectEntryId,
         new_path: impl Into<Arc<Path>>,
-        cx: &ModelContext<Worktree>,
+        cx: &Context<Worktree>,
     ) -> Task<Result<CreatedEntry>> {
         let old_path = match self.entry_for_id(entry_id) {
             Some(entry) => entry.path.clone(),
@@ -1836,7 +1831,7 @@ impl LocalWorktree {
         entry_id: ProjectEntryId,
         relative_worktree_source_path: Option<PathBuf>,
         new_path: impl Into<Arc<Path>>,
-        cx: &ModelContext<Worktree>,
+        cx: &Context<Worktree>,
     ) -> Task<Result<Option<Entry>>> {
         let old_path = match self.entry_for_id(entry_id) {
             Some(entry) => entry.path.clone(),
@@ -1877,7 +1872,7 @@ impl LocalWorktree {
         target_directory: PathBuf,
         paths: Vec<Arc<Path>>,
         overwrite_existing_files: bool,
-        cx: &ModelContext<Worktree>,
+        cx: &Context<Worktree>,
     ) -> Task<Result<Vec<ProjectEntryId>>> {
         let worktree_path = self.abs_path().clone();
         let fs = self.fs.clone();
@@ -1956,7 +1951,7 @@ impl LocalWorktree {
     fn expand_entry(
         &self,
         entry_id: ProjectEntryId,
-        cx: &ModelContext<Worktree>,
+        cx: &Context<Worktree>,
     ) -> Option<Task<Result<()>>> {
         let path = self.entry_for_id(entry_id)?.path.clone();
         let mut refresh = self.refresh_entries_for_paths(vec![path]);
@@ -1985,7 +1980,7 @@ impl LocalWorktree {
         &self,
         path: Arc<Path>,
         old_path: Option<Arc<Path>>,
-        cx: &ModelContext<Worktree>,
+        cx: &Context<Worktree>,
     ) -> Task<Result<Option<Entry>>> {
         if self.settings.is_path_excluded(&path) {
             return Task::ready(Ok(None));
@@ -2009,7 +2004,7 @@ impl LocalWorktree {
         })
     }
 
-    fn observe_updates<F, Fut>(&mut self, project_id: u64, cx: &ModelContext<Worktree>, callback: F)
+    fn observe_updates<F, Fut>(&mut self, project_id: u64, cx: &Context<Worktree>, callback: F)
     where
         F: 'static + Send + Fn(proto::UpdateWorktree) -> Fut,
         Fut: Send + Future<Output = bool>,
@@ -2064,7 +2059,7 @@ impl LocalWorktree {
         });
     }
 
-    pub fn share_private_files(&mut self, cx: &ModelContext<Worktree>) {
+    pub fn share_private_files(&mut self, cx: &Context<Worktree>) {
         self.share_private_files = true;
         self.restart_background_scanners(cx);
     }
@@ -2093,7 +2088,7 @@ impl RemoteWorktree {
         }
     }
 
-    fn observe_updates<F, Fut>(&mut self, project_id: u64, cx: &ModelContext<Worktree>, callback: F)
+    fn observe_updates<F, Fut>(&mut self, project_id: u64, cx: &Context<Worktree>, callback: F)
     where
         F: 'static + Send + Fn(proto::UpdateWorktree) -> Fut,
         Fut: 'static + Send + Future<Output = bool>,
@@ -2159,7 +2154,7 @@ impl RemoteWorktree {
         &mut self,
         entry: proto::Entry,
         scan_id: usize,
-        cx: &ModelContext<Worktree>,
+        cx: &Context<Worktree>,
     ) -> Task<Result<Entry>> {
         let wait_for_snapshot = self.wait_for_snapshot(scan_id);
         cx.spawn(|this, mut cx| async move {
@@ -2178,7 +2173,7 @@ impl RemoteWorktree {
         &self,
         entry_id: ProjectEntryId,
         trash: bool,
-        cx: &ModelContext<Worktree>,
+        cx: &Context<Worktree>,
     ) -> Option<Task<Result<()>>> {
         let response = self.client.request(proto::DeleteProjectEntry {
             project_id: self.project_id,
@@ -2207,7 +2202,7 @@ impl RemoteWorktree {
         &self,
         entry_id: ProjectEntryId,
         new_path: impl Into<Arc<Path>>,
-        cx: &ModelContext<Worktree>,
+        cx: &Context<Worktree>,
     ) -> Task<Result<CreatedEntry>> {
         let new_path = new_path.into();
         let response = self.client.request(proto::RenameProjectEntry {
@@ -3402,7 +3397,7 @@ impl fmt::Debug for Snapshot {
 
 #[derive(Clone, PartialEq)]
 pub struct File {
-    pub worktree: Model<Worktree>,
+    pub worktree: Entity<Worktree>,
     pub path: Arc<Path>,
     pub disk_state: DiskState,
     pub entry_id: Option<ProjectEntryId>,
@@ -3427,7 +3422,7 @@ impl language::File for File {
         &self.path
     }
 
-    fn full_path(&self, cx: &AppContext) -> PathBuf {
+    fn full_path(&self, cx: &App) -> PathBuf {
         let mut full_path = PathBuf::new();
         let worktree = self.worktree.read(cx);
 
@@ -3453,13 +3448,13 @@ impl language::File for File {
 
     /// Returns the last component of this handle's absolute path. If this handle refers to the root
     /// of its worktree, then this method will return the name of the worktree itself.
-    fn file_name<'a>(&'a self, cx: &'a AppContext) -> &'a OsStr {
+    fn file_name<'a>(&'a self, cx: &'a App) -> &'a OsStr {
         self.path
             .file_name()
             .unwrap_or_else(|| OsStr::new(&self.worktree.read(cx).root_name))
     }
 
-    fn worktree_id(&self, cx: &AppContext) -> WorktreeId {
+    fn worktree_id(&self, cx: &App) -> WorktreeId {
         self.worktree.read(cx).id()
     }
 
@@ -3467,7 +3462,7 @@ impl language::File for File {
         self
     }
 
-    fn to_proto(&self, cx: &AppContext) -> rpc::proto::File {
+    fn to_proto(&self, cx: &App) -> rpc::proto::File {
         rpc::proto::File {
             worktree_id: self.worktree.read(cx).id().to_proto(),
             entry_id: self.entry_id.map(|id| id.to_proto()),
@@ -3483,7 +3478,7 @@ impl language::File for File {
 }
 
 impl language::LocalFile for File {
-    fn abs_path(&self, cx: &AppContext) -> PathBuf {
+    fn abs_path(&self, cx: &App) -> PathBuf {
         let worktree_path = &self.worktree.read(cx).as_local().unwrap().abs_path;
         if self.path.as_ref() == Path::new("") {
             worktree_path.as_path().to_path_buf()
@@ -3492,7 +3487,7 @@ impl language::LocalFile for File {
         }
     }
 
-    fn load(&self, cx: &AppContext) -> Task<Result<String>> {
+    fn load(&self, cx: &App) -> Task<Result<String>> {
         let worktree = self.worktree.read(cx).as_local().unwrap();
         let abs_path = worktree.absolutize(&self.path);
         let fs = worktree.fs.clone();
@@ -3500,7 +3495,7 @@ impl language::LocalFile for File {
             .spawn(async move { fs.load(&abs_path?).await })
     }
 
-    fn load_bytes(&self, cx: &AppContext) -> Task<Result<Vec<u8>>> {
+    fn load_bytes(&self, cx: &App) -> Task<Result<Vec<u8>>> {
         let worktree = self.worktree.read(cx).as_local().unwrap();
         let abs_path = worktree.absolutize(&self.path);
         let fs = worktree.fs.clone();
@@ -3510,7 +3505,7 @@ impl language::LocalFile for File {
 }
 
 impl File {
-    pub fn for_entry(entry: Entry, worktree: Model<Worktree>) -> Arc<Self> {
+    pub fn for_entry(entry: Entry, worktree: Entity<Worktree>) -> Arc<Self> {
         Arc::new(Self {
             worktree,
             path: entry.path.clone(),
@@ -3527,8 +3522,8 @@ impl File {
 
     pub fn from_proto(
         proto: rpc::proto::File,
-        worktree: Model<Worktree>,
-        cx: &AppContext,
+        worktree: Entity<Worktree>,
+        cx: &App,
     ) -> Result<Self> {
         let worktree_id = worktree
             .read(cx)
@@ -3564,11 +3559,11 @@ impl File {
         file.and_then(|f| f.as_any().downcast_ref())
     }
 
-    pub fn worktree_id(&self, cx: &AppContext) -> WorktreeId {
+    pub fn worktree_id(&self, cx: &App) -> WorktreeId {
         self.worktree.read(cx).id()
     }
 
-    pub fn project_entry_id(&self, _: &AppContext) -> Option<ProjectEntryId> {
+    pub fn project_entry_id(&self, _: &App) -> Option<ProjectEntryId> {
         match self.disk_state {
             DiskState::Deleted => None,
             _ => self.entry_id,
@@ -5467,7 +5462,7 @@ pub trait WorktreeModelHandle {
     ) -> futures::future::LocalBoxFuture<'a, ()>;
 }
 
-impl WorktreeModelHandle for Model<Worktree> {
+impl WorktreeModelHandle for Entity<Worktree> {
     // When the worktree's FS event stream sometimes delivers "redundant" events for FS changes that
     // occurred before the worktree was constructed. These events can cause the worktree to perform
     // extra directory scans, and emit extra scan-state notifications.

crates/worktree/src/worktree_settings.rs 🔗

@@ -1,7 +1,7 @@
 use std::path::Path;
 
-use anyhow::Context;
-use gpui::AppContext;
+use anyhow::Context as _;
+use gpui::App;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsSources};
@@ -69,10 +69,7 @@ impl Settings for WorktreeSettings {
 
     type FileContent = WorktreeSettingsContent;
 
-    fn load(
-        sources: SettingsSources<Self::FileContent>,
-        _: &mut AppContext,
-    ) -> anyhow::Result<Self> {
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> anyhow::Result<Self> {
         let result: WorktreeSettingsContent = sources.json_merge()?;
         let mut file_scan_exclusions = result.file_scan_exclusions.unwrap_or_default();
         let mut private_files = result.private_files.unwrap_or_default();

crates/worktree/src/worktree_tests.rs 🔗

@@ -11,7 +11,7 @@ use git::{
     },
     GITIGNORE,
 };
-use gpui::{BorrowAppContext, ModelContext, Task, TestAppContext};
+use gpui::{BorrowAppContext, Context, Task, TestAppContext};
 use parking_lot::Mutex;
 use postage::stream::Stream;
 use pretty_assertions::assert_eq;
@@ -1882,9 +1882,9 @@ async fn test_random_worktree_changes(cx: &mut TestAppContext, mut rng: StdRng)
 
 // The worktree's `UpdatedEntries` event can be used to follow along with
 // all changes to the worktree's snapshot.
-fn check_worktree_change_events(tree: &mut Worktree, cx: &mut ModelContext<Worktree>) {
+fn check_worktree_change_events(tree: &mut Worktree, cx: &mut Context<Worktree>) {
     let mut entries = tree.entries(true, 0).cloned().collect::<Vec<_>>();
-    cx.subscribe(&cx.handle(), move |tree, _, event, _| {
+    cx.subscribe(&cx.model(), move |tree, _, event, _| {
         if let Event::UpdatedEntries(changes) = event {
             for (path, _, change_type) in changes.iter() {
                 let entry = tree.entry_for_path(path).cloned();
@@ -1921,7 +1921,7 @@ fn check_worktree_change_events(tree: &mut Worktree, cx: &mut ModelContext<Workt
 fn randomly_mutate_worktree(
     worktree: &mut Worktree,
     rng: &mut impl Rng,
-    cx: &mut ModelContext<Worktree>,
+    cx: &mut Context<Worktree>,
 ) -> Task<Result<()>> {
     log::info!("mutating worktree");
     let worktree = worktree.as_local_mut().unwrap();

crates/zed/src/main.rs 🔗

@@ -19,9 +19,9 @@ use fs::{Fs, RealFs};
 use futures::{future, StreamExt};
 use git::GitHostingProviderRegistry;
 use gpui::{
-    Action, App, AppContext, AsyncAppContext, Context, DismissEvent, UpdateGlobal as _,
-    VisualContext,
+    Action, App, AppContext as _, Application, AsyncAppContext, DismissEvent, UpdateGlobal as _,
 };
+
 use http_client::{read_proxy_from_env, Uri};
 use language::LanguageRegistry;
 use log::LevelFilter;
@@ -102,22 +102,23 @@ fn files_not_created_on_launch(errors: HashMap<io::ErrorKind, Vec<&Path>>) {
         .collect::<Vec<_>>().join("\n\n");
 
     eprintln!("{message}: {error_details}");
-    App::new().run(move |cx| {
-        if let Ok(window) = cx.open_window(gpui::WindowOptions::default(), |cx| {
-            cx.new_view(|_| gpui::Empty)
+    Application::new().run(move |cx| {
+        if let Ok(window) = cx.open_window(gpui::WindowOptions::default(), |_, cx| {
+            cx.new(|_| gpui::Empty)
         }) {
             window
-                .update(cx, |_, cx| {
-                    let response = cx.prompt(
+                .update(cx, |_, window, cx| {
+                    let response = window.prompt(
                         gpui::PromptLevel::Critical,
                         message,
                         Some(&error_details),
                         &["Exit"],
+                        cx,
                     );
 
-                    cx.spawn(|_, mut cx| async move {
+                    cx.spawn_in(window, |_, mut cx| async move {
                         response.await?;
-                        cx.update(|cx| cx.quit())
+                        cx.update(|_, cx| cx.quit())
                     })
                     .detach_and_log_err(cx);
                 })
@@ -132,7 +133,7 @@ fn fail_to_open_window_async(e: anyhow::Error, cx: &mut AsyncAppContext) {
     cx.update(|cx| fail_to_open_window(e, cx)).log_err();
 }
 
-fn fail_to_open_window(e: anyhow::Error, _cx: &mut AppContext) {
+fn fail_to_open_window(e: anyhow::Error, _cx: &mut App) {
     eprintln!(
         "Zed failed to open a window: {e:?}. See https://zed.dev/docs/linux for troubleshooting steps."
     );
@@ -188,7 +189,7 @@ fn main() {
 
     log::info!("========== starting zed ==========");
 
-    let app = App::new().with_assets(Assets);
+    let app = Application::new().with_assets(Assets);
 
     let system_id = app.background_executor().block(system_id()).ok();
     let installation_id = app.background_executor().block(installation_id()).ok();
@@ -359,8 +360,8 @@ fn main() {
         language::init(cx);
         language_extension::init(extension_host_proxy.clone(), languages.clone());
         languages::init(languages.clone(), node_runtime.clone(), cx);
-        let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
-        let workspace_store = cx.new_model(|cx| WorkspaceStore::new(client.clone(), cx));
+        let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
+        let workspace_store = cx.new(|cx| WorkspaceStore::new(client.clone(), cx));
 
         Client::set_global(client.clone(), cx);
 
@@ -390,7 +391,7 @@ fn main() {
                 }
             }
         }
-        let app_session = cx.new_model(|cx| AppSession::new(session, cx));
+        let app_session = cx.new(|cx| AppSession::new(session, cx));
 
         let app_state = Arc::new(AppState {
             languages: languages.clone(),
@@ -522,8 +523,8 @@ fn main() {
                 for &mut window in cx.windows().iter_mut() {
                     let background_appearance = cx.theme().window_background_appearance();
                     window
-                        .update(cx, |_, cx| {
-                            cx.set_background_appearance(background_appearance)
+                        .update(cx, |_, window, _| {
+                            window.set_background_appearance(background_appearance)
                         })
                         .ok();
                 }
@@ -615,13 +616,13 @@ fn main() {
     });
 }
 
-fn handle_settings_changed(error: Option<anyhow::Error>, cx: &mut AppContext) {
+fn handle_settings_changed(error: Option<anyhow::Error>, cx: &mut App) {
     struct SettingsParseErrorNotification;
     let id = NotificationId::unique::<SettingsParseErrorNotification>();
 
     for workspace in workspace::local_workspace_windows(cx) {
         workspace
-            .update(cx, |workspace, cx| {
+            .update(cx, |workspace, _, cx| {
                 match error.as_ref() {
                     Some(error) => {
                         if let Some(InvalidSettingsError::LocalSettings { .. }) =
@@ -630,13 +631,16 @@ fn handle_settings_changed(error: Option<anyhow::Error>, cx: &mut AppContext) {
                             // Local settings will be displayed by the projects
                         } else {
                             workspace.show_notification(id.clone(), cx, |cx| {
-                                cx.new_view(|_| {
+                                cx.new(|_cx| {
                                     MessageNotification::new(format!(
                                         "Invalid user settings file\n{error}"
                                     ))
                                     .with_click_message("Open settings file")
-                                    .on_click(|cx| {
-                                        cx.dispatch_action(zed_actions::OpenSettings.boxed_clone());
+                                    .on_click(|window, cx| {
+                                        window.dispatch_action(
+                                            zed_actions::OpenSettings.boxed_clone(),
+                                            cx,
+                                        );
                                         cx.emit(DismissEvent);
                                     })
                                 })
@@ -650,7 +654,7 @@ fn handle_settings_changed(error: Option<anyhow::Error>, cx: &mut AppContext) {
     }
 }
 
-fn handle_open_request(request: OpenRequest, app_state: Arc<AppState>, cx: &mut AppContext) {
+fn handle_open_request(request: OpenRequest, app_state: Arc<AppState>, cx: &mut App) {
     if let Some(connection) = request.cli_connection {
         let app_state = app_state.clone();
         cx.spawn(move |cx| handle_cli_connection(connection, app_state, cx))
@@ -722,15 +726,16 @@ fn handle_open_request(request: OpenRequest, app_state: Arc<AppState>, cx: &mut
 
                 let workspace_window =
                     workspace::get_any_active_workspace(app_state, cx.clone()).await?;
-                let workspace = workspace_window.root_view(&cx)?;
+                let workspace = workspace_window.root_model(&cx)?;
 
                 let mut promises = Vec::new();
                 for (channel_id, heading) in request.open_channel_notes {
-                    promises.push(cx.update_window(workspace_window.into(), |_, cx| {
+                    promises.push(cx.update_window(workspace_window.into(), |_, window, cx| {
                         ChannelView::open(
                             client::ChannelId(channel_id),
                             heading,
                             workspace.clone(),
+                            window,
                             cx,
                         )
                         .log_err()
@@ -853,9 +858,14 @@ async fn restore_or_create_workspace(
         cx.update(|cx| show_welcome_view(app_state, cx))?.await?;
     } else {
         cx.update(|cx| {
-            workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
-                Editor::new_file(workspace, &Default::default(), cx)
-            })
+            workspace::open_new(
+                Default::default(),
+                app_state,
+                cx,
+                |workspace, window, cx| {
+                    Editor::new_file(workspace, &Default::default(), window, cx)
+                },
+            )
         })?
         .await?;
     }
@@ -1053,7 +1063,7 @@ impl ToString for IdType {
     }
 }
 
-fn parse_url_arg(arg: &str, cx: &AppContext) -> Result<String> {
+fn parse_url_arg(arg: &str, cx: &App) -> Result<String> {
     match std::fs::canonicalize(Path::new(&arg)) {
         Ok(path) => Ok(format!("file://{}", path.display())),
         Err(error) => {
@@ -1070,7 +1080,7 @@ fn parse_url_arg(arg: &str, cx: &AppContext) -> Result<String> {
     }
 }
 
-fn load_embedded_fonts(cx: &AppContext) {
+fn load_embedded_fonts(cx: &App) {
     let asset_source = cx.asset_source();
     let font_paths = asset_source.list("fonts").unwrap();
     let embedded_fonts = Mutex::new(Vec::new());
@@ -1095,7 +1105,7 @@ fn load_embedded_fonts(cx: &AppContext) {
 }
 
 /// Spawns a background task to load the user themes from the themes directory.
-fn load_user_themes_in_background(fs: Arc<dyn fs::Fs>, cx: &mut AppContext) {
+fn load_user_themes_in_background(fs: Arc<dyn fs::Fs>, cx: &mut App) {
     cx.spawn({
         let fs = fs.clone();
         |cx| async move {
@@ -1129,7 +1139,7 @@ fn load_user_themes_in_background(fs: Arc<dyn fs::Fs>, cx: &mut AppContext) {
 }
 
 /// Spawns a background task to watch the themes directory for changes.
-fn watch_themes(fs: Arc<dyn fs::Fs>, cx: &mut AppContext) {
+fn watch_themes(fs: Arc<dyn fs::Fs>, cx: &mut App) {
     use std::time::Duration;
     cx.spawn(|cx| async move {
         let (mut events, _) = fs
@@ -1158,7 +1168,7 @@ fn watch_themes(fs: Arc<dyn fs::Fs>, cx: &mut AppContext) {
 }
 
 #[cfg(debug_assertions)]
-fn watch_languages(fs: Arc<dyn fs::Fs>, languages: Arc<LanguageRegistry>, cx: &mut AppContext) {
+fn watch_languages(fs: Arc<dyn fs::Fs>, languages: Arc<LanguageRegistry>, cx: &mut App) {
     use std::time::Duration;
 
     let path = {
@@ -1188,10 +1198,10 @@ fn watch_languages(fs: Arc<dyn fs::Fs>, languages: Arc<LanguageRegistry>, cx: &m
 }
 
 #[cfg(not(debug_assertions))]
-fn watch_languages(_fs: Arc<dyn fs::Fs>, _languages: Arc<LanguageRegistry>, _cx: &mut AppContext) {}
+fn watch_languages(_fs: Arc<dyn fs::Fs>, _languages: Arc<LanguageRegistry>, _cx: &mut App) {}
 
 #[cfg(debug_assertions)]
-fn watch_file_types(fs: Arc<dyn fs::Fs>, cx: &mut AppContext) {
+fn watch_file_types(fs: Arc<dyn fs::Fs>, cx: &mut App) {
     use std::time::Duration;
 
     use file_icons::FileIcons;
@@ -1220,4 +1230,4 @@ fn watch_file_types(fs: Arc<dyn fs::Fs>, cx: &mut AppContext) {
 }
 
 #[cfg(not(debug_assertions))]
-fn watch_file_types(_fs: Arc<dyn fs::Fs>, _cx: &mut AppContext) {}
+fn watch_file_types(_fs: Arc<dyn fs::Fs>, _cx: &mut App) {}

crates/zed/src/reliability.rs 🔗

@@ -1,10 +1,10 @@
 use crate::stdout_is_a_pty;
-use anyhow::{Context, Result};
+use anyhow::{Context as _, Result};
 use backtrace::{self, Backtrace};
 use chrono::Utc;
 use client::{telemetry, TelemetrySettings};
 use db::kvp::KEY_VALUE_STORE;
-use gpui::{AppContext, SemanticVersion};
+use gpui::{App, SemanticVersion};
 use http_client::{self, HttpClient, HttpClientWithUrl, HttpRequestExt, Method};
 use paths::{crashes_dir, crashes_retired_dir};
 use project::Project;
@@ -163,7 +163,7 @@ pub fn init(
     system_id: Option<String>,
     installation_id: Option<String>,
     session_id: String,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) {
     #[cfg(target_os = "macos")]
     monitor_main_thread_hangs(http_client.clone(), installation_id.clone(), cx);
@@ -182,7 +182,7 @@ pub fn init(
         cx,
     );
 
-    cx.observe_new_models(move |project: &mut Project, cx| {
+    cx.observe_new(move |project: &mut Project, _, cx| {
         let http_client = http_client.clone();
         let panic_report_url = panic_report_url.clone();
         let session_id = session_id.clone();
@@ -233,7 +233,7 @@ pub fn init(
 pub fn monitor_main_thread_hangs(
     http_client: Arc<HttpClientWithUrl>,
     installation_id: Option<String>,
-    cx: &AppContext,
+    cx: &App,
 ) {
     // This is too noisy to ship to stable for now.
     if !matches!(
@@ -435,7 +435,7 @@ fn upload_panics_and_crashes(
     http: Arc<HttpClientWithUrl>,
     panic_report_url: Url,
     installation_id: Option<String>,
-    cx: &AppContext,
+    cx: &App,
 ) {
     let telemetry_settings = *client::TelemetrySettings::get_global(cx);
     cx.background_executor()

crates/zed/src/zed.rs 🔗

@@ -23,9 +23,9 @@ use feature_flags::FeatureFlagAppExt;
 use futures::FutureExt;
 use futures::{channel::mpsc, select_biased, StreamExt};
 use gpui::{
-    actions, point, px, Action, AppContext, AsyncAppContext, Context, DismissEvent, Element,
-    FocusableView, KeyBinding, MenuItem, ParentElement, PathPromptOptions, PromptLevel, ReadGlobal,
-    SharedString, Styled, Task, TitlebarOptions, View, ViewContext, VisualContext, WindowKind,
+    actions, point, px, Action, App, AppContext as _, AsyncAppContext, Context, DismissEvent,
+    Element, Entity, Focusable, KeyBinding, MenuItem, ParentElement, PathPromptOptions,
+    PromptLevel, ReadGlobal, SharedString, Styled, Task, TitlebarOptions, Window, WindowKind,
     WindowOptions,
 };
 pub use open_listener::*;
@@ -85,7 +85,7 @@ actions!(
     ]
 );
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     #[cfg(target_os = "macos")]
     cx.on_action(|_: &Hide, cx| cx.hide());
     #[cfg(target_os = "macos")]
@@ -99,7 +99,7 @@ pub fn init(cx: &mut AppContext) {
     }
 }
 
-pub fn build_window_options(display_uuid: Option<Uuid>, cx: &mut AppContext) -> WindowOptions {
+pub fn build_window_options(display_uuid: Option<Uuid>, cx: &mut App) -> WindowOptions {
     let display = display_uuid.and_then(|uuid| {
         cx.displays()
             .into_iter()
@@ -137,38 +137,42 @@ pub fn build_window_options(display_uuid: Option<Uuid>, cx: &mut AppContext) ->
 pub fn initialize_workspace(
     app_state: Arc<AppState>,
     prompt_builder: Arc<PromptBuilder>,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) {
-    cx.observe_new_views(move |workspace: &mut Workspace, cx| {
-        let workspace_handle = cx.view().clone();
+    cx.observe_new(move |workspace: &mut Workspace, window, cx| {
+        let Some(window) = window else {
+            return;
+        };
+
+        let workspace_handle = cx.model().clone();
         let center_pane = workspace.active_pane().clone();
-        initialize_pane(workspace, &center_pane, cx);
-        cx.subscribe(&workspace_handle, {
-            move |workspace, _, event, cx| match event {
+        initialize_pane(workspace, &center_pane, window, cx);
+        cx.subscribe_in(&workspace_handle, window, {
+            move |workspace, _, event, window, cx| match event {
                 workspace::Event::PaneAdded(pane) => {
-                    initialize_pane(workspace, pane, cx);
+                    initialize_pane(workspace, &pane, window, cx);
                 }
                 workspace::Event::OpenBundledFile {
                     text,
                     title,
                     language,
-                } => open_bundled_file(workspace, text.clone(), title, language, cx),
+                } => open_bundled_file(workspace, text.clone(), title, language, window, cx),
                 _ => {}
             }
         })
         .detach();
 
         #[cfg(not(target_os = "macos"))]
-        initialize_file_watcher(cx);
+        initialize_file_watcher(window, cx);
 
-        if let Some(specs) = cx.gpu_specs() {
+        if let Some(specs) = window.gpu_specs() {
             log::info!("Using GPU: {:?}", specs);
-            show_software_emulation_warning_if_needed(specs, cx);
+            show_software_emulation_warning_if_needed(specs, window, cx);
         }
 
         let popover_menu_handle = PopoverMenuHandle::default();
 
-        let inline_completion_button = cx.new_view(|cx| {
+        let inline_completion_button = cx.new(|cx| {
             inline_completion_button::InlineCompletionButton::new(
                 workspace.weak_handle(),
                 app_state.fs.clone(),
@@ -179,56 +183,60 @@ pub fn initialize_workspace(
         });
 
         workspace.register_action({
-            move |_, _: &inline_completion_button::ToggleMenu, cx| {
-                popover_menu_handle.toggle(cx);
+            move |_, _: &inline_completion_button::ToggleMenu, window, cx| {
+                popover_menu_handle.toggle(window, cx);
             }
         });
 
         let diagnostic_summary =
-            cx.new_view(|cx| diagnostics::items::DiagnosticIndicator::new(workspace, cx));
-        let activity_indicator =
-            activity_indicator::ActivityIndicator::new(workspace, app_state.languages.clone(), cx);
+            cx.new(|cx| diagnostics::items::DiagnosticIndicator::new(workspace, cx));
+        let activity_indicator = activity_indicator::ActivityIndicator::new(
+            workspace,
+            app_state.languages.clone(),
+            window,
+            cx,
+        );
         let active_buffer_language =
-            cx.new_view(|_| language_selector::ActiveBufferLanguage::new(workspace));
+            cx.new(|_| language_selector::ActiveBufferLanguage::new(workspace));
         let active_toolchain_language =
-            cx.new_view(|cx| toolchain_selector::ActiveToolchain::new(workspace, cx));
-        let vim_mode_indicator = cx.new_view(vim::ModeIndicator::new);
+            cx.new(|cx| toolchain_selector::ActiveToolchain::new(workspace, window, cx));
+        let vim_mode_indicator = cx.new(|cx| vim::ModeIndicator::new(window, cx));
         let cursor_position =
-            cx.new_view(|_| go_to_line::cursor_position::CursorPosition::new(workspace));
+            cx.new(|_| go_to_line::cursor_position::CursorPosition::new(workspace));
         workspace.status_bar().update(cx, |status_bar, cx| {
-            status_bar.add_left_item(diagnostic_summary, cx);
-            status_bar.add_left_item(activity_indicator, cx);
-            status_bar.add_right_item(inline_completion_button, cx);
-            status_bar.add_right_item(active_buffer_language, cx);
-            status_bar.add_right_item(active_toolchain_language, cx);
-            status_bar.add_right_item(vim_mode_indicator, cx);
-            status_bar.add_right_item(cursor_position, cx);
+            status_bar.add_left_item(diagnostic_summary, window, cx);
+            status_bar.add_left_item(activity_indicator, window, cx);
+            status_bar.add_right_item(inline_completion_button, window, cx);
+            status_bar.add_right_item(active_buffer_language, window, cx);
+            status_bar.add_right_item(active_toolchain_language, window, cx);
+            status_bar.add_right_item(vim_mode_indicator, window, cx);
+            status_bar.add_right_item(cursor_position, window, cx);
         });
 
-        auto_update_ui::notify_of_any_new_update(cx);
+        auto_update_ui::notify_of_any_new_update(window, cx);
 
-        let handle = cx.view().downgrade();
-        cx.on_window_should_close(move |cx| {
+        let handle = cx.model().downgrade();
+        window.on_window_should_close(cx, move |window, cx| {
             handle
                 .update(cx, |workspace, cx| {
                     // We'll handle closing asynchronously
-                    workspace.close_window(&Default::default(), cx);
+                    workspace.close_window(&Default::default(), window, cx);
                     false
                 })
                 .unwrap_or(true)
         });
 
-        initialize_panels(prompt_builder.clone(), cx);
-        register_actions(app_state.clone(), workspace, cx);
+        initialize_panels(prompt_builder.clone(), window, cx);
+        register_actions(app_state.clone(), workspace, window, cx);
 
-        workspace.focus_handle(cx).focus(cx);
+        workspace.focus_handle(cx).focus(window);
     })
     .detach();
 
     feature_gate_zed_pro_actions(cx);
 }
 
-fn feature_gate_zed_pro_actions(cx: &mut AppContext) {
+fn feature_gate_zed_pro_actions(cx: &mut App) {
     let zed_pro_actions = [TypeId::of::<OpenAccountSettings>()];
 
     CommandPaletteFilter::update_global(cx, |filter, _cx| {
@@ -250,7 +258,7 @@ fn feature_gate_zed_pro_actions(cx: &mut AppContext) {
 }
 
 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
-fn initialize_file_watcher(cx: &mut ViewContext<Workspace>) {
+fn initialize_file_watcher(window: &mut Window, cx: &mut Context<Workspace>) {
     if let Err(e) = fs::fs_watcher::global(|_| {}) {
         let message = format!(
             db::indoc! {r#"
@@ -260,13 +268,14 @@ fn initialize_file_watcher(cx: &mut ViewContext<Workspace>) {
             "#},
             e
         );
-        let prompt = cx.prompt(
+        let prompt = window.prompt(
             PromptLevel::Critical,
             "Could not start inotify",
             Some(&message),
             &["Troubleshoot and Quit"],
+            cx,
         );
-        cx.spawn(|_, mut cx| async move {
+        cx.spawn(|_, cx| async move {
             if prompt.await == Ok(0) {
                 cx.update(|cx| {
                     cx.open_url("https://zed.dev/docs/linux#could-not-start-inotify");
@@ -280,7 +289,7 @@ fn initialize_file_watcher(cx: &mut ViewContext<Workspace>) {
 }
 
 #[cfg(target_os = "windows")]
-fn initialize_file_watcher(cx: &mut ViewContext<Workspace>) {
+fn initialize_file_watcher(window: &mut Window, cx: &mut Context<Workspace>) {
     if let Err(e) = fs::fs_watcher::global(|_| {}) {
         let message = format!(
             db::indoc! {r#"
@@ -290,13 +299,14 @@ fn initialize_file_watcher(cx: &mut ViewContext<Workspace>) {
             "#},
             e
         );
-        let prompt = cx.prompt(
+        let prompt = window.prompt(
             PromptLevel::Critical,
             "Could not start ReadDirectoryChangesW",
             Some(&message),
             &["Troubleshoot and Quit"],
+            cx,
         );
-        cx.spawn(|_, mut cx| async move {
+        cx.spawn(|_, cx| async move {
             if prompt.await == Ok(0) {
                 cx.update(|cx| {
                     cx.open_url("https://zed.dev/docs/windows");
@@ -311,7 +321,8 @@ fn initialize_file_watcher(cx: &mut ViewContext<Workspace>) {
 
 fn show_software_emulation_warning_if_needed(
     specs: gpui::GpuSpecs,
-    cx: &mut ViewContext<Workspace>,
+    window: &mut Window,
+    cx: &mut Context<Workspace>,
 ) {
     if specs.is_software_emulated && std::env::var("ZED_ALLOW_EMULATED_GPU").is_err() {
         let message = format!(
@@ -326,13 +337,14 @@ fn show_software_emulation_warning_if_needed(
             "#},
             specs.device_name
         );
-        let prompt = cx.prompt(
+        let prompt = window.prompt(
             PromptLevel::Critical,
             "Unsupported GPU",
             Some(&message),
             &["Skip", "Troubleshoot and Quit"],
+            cx,
         );
-        cx.spawn(|_, mut cx| async move {
+        cx.spawn(|_, cx| async move {
             if prompt.await == Ok(1) {
                 cx.update(|cx| {
                     cx.open_url("https://zed.dev/docs/linux#zed-fails-to-open-windows");
@@ -345,13 +357,17 @@ fn show_software_emulation_warning_if_needed(
     }
 }
 
-fn initialize_panels(prompt_builder: Arc<PromptBuilder>, cx: &mut ViewContext<Workspace>) {
+fn initialize_panels(
+    prompt_builder: Arc<PromptBuilder>,
+    window: &mut Window,
+    cx: &mut Context<Workspace>,
+) {
     let assistant2_feature_flag = cx.wait_for_flag::<feature_flags::Assistant2FeatureFlag>();
     let git_ui_feature_flag = cx.wait_for_flag::<feature_flags::GitUiFeatureFlag>();
 
     let prompt_builder = prompt_builder.clone();
 
-    cx.spawn(|workspace_handle, mut cx| async move {
+    cx.spawn_in(window, |workspace_handle, mut cx| async move {
         let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone());
         let outline_panel = OutlinePanel::load(workspace_handle.clone(), cx.clone());
         let terminal_panel = TerminalPanel::load(workspace_handle.clone(), cx.clone());
@@ -380,13 +396,13 @@ fn initialize_panels(prompt_builder: Arc<PromptBuilder>, cx: &mut ViewContext<Wo
             notification_panel,
         )?;
 
-        workspace_handle.update(&mut cx, |workspace, cx| {
-            workspace.add_panel(project_panel, cx);
-            workspace.add_panel(outline_panel, cx);
-            workspace.add_panel(terminal_panel, cx);
-            workspace.add_panel(channels_panel, cx);
-            workspace.add_panel(chat_panel, cx);
-            workspace.add_panel(notification_panel, cx);
+        workspace_handle.update_in(&mut cx, |workspace, window, cx| {
+            workspace.add_panel(project_panel, window, cx);
+            workspace.add_panel(outline_panel, window, cx);
+            workspace.add_panel(terminal_panel, window, cx);
+            workspace.add_panel(channels_panel, window, cx);
+            workspace.add_panel(chat_panel, window, cx);
+            workspace.add_panel(notification_panel, window, cx);
         })?;
 
         let git_ui_enabled = {
@@ -405,9 +421,9 @@ fn initialize_panels(prompt_builder: Arc<PromptBuilder>, cx: &mut ViewContext<Wo
         } else {
             None
         };
-        workspace_handle.update(&mut cx, |workspace, cx| {
+        workspace_handle.update_in(&mut cx, |workspace, window, cx| {
             if let Some(git_panel) = git_panel {
-                workspace.add_panel(git_panel, cx);
+                workspace.add_panel(git_panel, window, cx);
             }
         })?;
 
@@ -444,13 +460,13 @@ fn initialize_panels(prompt_builder: Arc<PromptBuilder>, cx: &mut ViewContext<Wo
             (Some(assistant_panel), None)
         };
 
-        workspace_handle.update(&mut cx, |workspace, cx| {
+        workspace_handle.update_in(&mut cx, |workspace, window, cx| {
             if let Some(assistant2_panel) = assistant2_panel {
-                workspace.add_panel(assistant2_panel, cx);
+                workspace.add_panel(assistant2_panel, window, cx);
             }
 
             if let Some(assistant_panel) = assistant_panel {
-                workspace.add_panel(assistant_panel, cx);
+                workspace.add_panel(assistant_panel, window, cx);
             }
 
             // Register the actions that are shared between `assistant` and `assistant2`.
@@ -488,24 +504,25 @@ fn initialize_panels(prompt_builder: Arc<PromptBuilder>, cx: &mut ViewContext<Wo
 fn register_actions(
     app_state: Arc<AppState>,
     workspace: &mut Workspace,
-    cx: &mut ViewContext<Workspace>,
+    _: &mut Window,
+    cx: &mut Context<Workspace>,
 ) {
     workspace
         .register_action(about)
-        .register_action(|_, _: &Minimize, cx| {
-            cx.minimize_window();
+        .register_action(|_, _: &Minimize, window, _| {
+            window.minimize_window();
         })
-        .register_action(|_, _: &Zoom, cx| {
-            cx.zoom_window();
+        .register_action(|_, _: &Zoom, window, _| {
+            window.zoom_window();
         })
-        .register_action(|_, _: &ToggleFullScreen, cx| {
-            cx.toggle_fullscreen();
+        .register_action(|_, _: &ToggleFullScreen, window, _| {
+            window.toggle_fullscreen();
         })
-        .register_action(|_, action: &OpenZedUrl, cx| {
+        .register_action(|_, action: &OpenZedUrl, _, cx| {
             OpenListener::global(cx).open_urls(vec![action.url.clone()])
         })
-        .register_action(|_, action: &OpenBrowser, cx| cx.open_url(&action.url))
-        .register_action(|workspace, _: &workspace::Open, cx| {
+        .register_action(|_, action: &OpenBrowser, _window, cx| cx.open_url(&action.url))
+        .register_action(|workspace, _: &workspace::Open, window, cx| {
             workspace
                 .client()
                 .telemetry()
@@ -517,20 +534,21 @@ fn register_actions(
                     multiple: true,
                 },
                 DirectoryLister::Project(workspace.project().clone()),
+                window,
                 cx,
             );
 
-            cx.spawn(|this, mut cx| async move {
+            cx.spawn_in(window, |this, mut cx| async move {
                 let Some(paths) = paths.await.log_err().flatten() else {
                     return;
                 };
 
                 if let Some(task) = this
-                    .update(&mut cx, |this, cx| {
+                    .update_in(&mut cx, |this, window, cx| {
                         if this.project().read(cx).is_local() {
-                            this.open_workspace_for_paths(false, paths, cx)
+                            this.open_workspace_for_paths(false, paths, window, cx)
                         } else {
-                            open_new_ssh_project_from_project(this, paths, cx)
+                            open_new_ssh_project_from_project(this, paths, window, cx)
                         }
                     })
                     .log_err()
@@ -542,7 +560,7 @@ fn register_actions(
         })
         .register_action({
             let fs = app_state.fs.clone();
-            move |_, _: &zed_actions::IncreaseUiFontSize, cx| {
+            move |_, _: &zed_actions::IncreaseUiFontSize, _window, cx| {
                 update_settings_file::<ThemeSettings>(fs.clone(), cx, move |settings, cx| {
                     let buffer_font_size = ThemeSettings::clamp_font_size(
                         ThemeSettings::get_global(cx).ui_font_size + px(1.),
@@ -554,7 +572,7 @@ fn register_actions(
         })
         .register_action({
             let fs = app_state.fs.clone();
-            move |_, _: &zed_actions::DecreaseUiFontSize, cx| {
+            move |_, _: &zed_actions::DecreaseUiFontSize, _window, cx| {
                 update_settings_file::<ThemeSettings>(fs.clone(), cx, move |settings, cx| {
                     let buffer_font_size = ThemeSettings::clamp_font_size(
                         ThemeSettings::get_global(cx).ui_font_size - px(1.),
@@ -566,7 +584,7 @@ fn register_actions(
         })
         .register_action({
             let fs = app_state.fs.clone();
-            move |_, _: &zed_actions::ResetUiFontSize, cx| {
+            move |_, _: &zed_actions::ResetUiFontSize, _window, cx| {
                 update_settings_file::<ThemeSettings>(fs.clone(), cx, move |settings, _| {
                     let _ = settings.ui_font_size.take();
                 });
@@ -574,7 +592,7 @@ fn register_actions(
         })
         .register_action({
             let fs = app_state.fs.clone();
-            move |_, _: &zed_actions::IncreaseBufferFontSize, cx| {
+            move |_, _: &zed_actions::IncreaseBufferFontSize, _window, cx| {
                 update_settings_file::<ThemeSettings>(fs.clone(), cx, move |settings, cx| {
                     let buffer_font_size = ThemeSettings::clamp_font_size(
                         ThemeSettings::get_global(cx).buffer_font_size() + px(1.),
@@ -586,7 +604,7 @@ fn register_actions(
         })
         .register_action({
             let fs = app_state.fs.clone();
-            move |_, _: &zed_actions::DecreaseBufferFontSize, cx| {
+            move |_, _: &zed_actions::DecreaseBufferFontSize, _window, cx| {
                 update_settings_file::<ThemeSettings>(fs.clone(), cx, move |settings, cx| {
                     let buffer_font_size = ThemeSettings::clamp_font_size(
                         ThemeSettings::get_global(cx).buffer_font_size() - px(1.),
@@ -597,17 +615,17 @@ fn register_actions(
         })
         .register_action({
             let fs = app_state.fs.clone();
-            move |_, _: &zed_actions::ResetBufferFontSize, cx| {
+            move |_, _: &zed_actions::ResetBufferFontSize, _window, cx| {
                 update_settings_file::<ThemeSettings>(fs.clone(), cx, move |settings, _| {
                     let _ = settings.buffer_font_size.take();
                 });
             }
         })
         .register_action(install_cli)
-        .register_action(|_, _: &install_cli::RegisterZedScheme, cx| {
-            cx.spawn(|workspace, mut cx| async move {
+        .register_action(|_, _: &install_cli::RegisterZedScheme, window, cx| {
+            cx.spawn_in(window, |workspace, mut cx| async move {
                 register_zed_scheme(&cx).await?;
-                workspace.update(&mut cx, |workspace, cx| {
+                workspace.update_in(&mut cx, |workspace, _, cx| {
                     struct RegisterZedScheme;
 
                     workspace.show_toast(
@@ -623,57 +641,77 @@ fn register_actions(
                 })?;
                 Ok(())
             })
-            .detach_and_prompt_err("Error registering zed:// scheme", cx, |_, _| None);
+            .detach_and_prompt_err(
+                "Error registering zed:// scheme",
+                window,
+                cx,
+                |_, _, _| None,
+            );
         })
-        .register_action(|workspace, _: &OpenLog, cx| {
-            open_log_file(workspace, cx);
+        .register_action(|workspace, _: &OpenLog, window, cx| {
+            open_log_file(workspace, window, cx);
         })
-        .register_action(|workspace, _: &zed_actions::OpenLicenses, cx| {
+        .register_action(|workspace, _: &zed_actions::OpenLicenses, window, cx| {
             open_bundled_file(
                 workspace,
                 asset_str::<Assets>("licenses.md"),
                 "Open Source License Attribution",
                 "Markdown",
+                window,
                 cx,
             );
         })
         .register_action(
             move |workspace: &mut Workspace,
                   _: &zed_actions::OpenTelemetryLog,
-                  cx: &mut ViewContext<Workspace>| {
-                open_telemetry_log_file(workspace, cx);
+                  window: &mut Window,
+                  cx: &mut Context<Workspace>| {
+                open_telemetry_log_file(workspace, window, cx);
             },
         )
         .register_action(
             move |_: &mut Workspace,
                   _: &zed_actions::OpenKeymap,
-                  cx: &mut ViewContext<Workspace>| {
+                  window: &mut Window,
+                  cx: &mut Context<Workspace>| {
                 open_settings_file(
                     paths::keymap_file(),
                     || settings::initial_keymap_content().as_ref().into(),
+                    window,
                     cx,
                 );
             },
         )
         .register_action(
-            move |_: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext<Workspace>| {
+            move |_: &mut Workspace,
+                  _: &OpenSettings,
+                  window: &mut Window,
+                  cx: &mut Context<Workspace>| {
                 open_settings_file(
                     paths::settings_file(),
                     || settings::initial_user_settings_content().as_ref().into(),
+                    window,
                     cx,
                 );
             },
         )
         .register_action(
-            |_: &mut Workspace, _: &OpenAccountSettings, cx: &mut ViewContext<Workspace>| {
+            |_: &mut Workspace,
+             _: &OpenAccountSettings,
+             _: &mut Window,
+             cx: &mut Context<Workspace>| {
                 cx.open_url(&zed_urls::account_url(cx));
             },
         )
         .register_action(
-            move |_: &mut Workspace, _: &OpenTasks, cx: &mut ViewContext<Workspace>| {
+            move |_: &mut Workspace,
+                  _: &OpenTasks,
+                  window: &mut Window,
+                  cx: &mut Context<Workspace>| {
                 open_settings_file(
                     paths::tasks_file(),
                     || settings::initial_tasks_content().as_ref().into(),
+                    window,
                     cx,
                 );
             },
@@ -683,12 +721,14 @@ fn register_actions(
         .register_action(
             move |workspace: &mut Workspace,
                   _: &zed_actions::OpenDefaultKeymap,
-                  cx: &mut ViewContext<Workspace>| {
+                  window: &mut Window,
+                  cx: &mut Context<Workspace>| {
                 open_bundled_file(
                     workspace,
                     settings::default_keymap(),
                     "Default Key Bindings",
                     "JSON",
+                    window,
                     cx,
                 );
             },
@@ -696,12 +736,14 @@ fn register_actions(
         .register_action(
             move |workspace: &mut Workspace,
                   _: &OpenDefaultSettings,
-                  cx: &mut ViewContext<Workspace>| {
+                  window: &mut Window,
+                  cx: &mut Context<Workspace>| {
                 open_bundled_file(
                     workspace,
                     settings::default_settings(),
                     "Default Settings",
                     "JSON",
+                    window,
                     cx,
                 );
             },
@@ -709,80 +751,97 @@ fn register_actions(
         .register_action(
             |workspace: &mut Workspace,
              _: &project_panel::ToggleFocus,
-             cx: &mut ViewContext<Workspace>| {
-                workspace.toggle_panel_focus::<ProjectPanel>(cx);
+             window: &mut Window,
+             cx: &mut Context<Workspace>| {
+                workspace.toggle_panel_focus::<ProjectPanel>(window, cx);
             },
         )
         .register_action(
             |workspace: &mut Workspace,
              _: &outline_panel::ToggleFocus,
-             cx: &mut ViewContext<Workspace>| {
-                workspace.toggle_panel_focus::<OutlinePanel>(cx);
+             window: &mut Window,
+             cx: &mut Context<Workspace>| {
+                workspace.toggle_panel_focus::<OutlinePanel>(window, cx);
             },
         )
         .register_action(
             |workspace: &mut Workspace,
              _: &collab_ui::collab_panel::ToggleFocus,
-             cx: &mut ViewContext<Workspace>| {
-                workspace.toggle_panel_focus::<collab_ui::collab_panel::CollabPanel>(cx);
+             window: &mut Window,
+             cx: &mut Context<Workspace>| {
+                workspace.toggle_panel_focus::<collab_ui::collab_panel::CollabPanel>(window, cx);
             },
         )
         .register_action(
             |workspace: &mut Workspace,
              _: &collab_ui::chat_panel::ToggleFocus,
-             cx: &mut ViewContext<Workspace>| {
-                workspace.toggle_panel_focus::<collab_ui::chat_panel::ChatPanel>(cx);
+             window: &mut Window,
+             cx: &mut Context<Workspace>| {
+                workspace.toggle_panel_focus::<collab_ui::chat_panel::ChatPanel>(window, cx);
             },
         )
         .register_action(
             |workspace: &mut Workspace,
              _: &collab_ui::notification_panel::ToggleFocus,
-             cx: &mut ViewContext<Workspace>| {
-                workspace
-                    .toggle_panel_focus::<collab_ui::notification_panel::NotificationPanel>(cx);
+             window: &mut Window,
+             cx: &mut Context<Workspace>| {
+                workspace.toggle_panel_focus::<collab_ui::notification_panel::NotificationPanel>(
+                    window, cx,
+                );
             },
         )
         .register_action(
             |workspace: &mut Workspace,
              _: &terminal_panel::ToggleFocus,
-             cx: &mut ViewContext<Workspace>| {
-                workspace.toggle_panel_focus::<TerminalPanel>(cx);
+             window: &mut Window,
+             cx: &mut Context<Workspace>| {
+                workspace.toggle_panel_focus::<TerminalPanel>(window, cx);
             },
         )
         .register_action({
             let app_state = Arc::downgrade(&app_state);
-            move |_, _: &NewWindow, cx| {
+            move |_, _: &NewWindow, _, cx| {
                 if let Some(app_state) = app_state.upgrade() {
-                    open_new(Default::default(), app_state, cx, |workspace, cx| {
-                        Editor::new_file(workspace, &Default::default(), cx)
-                    })
+                    open_new(
+                        Default::default(),
+                        app_state,
+                        cx,
+                        |workspace, window, cx| {
+                            Editor::new_file(workspace, &Default::default(), window, cx)
+                        },
+                    )
                     .detach();
                 }
             }
         })
         .register_action({
             let app_state = Arc::downgrade(&app_state);
-            move |_, _: &NewFile, cx| {
+            move |_, _: &NewFile, _, cx| {
                 if let Some(app_state) = app_state.upgrade() {
-                    open_new(Default::default(), app_state, cx, |workspace, cx| {
-                        Editor::new_file(workspace, &Default::default(), cx)
-                    })
+                    open_new(
+                        Default::default(),
+                        app_state,
+                        cx,
+                        |workspace, window, cx| {
+                            Editor::new_file(workspace, &Default::default(), window, cx)
+                        },
+                    )
                     .detach();
                 }
             }
         });
     if workspace.project().read(cx).is_via_ssh() {
         workspace.register_action({
-            move |workspace, _: &OpenServerSettings, cx| {
+            move |workspace, _: &OpenServerSettings, window, cx| {
                 let open_server_settings = workspace
                     .project()
                     .update(cx, |project, cx| project.open_server_settings(cx));
 
-                cx.spawn(|workspace, mut cx| async move {
+                cx.spawn_in(window, |workspace, mut cx| async move {
                     let buffer = open_server_settings.await?;
 
                     workspace
-                        .update(&mut cx, |workspace, cx| {
+                        .update_in(&mut cx, |workspace, window, cx| {
                             workspace.open_path(
                                 buffer
                                     .read(cx)
@@ -790,6 +849,7 @@ fn register_actions(
                                     .expect("Settings file must have a location"),
                                 None,
                                 true,
+                                window,
                                 cx,
                             )
                         })?
@@ -803,41 +863,50 @@ fn register_actions(
     }
 }
 
-fn initialize_pane(workspace: &Workspace, pane: &View<Pane>, cx: &mut ViewContext<Workspace>) {
+fn initialize_pane(
+    workspace: &Workspace,
+    pane: &Entity<Pane>,
+    window: &mut Window,
+    cx: &mut Context<Workspace>,
+) {
     pane.update(cx, |pane, cx| {
         pane.toolbar().update(cx, |toolbar, cx| {
-            let multibuffer_hint = cx.new_view(|_| MultibufferHint::new());
-            toolbar.add_item(multibuffer_hint, cx);
-            let breadcrumbs = cx.new_view(|_| Breadcrumbs::new());
-            toolbar.add_item(breadcrumbs, cx);
-            let buffer_search_bar = cx.new_view(search::BufferSearchBar::new);
-            toolbar.add_item(buffer_search_bar.clone(), cx);
-
-            let proposed_change_bar = cx.new_view(|_| ProposedChangesEditorToolbar::new());
-            toolbar.add_item(proposed_change_bar, cx);
+            let multibuffer_hint = cx.new(|_| MultibufferHint::new());
+            toolbar.add_item(multibuffer_hint, window, cx);
+            let breadcrumbs = cx.new(|_| Breadcrumbs::new());
+            toolbar.add_item(breadcrumbs, window, cx);
+            let buffer_search_bar = cx.new(|cx| search::BufferSearchBar::new(window, cx));
+            toolbar.add_item(buffer_search_bar.clone(), window, cx);
+
+            let proposed_change_bar = cx.new(|_| ProposedChangesEditorToolbar::new());
+            toolbar.add_item(proposed_change_bar, window, cx);
             let quick_action_bar =
-                cx.new_view(|cx| QuickActionBar::new(buffer_search_bar, workspace, cx));
-            toolbar.add_item(quick_action_bar, cx);
-            let diagnostic_editor_controls = cx.new_view(|_| diagnostics::ToolbarControls::new());
-            toolbar.add_item(diagnostic_editor_controls, cx);
-            let project_search_bar = cx.new_view(|_| ProjectSearchBar::new());
-            toolbar.add_item(project_search_bar, cx);
-            let lsp_log_item = cx.new_view(|_| language_tools::LspLogToolbarItemView::new());
-            toolbar.add_item(lsp_log_item, cx);
-            let syntax_tree_item =
-                cx.new_view(|_| language_tools::SyntaxTreeToolbarItemView::new());
-            toolbar.add_item(syntax_tree_item, cx);
+                cx.new(|cx| QuickActionBar::new(buffer_search_bar, workspace, cx));
+            toolbar.add_item(quick_action_bar, window, cx);
+            let diagnostic_editor_controls = cx.new(|_| diagnostics::ToolbarControls::new());
+            toolbar.add_item(diagnostic_editor_controls, window, cx);
+            let project_search_bar = cx.new(|_| ProjectSearchBar::new());
+            toolbar.add_item(project_search_bar, window, cx);
+            let lsp_log_item = cx.new(|_| language_tools::LspLogToolbarItemView::new());
+            toolbar.add_item(lsp_log_item, window, cx);
+            let syntax_tree_item = cx.new(|_| language_tools::SyntaxTreeToolbarItemView::new());
+            toolbar.add_item(syntax_tree_item, window, cx);
         })
     });
 }
 
-fn about(_: &mut Workspace, _: &zed_actions::About, cx: &mut ViewContext<Workspace>) {
+fn about(
+    _: &mut Workspace,
+    _: &zed_actions::About,
+    window: &mut Window,
+    cx: &mut Context<Workspace>,
+) {
     let release_channel = ReleaseChannel::global(cx).display_name();
     let version = env!("CARGO_PKG_VERSION");
     let message = format!("{release_channel} {version}");
     let detail = AppCommitSha::try_global(cx).map(|sha| sha.0.clone());
 
-    let prompt = cx.prompt(PromptLevel::Info, &message, detail.as_deref(), &["OK"]);
+    let prompt = window.prompt(PromptLevel::Info, &message, detail.as_deref(), &["OK"], cx);
     cx.foreground_executor()
         .spawn(async {
             prompt.await.ok();
@@ -845,14 +914,19 @@ fn about(_: &mut Workspace, _: &zed_actions::About, cx: &mut ViewContext<Workspa
         .detach();
 }
 
-fn test_panic(_: &TestPanic, _: &mut AppContext) {
+fn test_panic(_: &TestPanic, _: &mut App) {
     panic!("Ran the TestPanic action")
 }
 
-fn install_cli(_: &mut Workspace, _: &install_cli::Install, cx: &mut ViewContext<Workspace>) {
+fn install_cli(
+    _: &mut Workspace,
+    _: &install_cli::Install,
+    window: &mut Window,
+    cx: &mut Context<Workspace>,
+) {
     const LINUX_PROMPT_DETAIL: &str = "If you installed Zed from our official release add ~/.local/bin to your PATH.\n\nIf you installed Zed from a different source like your package manager, then you may need to create an alias/symlink manually.\n\nDepending on your package manager, the CLI might be named zeditor, zedit, zed-editor or something else.";
 
-    cx.spawn(|workspace, mut cx| async move {
+    cx.spawn_in(window, |workspace, mut cx| async move {
         if cfg!(any(target_os = "linux", target_os = "freebsd")) {
             let prompt = cx.prompt(
                 PromptLevel::Warning,
@@ -867,7 +941,7 @@ fn install_cli(_: &mut Workspace, _: &install_cli::Install, cx: &mut ViewContext
             .await
             .context("error creating CLI symlink")?;
 
-        workspace.update(&mut cx, |workspace, cx| {
+        workspace.update_in(&mut cx, |workspace, _, cx| {
             struct InstalledZedCli;
 
             workspace.show_toast(
@@ -885,10 +959,10 @@ fn install_cli(_: &mut Workspace, _: &install_cli::Install, cx: &mut ViewContext
         register_zed_scheme(&cx).await.log_err();
         Ok(())
     })
-    .detach_and_prompt_err("Error installing zed cli", cx, |_, _| None);
+    .detach_and_prompt_err("Error installing zed cli", window, cx, |_, _, _| None);
 }
 
-fn quit(_: &Quit, cx: &mut AppContext) {
+fn quit(_: &Quit, cx: &mut App) {
     let should_confirm = WorkspaceSettings::get_global(cx).confirm_quit;
     cx.spawn(|mut cx| async move {
         let mut workspace_windows = cx.update(|cx| {
@@ -907,12 +981,13 @@ fn quit(_: &Quit, cx: &mut AppContext) {
 
         if let (true, Some(workspace)) = (should_confirm, workspace_windows.first().copied()) {
             let answer = workspace
-                .update(&mut cx, |_, cx| {
-                    cx.prompt(
+                .update(&mut cx, |_, window, cx| {
+                    window.prompt(
                         PromptLevel::Info,
                         "Are you sure you want to quit?",
                         None,
                         &["Quit", "Cancel"],
+                        cx,
                     )
                 })
                 .log_err();
@@ -928,8 +1003,8 @@ fn quit(_: &Quit, cx: &mut AppContext) {
         // If the user cancels any save prompt, then keep the app open.
         for window in workspace_windows {
             if let Some(should_close) = window
-                .update(&mut cx, |workspace, cx| {
-                    workspace.prepare_to_close(CloseIntent::Quit, cx)
+                .update(&mut cx, |workspace, window, cx| {
+                    workspace.prepare_to_close(CloseIntent::Quit, window, cx)
                 })
                 .log_err()
             {
@@ -944,12 +1019,12 @@ fn quit(_: &Quit, cx: &mut AppContext) {
     .detach_and_log_err(cx);
 }
 
-fn open_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
+fn open_log_file(workspace: &mut Workspace, window: &mut Window, cx: &mut Context<Workspace>) {
     const MAX_LINES: usize = 1000;
     workspace
-        .with_local_workspace(cx, move |workspace, cx| {
+        .with_local_workspace(window, cx, move |workspace, window, cx| {
             let fs = workspace.app_state().fs.clone();
-            cx.spawn(|workspace, mut cx| async move {
+            cx.spawn_in(window, |workspace, mut cx| async move {
                 let (old_log, new_log) =
                     futures::join!(fs.load(paths::old_log_file()), fs.load(paths::log_file()));
                 let log = match (old_log, new_log) {
@@ -976,7 +1051,7 @@ fn open_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
                 };
 
                 workspace
-                    .update(&mut cx, |workspace, cx| {
+                    .update_in(&mut cx, |workspace, window, cx| {
                         let Some(log) = log else {
                             struct OpenLogError;
 
@@ -984,7 +1059,7 @@ fn open_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
                                 NotificationId::unique::<OpenLogError>(),
                                 cx,
                                 |cx| {
-                                    cx.new_view(|_| {
+                                    cx.new(|_| {
                                         MessageNotification::new(format!(
                                             "Unable to access/open log file at path {:?}",
                                             paths::log_file().as_path()
@@ -999,12 +1074,11 @@ fn open_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
                             project.create_local_buffer(&log, None, cx)
                         });
 
-                        let buffer = cx.new_model(|cx| {
-                            MultiBuffer::singleton(buffer, cx).with_title("Log".into())
-                        });
-                        let editor = cx.new_view(|cx| {
+                        let buffer = cx
+                            .new(|cx| MultiBuffer::singleton(buffer, cx).with_title("Log".into()));
+                        let editor = cx.new(|cx| {
                             let mut editor =
-                                Editor::for_multibuffer(buffer, Some(project), true, cx);
+                                Editor::for_multibuffer(buffer, Some(project), true, window, cx);
                             editor.set_breadcrumb_header(format!(
                                 "Last {} lines in {}",
                                 MAX_LINES,
@@ -1015,14 +1089,14 @@ fn open_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
 
                         editor.update(cx, |editor, cx| {
                             let last_multi_buffer_offset = editor.buffer().read(cx).len(cx);
-                            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
                                 s.select_ranges(Some(
                                     last_multi_buffer_offset..last_multi_buffer_offset,
                                 ));
                             })
                         });
 
-                        workspace.add_item_to_active_pane(Box::new(editor), None, true, cx);
+                        workspace.add_item_to_active_pane(Box::new(editor), None, true, window, cx);
                     })
                     .log_err();
             })
@@ -1033,7 +1107,7 @@ fn open_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
 
 pub fn handle_keymap_file_changes(
     mut user_keymap_file_rx: mpsc::UnboundedReceiver<String>,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) {
     BaseKeymap::register(cx);
     VimModeSetting::register(cx);
@@ -1111,16 +1185,16 @@ pub fn handle_keymap_file_changes(
 fn show_keymap_file_json_error(
     notification_id: NotificationId,
     error: &anyhow::Error,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) {
     let message: SharedString =
         format!("JSON parse error in keymap file. Bindings not reloaded.\n\n{error}").into();
     show_app_notification(notification_id, cx, move |cx| {
-        cx.new_view(|_cx| {
+        cx.new(|_cx| {
             MessageNotification::new(message.clone())
                 .with_click_message("Open keymap file")
-                .on_click(|cx| {
-                    cx.dispatch_action(zed_actions::OpenKeymap.boxed_clone());
+                .on_click(|window, cx| {
+                    window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx);
                     cx.emit(DismissEvent);
                 })
         })
@@ -1131,7 +1205,7 @@ fn show_keymap_file_json_error(
 fn show_keymap_file_load_error(
     notification_id: NotificationId,
     markdown_error_message: MarkdownString,
-    cx: &mut AppContext,
+    cx: &mut App,
 ) {
     let parsed_markdown = cx.background_executor().spawn(async move {
         let file_location_directory = None;
@@ -1148,22 +1222,23 @@ fn show_keymap_file_load_error(
         let parsed_markdown = Rc::new(parsed_markdown.await);
         cx.update(|cx| {
             show_app_notification(notification_id, cx, move |cx| {
-                let workspace_handle = cx.view().downgrade();
+                let workspace_handle = cx.model().downgrade();
                 let parsed_markdown = parsed_markdown.clone();
-                cx.new_view(move |_cx| {
-                    MessageNotification::new_from_builder(move |cx| {
+                cx.new(move |_cx| {
+                    MessageNotification::new_from_builder(move |window, cx| {
                         gpui::div()
                             .text_xs()
                             .child(markdown_preview::markdown_renderer::render_parsed_markdown(
                                 &parsed_markdown.clone(),
                                 Some(workspace_handle.clone()),
+                                window,
                                 cx,
                             ))
                             .into_any()
                     })
                     .with_click_message("Open keymap file")
-                    .on_click(|cx| {
-                        cx.dispatch_action(zed_actions::OpenKeymap.boxed_clone());
+                    .on_click(|window, cx| {
+                        window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx);
                         cx.emit(DismissEvent);
                     })
                 })
@@ -1175,7 +1250,7 @@ fn show_keymap_file_load_error(
     .detach();
 }
 
-fn reload_keymaps(cx: &mut AppContext, user_key_bindings: Vec<KeyBinding>) {
+fn reload_keymaps(cx: &mut App, user_key_bindings: Vec<KeyBinding>) {
     cx.clear_key_bindings();
     load_default_keymap(cx);
     cx.bind_keys(user_key_bindings);
@@ -1183,7 +1258,7 @@ fn reload_keymaps(cx: &mut AppContext, user_key_bindings: Vec<KeyBinding>) {
     cx.set_dock_menu(vec![MenuItem::action("New Window", workspace::NewWindow)]);
 }
 
-pub fn load_default_keymap(cx: &mut AppContext) {
+pub fn load_default_keymap(cx: &mut App) {
     let base_keymap = *BaseKeymap::get_global(cx);
     if base_keymap == BaseKeymap::None {
         return;
@@ -1202,14 +1277,15 @@ pub fn load_default_keymap(cx: &mut AppContext) {
 pub fn open_new_ssh_project_from_project(
     workspace: &mut Workspace,
     paths: Vec<PathBuf>,
-    cx: &mut ViewContext<Workspace>,
+    window: &mut Window,
+    cx: &mut Context<Workspace>,
 ) -> Task<anyhow::Result<()>> {
     let app_state = workspace.app_state().clone();
     let Some(ssh_client) = workspace.project().read(cx).ssh_client() else {
         return Task::ready(Err(anyhow::anyhow!("Not an ssh project")));
     };
     let connection_options = ssh_client.read(cx).connection_options();
-    cx.spawn(|_, mut cx| async move {
+    cx.spawn_in(window, |_, mut cx| async move {
         open_ssh_project(
             connection_options,
             paths,

crates/zed/src/zed/inline_completion_registry.rs 🔗

@@ -5,53 +5,65 @@ use collections::HashMap;
 use copilot::{Copilot, CopilotCompletionProvider};
 use editor::{Editor, EditorMode};
 use feature_flags::{FeatureFlagAppExt, PredictEditsFeatureFlag};
-use gpui::{AnyWindowHandle, AppContext, Context, Model, ViewContext, WeakView};
+use gpui::{AnyWindowHandle, App, AppContext as _, Context, Entity, WeakEntity, Window};
 use language::language_settings::{all_language_settings, InlineCompletionProvider};
 use settings::SettingsStore;
 use supermaven::{Supermaven, SupermavenCompletionProvider};
 use workspace::Workspace;
 use zed_predict_tos::ZedPredictTos;
 
-pub fn init(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut AppContext) {
-    let editors: Rc<RefCell<HashMap<WeakView<Editor>, AnyWindowHandle>>> = Rc::default();
-    cx.observe_new_views({
+pub fn init(client: Arc<Client>, user_store: Entity<UserStore>, cx: &mut App) {
+    let editors: Rc<RefCell<HashMap<WeakEntity<Editor>, AnyWindowHandle>>> = Rc::default();
+    cx.observe_new({
         let editors = editors.clone();
         let client = client.clone();
         let user_store = user_store.clone();
-        move |editor: &mut Editor, cx: &mut ViewContext<Editor>| {
+        move |editor: &mut Editor, window, cx: &mut Context<Editor>| {
             if editor.mode() != EditorMode::Full {
                 return;
             }
 
             register_backward_compatible_actions(editor, cx);
 
-            let editor_handle = cx.view().downgrade();
+            let Some(window) = window else {
+                return;
+            };
+
+            let editor_handle = cx.model().downgrade();
             cx.on_release({
                 let editor_handle = editor_handle.clone();
                 let editors = editors.clone();
-                move |_, _, _| {
+                move |_, _| {
                     editors.borrow_mut().remove(&editor_handle);
                 }
             })
             .detach();
             editors
                 .borrow_mut()
-                .insert(editor_handle, cx.window_handle());
+                .insert(editor_handle, window.window_handle());
             let provider = all_language_settings(None, cx).inline_completions.provider;
-            assign_inline_completion_provider(editor, provider, &client, user_store.clone(), cx);
+            assign_inline_completion_provider(
+                editor,
+                provider,
+                &client,
+                user_store.clone(),
+                window,
+                cx,
+            );
         }
     })
     .detach();
 
     let mut provider = all_language_settings(None, cx).inline_completions.provider;
     for (editor, window) in editors.borrow().iter() {
-        _ = window.update(cx, |_window, cx| {
+        _ = window.update(cx, |_window, window, cx| {
             _ = editor.update(cx, |editor, cx| {
                 assign_inline_completion_provider(
                     editor,
                     provider,
                     &client,
                     user_store.clone(),
+                    window,
                     cx,
                 );
             })
@@ -105,14 +117,19 @@ pub fn init(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut AppConte
 
                             let Some(workspace) = window
                                 .downcast::<Workspace>()
-                                .and_then(|w| w.root_view(cx).ok())
+                                .and_then(|w| w.root_model(cx).ok())
                             else {
                                 return;
                             };
 
                             window
-                                .update(cx, |_, cx| {
-                                    ZedPredictTos::toggle(workspace, user_store.clone(), cx);
+                                .update(cx, |_, window, cx| {
+                                    ZedPredictTos::toggle(
+                                        workspace,
+                                        user_store.clone(),
+                                        window,
+                                        cx,
+                                    );
                                 })
                                 .ok();
                         }
@@ -127,27 +144,28 @@ pub fn init(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut AppConte
     .detach();
 }
 
-fn clear_zeta_edit_history(_: &zeta::ClearHistory, cx: &mut AppContext) {
+fn clear_zeta_edit_history(_: &zeta::ClearHistory, cx: &mut App) {
     if let Some(zeta) = zeta::Zeta::global(cx) {
         zeta.update(cx, |zeta, _| zeta.clear_history());
     }
 }
 
 fn assign_inline_completion_providers(
-    editors: &Rc<RefCell<HashMap<WeakView<Editor>, AnyWindowHandle>>>,
+    editors: &Rc<RefCell<HashMap<WeakEntity<Editor>, AnyWindowHandle>>>,
     provider: InlineCompletionProvider,
     client: &Arc<Client>,
-    user_store: Model<UserStore>,
-    cx: &mut AppContext,
+    user_store: Entity<UserStore>,
+    cx: &mut App,
 ) {
     for (editor, window) in editors.borrow().iter() {
-        _ = window.update(cx, |_window, cx| {
+        _ = window.update(cx, |_window, window, cx| {
             _ = editor.update(cx, |editor, cx| {
                 assign_inline_completion_provider(
                     editor,
                     provider,
                     &client,
                     user_store.clone(),
+                    window,
                     cx,
                 );
             })
@@ -155,28 +173,31 @@ fn assign_inline_completion_providers(
     }
 }
 
-fn register_backward_compatible_actions(editor: &mut Editor, cx: &ViewContext<Editor>) {
+fn register_backward_compatible_actions(editor: &mut Editor, cx: &mut Context<Editor>) {
     // We renamed some of these actions to not be copilot-specific, but that
     // would have not been backwards-compatible. So here we are re-registering
     // the actions with the old names to not break people's keymaps.
     editor
         .register_action(cx.listener(
-            |editor, _: &copilot::Suggest, cx: &mut ViewContext<Editor>| {
-                editor.show_inline_completion(&Default::default(), cx);
+            |editor, _: &copilot::Suggest, window: &mut Window, cx: &mut Context<Editor>| {
+                editor.show_inline_completion(&Default::default(), window, cx);
             },
         ))
         .detach();
     editor
         .register_action(cx.listener(
-            |editor, _: &copilot::NextSuggestion, cx: &mut ViewContext<Editor>| {
-                editor.next_inline_completion(&Default::default(), cx);
+            |editor, _: &copilot::NextSuggestion, window: &mut Window, cx: &mut Context<Editor>| {
+                editor.next_inline_completion(&Default::default(), window, cx);
             },
         ))
         .detach();
     editor
         .register_action(cx.listener(
-            |editor, _: &copilot::PreviousSuggestion, cx: &mut ViewContext<Editor>| {
-                editor.previous_inline_completion(&Default::default(), cx);
+            |editor,
+             _: &copilot::PreviousSuggestion,
+             window: &mut Window,
+             cx: &mut Context<Editor>| {
+                editor.previous_inline_completion(&Default::default(), window, cx);
             },
         ))
         .detach();
@@ -184,8 +205,9 @@ fn register_backward_compatible_actions(editor: &mut Editor, cx: &ViewContext<Ed
         .register_action(cx.listener(
             |editor,
              _: &editor::actions::AcceptPartialCopilotSuggestion,
-             cx: &mut ViewContext<Editor>| {
-                editor.accept_partial_inline_completion(&Default::default(), cx);
+             window: &mut Window,
+             cx: &mut Context<Editor>| {
+                editor.accept_partial_inline_completion(&Default::default(), window, cx);
             },
         ))
         .detach();
@@ -195,8 +217,9 @@ fn assign_inline_completion_provider(
     editor: &mut Editor,
     provider: language::language_settings::InlineCompletionProvider,
     client: &Arc<Client>,
-    user_store: Model<UserStore>,
-    cx: &mut ViewContext<Editor>,
+    user_store: Entity<UserStore>,
+    window: &mut Window,
+    cx: &mut Context<Editor>,
 ) {
     match provider {
         language::language_settings::InlineCompletionProvider::None => {}
@@ -209,14 +232,14 @@ fn assign_inline_completion_provider(
                         });
                     }
                 }
-                let provider = cx.new_model(|_| CopilotCompletionProvider::new(copilot));
-                editor.set_inline_completion_provider(Some(provider), cx);
+                let provider = cx.new(|_| CopilotCompletionProvider::new(copilot));
+                editor.set_inline_completion_provider(Some(provider), window, cx);
             }
         }
         language::language_settings::InlineCompletionProvider::Supermaven => {
             if let Some(supermaven) = Supermaven::global(cx) {
-                let provider = cx.new_model(|_| SupermavenCompletionProvider::new(supermaven));
-                editor.set_inline_completion_provider(Some(provider), cx);
+                let provider = cx.new(|_| SupermavenCompletionProvider::new(supermaven));
+                editor.set_inline_completion_provider(Some(provider), window, cx);
             }
         }
 
@@ -232,8 +255,8 @@ fn assign_inline_completion_provider(
                         });
                     }
                 }
-                let provider = cx.new_model(|_| zeta::ZetaInlineCompletionProvider::new(zeta));
-                editor.set_inline_completion_provider(Some(provider), cx);
+                let provider = cx.new(|_| zeta::ZetaInlineCompletionProvider::new(zeta));
+                editor.set_inline_completion_provider(Some(provider), window, cx);
             }
         }
     }

crates/zed/src/zed/linux_prompts.rs 🔗

@@ -1,8 +1,7 @@
 use gpui::{
-    div, AppContext, EventEmitter, FocusHandle, FocusableView, FontWeight, InteractiveElement,
-    IntoElement, ParentElement, PromptHandle, PromptLevel, PromptResponse, Refineable, Render,
-    RenderablePromptHandle, Styled, TextStyleRefinement, View, ViewContext, VisualContext,
-    WindowContext,
+    div, App, AppContext as _, Context, Entity, EventEmitter, FocusHandle, Focusable, FontWeight,
+    InteractiveElement, IntoElement, ParentElement, PromptHandle, PromptLevel, PromptResponse,
+    Refineable, Render, RenderablePromptHandle, Styled, TextStyleRefinement, Window,
 };
 use markdown::{Markdown, MarkdownStyle};
 use settings::Settings;
@@ -13,7 +12,7 @@ use ui::{
 };
 use workspace::ui::StyledExt;
 
-pub fn init(cx: &mut AppContext) {
+pub fn init(cx: &mut App) {
     cx.set_prompt_builder(fallback_prompt_renderer)
 }
 /// Use this function in conjunction with [AppContext::set_prompt_renderer] to force
@@ -24,9 +23,10 @@ pub fn fallback_prompt_renderer(
     detail: Option<&str>,
     actions: &[&str],
     handle: PromptHandle,
-    cx: &mut WindowContext,
+    window: &mut Window,
+    cx: &mut App,
 ) -> RenderablePromptHandle {
-    let renderer = cx.new_view({
+    let renderer = cx.new({
         |cx| FallbackPromptRenderer {
             _level: level,
             message: message.to_string(),
@@ -34,9 +34,9 @@ pub fn fallback_prompt_renderer(
             focus: cx.focus_handle(),
             active_action_id: 0,
             detail: detail.filter(|text| !text.is_empty()).map(|text| {
-                cx.new_view(|cx| {
+                cx.new(|cx| {
                     let settings = ThemeSettings::get_global(cx);
-                    let mut base_text_style = cx.text_style();
+                    let mut base_text_style = window.text_style();
                     base_text_style.refine(&TextStyleRefinement {
                         font_family: Some(settings.ui_font.family.clone()),
                         font_size: Some(settings.ui_font_size.into()),
@@ -48,13 +48,13 @@ pub fn fallback_prompt_renderer(
                         selection_background_color: { cx.theme().players().local().selection },
                         ..Default::default()
                     };
-                    Markdown::new(text.to_string(), markdown_style, None, None, cx)
+                    Markdown::new(text.to_string(), markdown_style, None, None, window, cx)
                 })
             }),
         }
     });
 
-    handle.with_view(renderer, cx)
+    handle.with_view(renderer, window, cx)
 }
 
 /// The default GPUI fallback for rendering prompts, when the platform doesn't support it.
@@ -64,31 +64,36 @@ pub struct FallbackPromptRenderer {
     actions: Vec<String>,
     focus: FocusHandle,
     active_action_id: usize,
-    detail: Option<View<Markdown>>,
+    detail: Option<Entity<Markdown>>,
 }
 
 impl FallbackPromptRenderer {
-    fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
+    fn confirm(&mut self, _: &menu::Confirm, _window: &mut Window, cx: &mut Context<Self>) {
         cx.emit(PromptResponse(self.active_action_id));
     }
 
-    fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
+    fn cancel(&mut self, _: &menu::Cancel, _window: &mut Window, cx: &mut Context<Self>) {
         if let Some(ix) = self.actions.iter().position(|a| a == "Cancel") {
             cx.emit(PromptResponse(ix));
         }
     }
 
-    fn select_first(&mut self, _: &menu::SelectFirst, cx: &mut ViewContext<Self>) {
+    fn select_first(
+        &mut self,
+        _: &menu::SelectFirst,
+        _window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.active_action_id = self.actions.len().saturating_sub(1);
         cx.notify();
     }
 
-    fn select_last(&mut self, _: &menu::SelectLast, cx: &mut ViewContext<Self>) {
+    fn select_last(&mut self, _: &menu::SelectLast, _window: &mut Window, cx: &mut Context<Self>) {
         self.active_action_id = 0;
         cx.notify();
     }
 
-    fn select_next(&mut self, _: &menu::SelectNext, cx: &mut ViewContext<Self>) {
+    fn select_next(&mut self, _: &menu::SelectNext, _window: &mut Window, cx: &mut Context<Self>) {
         if self.active_action_id > 0 {
             self.active_action_id -= 1;
         } else {
@@ -97,14 +102,14 @@ impl FallbackPromptRenderer {
         cx.notify();
     }
 
-    fn select_prev(&mut self, _: &menu::SelectPrev, cx: &mut ViewContext<Self>) {
+    fn select_prev(&mut self, _: &menu::SelectPrev, _window: &mut Window, cx: &mut Context<Self>) {
         self.active_action_id = (self.active_action_id + 1) % self.actions.len();
         cx.notify();
     }
 }
 
 impl Render for FallbackPromptRenderer {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let settings = ThemeSettings::get_global(cx);
         let font_family = settings.ui_font.family.clone();
         let prompt = v_flex()
@@ -144,7 +149,7 @@ impl Render for FallbackPromptRenderer {
                             el.style(ButtonStyle::Tinted(TintColor::Accent))
                         })
                         .layer(ElevationIndex::ModalSurface)
-                        .on_click(cx.listener(move |_, _, cx| {
+                        .on_click(cx.listener(move |_, _, _window, cx| {
                             cx.emit(PromptResponse(ix));
                         }))
                 }),
@@ -173,8 +178,8 @@ impl Render for FallbackPromptRenderer {
 
 impl EventEmitter<PromptResponse> for FallbackPromptRenderer {}
 
-impl FocusableView for FallbackPromptRenderer {
-    fn focus_handle(&self, _: &crate::AppContext) -> FocusHandle {
+impl Focusable for FallbackPromptRenderer {
+    fn focus_handle(&self, _: &crate::App) -> FocusHandle {
         self.focus.clone()
     }
 }

crates/zed/src/zed/open_listener.rs 🔗

@@ -1,6 +1,6 @@
 use crate::handle_open_request;
 use crate::restorable_workspace_locations;
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use cli::{ipc, IpcHandshake};
 use cli::{ipc::IpcSender, CliRequest, CliResponse};
 use client::parse_zed_link;
@@ -12,7 +12,7 @@ use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
 use futures::channel::{mpsc, oneshot};
 use futures::future::join_all;
 use futures::{FutureExt, SinkExt, StreamExt};
-use gpui::{AppContext, AsyncAppContext, Global, WindowHandle};
+use gpui::{App, AsyncAppContext, Global, WindowHandle};
 use language::Point;
 use recent_projects::{open_ssh_project, SshSettings};
 use remote::SshConnectionOptions;
@@ -37,7 +37,7 @@ pub struct OpenRequest {
 }
 
 impl OpenRequest {
-    pub fn parse(urls: Vec<String>, cx: &AppContext) -> Result<Self> {
+    pub fn parse(urls: Vec<String>, cx: &App) -> Result<Self> {
         let mut this = Self::default();
         for url in urls {
             if let Some(server_name) = url.strip_prefix("zed-cli://") {
@@ -67,7 +67,7 @@ impl OpenRequest {
         }
     }
 
-    fn parse_ssh_file_path(&mut self, file: &str, cx: &AppContext) -> Result<()> {
+    fn parse_ssh_file_path(&mut self, file: &str, cx: &App) -> Result<()> {
         let url = url::Url::parse(file)?;
         let host = url
             .host()
@@ -233,9 +233,9 @@ pub async fn open_paths_with_positions(
         };
         if let Some(active_editor) = item.downcast::<Editor>() {
             workspace
-                .update(cx, |_, cx| {
+                .update(cx, |_, window, cx| {
                     active_editor.update(cx, |editor, cx| {
-                        editor.go_to_singleton_buffer_point(point, cx);
+                        editor.go_to_singleton_buffer_point(point, window, cx);
                     });
                 })
                 .log_err();
@@ -334,8 +334,8 @@ async fn open_workspaces(
                     env,
                     ..Default::default()
                 };
-                workspace::open_new(open_options, app_state, cx, |workspace, cx| {
-                    Editor::new_file(workspace, &Default::default(), cx)
+                workspace::open_new(open_options, app_state, cx, |workspace, window, cx| {
+                    Editor::new_file(workspace, &Default::default(), window, cx)
                 })
                 .detach();
             })
@@ -466,8 +466,8 @@ async fn open_local_workspace(
                 let wait = async move {
                     if paths_with_position.is_empty() {
                         let (done_tx, done_rx) = oneshot::channel();
-                        let _subscription = workspace.update(cx, |_, cx| {
-                            cx.on_release(move |_, _, _| {
+                        let _subscription = workspace.update(cx, |_, _, cx| {
+                            cx.on_release(move |_, _| {
                                 let _ = done_tx.send(());
                             })
                         });
@@ -565,7 +565,7 @@ mod tests {
         assert_eq!(cx.windows().len(), 1);
         let workspace = cx.windows()[0].downcast::<Workspace>().unwrap();
         workspace
-            .update(cx, |workspace, cx| {
+            .update(cx, |workspace, _, cx| {
                 assert!(workspace.active_item_as::<Editor>(cx).is_none())
             })
             .unwrap();
@@ -575,7 +575,7 @@ mod tests {
 
         assert_eq!(cx.windows().len(), 1);
         workspace
-            .update(cx, |workspace, cx| {
+            .update(cx, |workspace, _, cx| {
                 assert!(workspace.active_item_as::<Editor>(cx).is_some());
             })
             .unwrap();
@@ -587,7 +587,7 @@ mod tests {
 
         let workspace_2 = cx.windows()[1].downcast::<Workspace>().unwrap();
         workspace_2
-            .update(cx, |workspace, cx| {
+            .update(cx, |workspace, _, cx| {
                 assert!(workspace.active_item_as::<Editor>(cx).is_some());
                 let items = workspace.items(cx).collect::<Vec<_>>();
                 assert_eq!(items.len(), 1, "Workspace should have two items");
@@ -609,7 +609,7 @@ mod tests {
         assert_eq!(cx.windows().len(), 1);
         let workspace_1 = cx.windows()[0].downcast::<Workspace>().unwrap();
         workspace_1
-            .update(cx, |workspace, cx| {
+            .update(cx, |workspace, _, cx| {
                 assert!(workspace.active_item_as::<Editor>(cx).is_some())
             })
             .unwrap();
@@ -620,7 +620,7 @@ mod tests {
 
         assert_eq!(cx.windows().len(), 1);
         workspace_1
-            .update(cx, |workspace, cx| {
+            .update(cx, |workspace, _, cx| {
                 let items = workspace.items(cx).collect::<Vec<_>>();
                 assert_eq!(items.len(), 2, "Workspace should have two items");
             })
@@ -633,7 +633,7 @@ mod tests {
         assert_eq!(cx.windows().len(), 2);
         let workspace_2 = cx.windows()[1].downcast::<Workspace>().unwrap();
         workspace_2
-            .update(cx, |workspace, cx| {
+            .update(cx, |workspace, _, cx| {
                 let items = workspace.items(cx).collect::<Vec<_>>();
                 assert_eq!(items.len(), 1, "Workspace should have two items");
             })

crates/zed/src/zed/quick_action_bar.rs 🔗

@@ -10,8 +10,8 @@ use editor::actions::{
 };
 use editor::{Editor, EditorSettings};
 use gpui::{
-    Action, ClickEvent, Corner, ElementId, EventEmitter, FocusHandle, FocusableView,
-    InteractiveElement, ParentElement, Render, Styled, Subscription, View, ViewContext, WeakView,
+    Action, ClickEvent, Context, Corner, ElementId, Entity, EventEmitter, FocusHandle, Focusable,
+    InteractiveElement, ParentElement, Render, Styled, Subscription, WeakEntity, Window,
 };
 use search::{buffer_search, BufferSearchBar};
 use settings::{Settings, SettingsStore};
@@ -28,18 +28,18 @@ use zed_actions::{assistant::InlineAssist, outline::ToggleOutline};
 pub struct QuickActionBar {
     _inlay_hints_enabled_subscription: Option<Subscription>,
     active_item: Option<Box<dyn ItemHandle>>,
-    buffer_search_bar: View<BufferSearchBar>,
+    buffer_search_bar: Entity<BufferSearchBar>,
     show: bool,
     toggle_selections_handle: PopoverMenuHandle<ContextMenu>,
     toggle_settings_handle: PopoverMenuHandle<ContextMenu>,
-    workspace: WeakView<Workspace>,
+    workspace: WeakEntity<Workspace>,
 }
 
 impl QuickActionBar {
     pub fn new(
-        buffer_search_bar: View<BufferSearchBar>,
+        buffer_search_bar: Entity<BufferSearchBar>,
         workspace: &Workspace,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Self {
         let mut this = Self {
             _inlay_hints_enabled_subscription: None,
@@ -56,13 +56,13 @@ impl QuickActionBar {
         this
     }
 
-    fn active_editor(&self) -> Option<View<Editor>> {
+    fn active_editor(&self) -> Option<Entity<Editor>> {
         self.active_item
             .as_ref()
             .and_then(|item| item.downcast::<Editor>())
     }
 
-    fn apply_settings(&mut self, cx: &mut ViewContext<Self>) {
+    fn apply_settings(&mut self, cx: &mut Context<Self>) {
         let new_show = EditorSettings::get_global(cx).toolbar.quick_actions;
         if new_show != self.show {
             self.show = new_show;
@@ -82,7 +82,7 @@ impl QuickActionBar {
 }
 
 impl Render for QuickActionBar {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let Some(editor) = self.active_editor() else {
             return div().id("empty quick action bar");
         };
@@ -128,9 +128,9 @@ impl Render for QuickActionBar {
                 "Buffer Search",
                 {
                     let buffer_search_bar = self.buffer_search_bar.clone();
-                    move |_, cx| {
+                    move |_, window, cx| {
                         buffer_search_bar.update(cx, |search_bar, cx| {
-                            search_bar.toggle(&buffer_search::Deploy::find(), cx)
+                            search_bar.toggle(&buffer_search::Deploy::find(), window, cx)
                         });
                     }
                 },
@@ -146,10 +146,15 @@ impl Render for QuickActionBar {
             "Inline Assist",
             {
                 let workspace = self.workspace.clone();
-                move |_, cx| {
+                move |_, window, cx| {
                     if let Some(workspace) = workspace.upgrade() {
                         workspace.update(cx, |workspace, cx| {
-                            AssistantPanel::inline_assist(workspace, &InlineAssist::default(), cx);
+                            AssistantPanel::inline_assist(
+                                workspace,
+                                &InlineAssist::default(),
+                                window,
+                                cx,
+                            );
                         });
                     }
                 }
@@ -167,14 +172,14 @@ impl Render for QuickActionBar {
                         .style(ButtonStyle::Subtle)
                         .toggle_state(self.toggle_selections_handle.is_deployed())
                         .when(!self.toggle_selections_handle.is_deployed(), |this| {
-                            this.tooltip(|cx| Tooltip::text("Selection Controls", cx))
+                            this.tooltip(Tooltip::text("Selection Controls"))
                         }),
                 )
                 .with_handle(self.toggle_selections_handle.clone())
                 .anchor(Corner::TopRight)
-                .menu(move |cx| {
+                .menu(move |window, cx| {
                     let focus = focus.clone();
-                    let menu = ContextMenu::build(cx, move |menu, _| {
+                    let menu = ContextMenu::build(window, cx, move |menu, _, _| {
                         menu.context(focus.clone())
                             .action("Select All", Box::new(SelectAll))
                             .action(
@@ -217,13 +222,13 @@ impl Render for QuickActionBar {
                         .style(ButtonStyle::Subtle)
                         .toggle_state(self.toggle_settings_handle.is_deployed())
                         .when(!self.toggle_settings_handle.is_deployed(), |this| {
-                            this.tooltip(|cx| Tooltip::text("Editor Controls", cx))
+                            this.tooltip(Tooltip::text("Editor Controls"))
                         }),
                 )
                 .anchor(Corner::TopRight)
                 .with_handle(self.toggle_settings_handle.clone())
-                .menu(move |cx| {
-                    let menu = ContextMenu::build(cx, |mut menu, _| {
+                .menu(move |window, cx| {
+                    let menu = ContextMenu::build(window, cx, |mut menu, _, _| {
                         if supports_inlay_hints {
                             menu = menu.toggleable_entry(
                                 "Inlay Hints",
@@ -232,11 +237,12 @@ impl Render for QuickActionBar {
                                 Some(editor::actions::ToggleInlayHints.boxed_clone()),
                                 {
                                     let editor = editor.clone();
-                                    move |cx| {
+                                    move |window, cx| {
                                         editor
                                             .update(cx, |editor, cx| {
                                                 editor.toggle_inlay_hints(
                                                     &editor::actions::ToggleInlayHints,
+                                                    window,
                                                     cx,
                                                 );
                                             })
@@ -253,11 +259,12 @@ impl Render for QuickActionBar {
                             Some(editor::actions::ToggleSelectionMenu.boxed_clone()),
                             {
                                 let editor = editor.clone();
-                                move |cx| {
+                                move |window, cx| {
                                     editor
                                         .update(cx, |editor, cx| {
                                             editor.toggle_selection_menu(
                                                 &editor::actions::ToggleSelectionMenu,
+                                                window,
                                                 cx,
                                             )
                                         })
@@ -273,11 +280,12 @@ impl Render for QuickActionBar {
                             Some(editor::actions::ToggleAutoSignatureHelp.boxed_clone()),
                             {
                                 let editor = editor.clone();
-                                move |cx| {
+                                move |window, cx| {
                                     editor
                                         .update(cx, |editor, cx| {
                                             editor.toggle_auto_signature_help_menu(
                                                 &editor::actions::ToggleAutoSignatureHelp,
+                                                window,
                                                 cx,
                                             );
                                         })
@@ -293,11 +301,12 @@ impl Render for QuickActionBar {
                             Some(editor::actions::ToggleInlineCompletions.boxed_clone()),
                             {
                                 let editor = editor.clone();
-                                move |cx| {
+                                move |window, cx| {
                                     editor
                                         .update(cx, |editor, cx| {
                                             editor.toggle_inline_completions(
                                                 &editor::actions::ToggleInlineCompletions,
+                                                window,
                                                 cx,
                                             );
                                         })
@@ -315,11 +324,12 @@ impl Render for QuickActionBar {
                             Some(editor::actions::ToggleGitBlameInline.boxed_clone()),
                             {
                                 let editor = editor.clone();
-                                move |cx| {
+                                move |window, cx| {
                                     editor
                                         .update(cx, |editor, cx| {
                                             editor.toggle_git_blame_inline(
                                                 &editor::actions::ToggleGitBlameInline,
+                                                window,
                                                 cx,
                                             )
                                         })
@@ -335,11 +345,12 @@ impl Render for QuickActionBar {
                             Some(editor::actions::ToggleGitBlame.boxed_clone()),
                             {
                                 let editor = editor.clone();
-                                move |cx| {
+                                move |window, cx| {
                                     editor
                                         .update(cx, |editor, cx| {
                                             editor.toggle_git_blame(
                                                 &editor::actions::ToggleGitBlame,
+                                                window,
                                                 cx,
                                             )
                                         })
@@ -356,10 +367,10 @@ impl Render for QuickActionBar {
                             IconPosition::Start,
                             None,
                             {
-                                move |cx| {
+                                move |window, cx| {
                                     let new_value = !vim_mode_enabled;
                                     VimModeSetting::override_global(VimModeSetting(new_value), cx);
-                                    cx.refresh();
+                                    window.refresh();
                                 }
                             },
                         );
@@ -396,7 +407,7 @@ struct QuickActionBarButton {
     action: Box<dyn Action>,
     focus_handle: FocusHandle,
     tooltip: SharedString,
-    on_click: Box<dyn Fn(&ClickEvent, &mut WindowContext)>,
+    on_click: Box<dyn Fn(&ClickEvent, &mut Window, &mut App)>,
 }
 
 impl QuickActionBarButton {
@@ -407,7 +418,7 @@ impl QuickActionBarButton {
         action: Box<dyn Action>,
         focus_handle: FocusHandle,
         tooltip: impl Into<SharedString>,
-        on_click: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
+        on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
     ) -> Self {
         Self {
             id: id.into(),
@@ -422,7 +433,7 @@ impl QuickActionBarButton {
 }
 
 impl RenderOnce for QuickActionBarButton {
-    fn render(self, _: &mut WindowContext) -> impl IntoElement {
+    fn render(self, _window: &mut Window, _: &mut App) -> impl IntoElement {
         let tooltip = self.tooltip.clone();
         let action = self.action.boxed_clone();
 
@@ -431,10 +442,10 @@ impl RenderOnce for QuickActionBarButton {
             .icon_size(IconSize::Small)
             .style(ButtonStyle::Subtle)
             .toggle_state(self.toggled)
-            .tooltip(move |cx| {
-                Tooltip::for_action_in(tooltip.clone(), &*action, &self.focus_handle, cx)
+            .tooltip(move |window, cx| {
+                Tooltip::for_action_in(tooltip.clone(), &*action, &self.focus_handle, window, cx)
             })
-            .on_click(move |event, cx| (self.on_click)(event, cx))
+            .on_click(move |event, window, cx| (self.on_click)(event, window, cx))
     }
 }
 
@@ -442,7 +453,8 @@ impl ToolbarItemView for QuickActionBar {
     fn set_active_pane_item(
         &mut self,
         active_pane_item: Option<&dyn ItemHandle>,
-        cx: &mut ViewContext<Self>,
+        _: &mut Window,
+        cx: &mut Context<Self>,
     ) -> ToolbarItemLocation {
         self.active_item = active_pane_item.map(ItemHandle::boxed_clone);
         if let Some(active_item) = active_pane_item {

crates/zed/src/zed/quick_action_bar/markdown_preview.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{AnyElement, Modifiers, WeakView};
+use gpui::{AnyElement, Modifiers, WeakEntity};
 use markdown_preview::{
     markdown_preview_view::MarkdownPreviewView, OpenPreview, OpenPreviewToTheSide,
 };
@@ -10,8 +10,8 @@ use super::QuickActionBar;
 impl QuickActionBar {
     pub fn render_toggle_markdown_preview(
         &self,
-        workspace: WeakView<Workspace>,
-        cx: &mut ViewContext<Self>,
+        workspace: WeakEntity<Workspace>,
+        cx: &mut Context<Self>,
     ) -> Option<AnyElement> {
         let mut active_editor_is_markdown = false;
 
@@ -37,7 +37,7 @@ impl QuickActionBar {
             .shape(IconButtonShape::Square)
             .icon_size(IconSize::Small)
             .style(ButtonStyle::Subtle)
-            .tooltip(move |cx| {
+            .tooltip(move |window, cx| {
                 Tooltip::with_meta(
                     "Preview Markdown",
                     Some(&markdown_preview::OpenPreview),
@@ -45,16 +45,17 @@ impl QuickActionBar {
                         "{} to open in a split",
                         text_for_keystroke(&alt_click, PlatformStyle::platform())
                     ),
+                    window,
                     cx,
                 )
             })
-            .on_click(move |_, cx| {
+            .on_click(move |_, window, cx| {
                 if let Some(workspace) = workspace.upgrade() {
                     workspace.update(cx, |_, cx| {
-                        if cx.modifiers().alt {
-                            cx.dispatch_action(Box::new(OpenPreviewToTheSide));
+                        if window.modifiers().alt {
+                            window.dispatch_action(Box::new(OpenPreviewToTheSide), cx);
                         } else {
-                            cx.dispatch_action(Box::new(OpenPreview));
+                            window.dispatch_action(Box::new(OpenPreview), cx);
                         }
                     });
                 }

crates/zed/src/zed/quick_action_bar/repl_menu.rs 🔗

@@ -1,7 +1,7 @@
 use std::time::Duration;
 
 use gpui::ElementId;
-use gpui::{percentage, Animation, AnimationExt, AnyElement, Transformation, View};
+use gpui::{percentage, Animation, AnimationExt, AnyElement, Entity, Transformation};
 use picker::Picker;
 use repl::{
     components::{KernelPickerDelegate, KernelSelector},
@@ -32,7 +32,7 @@ struct ReplMenuState {
 }
 
 impl QuickActionBar {
-    pub fn render_repl_menu(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
+    pub fn render_repl_menu(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
         if !JupyterSettings::enabled(cx) {
             return None;
         }
@@ -82,10 +82,10 @@ impl QuickActionBar {
 
         let editor = editor.downgrade();
         let dropdown_menu = PopoverMenu::new(element_id("menu"))
-            .menu(move |cx| {
+            .menu(move |window, cx| {
                 let editor = editor.clone();
                 let session = session.clone();
-                ContextMenu::build(cx, move |menu, cx| {
+                ContextMenu::build(window, cx, move |menu, _, cx| {
                     let menu_state = session_state(session, cx);
                     let status = menu_state.status;
                     let editor = editor.clone();
@@ -93,7 +93,7 @@ impl QuickActionBar {
                     menu.map(|menu| {
                         if status.is_connected() {
                             let status = status.clone();
-                            menu.custom_row(move |_cx| {
+                            menu.custom_row(move |_window, _cx| {
                                 h_flex()
                                     .child(
                                         Label::new(format!(
@@ -106,7 +106,7 @@ impl QuickActionBar {
                                     )
                                     .into_any_element()
                             })
-                            .custom_row(move |_cx| {
+                            .custom_row(move |_window, _cx| {
                                 h_flex()
                                     .child(
                                         Label::new(status.clone().to_string())
@@ -117,7 +117,7 @@ impl QuickActionBar {
                             })
                         } else {
                             let status = status.clone();
-                            menu.custom_row(move |_cx| {
+                            menu.custom_row(move |_window, _cx| {
                                 h_flex()
                                     .child(
                                         Label::new(format!("{}...", status.clone().to_string()))
@@ -130,7 +130,7 @@ impl QuickActionBar {
                     })
                     .separator()
                     .custom_entry(
-                        move |_cx| {
+                        move |_window, _cx| {
                             Label::new(if has_nonempty_selection {
                                 "Run Selection"
                             } else {
@@ -140,13 +140,13 @@ impl QuickActionBar {
                         },
                         {
                             let editor = editor.clone();
-                            move |cx| {
-                                repl::run(editor.clone(), true, cx).log_err();
+                            move |window, cx| {
+                                repl::run(editor.clone(), true, window, cx).log_err();
                             }
                         },
                     )
                     .custom_entry(
-                        move |_cx| {
+                        move |_window, _cx| {
                             Label::new("Interrupt")
                                 .size(LabelSize::Small)
                                 .color(Color::Error)
@@ -154,13 +154,13 @@ impl QuickActionBar {
                         },
                         {
                             let editor = editor.clone();
-                            move |cx| {
+                            move |_, cx| {
                                 repl::interrupt(editor.clone(), cx);
                             }
                         },
                     )
                     .custom_entry(
-                        move |_cx| {
+                        move |_window, _cx| {
                             Label::new("Clear Outputs")
                                 .size(LabelSize::Small)
                                 .color(Color::Muted)
@@ -168,14 +168,14 @@ impl QuickActionBar {
                         },
                         {
                             let editor = editor.clone();
-                            move |cx| {
+                            move |_, cx| {
                                 repl::clear_outputs(editor.clone(), cx);
                             }
                         },
                     )
                     .separator()
                     .custom_entry(
-                        move |_cx| {
+                        move |_window, _cx| {
                             Label::new("Shut Down Kernel")
                                 .size(LabelSize::Small)
                                 .color(Color::Error)
@@ -183,13 +183,13 @@ impl QuickActionBar {
                         },
                         {
                             let editor = editor.clone();
-                            move |cx| {
-                                repl::shutdown(editor.clone(), cx);
+                            move |window, cx| {
+                                repl::shutdown(editor.clone(), window, cx);
                             }
                         },
                     )
                     .custom_entry(
-                        move |_cx| {
+                        move |_window, _cx| {
                             Label::new("Restart Kernel")
                                 .size(LabelSize::Small)
                                 .color(Color::Error)
@@ -197,8 +197,8 @@ impl QuickActionBar {
                         },
                         {
                             let editor = editor.clone();
-                            move |cx| {
-                                repl::restart(editor.clone(), cx);
+                            move |window, cx| {
+                                repl::restart(editor.clone(), window, cx);
                             }
                         },
                     )
@@ -216,7 +216,7 @@ impl QuickActionBar {
                             .size(IconSize::XSmall)
                             .color(Color::Muted),
                     )
-                    .tooltip(move |cx| Tooltip::text("REPL Menu", cx))
+                    .tooltip(Tooltip::text("REPL Menu"))
                     .width(rems(1.).into())
                     .disabled(menu_state.popover_disabled),
             );
@@ -241,8 +241,8 @@ impl QuickActionBar {
             })
             .size(ButtonSize::Compact)
             .style(ButtonStyle::Subtle)
-            .tooltip(move |cx| Tooltip::text(menu_state.tooltip.clone(), cx))
-            .on_click(|_, cx| cx.dispatch_action(Box::new(repl::Run {})))
+            .tooltip(Tooltip::text(menu_state.tooltip))
+            .on_click(|_, window, cx| window.dispatch_action(Box::new(repl::Run {}), cx))
             .into_any_element();
 
         Some(
@@ -256,7 +256,7 @@ impl QuickActionBar {
     pub fn render_repl_launch_menu(
         &self,
         kernel_specification: KernelSpecification,
-        cx: &mut ViewContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<AnyElement> {
         let tooltip: SharedString =
             SharedString::from(format!("Start REPL for {}", kernel_specification.name()));
@@ -269,14 +269,16 @@ impl QuickActionBar {
                         .size(ButtonSize::Compact)
                         .icon_color(Color::Muted)
                         .style(ButtonStyle::Subtle)
-                        .tooltip(move |cx| Tooltip::text(tooltip.clone(), cx))
-                        .on_click(|_, cx| cx.dispatch_action(Box::new(repl::Run {}))),
+                        .tooltip(Tooltip::text(tooltip))
+                        .on_click(|_, window, cx| {
+                            window.dispatch_action(Box::new(repl::Run {}), cx)
+                        }),
                 )
                 .into_any_element(),
         )
     }
 
-    pub fn render_kernel_selector(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    pub fn render_kernel_selector(&self, cx: &mut Context<Self>) -> impl IntoElement {
         let editor = if let Some(editor) = self.active_editor() {
             editor
         } else {
@@ -290,7 +292,9 @@ impl QuickActionBar {
         let session = repl::session(editor.downgrade(), cx);
 
         let current_kernelspec = match session {
-            SessionSupport::ActiveSession(view) => Some(view.read(cx).kernel_specification.clone()),
+            SessionSupport::ActiveSession(session) => {
+                Some(session.read(cx).kernel_specification.clone())
+            }
             SessionSupport::Inactive(kernel_specification) => Some(kernel_specification),
             SessionSupport::RequiresSetup(_language_name) => None,
             SessionSupport::Unsupported => None,
@@ -302,8 +306,8 @@ impl QuickActionBar {
             PopoverMenuHandle::default();
         KernelSelector::new(
             {
-                Box::new(move |kernelspec, cx| {
-                    repl::assign_kernelspec(kernelspec, editor.downgrade(), cx).ok();
+                Box::new(move |kernelspec, window, cx| {
+                    repl::assign_kernelspec(kernelspec, editor.downgrade(), window, cx).ok();
                 })
             },
             worktree_id,
@@ -340,17 +344,13 @@ impl QuickActionBar {
                                 .size(IconSize::XSmall),
                         ),
                 )
-                .tooltip(move |cx| Tooltip::text("Select Kernel", cx)),
+                .tooltip(Tooltip::text("Select Kernel")),
         )
         .with_handle(menu_handle.clone())
         .into_any_element()
     }
 
-    pub fn render_repl_setup(
-        &self,
-        language: &str,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<AnyElement> {
+    pub fn render_repl_setup(&self, language: &str, cx: &mut Context<Self>) -> Option<AnyElement> {
         let tooltip: SharedString = SharedString::from(format!("Setup Zed REPL for {}", language));
         Some(
             h_flex()
@@ -362,8 +362,8 @@ impl QuickActionBar {
                         .shape(ui::IconButtonShape::Square)
                         .icon_size(ui::IconSize::Small)
                         .icon_color(Color::Muted)
-                        .tooltip(move |cx| Tooltip::text(tooltip.clone(), cx))
-                        .on_click(|_, cx| {
+                        .tooltip(Tooltip::text(tooltip.clone()))
+                        .on_click(|_, _window, cx| {
                             cx.open_url(&format!("{}#installation", ZED_REPL_DOCUMENTATION))
                         }),
                 )
@@ -372,7 +372,7 @@ impl QuickActionBar {
     }
 }
 
-fn session_state(session: View<Session>, cx: &WindowContext) -> ReplMenuState {
+fn session_state(session: Entity<Session>, cx: &mut App) -> ReplMenuState {
     let session = session.read(cx);
 
     let kernel_name = session.kernel_specification.name();

crates/zed_actions/src/lib.rs 🔗

@@ -166,9 +166,9 @@ impl_actions!(task, [Spawn, Rerun]);
 pub mod outline {
     use std::sync::OnceLock;
 
-    use gpui::{action_as, AnyView, WindowContext};
+    use gpui::{action_as, AnyView, App, Window};
 
     action_as!(outline, ToggleOutline as Toggle);
     /// A pointer to outline::toggle function, exposed here to sewer the breadcrumbs <-> outline dependency.
-    pub static TOGGLE_OUTLINE: OnceLock<fn(AnyView, &mut WindowContext<'_>)> = OnceLock::new();
+    pub static TOGGLE_OUTLINE: OnceLock<fn(AnyView, &mut Window, &mut App)> = OnceLock::new();
 }

crates/zed_predict_tos/src/zed_predict_tos.rs 🔗

@@ -2,8 +2,8 @@
 
 use client::UserStore;
 use gpui::{
-    AppContext, ClickEvent, DismissEvent, EventEmitter, FocusHandle, FocusableView, Model,
-    MouseDownEvent, Render, View,
+    App, ClickEvent, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, MouseDownEvent,
+    Render,
 };
 use ui::{prelude::*, TintColor};
 use workspace::{ModalView, Workspace};
@@ -11,16 +11,16 @@ use workspace::{ModalView, Workspace};
 /// Terms of acceptance for AI inline prediction.
 pub struct ZedPredictTos {
     focus_handle: FocusHandle,
-    user_store: Model<UserStore>,
-    workspace: View<Workspace>,
+    user_store: Entity<UserStore>,
+    workspace: Entity<Workspace>,
     viewed: bool,
 }
 
 impl ZedPredictTos {
     fn new(
-        workspace: View<Workspace>,
-        user_store: Model<UserStore>,
-        cx: &mut ViewContext<Self>,
+        workspace: Entity<Workspace>,
+        user_store: Entity<UserStore>,
+        cx: &mut Context<Self>,
     ) -> Self {
         ZedPredictTos {
             viewed: false,
@@ -30,23 +30,26 @@ impl ZedPredictTos {
         }
     }
     pub fn toggle(
-        workspace: View<Workspace>,
-        user_store: Model<UserStore>,
-        cx: &mut WindowContext,
+        workspace: Entity<Workspace>,
+        user_store: Entity<UserStore>,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         workspace.update(cx, |this, cx| {
-            let workspace = cx.view().clone();
-            this.toggle_modal(cx, |cx| ZedPredictTos::new(workspace, user_store, cx));
+            let workspace = cx.model().clone();
+            this.toggle_modal(window, cx, |_window, cx| {
+                ZedPredictTos::new(workspace, user_store, cx)
+            });
         });
     }
 
-    fn view_terms(&mut self, _: &ClickEvent, cx: &mut ViewContext<Self>) {
+    fn view_terms(&mut self, _: &ClickEvent, _window: &mut Window, cx: &mut Context<Self>) {
         self.viewed = true;
         cx.open_url("https://zed.dev/terms-of-service");
         cx.notify();
     }
 
-    fn accept_terms(&mut self, _: &ClickEvent, cx: &mut ViewContext<Self>) {
+    fn accept_terms(&mut self, _: &ClickEvent, _window: &mut Window, cx: &mut Context<Self>) {
         let task = self
             .user_store
             .update(cx, |this, cx| this.accept_terms_of_service(cx));
@@ -66,15 +69,15 @@ impl ZedPredictTos {
         .detach_and_log_err(cx);
     }
 
-    fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
+    fn cancel(&mut self, _: &menu::Cancel, _window: &mut Window, cx: &mut Context<Self>) {
         cx.emit(DismissEvent);
     }
 }
 
 impl EventEmitter<DismissEvent> for ZedPredictTos {}
 
-impl FocusableView for ZedPredictTos {
-    fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
+impl Focusable for ZedPredictTos {
+    fn focus_handle(&self, _cx: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
@@ -82,7 +85,7 @@ impl FocusableView for ZedPredictTos {
 impl ModalView for ZedPredictTos {}
 
 impl Render for ZedPredictTos {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         v_flex()
             .id("zed predict tos")
             .track_focus(&self.focus_handle(cx))
@@ -93,11 +96,11 @@ impl Render for ZedPredictTos {
             .items_center()
             .p_4()
             .gap_2()
-            .on_action(cx.listener(|_, _: &menu::Cancel, cx| {
+            .on_action(cx.listener(|_, _: &menu::Cancel, _window, cx| {
                 cx.emit(DismissEvent);
             }))
-            .on_any_mouse_down(cx.listener(|this, _: &MouseDownEvent, cx| {
-                cx.focus(&this.focus_handle);
+            .on_any_mouse_down(cx.listener(|this, _: &MouseDownEvent, window, _cx| {
+                this.focus_handle.focus(window);
             }))
             .child(
                 h_flex()
@@ -143,7 +146,7 @@ impl Render for ZedPredictTos {
                     .child(
                         Button::new("cancel", "Cancel")
                             .full_width()
-                            .on_click(cx.listener(|_, _: &ClickEvent, cx| {
+                            .on_click(cx.listener(|_, _: &ClickEvent, _window, cx| {
                                 cx.emit(DismissEvent);
                             })),
                     ),

crates/zeta/src/completion_diff_element.rs 🔗

@@ -2,8 +2,8 @@ use std::cmp;
 
 use crate::InlineCompletion;
 use gpui::{
-    point, prelude::*, quad, size, AnyElement, AppContext, Bounds, Corners, Edges, HighlightStyle,
-    Hsla, StyledText, TextLayout, TextStyle,
+    point, prelude::*, quad, size, AnyElement, App, Bounds, Corners, Edges, HighlightStyle, Hsla,
+    StyledText, TextLayout, TextStyle,
 };
 use language::OffsetRangeExt;
 use settings::Settings;
@@ -17,7 +17,7 @@ pub struct CompletionDiffElement {
 }
 
 impl CompletionDiffElement {
-    pub fn new(completion: &InlineCompletion, cx: &AppContext) -> Self {
+    pub fn new(completion: &InlineCompletion, cx: &App) -> Self {
         let mut diff = completion
             .snapshot
             .text_for_range(completion.excerpt_range.clone())
@@ -108,9 +108,10 @@ impl Element for CompletionDiffElement {
     fn request_layout(
         &mut self,
         _id: Option<&gpui::GlobalElementId>,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> (gpui::LayoutId, Self::RequestLayoutState) {
-        (self.element.request_layout(cx), ())
+        (self.element.request_layout(window, cx), ())
     }
 
     fn prepaint(
@@ -118,9 +119,10 @@ impl Element for CompletionDiffElement {
         _id: Option<&gpui::GlobalElementId>,
         _bounds: gpui::Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) -> Self::PrepaintState {
-        self.element.prepaint(cx);
+        self.element.prepaint(window, cx);
     }
 
     fn paint(
@@ -129,7 +131,8 @@ impl Element for CompletionDiffElement {
         _bounds: gpui::Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
         _prepaint: &mut Self::PrepaintState,
-        cx: &mut WindowContext,
+        window: &mut Window,
+        cx: &mut App,
     ) {
         if let Some(position) = self.text_layout.position_for_index(self.cursor_offset) {
             let bounds = self.text_layout.bounds();
@@ -138,7 +141,7 @@ impl Element for CompletionDiffElement {
                 .text_layout
                 .line_layout_for_index(self.cursor_offset)
                 .map_or(bounds.size.width, |layout| layout.width());
-            cx.paint_quad(quad(
+            window.paint_quad(quad(
                 Bounds::new(
                     point(bounds.origin.x, position.y),
                     size(cmp::max(bounds.size.width, line_width), line_height),
@@ -148,8 +151,8 @@ impl Element for CompletionDiffElement {
                 Edges::default(),
                 Hsla::transparent_black(),
             ));
-            self.element.paint(cx);
-            cx.paint_quad(quad(
+            self.element.paint(window, cx);
+            window.paint_quad(quad(
                 Bounds::new(position, size(px(2.), line_height)),
                 Corners::default(),
                 cx.theme().players().local().cursor,

crates/zeta/src/rate_completion_modal.rs 🔗

@@ -1,9 +1,6 @@
 use crate::{CompletionDiffElement, InlineCompletion, InlineCompletionRating, Zeta};
 use editor::Editor;
-use gpui::{
-    actions, prelude::*, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Model,
-    View, ViewContext,
-};
+use gpui::{actions, prelude::*, App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable};
 use language::language_settings;
 use std::time::Duration;
 use ui::{prelude::*, KeyBinding, List, ListItem, ListItemSpacing, Tooltip};
@@ -22,17 +19,17 @@ actions!(
     ]
 );
 
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(move |workspace: &mut Workspace, _cx| {
-        workspace.register_action(|workspace, _: &RateCompletions, cx| {
-            RateCompletionModal::toggle(workspace, cx);
+pub fn init(cx: &mut App) {
+    cx.observe_new(move |workspace: &mut Workspace, _, _cx| {
+        workspace.register_action(|workspace, _: &RateCompletions, window, cx| {
+            RateCompletionModal::toggle(workspace, window, cx);
         });
     })
     .detach();
 }
 
 pub struct RateCompletionModal {
-    zeta: Model<Zeta>,
+    zeta: Entity<Zeta>,
     active_completion: Option<ActiveCompletion>,
     selected_index: usize,
     focus_handle: FocusHandle,
@@ -42,7 +39,7 @@ pub struct RateCompletionModal {
 
 struct ActiveCompletion {
     completion: InlineCompletion,
-    feedback_editor: View<Editor>,
+    feedback_editor: Entity<Editor>,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
@@ -61,13 +58,13 @@ impl RateCompletionView {
 }
 
 impl RateCompletionModal {
-    pub fn toggle(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
+    pub fn toggle(workspace: &mut Workspace, window: &mut Window, cx: &mut Context<Workspace>) {
         if let Some(zeta) = Zeta::global(cx) {
-            workspace.toggle_modal(cx, |cx| RateCompletionModal::new(zeta, cx));
+            workspace.toggle_modal(window, cx, |_window, cx| RateCompletionModal::new(zeta, cx));
         }
     }
 
-    pub fn new(zeta: Model<Zeta>, cx: &mut ViewContext<Self>) -> Self {
+    pub fn new(zeta: Entity<Zeta>, cx: &mut Context<Self>) -> Self {
         let subscription = cx.observe(&zeta, |_, _, cx| cx.notify());
 
         Self {
@@ -80,11 +77,11 @@ impl RateCompletionModal {
         }
     }
 
-    fn dismiss(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
+    fn dismiss(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {
         cx.emit(DismissEvent);
     }
 
-    fn select_next(&mut self, _: &menu::SelectNext, cx: &mut ViewContext<Self>) {
+    fn select_next(&mut self, _: &menu::SelectNext, _: &mut Window, cx: &mut Context<Self>) {
         self.selected_index += 1;
         self.selected_index = usize::min(
             self.selected_index,
@@ -93,12 +90,12 @@ impl RateCompletionModal {
         cx.notify();
     }
 
-    fn select_prev(&mut self, _: &menu::SelectPrev, cx: &mut ViewContext<Self>) {
+    fn select_prev(&mut self, _: &menu::SelectPrev, _: &mut Window, cx: &mut Context<Self>) {
         self.selected_index = self.selected_index.saturating_sub(1);
         cx.notify();
     }
 
-    fn select_next_edit(&mut self, _: &NextEdit, cx: &mut ViewContext<Self>) {
+    fn select_next_edit(&mut self, _: &NextEdit, _: &mut Window, cx: &mut Context<Self>) {
         let next_index = self
             .zeta
             .read(cx)
@@ -115,7 +112,7 @@ impl RateCompletionModal {
         }
     }
 
-    fn select_prev_edit(&mut self, _: &PreviousEdit, cx: &mut ViewContext<Self>) {
+    fn select_prev_edit(&mut self, _: &PreviousEdit, _: &mut Window, cx: &mut Context<Self>) {
         let zeta = self.zeta.read(cx);
         let completions_len = zeta.shown_completions_len();
 
@@ -137,17 +134,22 @@ impl RateCompletionModal {
         cx.notify();
     }
 
-    fn select_first(&mut self, _: &menu::SelectFirst, cx: &mut ViewContext<Self>) {
+    fn select_first(&mut self, _: &menu::SelectFirst, _: &mut Window, cx: &mut Context<Self>) {
         self.selected_index = 0;
         cx.notify();
     }
 
-    fn select_last(&mut self, _: &menu::SelectLast, cx: &mut ViewContext<Self>) {
+    fn select_last(&mut self, _: &menu::SelectLast, _window: &mut Window, cx: &mut Context<Self>) {
         self.selected_index = self.zeta.read(cx).shown_completions_len() - 1;
         cx.notify();
     }
 
-    pub fn thumbs_up_active(&mut self, _: &ThumbsUpActiveCompletion, cx: &mut ViewContext<Self>) {
+    pub fn thumbs_up_active(
+        &mut self,
+        _: &ThumbsUpActiveCompletion,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         self.zeta.update(cx, |zeta, cx| {
             if let Some(active) = &self.active_completion {
                 zeta.rate_completion(
@@ -163,9 +165,9 @@ impl RateCompletionModal {
             .active_completion
             .as_ref()
             .map(|completion| completion.completion.clone());
-        self.select_completion(current_completion, false, cx);
-        self.select_next_edit(&Default::default(), cx);
-        self.confirm(&Default::default(), cx);
+        self.select_completion(current_completion, false, window, cx);
+        self.select_next_edit(&Default::default(), window, cx);
+        self.confirm(&Default::default(), window, cx);
 
         cx.notify();
     }
@@ -173,7 +175,8 @@ impl RateCompletionModal {
     pub fn thumbs_down_active(
         &mut self,
         _: &ThumbsDownActiveCompletion,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         if let Some(active) = &self.active_completion {
             if active.feedback_editor.read(cx).text(cx).is_empty() {
@@ -194,19 +197,29 @@ impl RateCompletionModal {
             .active_completion
             .as_ref()
             .map(|completion| completion.completion.clone());
-        self.select_completion(current_completion, false, cx);
-        self.select_next_edit(&Default::default(), cx);
-        self.confirm(&Default::default(), cx);
+        self.select_completion(current_completion, false, window, cx);
+        self.select_next_edit(&Default::default(), window, cx);
+        self.confirm(&Default::default(), window, cx);
 
         cx.notify();
     }
 
-    fn focus_completions(&mut self, _: &FocusCompletions, cx: &mut ViewContext<Self>) {
-        cx.focus_self();
+    fn focus_completions(
+        &mut self,
+        _: &FocusCompletions,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        cx.focus_self(window);
         cx.notify();
     }
 
-    fn preview_completion(&mut self, _: &PreviewCompletion, cx: &mut ViewContext<Self>) {
+    fn preview_completion(
+        &mut self,
+        _: &PreviewCompletion,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let completion = self
             .zeta
             .read(cx)
@@ -216,10 +229,10 @@ impl RateCompletionModal {
             .next()
             .cloned();
 
-        self.select_completion(completion, false, cx);
+        self.select_completion(completion, false, window, cx);
     }
 
-    fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
+    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
         let completion = self
             .zeta
             .read(cx)
@@ -229,14 +242,15 @@ impl RateCompletionModal {
             .next()
             .cloned();
 
-        self.select_completion(completion, true, cx);
+        self.select_completion(completion, true, window, cx);
     }
 
     pub fn select_completion(
         &mut self,
         completion: Option<InlineCompletion>,
         focus: bool,
-        cx: &mut ViewContext<Self>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
     ) {
         // Avoid resetting completion rating if it's already selected.
         if let Some(completion) = completion.as_ref() {
@@ -253,7 +267,7 @@ impl RateCompletionModal {
             if let Some(prev_completion) = self.active_completion.as_ref() {
                 if completion.id == prev_completion.completion.id {
                     if focus {
-                        cx.focus_view(&prev_completion.feedback_editor);
+                        window.focus(&prev_completion.feedback_editor.focus_handle(cx));
                     }
                     return;
                 }
@@ -262,8 +276,8 @@ impl RateCompletionModal {
 
         self.active_completion = completion.map(|completion| ActiveCompletion {
             completion,
-            feedback_editor: cx.new_view(|cx| {
-                let mut editor = Editor::multi_line(cx);
+            feedback_editor: cx.new(|cx| {
+                let mut editor = Editor::multi_line(window, cx);
                 editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx);
                 editor.set_show_line_numbers(false, cx);
                 editor.set_show_scrollbars(false, cx);
@@ -272,10 +286,10 @@ impl RateCompletionModal {
                 editor.set_show_runnables(false, cx);
                 editor.set_show_wrap_guides(false, cx);
                 editor.set_show_indent_guides(false, cx);
-                editor.set_show_inline_completions(Some(false), cx);
+                editor.set_show_inline_completions(Some(false), window, cx);
                 editor.set_placeholder_text("Add your feedback…", cx);
                 if focus {
-                    cx.focus_self();
+                    cx.focus_self(window);
                 }
                 editor
             }),
@@ -283,7 +297,7 @@ impl RateCompletionModal {
         cx.notify();
     }
 
-    fn render_view_nav(&self, cx: &ViewContext<Self>) -> impl IntoElement {
+    fn render_view_nav(&self, cx: &Context<Self>) -> impl IntoElement {
         h_flex()
             .h_8()
             .px_1()
@@ -297,7 +311,7 @@ impl RateCompletionModal {
                     RateCompletionView::SuggestedEdits.name(),
                 )
                 .label_size(LabelSize::Small)
-                .on_click(cx.listener(move |this, _, cx| {
+                .on_click(cx.listener(move |this, _, _window, cx| {
                     this.current_view = RateCompletionView::SuggestedEdits;
                     cx.notify();
                 }))
@@ -309,7 +323,7 @@ impl RateCompletionModal {
                     RateCompletionView::RawInput.name(),
                 )
                 .label_size(LabelSize::Small)
-                .on_click(cx.listener(move |this, _, cx| {
+                .on_click(cx.listener(move |this, _, _window, cx| {
                     this.current_view = RateCompletionView::RawInput;
                     cx.notify();
                 }))
@@ -317,7 +331,7 @@ impl RateCompletionModal {
             )
     }
 
-    fn render_suggested_edits(&self, cx: &mut ViewContext<Self>) -> Option<gpui::Stateful<Div>> {
+    fn render_suggested_edits(&self, cx: &mut Context<Self>) -> Option<gpui::Stateful<Div>> {
         let active_completion = self.active_completion.as_ref()?;
         let bg_color = cx.theme().colors().editor_background;
 
@@ -336,7 +350,7 @@ impl RateCompletionModal {
         )
     }
 
-    fn render_raw_input(&self, cx: &mut ViewContext<Self>) -> Option<gpui::Stateful<Div>> {
+    fn render_raw_input(&self, cx: &mut Context<Self>) -> Option<gpui::Stateful<Div>> {
         Some(
             v_flex()
                 .size_full()
@@ -364,7 +378,11 @@ impl RateCompletionModal {
         )
     }
 
-    fn render_active_completion(&mut self, cx: &mut ViewContext<Self>) -> Option<impl IntoElement> {
+    fn render_active_completion(
+        &mut self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<impl IntoElement> {
         let active_completion = self.active_completion.as_ref()?;
         let completion_id = active_completion.completion.id;
         let focus_handle = &self.focus_handle(cx);
@@ -473,19 +491,17 @@ impl RateCompletionModal {
                                         .icon_position(IconPosition::Start)
                                         .disabled(rated || feedback_empty)
                                         .when(feedback_empty, |this| {
-                                            this.tooltip(|cx| {
-                                                Tooltip::text("Explain what's bad about it before reporting it", cx)
-                                            })
+                                            this.tooltip(Tooltip::text("Explain what's bad about it before reporting it"))
                                         })
                                         .key_binding(KeyBinding::for_action_in(
                                             &ThumbsDownActiveCompletion,
                                             focus_handle,
-                                            cx,
+                                            window,
                                         ))
-                                        .on_click(cx.listener(move |this, _, cx| {
+                                        .on_click(cx.listener(move |this, _, window, cx| {
                                             this.thumbs_down_active(
                                                 &ThumbsDownActiveCompletion,
-                                                cx,
+                                                window, cx,
                                             );
                                         })),
                                 )
@@ -498,10 +514,10 @@ impl RateCompletionModal {
                                         .key_binding(KeyBinding::for_action_in(
                                             &ThumbsUpActiveCompletion,
                                             focus_handle,
-                                            cx,
+                                            window,
                                         ))
-                                        .on_click(cx.listener(move |this, _, cx| {
-                                            this.thumbs_up_active(&ThumbsUpActiveCompletion, cx);
+                                        .on_click(cx.listener(move |this, _, window, cx| {
+                                            this.thumbs_up_active(&ThumbsUpActiveCompletion, window, cx);
                                         })),
                                 ),
                         ),
@@ -511,7 +527,7 @@ impl RateCompletionModal {
 }
 
 impl Render for RateCompletionModal {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let border_color = cx.theme().colors().border;
 
         h_flex()
@@ -532,8 +548,8 @@ impl Render for RateCompletionModal {
             .bg(cx.theme().colors().elevated_surface_background)
             .border_1()
             .border_color(border_color)
-            .w(cx.viewport_size().width - px(320.))
-            .h(cx.viewport_size().height - px(300.))
+            .w(window.viewport_size().width - px(320.))
+            .h(window.viewport_size().height - px(300.))
             .rounded_lg()
             .shadow_lg()
             .child(
@@ -623,26 +639,24 @@ impl Render for RateCompletionModal {
                                                                 )
                                                         )
                                                 )
-                                                .tooltip(move |cx| {
-                                                    Tooltip::text(tooltip_text, cx)
-                                                })
-                                                .on_click(cx.listener(move |this, _, cx| {
-                                                    this.select_completion(Some(completion.clone()), true, cx);
+                                                .tooltip(Tooltip::text(tooltip_text))
+                                                .on_click(cx.listener(move |this, _, window, cx| {
+                                                    this.select_completion(Some(completion.clone()), true, window, cx);
                                                 }))
                                         },
                                     )),
                             )
                     ),
             )
-            .children(self.render_active_completion(cx))
-            .on_mouse_down_out(cx.listener(|_, _, cx| cx.emit(DismissEvent)))
+            .children(self.render_active_completion(window, cx))
+            .on_mouse_down_out(cx.listener(|_, _, _, cx| cx.emit(DismissEvent)))
     }
 }
 
 impl EventEmitter<DismissEvent> for RateCompletionModal {}
 
-impl FocusableView for RateCompletionModal {
-    fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
+impl Focusable for RateCompletionModal {
+    fn focus_handle(&self, _cx: &App) -> FocusHandle {
         self.focus_handle.clone()
     }
 }

crates/zeta/src/zeta.rs 🔗

@@ -10,7 +10,7 @@ use client::{Client, UserStore};
 use collections::{HashMap, HashSet, VecDeque};
 use futures::AsyncReadExt;
 use gpui::{
-    actions, AppContext, AsyncAppContext, Context, EntityId, Global, Model, ModelContext,
+    actions, App, AppContext as _, AsyncAppContext, Context, Entity, EntityId, Global,
     Subscription, Task,
 };
 use http_client::{HttpClient, Method};
@@ -65,7 +65,7 @@ impl InlineCompletionId {
 }
 
 #[derive(Clone)]
-struct ZetaGlobal(Model<Zeta>);
+struct ZetaGlobal(Entity<Zeta>);
 
 impl Global for ZetaGlobal {}
 
@@ -176,17 +176,17 @@ pub struct Zeta {
 }
 
 impl Zeta {
-    pub fn global(cx: &mut AppContext) -> Option<Model<Self>> {
+    pub fn global(cx: &mut App) -> Option<Entity<Self>> {
         cx.try_global::<ZetaGlobal>().map(|global| global.0.clone())
     }
 
     pub fn register(
         client: Arc<Client>,
-        user_store: Model<UserStore>,
-        cx: &mut AppContext,
-    ) -> Model<Self> {
+        user_store: Entity<UserStore>,
+        cx: &mut App,
+    ) -> Entity<Self> {
         Self::global(cx).unwrap_or_else(|| {
-            let model = cx.new_model(|cx| Self::new(client, user_store, cx));
+            let model = cx.new(|cx| Self::new(client, user_store, cx));
             cx.set_global(ZetaGlobal(model.clone()));
             model
         })
@@ -196,7 +196,7 @@ impl Zeta {
         self.events.clear();
     }
 
-    fn new(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut ModelContext<Self>) -> Self {
+    fn new(client: Arc<Client>, user_store: Entity<UserStore>, cx: &mut Context<Self>) -> Self {
         let refresh_llm_token_listener = language_models::RefreshLlmTokenListener::global(cx);
 
         Self {
@@ -263,7 +263,7 @@ impl Zeta {
         }
     }
 
-    pub fn register_buffer(&mut self, buffer: &Model<Buffer>, cx: &mut ModelContext<Self>) {
+    pub fn register_buffer(&mut self, buffer: &Entity<Buffer>, cx: &mut Context<Self>) {
         let buffer_id = buffer.entity_id();
         let weak_buffer = buffer.downgrade();
 
@@ -288,9 +288,9 @@ impl Zeta {
 
     fn handle_buffer_event(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         event: &language::BufferEvent,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         match event {
             language::BufferEvent::Edited => {
@@ -302,9 +302,9 @@ impl Zeta {
 
     pub fn request_completion_impl<F, R>(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         position: language::Anchor,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
         perform_predict_edits: F,
     ) -> Task<Result<InlineCompletion>>
     where
@@ -383,7 +383,7 @@ impl Zeta {
 
     // Generates several example completions of various states to fill the Zeta completion modal
     #[cfg(any(test, feature = "test-support"))]
-    pub fn fill_with_fake_completions(&mut self, cx: &mut ModelContext<Self>) -> Task<()> {
+    pub fn fill_with_fake_completions(&mut self, cx: &mut Context<Self>) -> Task<()> {
         let test_buffer_text = indoc::indoc! {r#"a longggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg line
             And maybe a short line
 
@@ -392,7 +392,7 @@ impl Zeta {
             and then another
             "#};
 
-        let buffer = cx.new_model(|cx| Buffer::local(test_buffer_text, cx));
+        let buffer = cx.new(|cx| Buffer::local(test_buffer_text, cx));
         let position = buffer.read(cx).anchor_before(Point::new(1, 0));
 
         let completion_tasks = vec![
@@ -520,10 +520,10 @@ and then another
     #[cfg(any(test, feature = "test-support"))]
     pub fn fake_completion(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         position: language::Anchor,
         response: PredictEditsResponse,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<InlineCompletion>> {
         use std::future::ready;
 
@@ -532,9 +532,9 @@ and then another
 
     pub fn request_completion(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         position: language::Anchor,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Task<Result<InlineCompletion>> {
         self.request_completion_impl(buffer, position, cx, Self::perform_predict_edits)
     }
@@ -592,7 +592,7 @@ and then another
     #[allow(clippy::too_many_arguments)]
     fn process_completion_response(
         output_excerpt: String,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         snapshot: &BufferSnapshot,
         excerpt_range: Range<usize>,
         cursor_offset: usize,
@@ -778,7 +778,7 @@ and then another
         self.rated_completions.contains(&completion_id)
     }
 
-    pub fn completion_shown(&mut self, completion: &InlineCompletion, cx: &mut ModelContext<Self>) {
+    pub fn completion_shown(&mut self, completion: &InlineCompletion, cx: &mut Context<Self>) {
         self.shown_completions.push_front(completion.clone());
         if self.shown_completions.len() > 50 {
             let completion = self.shown_completions.pop_back().unwrap();
@@ -792,7 +792,7 @@ and then another
         completion: &InlineCompletion,
         rating: InlineCompletionRating,
         feedback: String,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         self.rated_completions.insert(completion.id);
         telemetry::event!(
@@ -818,8 +818,8 @@ and then another
 
     fn report_changes_for_buffer(
         &mut self,
-        buffer: &Model<Buffer>,
-        cx: &mut ModelContext<Self>,
+        buffer: &Entity<Buffer>,
+        cx: &mut Context<Self>,
     ) -> BufferSnapshot {
         self.register_buffer(buffer, cx);
 
@@ -1040,7 +1040,7 @@ struct PendingCompletion {
 }
 
 pub struct ZetaInlineCompletionProvider {
-    zeta: Model<Zeta>,
+    zeta: Entity<Zeta>,
     pending_completions: ArrayVec<PendingCompletion, 2>,
     next_pending_completion_id: usize,
     current_completion: Option<CurrentInlineCompletion>,
@@ -1049,7 +1049,7 @@ pub struct ZetaInlineCompletionProvider {
 impl ZetaInlineCompletionProvider {
     pub const DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(8);
 
-    pub fn new(zeta: Model<Zeta>) -> Self {
+    pub fn new(zeta: Entity<Zeta>) -> Self {
         Self {
             zeta,
             pending_completions: ArrayVec::new(),
@@ -1082,9 +1082,9 @@ impl inline_completion::InlineCompletionProvider for ZetaInlineCompletionProvide
 
     fn is_enabled(
         &self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         cursor_position: language::Anchor,
-        cx: &AppContext,
+        cx: &App,
     ) -> bool {
         let buffer = buffer.read(cx);
         let file = buffer.file();
@@ -1093,7 +1093,7 @@ impl inline_completion::InlineCompletionProvider for ZetaInlineCompletionProvide
         settings.inline_completions_enabled(language.as_ref(), file.map(|f| f.path().as_ref()), cx)
     }
 
-    fn needs_terms_acceptance(&self, cx: &AppContext) -> bool {
+    fn needs_terms_acceptance(&self, cx: &App) -> bool {
         !self.zeta.read(cx).tos_accepted
     }
 
@@ -1103,10 +1103,10 @@ impl inline_completion::InlineCompletionProvider for ZetaInlineCompletionProvide
 
     fn refresh(
         &mut self,
-        buffer: Model<Buffer>,
+        buffer: Entity<Buffer>,
         position: language::Anchor,
         debounce: bool,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) {
         if !self.zeta.read(cx).tos_accepted {
             return;
@@ -1185,28 +1185,28 @@ impl inline_completion::InlineCompletionProvider for ZetaInlineCompletionProvide
 
     fn cycle(
         &mut self,
-        _buffer: Model<Buffer>,
+        _buffer: Entity<Buffer>,
         _cursor_position: language::Anchor,
         _direction: inline_completion::Direction,
-        _cx: &mut ModelContext<Self>,
+        _cx: &mut Context<Self>,
     ) {
         // Right now we don't support cycling.
     }
 
-    fn accept(&mut self, _cx: &mut ModelContext<Self>) {
+    fn accept(&mut self, _cx: &mut Context<Self>) {
         self.pending_completions.clear();
     }
 
-    fn discard(&mut self, _cx: &mut ModelContext<Self>) {
+    fn discard(&mut self, _cx: &mut Context<Self>) {
         self.pending_completions.clear();
         self.current_completion.take();
     }
 
     fn suggest(
         &mut self,
-        buffer: &Model<Buffer>,
+        buffer: &Entity<Buffer>,
         cursor_position: language::Anchor,
-        cx: &mut ModelContext<Self>,
+        cx: &mut Context<Self>,
     ) -> Option<inline_completion::InlineCompletion> {
         let CurrentInlineCompletion {
             buffer_id,
@@ -1278,7 +1278,7 @@ mod tests {
 
     #[gpui::test]
     async fn test_inline_completion_basic_interpolation(cx: &mut TestAppContext) {
-        let buffer = cx.new_model(|cx| Buffer::local("Lorem ipsum dolor", cx));
+        let buffer = cx.new(|cx| Buffer::local("Lorem ipsum dolor", cx));
         let edits: Arc<[(Range<Anchor>, String)]> = cx.update(|cx| {
             to_completion_edits(
                 [(2..5, "REM".to_string()), (9..11, "".to_string())],
@@ -1429,10 +1429,10 @@ mod tests {
             RefreshLlmTokenListener::register(client.clone(), cx);
         });
         let server = FakeServer::for_client(42, &client, cx).await;
-        let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
-        let zeta = cx.new_model(|cx| Zeta::new(client, user_store, cx));
+        let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
+        let zeta = cx.new(|cx| Zeta::new(client, user_store, cx));
 
-        let buffer = cx.new_model(|cx| Buffer::local(buffer_content, cx));
+        let buffer = cx.new(|cx| Buffer::local(buffer_content, cx));
         let cursor = buffer.read_with(cx, |buffer, _| buffer.anchor_before(Point::new(1, 0)));
         let completion_task =
             zeta.update(cx, |zeta, cx| zeta.request_completion(&buffer, cursor, cx));
@@ -1455,8 +1455,8 @@ mod tests {
 
     fn to_completion_edits(
         iterator: impl IntoIterator<Item = (Range<usize>, String)>,
-        buffer: &Model<Buffer>,
-        cx: &AppContext,
+        buffer: &Entity<Buffer>,
+        cx: &App,
     ) -> Vec<(Range<Anchor>, String)> {
         let buffer = buffer.read(cx);
         iterator
@@ -1472,8 +1472,8 @@ mod tests {
 
     fn from_completion_edits(
         editor_edits: &[(Range<Anchor>, String)],
-        buffer: &Model<Buffer>,
-        cx: &AppContext,
+        buffer: &Entity<Buffer>,
+        cx: &App,
     ) -> Vec<(Range<usize>, String)> {
         let buffer = buffer.read(cx);
         editor_edits

refactor.sh 🔗

@@ -0,0 +1,111 @@
+#!/bin/bash
+
+# Merge process:
+#
+# * Use mergiraf for merge, with `git merge main -X theirs`
+#
+#    - Need to use it with a patched tree-sitter-rust. I (Michael)
+#      haven't yet uploaded a fork for this, can do if helpful.
+#      https://github.com/tree-sitter/tree-sitter-rust/pull/245
+#
+#    - Watch for newlines between top level decls sometimes disappearing
+#
+# * Run this script.
+
+dry=true
+if [ "$1" = "apply" ]; then
+    dry=false
+fi
+
+re() {
+    echo "$1" "    -->    " "$2"
+    if [ "$dry" = true ]; then
+        ruplacer "$1" "$2" crates/ --type *.rs
+    else
+        ruplacer "$1" "$2" crates/ --type *.rs --go
+    fi
+}
+
+re '\.new_view\('                    '.new_model('
+re 'cx.view\('                       'cx.model('
+re '\.observe_new_views\('           '.observe_new_models('
+re 'View<'                           'Model<'
+re 'FocusableView'                   'Focusable'
+
+# closure parameters
+re ', &mut WindowContext\)'          ', &mut Window, &mut AppContext)'
+re ', &mut ViewContext<([^>]+)>\)'   ', &mut Window, &mut ModelContext<$1>)'
+re '\(&mut WindowContext\)'          '(&mut Window, &mut AppContext)'
+re '\(&mut ViewContext<([^>]+)>\)'   '(&mut Window, &mut ModelContext<$1>)'
+
+# function parameters
+re '_: &mut WindowContext\)'          '_window: &mut Window, _cx: &mut AppContext)'
+re '_: &mut ViewContext<([^>]+)>\)'   '_window: &mut Window, _cx: &mut ModelContext<$1>)'
+re '_: &mut WindowContext,'           '_window: &mut Window, _cx: &mut AppContext,'
+re '_: &mut ViewContext<([^>]+)>,'    '_window: &mut Window, _cx: &mut ModelContext<$1>,'
+re '_cx: &mut WindowContext\)'        '_window: &mut Window, _cx: &mut AppContext)'
+re '_cx: &mut ViewContext<([^>]+)>\)' '_window: &mut Window, _cx: &mut ModelContext<$1>)'
+re '_cx: &mut WindowContext,'         '_window: &mut Window, _cx: &mut AppContext,'
+re '_cx: &mut ViewContext<([^>]+)>,'  '_window: &mut Window, _cx: &mut ModelContext<$1>,'
+re 'cx: &mut WindowContext\)'         'window: &mut Window, cx: &mut AppContext)'
+re 'cx: &mut ViewContext<([^>]+)>\)'  'window: &mut Window, cx: &mut ModelContext<$1>)'
+re 'cx: &mut WindowContext,'          'window: &mut Window, cx: &mut AppContext,'
+re 'cx: &mut ViewContext<([^>]+)>,'   'window: &mut Window, cx: &mut ModelContext<$1>,'
+
+re '_: &WindowContext\)'              '_window: &Window, _cx: &AppContext)'
+re '_: &ViewContext<([^>]+)>\)'       '_window: &Window, _cx: &ModelContext<$1>)'
+re '_: &WindowContext,'               '_window: &Window, _cx: &AppContext,'
+re '_: &ViewContext<([^>]+)>,'        '_window: &Window, _cx: &ModelContext<$1>,'
+re '_cx: &WindowContext\)'            '_window: &Window, _cx: &AppContext)'
+re '_cx: &ViewContext<([^>]+)>\)'     '_window: &Window, _cx: &ModelContext<$1>)'
+re '_cx: &WindowContext,'             '_window: &Window, _cx: &AppContext,'
+re '_cx: &ViewContext<([^>]+)>,'      '_window: &Window, _cx: &ModelContext<$1>,'
+re 'cx: &WindowContext\)'             'window: &Window, cx: &AppContext)'
+re 'cx: &ViewContext<([^>]+)>\)'      'window: &Window, cx: &ModelContext<$1>)'
+re 'cx: &WindowContext,'              'window: &Window, cx: &AppContext,'
+re 'cx: &ViewContext<([^>]+)>,'       'window: &Window, cx: &ModelContext<$1>,'
+
+# VisualContext methods moved to window, that take context
+re 'cx.dismiss_view\(' 'window.dismiss_view(cx, '
+re 'cx.focus_view\(' 'window.focus_view(cx, '
+re 'cx.new_view\(' 'window.new_view(cx, '
+re 'cx.replace_root_view\(' 'window.replace_root_view(cx, '
+
+# AppContext methods moved to window, that take context
+re 'cx.appearance_changed\(\)' 'window.appearance_changed(cx)'
+re 'cx.available_actions\(\)' 'window.available_actions(cx)'
+re 'cx.dispatch_keystroke_observers\(' 'window.dispatch_keystroke_observers(cx, '
+re 'cx.display\(\)' 'window.display(cx)'
+re 'cx.focused\(\)' 'window.focused(cx)'
+re 'cx.handle_input\(' 'window.handle_input(cx, '
+re 'cx.paint_svg\(' 'window.paint_svg(cx, '
+re 'cx.request_layout\(' 'window.request_layout(cx, '
+re 'cx.use_asset\(' 'window.use_asset(cx, '
+
+# Subset of AppContext methods moved to window that don't take context
+re 'cx\.set_cursor_style\('           'window.set_cursor_style('
+re 'cx\.modifiers\('                  'window.modifiers('
+re 'cx\.mouse_position\('             'window.mouse_position('
+re 'cx\.text_style\('                 'window.text_style('
+re 'cx\.line_height\('                'window.line_height('
+
+# common closure patterns
+re 'cx.listener\(move \|this, _, cx\|' 'cx.listener(move |this, _, window, cx|'
+re 'cx.listener\(\|this, _, cx\|'     'cx.listener(|this, _, window, cx|'
+re 'cx.listener\(move \|_, _, cx\|'   'cx.listener(move |_, _, window, cx|'
+re 'cx.listener\(\|_, _, cx\|'        'cx.listener(|_, _, window, cx|'
+re '\.on_click\(move \|_, cx\|'       '.on_click(move |_, window, cx|'
+re '\.on_mouse_move\(\|_, cx\|'       '.on_mouse_move(|_, window, cx|'
+
+# cleanup imports
+re ' ViewContext,'                     ''
+re ' WindowContext,'                   ''
+re ' WeakView,'                        ''
+re ' View,'                            ''
+re ', ViewContext\}'                   '}'
+re ', WindowContext\}'                 '}'
+re ', WeakView\}'                      '}'
+re ', View\}'                          '}'
+
+# other patterns
+re '\.detach_and_notify_err\(cx'       '.detach_and_notify_err(window, cx'

resolve-their-hunks.py 🔗

@@ -0,0 +1,68 @@
+#!/bin/env python3
+
+import os
+from pathlib import Path
+
+def process_file(filepath):
+    with open(filepath, 'r', encoding='utf-8') as f:
+        lines = f.readlines()
+
+    modified_lines = []
+    in_conflict = False
+    after_equals = False
+    keep_lines = []
+
+    for line in lines:
+        if line.startswith('<<<<<<<'):
+            in_conflict = True
+            after_equals = False
+            keep_lines = []
+            continue
+        elif line.startswith('======='):
+            after_equals = True
+            continue
+        elif line.startswith('>>>>>>>'):
+            in_conflict = False
+            after_equals = False
+            modified_lines.extend(keep_lines)
+            continue
+
+        if in_conflict:
+            if after_equals:
+                keep_lines.append(line)
+        else:
+            modified_lines.append(line)
+
+    # Only write if changes were made
+    if lines != modified_lines:
+        with open(filepath, 'w', encoding='utf-8') as f:
+            f.writelines(modified_lines)
+        print(f"Processed: {filepath}")
+        return True
+    return False
+
+def main():
+    # Get current directory
+    current_dir = Path('.')
+
+    # Find all .rs files recursively
+    rust_files = list(current_dir.rglob('*.rs'))
+
+    files_processed = 0
+    files_modified = 0
+
+    # Process each file
+    for filepath in rust_files:
+        try:
+            files_processed += 1
+            if process_file(filepath):
+                files_modified += 1
+        except Exception as e:
+            print(f"Error processing {filepath}: {str(e)}")
+
+    print(f"\nSummary:")
+    print(f"Files processed: {files_processed}")
+    print(f"Files modified: {files_modified}")
+
+if __name__ == "__main__":
+    main()

tooling/xtask/src/tasks/package_conformity.rs 🔗

@@ -2,7 +2,7 @@ use std::collections::BTreeMap;
 use std::fs;
 use std::path::Path;
 
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use cargo_toml::{Dependency, Manifest};
 use clap::Parser;
 

tooling/xtask/src/workspace.rs 🔗

@@ -1,4 +1,4 @@
-use anyhow::{Context, Result};
+use anyhow::{Context as _, Result};
 use cargo_metadata::{Metadata, MetadataCommand};
 
 /// Returns the Cargo workspace.