Merge branch 'main' into v0.119.x

Joseph T. Lyons created

Change summary

.github/workflows/ci.yml                                         |  277 
.vscode/launch.json                                              |   69 
Cargo.lock                                                       |    5 
assets/keymaps/default.json                                      |    4 
assets/keymaps/vim.json                                          |   16 
assets/settings/default.json                                     |    3 
crates/ai/src/providers/open_ai/completion.rs                    |    2 
crates/assistant/src/assistant_panel.rs                          |   36 
crates/auto_update/src/auto_update.rs                            |    6 
crates/call/src/room.rs                                          |    2 
crates/channel/src/channel_buffer.rs                             |   12 
crates/channel/src/channel_store/channel_index.rs                |   10 
crates/client/src/client.rs                                      |    2 
crates/client/src/telemetry.rs                                   |  213 
crates/client/src/user.rs                                        |   45 
crates/collab/Cargo.toml                                         |    2 
crates/collab/src/db/queries/buffers.rs                          |   17 
crates/collab/src/db/queries/notifications.rs                    |    2 
crates/collab/src/db/queries/users.rs                            |    4 
crates/collab/src/rpc.rs                                         |   16 
crates/collab/src/tests/integration_tests.rs                     |   24 
crates/collab/src/tests/test_server.rs                           |    5 
crates/collab_ui/Cargo.toml                                      |    1 
crates/collab_ui/src/channel_view.rs                             |    9 
crates/collab_ui/src/chat_panel.rs                               |  178 
crates/collab_ui/src/chat_panel/message_editor.rs                |  122 
crates/collab_ui/src/collab_panel.rs                             |   16 
crates/collab_ui/src/collab_titlebar_item.rs                     |   14 
crates/collab_ui/src/face_pile.rs                                |   37 
crates/collab_ui/src/notification_panel.rs                       |    3 
crates/collab_ui/src/notifications/collab_notification.rs        |    4 
crates/command_palette/src/command_palette.rs                    |    2 
crates/copilot/src/copilot.rs                                    |    2 
crates/copilot_ui/src/copilot_button.rs                          |    2 
crates/diagnostics/src/diagnostics.rs                            |   37 
crates/editor/src/actions.rs                                     |    1 
crates/editor/src/display_map/block_map.rs                       |   11 
crates/editor/src/editor.rs                                      |  469 
crates/editor/src/element.rs                                     |  362 
crates/editor/src/hover_popover.rs                               |    3 
crates/fs/src/repository.rs                                      |    6 
crates/git/src/diff.rs                                           |    2 
crates/gpui/Cargo.toml                                           |    2 
crates/gpui/src/action.rs                                        |   13 
crates/gpui/src/app.rs                                           |   10 
crates/gpui/src/app/async_context.rs                             |    7 
crates/gpui/src/app/entity_map.rs                                |    6 
crates/gpui/src/app/model_context.rs                             |    2 
crates/gpui/src/app/test_context.rs                              |   13 
crates/gpui/src/assets.rs                                        |   10 
crates/gpui/src/color.rs                                         |   91 
crates/gpui/src/element.rs                                       |  189 
crates/gpui/src/elements/canvas.rs                               |   14 
crates/gpui/src/elements/div.rs                                  |  401 
crates/gpui/src/elements/img.rs                                  |   15 
crates/gpui/src/elements/list.rs                                 |   49 
crates/gpui/src/elements/overlay.rs                              |   27 
crates/gpui/src/elements/svg.rs                                  |   11 
crates/gpui/src/elements/text.rs                                 |  156 
crates/gpui/src/elements/uniform_list.rs                         |   22 
crates/gpui/src/executor.rs                                      |   20 
crates/gpui/src/geometry.rs                                      |   14 
crates/gpui/src/gpui.rs                                          |   93 
crates/gpui/src/image_cache.rs                                   |    6 
crates/gpui/src/input.rs                                         |   99 
crates/gpui/src/interactive.rs                                   |  120 
crates/gpui/src/key_dispatch.rs                                  |  114 
crates/gpui/src/keymap/binding.rs                                |    8 
crates/gpui/src/keymap/context.rs                                |   56 
crates/gpui/src/keymap/keymap.rs                                 |    8 
crates/gpui/src/keymap/matcher.rs                                |  424 
crates/gpui/src/keymap/mod.rs                                    |    2 
crates/gpui/src/platform.rs                                      |  295 
crates/gpui/src/platform/app_menu.rs                             |   38 
crates/gpui/src/platform/keystroke.rs                            |   60 
crates/gpui/src/platform/mac.rs                                  |   14 
crates/gpui/src/platform/mac/dispatcher.rs                       |    2 
crates/gpui/src/platform/mac/display.rs                          |   52 
crates/gpui/src/platform/mac/display_linker.rs                   |   14 
crates/gpui/src/platform/mac/events.rs                           |    5 
crates/gpui/src/platform/mac/metal_atlas.rs                      |    4 
crates/gpui/src/platform/mac/platform.rs                         |   14 
crates/gpui/src/platform/mac/text_system.rs                      |    4 
crates/gpui/src/platform/mac/window.rs                           |   88 
crates/gpui/src/platform/mac/window_appearance.rs                |    2 
crates/gpui/src/platform/test.rs                                 |    8 
crates/gpui/src/platform/test/dispatcher.rs                      |    1 
crates/gpui/src/platform/test/display.rs                         |    2 
crates/gpui/src/platform/test/platform.rs                        |   17 
crates/gpui/src/platform/test/window.rs                          |   36 
crates/gpui/src/prelude.rs                                       |   10 
crates/gpui/src/scene.rs                                         |   41 
crates/gpui/src/shared_string.rs                                 |    2 
crates/gpui/src/style.rs                                         |   69 
crates/gpui/src/styled.rs                                        |   39 
crates/gpui/src/subscription.rs                                  |    5 
crates/gpui/src/svg_renderer.rs                                  |    4 
crates/gpui/src/taffy.rs                                         |    2 
crates/gpui/src/test.rs                                          |    4 
crates/gpui/src/text_system.rs                                   |    2 
crates/gpui/src/text_system/line.rs                              |   11 
crates/gpui/src/util.rs                                          |   52 
crates/gpui/src/view.rs                                          |   45 
crates/gpui/src/window.rs                                        |  857 -
crates/gpui/src/window/element_cx.rs                             | 1136 ++
crates/journal/src/journal.rs                                    |    2 
crates/language/src/buffer.rs                                    |  297 
crates/language/src/diagnostic_set.rs                            |   33 
crates/language/src/highlight_map.rs                             |    4 
crates/language/src/language.rs                                  |   95 
crates/language/src/language_settings.rs                         |  103 
crates/language/src/markdown.rs                                  |   37 
crates/language/src/outline.rs                                   |    1 
crates/language/src/proto.rs                                     |   39 
crates/language/src/syntax_map.rs                                |   81 
crates/language_selector/src/active_buffer_language.rs           |    2 
crates/language_tools/src/lsp_log.rs                             |   24 
crates/language_tools/src/syntax_tree_view.rs                    |    6 
crates/lsp/src/lsp.rs                                            |   16 
crates/multi_buffer/src/multi_buffer.rs                          |    6 
crates/plugin_runtime/src/plugin.rs                              |    6 
crates/project/src/lsp_command.rs                                |    4 
crates/project/src/project.rs                                    |  207 
crates/project/src/terminals.rs                                  |    2 
crates/project/src/worktree.rs                                   |   18 
crates/project_panel/src/project_panel.rs                        |  112 
crates/rich_text/Cargo.toml                                      |    1 
crates/rich_text/src/rich_text.rs                                |   19 
crates/rpc/proto/zed.proto                                       |    3 
crates/settings/src/settings_store.rs                            |   15 
crates/story/src/story.rs                                        |   10 
crates/storybook/src/stories/text.rs                             |   63 
crates/storybook/src/stories/z_index.rs                          |    4 
crates/terminal/src/terminal.rs                                  |   60 
crates/terminal/src/terminal_settings.rs                         |   14 
crates/terminal_view/src/terminal_element.rs                     |  156 
crates/terminal_view/src/terminal_view.rs                        |    4 
crates/theme/src/default_colors.rs                               |   38 
crates/theme/src/one_themes.rs                                   |   11 
crates/theme/src/styles/colors.rs                                |   73 
crates/theme/src/themes/andromeda.rs                             |   27 
crates/theme/src/themes/atelier.rs                               |  540 
crates/theme/src/themes/ayu.rs                                   |   81 
crates/theme/src/themes/gruvbox.rs                               |  162 
crates/theme/src/themes/one.rs                                   |   54 
crates/theme/src/themes/rose_pine.rs                             |   81 
crates/theme/src/themes/sandcastle.rs                            |   27 
crates/theme/src/themes/solarized.rs                             |   54 
crates/theme/src/themes/summercamp.rs                            |   27 
crates/theme_importer/src/theme_printer.rs                       |   33 
crates/theme_importer/src/zed1/converter.rs                      |   27 
crates/ui/src/components/avatar/avatar.rs                        |    2 
crates/ui/src/components/avatar/avatar_availability_indicator.rs |    2 
crates/ui/src/components/button/button_like.rs                   |    4 
crates/ui/src/components/context_menu.rs                         |   18 
crates/ui/src/components/keybinding.rs                           |    2 
crates/ui/src/components/label/label.rs                          |    2 
crates/ui/src/components/label/label_like.rs                     |    4 
crates/ui/src/components/list/list.rs                            |    4 
crates/ui/src/components/list/list_item.rs                       |   14 
crates/ui/src/components/popover.rs                              |    6 
crates/ui/src/components/popover_menu.rs                         |   13 
crates/ui/src/components/right_click_menu.rs                     |   12 
crates/ui/src/components/stories/list_item.rs                    |   26 
crates/ui/src/components/tab.rs                                  |    4 
crates/ui/src/components/tab_bar.rs                              |    6 
crates/ui/src/components/tooltip.rs                              |   91 
crates/ui/src/prelude.rs                                         |    6 
crates/ui/src/styles/elevation.rs                                |    2 
crates/util/src/http.rs                                          |    9 
crates/util/src/util.rs                                          |   30 
crates/vim/Cargo.toml                                            |    1 
crates/vim/README.md                                             |   36 
crates/vim/src/motion.rs                                         |   17 
crates/vim/src/normal/change.rs                                  |   12 
crates/vim/src/normal/repeat.rs                                  |    2 
crates/vim/src/normal/search.rs                                  |   56 
crates/vim/src/object.rs                                         |   37 
crates/vim/src/test.rs                                           |   75 
crates/vim/src/test/neovim_backed_test_context.rs                |   20 
crates/vim/src/test/neovim_connection.rs                         |   27 
crates/vim/src/utils.rs                                          |   10 
crates/vim/test_data/test_comma_w.json                           |   15 
crates/vim/test_data/test_jk.json                                |    8 
crates/vim/test_data/test_visual_star_hash.json                  |    6 
crates/workspace/src/dock.rs                                     |   19 
crates/workspace/src/notifications.rs                            |    2 
crates/workspace/src/pane.rs                                     |    8 
crates/workspace/src/pane_group.rs                               |   10 
crates/workspace/src/persistence.rs                              |    2 
crates/workspace/src/searchable.rs                               |    2 
crates/workspace/src/status_bar.rs                               |   14 
crates/workspace/src/toolbar.rs                                  |    8 
crates/workspace/src/workspace.rs                                |  186 
crates/zed/resources/app-icon-nightly.png                        |    0 
crates/zed/resources/app-icon-nightly@2x.png                     |    0 
crates/zed/src/main.rs                                           |   17 
crates/zed/src/zed.rs                                            |   11 
script/deploy-collab                                             |    1 
script/histogram                                                 |   73 
200 files changed, 7,070 insertions(+), 3,531 deletions(-)

Detailed changes

.github/workflows/ci.yml ๐Ÿ”—

@@ -1,149 +1,148 @@
 name: CI
 
 on:
-    push:
-        branches:
-            - main
-            - "v[0-9]+.[0-9]+.x"
-        tags:
-            - "v*"
-    pull_request:
-        branches:
-            - "**"
+  push:
+    branches:
+      - main
+      - "v[0-9]+.[0-9]+.x"
+    tags:
+      - "v*"
+  pull_request:
+    branches:
+      - "**"
 
 concurrency:
-    # Allow only one workflow per any non-`main` branch.
-    group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
-    cancel-in-progress: true
+  # Allow only one workflow per any non-`main` branch.
+  group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
+  cancel-in-progress: true
 
 env:
-    CARGO_TERM_COLOR: always
-    CARGO_INCREMENTAL: 0
-    RUST_BACKTRACE: 1
+  CARGO_TERM_COLOR: always
+  CARGO_INCREMENTAL: 0
+  RUST_BACKTRACE: 1
 
 jobs:
-    style:
-        name: Check formatting, Clippy lints, and spelling
-        runs-on:
-            - self-hosted
-            - test
-        steps:
-            - name: Checkout repo
-              uses: actions/checkout@v3
-              with:
-                  clean: false
-                  submodules: "recursive"
-                  fetch-depth: 0
-
-            - name: Set up default .cargo/config.toml
-              run: cp ./.cargo/ci-config.toml ~/.cargo/config.toml
-
-            - name: Check spelling
-              run: |
-                  if ! which typos > /dev/null; then
-                    cargo install typos-cli
-                  fi
-                  typos
-
-            - name: Run style checks
-              uses: ./.github/actions/check_style
-
-    tests:
-        name: Run tests
-        runs-on:
-            - self-hosted
-            - test
-        needs: style
-        steps:
-            - name: Checkout repo
-              uses: actions/checkout@v3
-              with:
-                  clean: false
-                  submodules: "recursive"
-
-            - name: Run tests
-              uses: ./.github/actions/run_tests
-
-            - name: Build collab
-              run: cargo build -p collab
-
-            - name: Build other binaries
-              run: cargo build --workspace --bins --all-features
-
-    bundle:
-        name: Bundle app
-        runs-on:
-            - self-hosted
-            - bundle
-        if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-build-dmg') }}
-        needs: tests
+  style:
+    name: Check formatting, Clippy lints, and spelling
+    runs-on:
+      - self-hosted
+      - test
+    steps:
+      - name: Checkout repo
+        uses: actions/checkout@v3
+        with:
+          clean: false
+          submodules: "recursive"
+          fetch-depth: 0
+
+      - name: Set up default .cargo/config.toml
+        run: cp ./.cargo/ci-config.toml ~/.cargo/config.toml
+
+      - name: Check spelling
+        run: |
+          if ! which typos > /dev/null; then
+            cargo install typos-cli
+          fi
+          typos
+
+      - name: Run style checks
+        uses: ./.github/actions/check_style
+
+  tests:
+    name: Run tests
+    runs-on:
+      - self-hosted
+      - test
+    steps:
+      - name: Checkout repo
+        uses: actions/checkout@v3
+        with:
+          clean: false
+          submodules: "recursive"
+
+      - name: Run tests
+        uses: ./.github/actions/run_tests
+
+      - name: Build collab
+        run: cargo build -p collab
+
+      - name: Build other binaries
+        run: cargo build --workspace --bins --all-features
+
+  bundle:
+    name: Bundle app
+    runs-on:
+      - self-hosted
+      - bundle
+    if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-build-dmg') }}
+    needs: tests
+    env:
+      MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
+      MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
+      APPLE_NOTARIZATION_USERNAME: ${{ secrets.APPLE_NOTARIZATION_USERNAME }}
+      APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }}
+    steps:
+      - name: Install Node
+        uses: actions/setup-node@v3
+        with:
+          node-version: "18"
+
+      - name: Checkout repo
+        uses: actions/checkout@v3
+        with:
+          clean: false
+          submodules: "recursive"
+
+      - name: Limit target directory size
+        run: script/clear-target-dir-if-larger-than 100
+
+      - name: Determine version and release channel
+        if: ${{ startsWith(github.ref, 'refs/tags/v') }}
+        run: |
+          set -eu
+
+          version=$(script/get-crate-version zed)
+          channel=$(cat crates/zed/RELEASE_CHANNEL)
+          echo "Publishing version: ${version} on release channel ${channel}"
+          echo "RELEASE_CHANNEL=${channel}" >> $GITHUB_ENV
+
+          expected_tag_name=""
+          case ${channel} in
+            stable)
+              expected_tag_name="v${version}";;
+            preview)
+              expected_tag_name="v${version}-pre";;
+            nightly)
+              expected_tag_name="v${version}-nightly";;
+            *)
+              echo "can't publish a release on channel ${channel}"
+              exit 1;;
+          esac
+          if [[ $GITHUB_REF_NAME != $expected_tag_name ]]; then
+            echo "invalid release tag ${GITHUB_REF_NAME}. expected ${expected_tag_name}"
+            exit 1
+          fi
+
+      - name: Generate license file
+        run: script/generate-licenses
+
+      - name: Create app bundle
+        run: script/bundle
+
+      - name: Upload app bundle to workflow run if main branch or specific label
+        uses: actions/upload-artifact@v3
+        if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-build-dmg') }}
+        with:
+          name: Zed_${{ github.event.pull_request.head.sha || github.sha }}.dmg
+          path: target/release/Zed.dmg
+
+      - uses: softprops/action-gh-release@v1
+        name: Upload app bundle to release
+        if: ${{ env.RELEASE_CHANNEL == 'preview' || env.RELEASE_CHANNEL == 'stable' }}
+        with:
+          draft: true
+          prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
+          files: target/release/Zed.dmg
+          body: ""
         env:
-            MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
-            MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
-            APPLE_NOTARIZATION_USERNAME: ${{ secrets.APPLE_NOTARIZATION_USERNAME }}
-            APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }}
-        steps:
-            - name: Install Node
-              uses: actions/setup-node@v3
-              with:
-                  node-version: "18"
-
-            - name: Checkout repo
-              uses: actions/checkout@v3
-              with:
-                  clean: false
-                  submodules: "recursive"
-
-            - name: Limit target directory size
-              run: script/clear-target-dir-if-larger-than 100
-
-            - name: Determine version and release channel
-              if: ${{ startsWith(github.ref, 'refs/tags/v') }}
-              run: |
-                  set -eu
-
-                  version=$(script/get-crate-version zed)
-                  channel=$(cat crates/zed/RELEASE_CHANNEL)
-                  echo "Publishing version: ${version} on release channel ${channel}"
-                  echo "RELEASE_CHANNEL=${channel}" >> $GITHUB_ENV
-
-                  expected_tag_name=""
-                  case ${channel} in
-                    stable)
-                      expected_tag_name="v${version}";;
-                    preview)
-                      expected_tag_name="v${version}-pre";;
-                    nightly)
-                      expected_tag_name="v${version}-nightly";;
-                    *)
-                      echo "can't publish a release on channel ${channel}"
-                      exit 1;;
-                  esac
-                  if [[ $GITHUB_REF_NAME != $expected_tag_name ]]; then
-                    echo "invalid release tag ${GITHUB_REF_NAME}. expected ${expected_tag_name}"
-                    exit 1
-                  fi
-
-            - name: Generate license file
-              run: script/generate-licenses
-
-            - name: Create app bundle
-              run: script/bundle
-
-            - name: Upload app bundle to workflow run if main branch or specific label
-              uses: actions/upload-artifact@v3
-              if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-build-dmg') }}
-              with:
-                  name: Zed_${{ github.event.pull_request.head.sha || github.sha }}.dmg
-                  path: target/release/Zed.dmg
-
-            - uses: softprops/action-gh-release@v1
-              name: Upload app bundle to release
-              if: ${{ env.RELEASE_CHANNEL == 'preview' || env.RELEASE_CHANNEL == 'stable' }}
-              with:
-                  draft: true
-                  prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
-                  files: target/release/Zed.dmg
-                  body: ""
-              env:
-                  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.vscode/launch.json ๐Ÿ”—

@@ -1,69 +0,0 @@
-{
-    // Use IntelliSense to learn about possible attributes.
-    // Hover to view descriptions of existing attributes.
-    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
-    "version": "0.2.0",
-    "configurations": [
-        {
-            "type": "lldb",
-            "request": "launch",
-            "name": "Debug executable 'Zed'",
-            "env": {
-                "ZED_SERVER_URL": "http://localhost:8080"
-            },
-            "cargo": {
-                "args": [
-                    "build",
-                    "--bin=Zed",
-                    "--package=zed"
-                ],
-                "filter": {
-                    "name": "Zed",
-                    "kind": "bin"
-                }
-            },
-            "args": [
-                "${workspaceFolder}"
-            ],
-            "cwd": "${workspaceFolder}"
-        },
-        {
-            "type": "lldb",
-            "request": "launch",
-            "name": "Debug unit tests in executable 'zed'",
-            "cargo": {
-                "args": [
-                    "test",
-                    "--no-run",
-                    "--bin=zed",
-                    "--package=zed"
-                ],
-                "filter": {
-                    "name": "zed",
-                    "kind": "bin"
-                }
-            },
-            "args": [],
-            "cwd": "${workspaceFolder}"
-        },
-        {
-            "type": "lldb",
-            "request": "launch",
-            "name": "Debug unit tests in library 'gpui'",
-            "cargo": {
-                "args": [
-                    "test",
-                    "--no-run",
-                    "--lib",
-                    "--package=gpui"
-                ],
-                "filter": {
-                    "name": "gpui",
-                    "kind": "lib"
-                }
-            },
-            "args": [],
-            "cwd": "${workspaceFolder}"
-        }
-    ]
-}

Cargo.lock ๐Ÿ”—

@@ -1452,7 +1452,7 @@ dependencies = [
 
 [[package]]
 name = "collab"
-version = "0.37.0"
+version = "0.38.0"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -1548,6 +1548,7 @@ dependencies = [
  "log",
  "menu",
  "notifications",
+ "parking_lot 0.11.2",
  "picker",
  "postage",
  "pretty_assertions",
@@ -6144,6 +6145,7 @@ dependencies = [
  "smol",
  "sum_tree",
  "theme",
+ "ui",
  "util",
 ]
 
@@ -9079,6 +9081,7 @@ dependencies = [
  "nvim-rs",
  "parking_lot 0.11.2",
  "project",
+ "regex",
  "search",
  "serde",
  "serde_derive",

assets/keymaps/default.json ๐Ÿ”—

@@ -183,6 +183,7 @@
     "context": "Editor && mode == auto_height",
     "bindings": {
       "ctrl-enter": "editor::Newline",
+      "shift-enter": "editor::Newline",
       "ctrl-shift-enter": "editor::NewlineBelow"
     }
   },
@@ -349,7 +350,8 @@
       "alt-cmd-]": "editor::UnfoldLines",
       "ctrl-space": "editor::ShowCompletions",
       "cmd-.": "editor::ToggleCodeActions",
-      "alt-cmd-r": "editor::RevealInFinder"
+      "alt-cmd-r": "editor::RevealInFinder",
+      "ctrl-cmd-c": "editor::DisplayCursorNames"
     }
   },
   {

assets/keymaps/vim.json ๐Ÿ”—

@@ -104,8 +104,6 @@
       "shift-v": "vim::ToggleVisualLine",
       "ctrl-v": "vim::ToggleVisualBlock",
       "ctrl-q": "vim::ToggleVisualBlock",
-      "*": "vim::MoveToNext",
-      "#": "vim::MoveToPrev",
       "0": "vim::StartOfLine", // When no number operator present, use start of line motion
       "ctrl-f": "vim::PageDown",
       "pagedown": "vim::PageDown",
@@ -329,6 +327,8 @@
           "backwards": true
         }
       ],
+      "*": "vim::MoveToNext",
+      "#": "vim::MoveToPrev",
       ";": "vim::RepeatFind",
       ",": [
         "vim::RepeatFind",
@@ -421,6 +421,18 @@
       "shift-r": "vim::SubstituteLine",
       "c": "vim::Substitute",
       "~": "vim::ChangeCase",
+      "*": [
+        "vim::MoveToNext",
+        {
+          "partialWord": true
+        }
+      ],
+      "#": [
+        "vim::MoveToPrev",
+        {
+          "partialWord": true
+        }
+      ],
       "ctrl-a": "vim::Increment",
       "ctrl-x": "vim::Decrement",
       "g ctrl-a": [

assets/settings/default.json ๐Ÿ”—

@@ -72,6 +72,9 @@
   // Whether to use additional LSP queries to format (and amend) the code after
   // every "trigger" symbol input, defined by LSP server capabilities.
   "use_on_type_format": true,
+  // Whether to automatically type closing characters for you. For example,
+  // when you type (, Zed will automatically add a closing ) at the correct position.
+  "use_autoclose": true,
   // Controls whether copilot provides suggestion immediately
   // or waits for a `copilot::Toggle`
   "show_copilot_suggestions": true,

crates/ai/src/providers/open_ai/completion.rs ๐Ÿ”—

@@ -134,7 +134,7 @@ pub async fn stream_completion(
                     line: Result<String, io::Error>,
                 ) -> Result<Option<OpenAIResponseStreamEvent>> {
                     if let Some(data) = line?.strip_prefix("data: ") {
-                        let event = serde_json::from_str(&data)?;
+                        let event = serde_json::from_str(data)?;
                         Ok(Some(event))
                     } else {
                         Ok(None)

crates/assistant/src/assistant_panel.rs ๐Ÿ”—

@@ -1172,23 +1172,25 @@ impl Render for AssistantPanel {
                         .px_2()
                         .child(Label::new(editor.read(cx).title(cx)).into_element())
                 }))
-                .end_child(if self.focus_handle.contains_focused(cx) {
-                    h_flex()
-                        .gap_2()
-                        .child(h_flex().gap_1().children(self.render_editor_tools(cx)))
-                        .child(
-                            ui::Divider::vertical()
-                                .inset()
-                                .color(ui::DividerColor::Border),
-                        )
-                        .child(
-                            h_flex()
-                                .gap_1()
-                                .child(Self::render_plus_button(cx))
-                                .child(self.render_zoom_button(cx)),
-                        )
-                } else {
-                    div()
+                .when(self.focus_handle.contains_focused(cx), |this| {
+                    this.end_child(
+                        h_flex()
+                            .gap_2()
+                            .when(self.active_editor().is_some(), |this| {
+                                this.child(h_flex().gap_1().children(self.render_editor_tools(cx)))
+                                    .child(
+                                        ui::Divider::vertical()
+                                            .inset()
+                                            .color(ui::DividerColor::Border),
+                                    )
+                            })
+                            .child(
+                                h_flex()
+                                    .gap_1()
+                                    .child(Self::render_plus_button(cx))
+                                    .child(self.render_zoom_button(cx)),
+                            ),
+                    )
                 });
 
             let contents = if self.active_editor().is_some() {

crates/auto_update/src/auto_update.rs ๐Ÿ”—

@@ -96,12 +96,6 @@ pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut AppCo
         workspace.register_action(|_, action, cx| {
             view_release_notes(action, cx);
         });
-
-        // @nate - code to trigger update notification on launch
-        // todo!("remove this when Nate is done")
-        // workspace.show_notification(0, _cx, |cx| {
-        //     cx.build_view(|_| UpdateNotification::new(SemanticVersion::from_str("1.1.1").unwrap()))
-        // });
     })
     .detach();
 

crates/call/src/room.rs ๐Ÿ”—

@@ -1432,7 +1432,7 @@ impl Room {
                 let display = displays
                     .first()
                     .ok_or_else(|| anyhow!("no display found"))?;
-                let track = LocalVideoTrack::screen_share_for_display(&display);
+                let track = LocalVideoTrack::screen_share_for_display(display);
                 this.upgrade()
                     .ok_or_else(|| anyhow!("room was dropped"))?
                     .update(&mut cx, |this, _| {

crates/channel/src/channel_buffer.rs ๐Ÿ”—

@@ -1,6 +1,6 @@
 use crate::{Channel, ChannelId, ChannelStore};
 use anyhow::Result;
-use client::{Client, Collaborator, UserStore};
+use client::{Client, Collaborator, UserStore, ZED_ALWAYS_ACTIVE};
 use collections::HashMap;
 use gpui::{AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task};
 use language::proto::serialize_version;
@@ -181,6 +181,16 @@ impl ChannelBuffer {
     ) {
         match event {
             language::Event::Operation(operation) => {
+                if *ZED_ALWAYS_ACTIVE {
+                    match operation {
+                        language::Operation::UpdateSelections { selections, .. } => {
+                            if selections.is_empty() {
+                                return;
+                            }
+                        }
+                        _ => {}
+                    }
+                }
                 let operation = language::proto::serialize_operation(operation);
                 self.client
                     .send(proto::UpdateChannelBuffer {

crates/channel/src/channel_store/channel_index.rs ๐Ÿ”—

@@ -86,11 +86,11 @@ pub struct ChannelPathsInsertGuard<'a> {
 
 impl<'a> ChannelPathsInsertGuard<'a> {
     pub fn note_changed(&mut self, channel_id: ChannelId, epoch: u64, version: &clock::Global) {
-        insert_note_changed(&mut self.channels_by_id, channel_id, epoch, &version);
+        insert_note_changed(self.channels_by_id, channel_id, epoch, version);
     }
 
     pub fn new_messages(&mut self, channel_id: ChannelId, message_id: u64) {
-        insert_new_message(&mut self.channels_by_id, channel_id, message_id)
+        insert_new_message(self.channels_by_id, channel_id, message_id)
     }
 
     pub fn insert(&mut self, channel_proto: proto::Channel) -> bool {
@@ -131,8 +131,8 @@ impl<'a> ChannelPathsInsertGuard<'a> {
 impl<'a> Drop for ChannelPathsInsertGuard<'a> {
     fn drop(&mut self) {
         self.channels_ordered.sort_by(|a, b| {
-            let a = channel_path_sorting_key(*a, &self.channels_by_id);
-            let b = channel_path_sorting_key(*b, &self.channels_by_id);
+            let a = channel_path_sorting_key(*a, self.channels_by_id);
+            let b = channel_path_sorting_key(*b, self.channels_by_id);
             a.cmp(b)
         });
         self.channels_ordered.dedup();
@@ -167,7 +167,7 @@ fn insert_note_changed(
         if epoch > unseen_version.0 {
             *unseen_version = (epoch, version.clone());
         } else {
-            unseen_version.1.join(&version);
+            unseen_version.1.join(version);
         }
     }
 }

crates/client/src/client.rs ๐Ÿ”—

@@ -1310,7 +1310,7 @@ impl Client {
         drop(state);
 
         if let Some(handler) = handler {
-            let future = handler(subscriber, message, &self, cx.clone());
+            let future = handler(subscriber, message, self, cx.clone());
             let client_id = self.id();
             log::debug!(
                 "rpc message received. client_id:{}, sender_id:{:?}, type:{}",

crates/client/src/telemetry.rs ๐Ÿ”—

@@ -38,8 +38,9 @@ struct TelemetryState {
     flush_events_task: Option<Task<()>>,
     log_file: Option<NamedTempFile>,
     is_staff: Option<bool>,
-    first_event_datetime: Option<DateTime<Utc>>,
+    first_event_date_time: Option<DateTime<Utc>>,
     event_coalescer: EventCoalescer,
+    max_queue_size: usize,
 }
 
 const EVENTS_URL_PATH: &'static str = "/api/events";
@@ -69,14 +70,14 @@ struct EventWrapper {
     event: Event,
 }
 
-#[derive(Serialize, Debug)]
+#[derive(Clone, Debug, PartialEq, Serialize)]
 #[serde(rename_all = "snake_case")]
 pub enum AssistantKind {
     Panel,
     Inline,
 }
 
-#[derive(Serialize, Debug)]
+#[derive(Clone, Debug, PartialEq, Serialize)]
 #[serde(tag = "type")]
 pub enum Event {
     Editor {
@@ -168,8 +169,9 @@ impl Telemetry {
             flush_events_task: None,
             log_file: None,
             is_staff: None,
-            first_event_datetime: None,
+            first_event_date_time: None,
             event_coalescer: EventCoalescer::new(),
+            max_queue_size: MAX_QUEUE_LEN,
         }));
 
         #[cfg(not(debug_assertions))]
@@ -310,7 +312,7 @@ impl Telemetry {
             operation,
             copilot_enabled,
             copilot_enabled_for_language,
-            milliseconds_since_first_event: self.milliseconds_since_first_event(),
+            milliseconds_since_first_event: self.milliseconds_since_first_event(Utc::now()),
         };
 
         self.report_event(event)
@@ -326,7 +328,7 @@ impl Telemetry {
             suggestion_id,
             suggestion_accepted,
             file_extension,
-            milliseconds_since_first_event: self.milliseconds_since_first_event(),
+            milliseconds_since_first_event: self.milliseconds_since_first_event(Utc::now()),
         };
 
         self.report_event(event)
@@ -342,7 +344,7 @@ impl Telemetry {
             conversation_id,
             kind,
             model,
-            milliseconds_since_first_event: self.milliseconds_since_first_event(),
+            milliseconds_since_first_event: self.milliseconds_since_first_event(Utc::now()),
         };
 
         self.report_event(event)
@@ -358,7 +360,7 @@ impl Telemetry {
             operation,
             room_id,
             channel_id,
-            milliseconds_since_first_event: self.milliseconds_since_first_event(),
+            milliseconds_since_first_event: self.milliseconds_since_first_event(Utc::now()),
         };
 
         self.report_event(event)
@@ -368,7 +370,7 @@ impl Telemetry {
         let event = Event::Cpu {
             usage_as_percentage,
             core_count,
-            milliseconds_since_first_event: self.milliseconds_since_first_event(),
+            milliseconds_since_first_event: self.milliseconds_since_first_event(Utc::now()),
         };
 
         self.report_event(event)
@@ -382,26 +384,36 @@ impl Telemetry {
         let event = Event::Memory {
             memory_in_bytes,
             virtual_memory_in_bytes,
-            milliseconds_since_first_event: self.milliseconds_since_first_event(),
+            milliseconds_since_first_event: self.milliseconds_since_first_event(Utc::now()),
         };
 
         self.report_event(event)
     }
 
     pub fn report_app_event(self: &Arc<Self>, operation: String) {
+        self.report_app_event_with_date_time(operation, Utc::now());
+    }
+
+    fn report_app_event_with_date_time(
+        self: &Arc<Self>,
+        operation: String,
+        date_time: DateTime<Utc>,
+    ) -> Event {
         let event = Event::App {
             operation,
-            milliseconds_since_first_event: self.milliseconds_since_first_event(),
+            milliseconds_since_first_event: self.milliseconds_since_first_event(date_time),
         };
 
-        self.report_event(event)
+        self.report_event(event.clone());
+
+        event
     }
 
     pub fn report_setting_event(self: &Arc<Self>, setting: &'static str, value: String) {
         let event = Event::Setting {
             setting,
             value,
-            milliseconds_since_first_event: self.milliseconds_since_first_event(),
+            milliseconds_since_first_event: self.milliseconds_since_first_event(Utc::now()),
         };
 
         self.report_event(event)
@@ -416,7 +428,7 @@ impl Telemetry {
             let event = Event::Edit {
                 duration: end.timestamp_millis() - start.timestamp_millis(),
                 environment,
-                milliseconds_since_first_event: self.milliseconds_since_first_event(),
+                milliseconds_since_first_event: self.milliseconds_since_first_event(Utc::now()),
             };
 
             self.report_event(event);
@@ -427,22 +439,21 @@ impl Telemetry {
         let event = Event::Action {
             source,
             action,
-            milliseconds_since_first_event: self.milliseconds_since_first_event(),
+            milliseconds_since_first_event: self.milliseconds_since_first_event(Utc::now()),
         };
 
         self.report_event(event)
     }
 
-    fn milliseconds_since_first_event(&self) -> i64 {
+    fn milliseconds_since_first_event(self: &Arc<Self>, date_time: DateTime<Utc>) -> i64 {
         let mut state = self.state.lock();
 
-        match state.first_event_datetime {
-            Some(first_event_datetime) => {
-                let now: DateTime<Utc> = Utc::now();
-                now.timestamp_millis() - first_event_datetime.timestamp_millis()
+        match state.first_event_date_time {
+            Some(first_event_date_time) => {
+                date_time.timestamp_millis() - first_event_date_time.timestamp_millis()
             }
             None => {
-                state.first_event_datetime = Some(Utc::now());
+                state.first_event_date_time = Some(date_time);
                 0
             }
         }
@@ -468,7 +479,7 @@ impl Telemetry {
         state.events_queue.push(EventWrapper { signed_in, event });
 
         if state.installation_id.is_some() {
-            if state.events_queue.len() >= MAX_QUEUE_LEN {
+            if state.events_queue.len() >= state.max_queue_size {
                 drop(state);
                 self.flush_events();
             }
@@ -489,7 +500,7 @@ impl Telemetry {
 
     pub fn flush_events(self: &Arc<Self>) {
         let mut state = self.state.lock();
-        state.first_event_datetime = None;
+        state.first_event_date_time = None;
         let mut events = mem::take(&mut state.events_queue);
         state.flush_events_task.take();
         drop(state);
@@ -548,3 +559,159 @@ impl Telemetry {
             .detach();
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use chrono::TimeZone;
+    use gpui::TestAppContext;
+    use util::http::FakeHttpClient;
+
+    #[gpui::test]
+    fn test_telemetry_flush_on_max_queue_size(cx: &mut TestAppContext) {
+        init_test(cx);
+        let http = FakeHttpClient::with_200_response();
+        let installation_id = Some("installation_id".to_string());
+        let session_id = "session_id".to_string();
+
+        cx.update(|cx| {
+            let telemetry = Telemetry::new(http, cx);
+
+            telemetry.state.lock().max_queue_size = 4;
+            telemetry.start(installation_id, session_id, cx);
+
+            assert!(is_empty_state(&telemetry));
+
+            let first_date_time = Utc.with_ymd_and_hms(1990, 4, 12, 12, 0, 0).unwrap();
+            let operation = "test".to_string();
+
+            let event =
+                telemetry.report_app_event_with_date_time(operation.clone(), first_date_time);
+            assert_eq!(
+                event,
+                Event::App {
+                    operation: operation.clone(),
+                    milliseconds_since_first_event: 0
+                }
+            );
+            assert_eq!(telemetry.state.lock().events_queue.len(), 1);
+            assert!(telemetry.state.lock().flush_events_task.is_some());
+            assert_eq!(
+                telemetry.state.lock().first_event_date_time,
+                Some(first_date_time)
+            );
+
+            let mut date_time = first_date_time + chrono::Duration::milliseconds(100);
+
+            let event = telemetry.report_app_event_with_date_time(operation.clone(), date_time);
+            assert_eq!(
+                event,
+                Event::App {
+                    operation: operation.clone(),
+                    milliseconds_since_first_event: 100
+                }
+            );
+            assert_eq!(telemetry.state.lock().events_queue.len(), 2);
+            assert!(telemetry.state.lock().flush_events_task.is_some());
+            assert_eq!(
+                telemetry.state.lock().first_event_date_time,
+                Some(first_date_time)
+            );
+
+            date_time += chrono::Duration::milliseconds(100);
+
+            let event = telemetry.report_app_event_with_date_time(operation.clone(), date_time);
+            assert_eq!(
+                event,
+                Event::App {
+                    operation: operation.clone(),
+                    milliseconds_since_first_event: 200
+                }
+            );
+            assert_eq!(telemetry.state.lock().events_queue.len(), 3);
+            assert!(telemetry.state.lock().flush_events_task.is_some());
+            assert_eq!(
+                telemetry.state.lock().first_event_date_time,
+                Some(first_date_time)
+            );
+
+            date_time += chrono::Duration::milliseconds(100);
+
+            // Adding a 4th event should cause a flush
+            let event = telemetry.report_app_event_with_date_time(operation.clone(), date_time);
+            assert_eq!(
+                event,
+                Event::App {
+                    operation: operation.clone(),
+                    milliseconds_since_first_event: 300
+                }
+            );
+
+            assert!(is_empty_state(&telemetry));
+        });
+    }
+
+    #[gpui::test]
+    async fn test_connection_timeout(executor: BackgroundExecutor, cx: &mut TestAppContext) {
+        init_test(cx);
+        let http = FakeHttpClient::with_200_response();
+        let installation_id = Some("installation_id".to_string());
+        let session_id = "session_id".to_string();
+
+        cx.update(|cx| {
+            let telemetry = Telemetry::new(http, cx);
+            telemetry.state.lock().max_queue_size = 4;
+            telemetry.start(installation_id, session_id, cx);
+
+            assert!(is_empty_state(&telemetry));
+
+            let first_date_time = Utc.with_ymd_and_hms(1990, 4, 12, 12, 0, 0).unwrap();
+            let operation = "test".to_string();
+
+            let event =
+                telemetry.report_app_event_with_date_time(operation.clone(), first_date_time);
+            assert_eq!(
+                event,
+                Event::App {
+                    operation: operation.clone(),
+                    milliseconds_since_first_event: 0
+                }
+            );
+            assert_eq!(telemetry.state.lock().events_queue.len(), 1);
+            assert!(telemetry.state.lock().flush_events_task.is_some());
+            assert_eq!(
+                telemetry.state.lock().first_event_date_time,
+                Some(first_date_time)
+            );
+
+            let duration = Duration::from_millis(1);
+
+            // Test 1 millisecond before the flush interval limit is met
+            executor.advance_clock(FLUSH_INTERVAL - duration);
+
+            assert!(!is_empty_state(&telemetry));
+
+            // Test the exact moment the flush interval limit is met
+            executor.advance_clock(duration);
+
+            assert!(is_empty_state(&telemetry));
+        });
+    }
+
+    // TODO:
+    // Test settings
+    // Update FakeHTTPClient to keep track of the number of requests and assert on it
+
+    fn init_test(cx: &mut TestAppContext) {
+        cx.update(|cx| {
+            let settings_store = SettingsStore::test(cx);
+            cx.set_global(settings_store);
+        });
+    }
+
+    fn is_empty_state(telemetry: &Telemetry) -> bool {
+        telemetry.state.lock().events_queue.is_empty()
+            && telemetry.state.lock().flush_events_task.is_none()
+            && telemetry.state.lock().first_event_date_time.is_none()
+    }
+}

crates/client/src/user.rs ๐Ÿ”—

@@ -3,7 +3,10 @@ use anyhow::{anyhow, Context, Result};
 use collections::{hash_map::Entry, HashMap, HashSet};
 use feature_flags::FeatureFlagAppExt;
 use futures::{channel::mpsc, Future, StreamExt};
-use gpui::{AsyncAppContext, EventEmitter, Model, ModelContext, SharedUrl, Task};
+use gpui::{
+    AppContext, AsyncAppContext, EventEmitter, Model, ModelContext, SharedString, SharedUrl, Task,
+    WeakModel,
+};
 use postage::{sink::Sink, watch};
 use rpc::proto::{RequestMessage, UsersResponse};
 use std::sync::{Arc, Weak};
@@ -77,6 +80,7 @@ pub struct UserStore {
     client: Weak<Client>,
     _maintain_contacts: Task<()>,
     _maintain_current_user: Task<Result<()>>,
+    weak_self: WeakModel<Self>,
 }
 
 #[derive(Clone)]
@@ -194,6 +198,7 @@ impl UserStore {
                 Ok(())
             }),
             pending_contact_requests: Default::default(),
+            weak_self: cx.weak_model(),
         }
     }
 
@@ -579,6 +584,19 @@ impl UserStore {
         self.users.get(&user_id).cloned()
     }
 
+    pub fn get_user_optimistic(
+        &mut self,
+        user_id: u64,
+        cx: &mut ModelContext<Self>,
+    ) -> Option<Arc<User>> {
+        if let Some(user) = self.users.get(&user_id).cloned() {
+            return Some(user);
+        }
+
+        self.get_user(user_id, cx).detach_and_log_err(cx);
+        None
+    }
+
     pub fn get_user(
         &mut self,
         user_id: u64,
@@ -651,6 +669,31 @@ impl UserStore {
     pub fn participant_indices(&self) -> &HashMap<u64, ParticipantIndex> {
         &self.participant_indices
     }
+
+    pub fn participant_names(
+        &self,
+        user_ids: impl Iterator<Item = u64>,
+        cx: &AppContext,
+    ) -> HashMap<u64, SharedString> {
+        let mut ret = HashMap::default();
+        let mut missing_user_ids = Vec::new();
+        for id in user_ids {
+            if let Some(github_login) = self.get_cached_user(id).map(|u| u.github_login.clone()) {
+                ret.insert(id, github_login.into());
+            } else {
+                missing_user_ids.push(id)
+            }
+        }
+        if !missing_user_ids.is_empty() {
+            let this = self.weak_self.clone();
+            cx.spawn(|mut cx| async move {
+                this.update(&mut cx, |this, cx| this.get_users(missing_user_ids, cx))?
+                    .await
+            })
+            .detach_and_log_err(cx);
+        }
+        ret
+    }
 }
 
 impl User {

crates/collab/Cargo.toml ๐Ÿ”—

@@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathan@zed.dev>"]
 default-run = "collab"
 edition = "2021"
 name = "collab"
-version = "0.37.0"
+version = "0.38.0"
 publish = false
 
 [[bin]]

crates/collab/src/db/queries/buffers.rs ๐Ÿ”—

@@ -450,8 +450,21 @@ impl Database {
     )> {
         self.transaction(move |tx| async move {
             let channel = self.get_channel_internal(channel_id, &*tx).await?;
-            self.check_user_is_channel_member(&channel, user, &*tx)
-                .await?;
+
+            let mut requires_write_permission = false;
+            for op in operations.iter() {
+                match op.variant {
+                    None | Some(proto::operation::Variant::UpdateSelections(_)) => {}
+                    Some(_) => requires_write_permission = true,
+                }
+            }
+            if requires_write_permission {
+                self.check_user_is_channel_member(&channel, user, &*tx)
+                    .await?;
+            } else {
+                self.check_user_is_channel_participant(&channel, user, &*tx)
+                    .await?;
+            }
 
             let buffer = buffer::Entity::find()
                 .filter(buffer::Column::ChannelId.eq(channel_id))

crates/collab/src/db/queries/notifications.rs ๐Ÿ”—

@@ -66,7 +66,7 @@ impl Database {
         .await
     }
 
-    /// Create a notification. If `avoid_duplicates` is set to true, then avoid
+    /// Creates a notification. If `avoid_duplicates` is set to true, then avoid
     /// creating a new notification if the given recipient already has an
     /// unread notification with the given kind and entity id.
     pub async fn create_notification(

crates/collab/src/db/queries/users.rs ๐Ÿ”—

@@ -153,7 +153,7 @@ impl Database {
         .await
     }
 
-    /// Set "connected_once" on the user for analytics.
+    /// Sets "connected_once" on the user for analytics.
     pub async fn set_user_connected_once(&self, id: UserId, connected_once: bool) -> Result<()> {
         self.transaction(|tx| async move {
             user::Entity::update_many()
@@ -252,7 +252,7 @@ impl Database {
         .await
     }
 
-    /// Return the active flags for the user.
+    /// Returns the active flags for the user.
     pub async fn get_user_flags(&self, user: UserId) -> Result<Vec<String>> {
         self.transaction(|tx| async move {
             #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]

crates/collab/src/rpc.rs ๐Ÿ”—

@@ -950,7 +950,7 @@ async fn ping(_: proto::Ping, response: Response<proto::Ping>, _session: Session
     Ok(())
 }
 
-/// Create a new room for calling (outside of channels)
+/// Creates a new room for calling (outside of channels)
 async fn create_room(
     _request: proto::CreateRoom,
     response: Response<proto::CreateRoom>,
@@ -1276,7 +1276,7 @@ async fn leave_room(
     Ok(())
 }
 
-/// Update the permissions of someone else in the room.
+/// Updates the permissions of someone else in the room.
 async fn set_room_participant_role(
     request: proto::SetRoomParticipantRole,
     response: Response<proto::SetRoomParticipantRole>,
@@ -1460,7 +1460,7 @@ async fn decline_call(message: proto::DeclineCall, session: Session) -> Result<(
     Ok(())
 }
 
-/// Update other participants in the room with your current location.
+/// Updates other participants in the room with your current location.
 async fn update_participant_location(
     request: proto::UpdateParticipantLocation,
     response: Response<proto::UpdateParticipantLocation>,
@@ -1673,7 +1673,7 @@ async fn leave_project(request: proto::LeaveProject, session: Session) -> Result
     Ok(())
 }
 
-/// Update other participants with changes to the project
+/// Updates other participants with changes to the project
 async fn update_project(
     request: proto::UpdateProject,
     response: Response<proto::UpdateProject>,
@@ -1700,7 +1700,7 @@ async fn update_project(
     Ok(())
 }
 
-/// Update other participants with changes to the worktree
+/// Updates other participants with changes to the worktree
 async fn update_worktree(
     request: proto::UpdateWorktree,
     response: Response<proto::UpdateWorktree>,
@@ -1725,7 +1725,7 @@ async fn update_worktree(
     Ok(())
 }
 
-/// Update other participants with changes to the diagnostics
+/// Updates other participants with changes to the diagnostics
 async fn update_diagnostic_summary(
     message: proto::UpdateDiagnosticSummary,
     session: Session,
@@ -1749,7 +1749,7 @@ async fn update_diagnostic_summary(
     Ok(())
 }
 
-/// Update other participants with changes to the worktree settings
+/// Updates other participants with changes to the worktree settings
 async fn update_worktree_settings(
     message: proto::UpdateWorktreeSettings,
     session: Session,
@@ -2293,7 +2293,7 @@ async fn remove_contact(
     Ok(())
 }
 
-/// Create a new channel.
+/// Creates a new channel.
 async fn create_channel(
     request: proto::CreateChannel,
     response: Response<proto::CreateChannel>,

crates/collab/src/tests/integration_tests.rs ๐Ÿ”—

@@ -34,6 +34,7 @@ use std::{
         atomic::{AtomicBool, Ordering::SeqCst},
         Arc,
     },
+    time::Duration,
 };
 use unindent::Unindent as _;
 
@@ -5945,3 +5946,26 @@ async fn test_right_click_menu_behind_collab_panel(cx: &mut TestAppContext) {
     });
     assert!(cx.debug_bounds("MENU_ITEM-Close").is_some());
 }
+
+#[gpui::test]
+async fn test_cmd_k_left(cx: &mut TestAppContext) {
+    let client = TestServer::start1(cx).await;
+    let (workspace, cx) = client.build_test_workspace(cx).await;
+
+    cx.simulate_keystrokes("cmd-n");
+    workspace.update(cx, |workspace, cx| {
+        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 1);
+    });
+    cx.simulate_keystrokes("cmd-k left");
+    workspace.update(cx, |workspace, cx| {
+        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 2);
+    });
+    cx.simulate_keystrokes("cmd-k");
+    // sleep for longer than the timeout in keyboard shortcut handling
+    // to verify that it doesn't fire in this case.
+    cx.executor().advance_clock(Duration::from_secs(2));
+    cx.simulate_keystrokes("left");
+    workspace.update(cx, |workspace, cx| {
+        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 3);
+    });
+}

crates/collab/src/tests/test_server.rs ๐Ÿ”—

@@ -127,6 +127,11 @@ impl TestServer {
         (client_a, client_b, channel_id)
     }
 
+    pub async fn start1<'a>(cx: &'a mut TestAppContext) -> TestClient {
+        let mut server = Self::start(cx.executor().clone()).await;
+        server.create_client(cx, "user_a").await
+    }
+
     pub async fn reset(&self) {
         self.app_state.db.reset();
         let epoch = self

crates/collab_ui/Cargo.toml ๐Ÿ”—

@@ -60,6 +60,7 @@ anyhow.workspace = true
 futures.workspace = true
 lazy_static.workspace = true
 log.workspace = true
+parking_lot.workspace = true
 schemars.workspace = true
 postage.workspace = true
 serde.workspace = true

crates/collab_ui/src/channel_view.rs ๐Ÿ”—

@@ -442,4 +442,13 @@ impl CollaborationHub for ChannelBufferCollaborationHub {
     ) -> &'a HashMap<u64, ParticipantIndex> {
         self.0.read(cx).user_store().read(cx).participant_indices()
     }
+
+    fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
+        let user_ids = self.collaborators(cx).values().map(|c| c.user_id);
+        self.0
+            .read(cx)
+            .user_store()
+            .read(cx)
+            .participant_names(user_ids, cx)
+    }
 }

crates/collab_ui/src/chat_panel.rs ๐Ÿ”—

@@ -18,7 +18,7 @@ use project::Fs;
 use rich_text::RichText;
 use serde::{Deserialize, Serialize};
 use settings::Settings;
-use std::sync::Arc;
+use std::{sync::Arc, time::Duration};
 use time::{OffsetDateTime, UtcOffset};
 use ui::{
     popover_menu, prelude::*, Avatar, Button, ContextMenu, IconButton, IconName, KeyBinding, Label,
@@ -304,8 +304,11 @@ impl ChatPanel {
                 let last_message = active_chat.message(ix.saturating_sub(1));
                 let this_message = active_chat.message(ix).clone();
 
-                let is_continuation_from_previous = last_message.id != this_message.id
-                    && last_message.sender.id == this_message.sender.id;
+                let duration_since_last_message = this_message.timestamp - last_message.timestamp;
+                let is_continuation_from_previous = last_message.sender.id
+                    == this_message.sender.id
+                    && last_message.id != this_message.id
+                    && duration_since_last_message < Duration::from_secs(5 * 60);
 
                 if let ChannelMessageId::Saved(id) = this_message.id {
                     if this_message
@@ -325,8 +328,6 @@ impl ChatPanel {
             Self::render_markdown_with_mentions(&self.languages, self.client.id(), &message)
         });
 
-        let now = OffsetDateTime::now_utc();
-
         let belongs_to_user = Some(message.sender.id) == self.client.user_id();
         let message_id_to_remove = if let (ChannelMessageId::Saved(id), true) =
             (message.id, belongs_to_user || is_admin)
@@ -349,23 +350,21 @@ impl ChatPanel {
             .when(!is_continuation_from_previous, |this| {
                 this.pt_3().child(
                     h_flex()
-                        .child(
-                            div().absolute().child(
-                                Avatar::new(message.sender.avatar_uri.clone())
-                                    .size(cx.rem_size() * 1.5),
-                            ),
-                        )
+                        .text_ui_sm()
+                        .child(div().absolute().child(
+                            Avatar::new(message.sender.avatar_uri.clone()).size(cx.rem_size()),
+                        ))
                         .child(
                             div()
-                                .pl(cx.rem_size() * 1.5 + px(6.0))
+                                .pl(cx.rem_size() + px(6.0))
                                 .pr(px(8.0))
                                 .font_weight(FontWeight::BOLD)
                                 .child(Label::new(message.sender.github_login.clone())),
                         )
                         .child(
                             Label::new(format_timestamp(
+                                OffsetDateTime::now_utc(),
                                 message.timestamp,
-                                now,
                                 self.local_timezone,
                             ))
                             .size(LabelSize::Small)
@@ -559,6 +558,7 @@ impl Render for ChatPanel {
                 } else {
                     this.child(
                         div()
+                            .full()
                             .p_4()
                             .child(
                                 Label::new("Select a channel to chat in.")
@@ -596,7 +596,7 @@ impl Render for ChatPanel {
                             el.child(
                                 div()
                                     .rounded_md()
-                                    .h_7()
+                                    .h_6()
                                     .w_full()
                                     .bg(cx.theme().colors().editor_background),
                             )
@@ -670,28 +670,44 @@ impl Panel for ChatPanel {
 impl EventEmitter<PanelEvent> for ChatPanel {}
 
 fn format_timestamp(
-    mut timestamp: OffsetDateTime,
-    mut now: OffsetDateTime,
-    local_timezone: UtcOffset,
+    reference: OffsetDateTime,
+    timestamp: OffsetDateTime,
+    timezone: UtcOffset,
 ) -> String {
-    timestamp = timestamp.to_offset(local_timezone);
-    now = now.to_offset(local_timezone);
-
-    let today = now.date();
-    let date = timestamp.date();
-    let mut hour = timestamp.hour();
-    let mut part = "am";
-    if hour > 12 {
-        hour -= 12;
-        part = "pm";
-    }
-    if date == today {
-        format!("{:02}:{:02}{}", hour, timestamp.minute(), part)
-    } else if date.next_day() == Some(today) {
-        format!("yesterday at {:02}:{:02}{}", hour, timestamp.minute(), part)
+    let timestamp_local = timestamp.to_offset(timezone);
+    let timestamp_local_hour = timestamp_local.hour();
+
+    let hour_12 = match timestamp_local_hour {
+        0 => 12,                              // Midnight
+        13..=23 => timestamp_local_hour - 12, // PM hours
+        _ => timestamp_local_hour,            // AM hours
+    };
+    let meridiem = if timestamp_local_hour >= 12 {
+        "pm"
     } else {
-        format!("{:02}/{}/{}", date.month() as u32, date.day(), date.year())
+        "am"
+    };
+    let timestamp_local_minute = timestamp_local.minute();
+    let formatted_time = format!("{:02}:{:02} {}", hour_12, timestamp_local_minute, meridiem);
+
+    let reference_local = reference.to_offset(timezone);
+    let reference_local_date = reference_local.date();
+    let timestamp_local_date = timestamp_local.date();
+
+    if timestamp_local_date == reference_local_date {
+        return formatted_time;
     }
+
+    if reference_local_date.previous_day() == Some(timestamp_local_date) {
+        return format!("yesterday at {}", formatted_time);
+    }
+
+    format!(
+        "{:02}/{:02}/{}",
+        timestamp_local_date.month() as u32,
+        timestamp_local_date.day(),
+        timestamp_local_date.year()
+    )
 }
 
 #[cfg(test)]
@@ -700,6 +716,7 @@ mod tests {
     use gpui::HighlightStyle;
     use pretty_assertions::assert_eq;
     use rich_text::Highlight;
+    use time::{Date, OffsetDateTime, Time, UtcOffset};
     use util::test::marked_text_ranges;
 
     #[gpui::test]
@@ -748,4 +765,99 @@ mod tests {
             ]
         );
     }
+
+    #[test]
+    fn test_format_today() {
+        let reference = create_offset_datetime(1990, 4, 12, 16, 45, 0);
+        let timestamp = create_offset_datetime(1990, 4, 12, 15, 30, 0);
+
+        assert_eq!(
+            format_timestamp(reference, timestamp, test_timezone()),
+            "03:30 pm"
+        );
+    }
+
+    #[test]
+    fn test_format_yesterday() {
+        let reference = create_offset_datetime(1990, 4, 12, 10, 30, 0);
+        let timestamp = create_offset_datetime(1990, 4, 11, 9, 0, 0);
+
+        assert_eq!(
+            format_timestamp(reference, timestamp, test_timezone()),
+            "yesterday at 09:00 am"
+        );
+    }
+
+    #[test]
+    fn test_format_yesterday_less_than_24_hours_ago() {
+        let reference = create_offset_datetime(1990, 4, 12, 19, 59, 0);
+        let timestamp = create_offset_datetime(1990, 4, 11, 20, 0, 0);
+
+        assert_eq!(
+            format_timestamp(reference, timestamp, test_timezone()),
+            "yesterday at 08:00 pm"
+        );
+    }
+
+    #[test]
+    fn test_format_yesterday_more_than_24_hours_ago() {
+        let reference = create_offset_datetime(1990, 4, 12, 19, 59, 0);
+        let timestamp = create_offset_datetime(1990, 4, 11, 18, 0, 0);
+
+        assert_eq!(
+            format_timestamp(reference, timestamp, test_timezone()),
+            "yesterday at 06:00 pm"
+        );
+    }
+
+    #[test]
+    fn test_format_yesterday_over_midnight() {
+        let reference = create_offset_datetime(1990, 4, 12, 0, 5, 0);
+        let timestamp = create_offset_datetime(1990, 4, 11, 23, 55, 0);
+
+        assert_eq!(
+            format_timestamp(reference, timestamp, test_timezone()),
+            "yesterday at 11:55 pm"
+        );
+    }
+
+    #[test]
+    fn test_format_yesterday_over_month() {
+        let reference = create_offset_datetime(1990, 4, 2, 9, 0, 0);
+        let timestamp = create_offset_datetime(1990, 4, 1, 20, 0, 0);
+
+        assert_eq!(
+            format_timestamp(reference, timestamp, test_timezone()),
+            "yesterday at 08:00 pm"
+        );
+    }
+
+    #[test]
+    fn test_format_before_yesterday() {
+        let reference = create_offset_datetime(1990, 4, 12, 10, 30, 0);
+        let timestamp = create_offset_datetime(1990, 4, 10, 20, 20, 0);
+
+        assert_eq!(
+            format_timestamp(reference, timestamp, test_timezone()),
+            "04/10/1990"
+        );
+    }
+
+    fn test_timezone() -> UtcOffset {
+        UtcOffset::from_hms(0, 0, 0).expect("Valid timezone offset")
+    }
+
+    fn create_offset_datetime(
+        year: i32,
+        month: u8,
+        day: u8,
+        hour: u8,
+        minute: u8,
+        second: u8,
+    ) -> OffsetDateTime {
+        let date =
+            Date::from_calendar_date(year, time::Month::try_from(month).unwrap(), day).unwrap();
+        let time = Time::from_hms(hour, minute, second).unwrap();
+        date.with_time(time).assume_utc() // Assume UTC for simplicity
+    }
 }

crates/collab_ui/src/chat_panel/message_editor.rs ๐Ÿ”—

@@ -1,19 +1,24 @@
-use std::{sync::Arc, time::Duration};
-
+use anyhow::Result;
 use channel::{ChannelId, ChannelMembership, ChannelStore, MessageParams};
 use client::UserId;
 use collections::HashMap;
-use editor::{AnchorRangeExt, Editor, EditorElement, EditorStyle};
+use editor::{AnchorRangeExt, CompletionProvider, Editor, EditorElement, EditorStyle};
+use fuzzy::StringMatchCandidate;
 use gpui::{
     AsyncWindowContext, FocusableView, FontStyle, FontWeight, HighlightStyle, IntoElement, Model,
     Render, SharedString, Task, TextStyle, View, ViewContext, WeakView, WhiteSpace,
 };
-use language::{language_settings::SoftWrap, Buffer, BufferSnapshot, LanguageRegistry};
+use language::{
+    language_settings::SoftWrap, Anchor, Buffer, BufferSnapshot, CodeLabel, Completion,
+    LanguageRegistry, LanguageServerId, ToOffset,
+};
 use lazy_static::lazy_static;
+use parking_lot::RwLock;
 use project::search::SearchQuery;
 use settings::Settings;
+use std::{sync::Arc, time::Duration};
 use theme::ThemeSettings;
-use ui::prelude::*;
+use ui::{prelude::*, UiTextSize};
 
 const MENTIONS_DEBOUNCE_INTERVAL: Duration = Duration::from_millis(50);
 
@@ -31,6 +36,43 @@ pub struct MessageEditor {
     channel_id: Option<ChannelId>,
 }
 
+struct MessageEditorCompletionProvider(WeakView<MessageEditor>);
+
+impl CompletionProvider for MessageEditorCompletionProvider {
+    fn completions(
+        &self,
+        buffer: &Model<Buffer>,
+        buffer_position: language::Anchor,
+        cx: &mut ViewContext<Editor>,
+    ) -> Task<anyhow::Result<Vec<language::Completion>>> {
+        let Some(handle) = self.0.upgrade() else {
+            return Task::ready(Ok(Vec::new()));
+        };
+        handle.update(cx, |message_editor, cx| {
+            message_editor.completions(buffer, buffer_position, cx)
+        })
+    }
+
+    fn resolve_completions(
+        &self,
+        _completion_indices: Vec<usize>,
+        _completions: Arc<RwLock<Box<[language::Completion]>>>,
+        _cx: &mut ViewContext<Editor>,
+    ) -> Task<anyhow::Result<bool>> {
+        Task::ready(Ok(false))
+    }
+
+    fn apply_additional_edits_for_completion(
+        &self,
+        _buffer: Model<Buffer>,
+        _completion: Completion,
+        _push_to_history: bool,
+        _cx: &mut ViewContext<Editor>,
+    ) -> Task<Result<Option<language::Transaction>>> {
+        Task::ready(Ok(None))
+    }
+}
+
 impl MessageEditor {
     pub fn new(
         language_registry: Arc<LanguageRegistry>,
@@ -38,8 +80,11 @@ impl MessageEditor {
         editor: View<Editor>,
         cx: &mut ViewContext<Self>,
     ) -> Self {
+        let this = cx.view().downgrade();
         editor.update(cx, |editor, cx| {
             editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
+            editor.set_use_autoclose(false);
+            editor.set_completion_provider(Box::new(MessageEditorCompletionProvider(this)));
         });
 
         let buffer = editor
@@ -149,6 +194,71 @@ impl MessageEditor {
         }
     }
 
+    fn completions(
+        &mut self,
+        buffer: &Model<Buffer>,
+        end_anchor: Anchor,
+        cx: &mut ViewContext<Self>,
+    ) -> Task<Result<Vec<Completion>>> {
+        let end_offset = end_anchor.to_offset(buffer.read(cx));
+
+        let Some(query) = buffer.update(cx, |buffer, _| {
+            let mut query = String::new();
+            for ch in buffer.reversed_chars_at(end_offset).take(100) {
+                if ch == '@' {
+                    return Some(query.chars().rev().collect::<String>());
+                }
+                if ch.is_whitespace() || !ch.is_ascii() {
+                    break;
+                }
+                query.push(ch);
+            }
+            return None;
+        }) else {
+            return Task::ready(Ok(vec![]));
+        };
+
+        let start_offset = end_offset - query.len();
+        let start_anchor = buffer.read(cx).anchor_before(start_offset);
+
+        let candidates = self
+            .users
+            .keys()
+            .map(|user| StringMatchCandidate {
+                id: 0,
+                string: user.clone(),
+                char_bag: user.chars().collect(),
+            })
+            .collect::<Vec<_>>();
+        cx.spawn(|_, cx| async move {
+            let matches = fuzzy::match_strings(
+                &candidates,
+                &query,
+                true,
+                10,
+                &Default::default(),
+                cx.background_executor().clone(),
+            )
+            .await;
+
+            Ok(matches
+                .into_iter()
+                .map(|mat| Completion {
+                    old_range: start_anchor..end_anchor,
+                    new_text: mat.string.clone(),
+                    label: CodeLabel {
+                        filter_range: 1..mat.string.len() + 1,
+                        text: format!("@{}", mat.string),
+                        runs: Vec::new(),
+                    },
+                    documentation: None,
+                    server_id: LanguageServerId(0), // TODO: Make this optional or something?
+                    lsp_completion: Default::default(), // TODO: Make this optional or something?
+                })
+                .collect())
+        })
+    }
+
     async fn find_mentions(
         this: WeakView<MessageEditor>,
         buffer: BufferSnapshot,
@@ -216,7 +326,7 @@ impl Render for MessageEditor {
             },
             font_family: settings.ui_font.family.clone(),
             font_features: settings.ui_font.features,
-            font_size: rems(0.875).into(),
+            font_size: UiTextSize::Small.rems().into(),
             font_weight: FontWeight::NORMAL,
             font_style: FontStyle::Normal,
             line_height: relative(1.3).into(),

crates/collab_ui/src/collab_panel.rs ๐Ÿ”—

@@ -20,7 +20,7 @@ use gpui::{
     Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render, SharedString, Styled,
     Subscription, Task, TextStyle, View, ViewContext, VisualContext, WeakView, WhiteSpace,
 };
-use menu::{Cancel, Confirm, SelectNext, SelectPrev};
+use menu::{Cancel, Confirm, SecondaryConfirm, SelectNext, SelectPrev};
 use project::{Fs, Project};
 use rpc::proto::{self, PeerId};
 use serde_derive::{Deserialize, Serialize};
@@ -1124,7 +1124,7 @@ impl CollabPanel {
                     )
                     .entry(
                         "Rename",
-                        None,
+                        Some(Box::new(SecondaryConfirm)),
                         cx.handler_for(&this, move |this, cx| this.rename_channel(channel_id, cx)),
                     )
                     .entry(
@@ -1492,7 +1492,7 @@ impl CollabPanel {
         }
     }
 
-    fn rename_selected_channel(&mut self, _: &menu::SecondaryConfirm, cx: &mut ViewContext<Self>) {
+    fn rename_selected_channel(&mut self, _: &SecondaryConfirm, cx: &mut ViewContext<Self>) {
         if let Some(channel) = self.selected_channel() {
             self.rename_channel(channel.id, cx);
         }
@@ -2214,15 +2214,15 @@ impl CollabPanel {
 
         let face_pile = if !participants.is_empty() {
             let extra_count = participants.len().saturating_sub(FACEPILE_LIMIT);
-            let result = FacePile {
-                faces: participants
+            let result = FacePile::new(
+                participants
                     .iter()
                     .map(|user| Avatar::new(user.avatar_uri.clone()).into_any_element())
                     .take(FACEPILE_LIMIT)
                     .chain(if extra_count > 0 {
                         Some(
                             div()
-                                .ml_1()
+                                .ml_2()
                                 .child(Label::new(format!("+{extra_count}")))
                                 .into_any_element(),
                         )
@@ -2230,7 +2230,7 @@ impl CollabPanel {
                         None
                     })
                     .collect::<SmallVec<_>>(),
-            };
+            );
 
             Some(result)
         } else {
@@ -2295,7 +2295,7 @@ impl CollabPanel {
                         h_flex()
                             .id(channel_id as usize)
                             .child(Label::new(channel.name.clone()))
-                            .children(face_pile.map(|face_pile| face_pile.render().p_1())),
+                            .children(face_pile.map(|face_pile| face_pile.p_1())),
                     ),
             )
             .child(

crates/collab_ui/src/collab_titlebar_item.rs ๐Ÿ”—

@@ -85,7 +85,14 @@ impl Render for CollabTitlebarItem {
                     .gap_1()
                     .children(self.render_project_host(cx))
                     .child(self.render_project_name(cx))
-                    .child(div().pr_1().children(self.render_project_branch(cx)))
+                    .children(self.render_project_branch(cx)),
+            )
+            .child(
+                h_flex()
+                    .id("collaborator-list")
+                    .w_full()
+                    .gap_1()
+                    .overflow_x_scroll()
                     .when_some(
                         current_user.clone().zip(client.peer_id()).zip(room.clone()),
                         |this, ((current_user, peer_id), room)| {
@@ -495,7 +502,7 @@ impl CollabTitlebarItem {
                     div.rounded_md().bg(color)
                 })
                 .child(
-                    FacePile::default()
+                    FacePile::empty()
                         .child(
                             Avatar::new(user.avatar_uri.clone())
                                 .grayscale(!is_present)
@@ -547,8 +554,7 @@ impl CollabTitlebarItem {
                             )
                         } else {
                             None
-                        })
-                        .render(),
+                        }),
                 ),
         )
     }

crates/collab_ui/src/face_pile.rs ๐Ÿ”—

@@ -1,28 +1,49 @@
-use gpui::{div, AnyElement, Div, IntoElement, ParentElement, Styled};
+use gpui::AnyElement;
 use smallvec::SmallVec;
+use ui::prelude::*;
 
-#[derive(Default)]
+#[derive(IntoElement)]
 pub struct FacePile {
-    pub faces: SmallVec<[AnyElement; 2]>,
+    base: Div,
+    faces: SmallVec<[AnyElement; 2]>,
 }
 
 impl FacePile {
-    pub fn render(self) -> Div {
+    pub fn empty() -> Self {
+        Self::new(SmallVec::new())
+    }
+
+    pub fn new(faces: SmallVec<[AnyElement; 2]>) -> Self {
+        Self {
+            base: h_flex(),
+            faces,
+        }
+    }
+}
+
+impl RenderOnce for FacePile {
+    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
         let player_count = self.faces.len();
         let player_list = self.faces.into_iter().enumerate().map(|(ix, player)| {
             let isnt_last = ix < player_count - 1;
 
             div()
-                .z_index((player_count - ix) as u8)
+                .z_index((player_count - ix) as u16)
                 .when(isnt_last, |div| div.neg_mr_1())
                 .child(player)
         });
-        div().flex().items_center().children(player_list)
+        self.base.children(player_list)
     }
 }
 
 impl ParentElement for FacePile {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
-        &mut self.faces
+    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
+        self.faces.extend(elements);
+    }
+}
+
+impl Styled for FacePile {
+    fn style(&mut self) -> &mut gpui::StyleRefinement {
+        self.base.style()
     }
 }

crates/collab_ui/src/notification_panel.rs ๐Ÿ”—

@@ -713,6 +713,9 @@ impl Render for NotificationToast {
 
         h_flex()
             .id("notification_panel_toast")
+            .elevation_3(cx)
+            .p_2()
+            .gap_2()
             .children(user.map(|user| Avatar::new(user.avatar_uri.clone())))
             .child(Label::new(self.text.clone()))
             .child(

crates/collab_ui/src/notifications/collab_notification.rs ๐Ÿ”—

@@ -26,8 +26,8 @@ impl CollabNotification {
 }
 
 impl ParentElement for CollabNotification {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
-        &mut self.children
+    fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
+        self.children.extend(elements)
     }
 }
 

crates/command_palette/src/command_palette.rs ๐Ÿ”—

@@ -311,7 +311,7 @@ impl PickerDelegate for CommandPaletteDelegate {
         let action = command.action;
         cx.focus(&self.previous_focus_handle);
         cx.window_context()
-            .spawn(move |mut cx| async move { cx.update(|_, cx| cx.dispatch_action(action)) })
+            .spawn(move |mut cx| async move { cx.update(|cx| cx.dispatch_action(action)) })
             .detach_and_log_err(cx);
         self.dismissed(cx);
     }

crates/copilot/src/copilot.rs ๐Ÿ”—

@@ -974,7 +974,7 @@ async fn get_copilot_lsp(http: Arc<dyn HttpClient>) -> anyhow::Result<PathBuf> {
                 .browser_download_url;
 
             let mut response = http
-                .get(&url, Default::default(), true)
+                .get(url, Default::default(), true)
                 .await
                 .map_err(|err| anyhow!("error downloading copilot release: {}", err))?;
             let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));

crates/copilot_ui/src/copilot_button.rs ๐Ÿ”—

@@ -355,7 +355,7 @@ fn initiate_sign_in(cx: &mut WindowContext) {
 
             cx.spawn(|mut cx| async move {
                 task.await;
-                if let Some(copilot) = cx.update(|_, cx| Copilot::global(cx)).ok().flatten() {
+                if let Some(copilot) = cx.update(|cx| Copilot::global(cx)).ok().flatten() {
                     workspace
                         .update(&mut cx, |workspace, cx| match copilot.read(cx).status() {
                             Status::Authorized => workspace.show_toast(

crates/diagnostics/src/diagnostics.rs ๐Ÿ”—

@@ -1584,27 +1584,34 @@ mod tests {
     }
 
     fn editor_blocks(editor: &View<Editor>, cx: &mut WindowContext) -> Vec<(u32, SharedString)> {
+        let editor_view = editor.clone();
         editor.update(cx, |editor, cx| {
             let snapshot = editor.snapshot(cx);
             snapshot
                 .blocks_in_range(0..snapshot.max_point().row())
                 .enumerate()
                 .filter_map(|(ix, (row, block))| {
-                    let name = match block {
-                        TransformBlock::Custom(block) => block
-                            .render(&mut BlockContext {
-                                view_context: cx,
-                                anchor_x: px(0.),
-                                gutter_padding: px(0.),
-                                gutter_width: px(0.),
-                                line_height: px(0.),
-                                em_width: px(0.),
-                                block_id: ix,
-                                editor_style: &editor::EditorStyle::default(),
-                            })
-                            .inner_id()?
-                            .try_into()
-                            .ok()?,
+                    let name: SharedString = match block {
+                        TransformBlock::Custom(block) => cx.with_element_context({
+                            let editor_view = editor_view.clone();
+                            |cx| -> Option<SharedString> {
+                                block
+                                    .render(&mut BlockContext {
+                                        context: cx,
+                                        anchor_x: px(0.),
+                                        gutter_padding: px(0.),
+                                        gutter_width: px(0.),
+                                        line_height: px(0.),
+                                        em_width: px(0.),
+                                        block_id: ix,
+                                        view: editor_view,
+                                        editor_style: &editor::EditorStyle::default(),
+                                    })
+                                    .inner_id()?
+                                    .try_into()
+                                    .ok()
+                            }
+                        })?,
 
                         TransformBlock::ExcerptHeader {
                             starts_new_buffer, ..

crates/editor/src/display_map/block_map.rs ๐Ÿ”—

@@ -4,7 +4,7 @@ use super::{
 };
 use crate::{Anchor, Editor, EditorStyle, ExcerptId, ExcerptRange, ToPoint as _};
 use collections::{Bound, HashMap, HashSet};
-use gpui::{AnyElement, Pixels, ViewContext};
+use gpui::{AnyElement, ElementContext, Pixels, View};
 use language::{BufferSnapshot, Chunk, Patch, Point};
 use parking_lot::Mutex;
 use std::{
@@ -81,7 +81,8 @@ pub enum BlockStyle {
 }
 
 pub struct BlockContext<'a, 'b> {
-    pub view_context: &'b mut ViewContext<'a, Editor>,
+    pub context: &'b mut ElementContext<'a>,
+    pub view: View<Editor>,
     pub anchor_x: Pixels,
     pub gutter_width: Pixels,
     pub gutter_padding: Pixels,
@@ -933,16 +934,16 @@ impl BlockDisposition {
 }
 
 impl<'a> Deref for BlockContext<'a, '_> {
-    type Target = ViewContext<'a, Editor>;
+    type Target = ElementContext<'a>;
 
     fn deref(&self) -> &Self::Target {
-        self.view_context
+        self.context
     }
 }
 
 impl DerefMut for BlockContext<'_, '_> {
     fn deref_mut(&mut self) -> &mut Self::Target {
-        self.view_context
+        self.context
     }
 }
 

crates/editor/src/editor.rs ๐Ÿ”—

@@ -40,7 +40,7 @@ pub(crate) use actions::*;
 use aho_corasick::AhoCorasick;
 use anyhow::{anyhow, Context as _, Result};
 use blink_manager::BlinkManager;
-use client::{Client, Collaborator, ParticipantIndex};
+use client::{Collaborator, ParticipantIndex};
 use clock::ReplicaId;
 use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
 use convert_case::{Case, Casing};
@@ -56,10 +56,11 @@ use git::diff_hunk_to_display;
 use gpui::{
     div, impl_actions, point, prelude::*, px, relative, rems, size, uniform_list, Action,
     AnyElement, AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Context,
-    DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight,
-    HighlightStyle, Hsla, InputHandler, InteractiveText, KeyContext, Model, MouseButton,
+    DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusableView, FontId, FontStyle,
+    FontWeight, HighlightStyle, Hsla, InteractiveText, KeyContext, Model, MouseButton,
     ParentElement, Pixels, Render, SharedString, Styled, StyledText, Subscription, Task, TextStyle,
-    UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WhiteSpace, WindowContext,
+    UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext, WeakView,
+    WhiteSpace, WindowContext,
 };
 use highlight_matching_bracket::refresh_matching_bracket_highlights;
 use hover_popover::{hide_hover, HoverState};
@@ -71,8 +72,7 @@ use language::{
     language_settings::{self, all_language_settings, InlayHintSettings},
     markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CodeAction,
     CodeLabel, Completion, CursorShape, Diagnostic, Documentation, IndentKind, IndentSize,
-    Language, LanguageRegistry, LanguageServerName, OffsetRangeExt, Point, Selection,
-    SelectionGoal, TransactionId,
+    Language, LanguageServerName, OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId,
 };
 
 use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState};
@@ -88,7 +88,7 @@ use ordered_float::OrderedFloat;
 use parking_lot::RwLock;
 use project::{FormatTrigger, Location, Project, ProjectPath, ProjectTransaction};
 use rand::prelude::*;
-use rpc::proto::{self, *};
+use rpc::proto::*;
 use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
 use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
 use serde::{Deserialize, Serialize};
@@ -365,8 +365,11 @@ pub struct Editor {
     active_diagnostics: Option<ActiveDiagnosticGroup>,
     soft_wrap_mode_override: Option<language_settings::SoftWrap>,
     project: Option<Model<Project>>,
+    completion_provider: Option<Box<dyn CompletionProvider>>,
     collaboration_hub: Option<Box<dyn CollaborationHub>>,
     blink_manager: Model<BlinkManager>,
+    show_cursor_names: bool,
+    hovered_cursor: Option<HoveredCursor>,
     pub show_local_selections: bool,
     mode: EditorMode,
     show_gutter: bool,
@@ -406,11 +409,12 @@ pub struct Editor {
     style: Option<EditorStyle>,
     editor_actions: Vec<Box<dyn Fn(&mut ViewContext<Self>)>>,
     show_copilot_suggestions: bool,
+    use_autoclose: bool,
 }
 
 pub struct EditorSnapshot {
     pub mode: EditorMode,
-    pub show_gutter: bool,
+    show_gutter: bool,
     pub display_snapshot: DisplaySnapshot,
     pub placeholder_text: Option<Arc<str>>,
     is_focused: bool,
@@ -418,6 +422,23 @@ pub struct EditorSnapshot {
     ongoing_scroll: OngoingScroll,
 }
 
+pub struct GutterDimensions {
+    pub padding: Pixels,
+    pub width: Pixels,
+    pub margin: Pixels,
+}
+
+impl Default for GutterDimensions {
+    fn default() -> Self {
+        Self {
+            padding: Pixels::ZERO,
+            width: Pixels::ZERO,
+            margin: Pixels::ZERO,
+        }
+    }
+}
+
+#[derive(Debug)]
 pub struct RemoteSelection {
     pub replica_id: ReplicaId,
     pub selection: Selection<Anchor>,
@@ -425,6 +446,7 @@ pub struct RemoteSelection {
     pub peer_id: PeerId,
     pub line_mode: bool,
     pub participant_index: Option<ParticipantIndex>,
+    pub user_name: Option<SharedString>,
 }
 
 #[derive(Clone, Debug)]
@@ -441,6 +463,11 @@ enum SelectionHistoryMode {
     Redoing,
 }
 
+struct HoveredCursor {
+    replica_id: u16,
+    selection_id: usize,
+}
+
 impl Default for SelectionHistoryMode {
     fn default() -> Self {
         Self::Normal
@@ -722,85 +749,21 @@ impl CompletionsMenu {
             return None;
         }
 
-        let Some(project) = editor.project.clone() else {
+        let Some(provider) = editor.completion_provider.as_ref() else {
             return None;
         };
 
-        let client = project.read(cx).client();
-        let language_registry = project.read(cx).languages().clone();
-
-        let is_remote = project.read(cx).is_remote();
-        let project_id = project.read(cx).remote_id();
-
-        let completions = self.completions.clone();
-        let completion_indices: Vec<_> = self.matches.iter().map(|m| m.candidate_id).collect();
-
-        Some(cx.spawn(move |this, mut cx| async move {
-            if is_remote {
-                let Some(project_id) = project_id else {
-                    log::error!("Remote project without remote_id");
-                    return;
-                };
-
-                for completion_index in completion_indices {
-                    let completions_guard = completions.read();
-                    let completion = &completions_guard[completion_index];
-                    if completion.documentation.is_some() {
-                        continue;
-                    }
-
-                    let server_id = completion.server_id;
-                    let completion = completion.lsp_completion.clone();
-                    drop(completions_guard);
-
-                    Self::resolve_completion_documentation_remote(
-                        project_id,
-                        server_id,
-                        completions.clone(),
-                        completion_index,
-                        completion,
-                        client.clone(),
-                        language_registry.clone(),
-                    )
-                    .await;
-
-                    _ = this.update(&mut cx, |_, cx| cx.notify());
-                }
-            } else {
-                for completion_index in completion_indices {
-                    let completions_guard = completions.read();
-                    let completion = &completions_guard[completion_index];
-                    if completion.documentation.is_some() {
-                        continue;
-                    }
-
-                    let server_id = completion.server_id;
-                    let completion = completion.lsp_completion.clone();
-                    drop(completions_guard);
-
-                    let server = project
-                        .read_with(&mut cx, |project, _| {
-                            project.language_server_for_id(server_id)
-                        })
-                        .ok()
-                        .flatten();
-                    let Some(server) = server else {
-                        return;
-                    };
-
-                    Self::resolve_completion_documentation_local(
-                        server,
-                        completions.clone(),
-                        completion_index,
-                        completion,
-                        language_registry.clone(),
-                    )
-                    .await;
+        let resolve_task = provider.resolve_completions(
+            self.matches.iter().map(|m| m.candidate_id).collect(),
+            self.completions.clone(),
+            cx,
+        );
 
-                    _ = this.update(&mut cx, |_, cx| cx.notify());
-                }
+        return Some(cx.spawn(move |this, mut cx| async move {
+            if let Some(true) = resolve_task.await.log_err() {
+                this.update(&mut cx, |_, cx| cx.notify()).ok();
             }
-        }))
+        }));
     }
 
     fn attempt_resolve_selected_completion_documentation(
@@ -817,146 +780,16 @@ impl CompletionsMenu {
         let Some(project) = project else {
             return;
         };
-        let language_registry = project.read(cx).languages().clone();
-
-        let completions = self.completions.clone();
-        let completions_guard = completions.read();
-        let completion = &completions_guard[completion_index];
-        if completion.documentation.is_some() {
-            return;
-        }
-
-        let server_id = completion.server_id;
-        let completion = completion.lsp_completion.clone();
-        drop(completions_guard);
-
-        if project.read(cx).is_remote() {
-            let Some(project_id) = project.read(cx).remote_id() else {
-                log::error!("Remote project without remote_id");
-                return;
-            };
-
-            let client = project.read(cx).client();
-
-            cx.spawn(move |this, mut cx| async move {
-                Self::resolve_completion_documentation_remote(
-                    project_id,
-                    server_id,
-                    completions.clone(),
-                    completion_index,
-                    completion,
-                    client,
-                    language_registry.clone(),
-                )
-                .await;
-
-                _ = this.update(&mut cx, |_, cx| cx.notify());
-            })
-            .detach();
-        } else {
-            let Some(server) = project.read(cx).language_server_for_id(server_id) else {
-                return;
-            };
-
-            cx.spawn(move |this, mut cx| async move {
-                Self::resolve_completion_documentation_local(
-                    server,
-                    completions,
-                    completion_index,
-                    completion,
-                    language_registry,
-                )
-                .await;
-
-                _ = this.update(&mut cx, |_, cx| cx.notify());
-            })
-            .detach();
-        }
-    }
-
-    async fn resolve_completion_documentation_remote(
-        project_id: u64,
-        server_id: LanguageServerId,
-        completions: Arc<RwLock<Box<[Completion]>>>,
-        completion_index: usize,
-        completion: lsp::CompletionItem,
-        client: Arc<Client>,
-        language_registry: Arc<LanguageRegistry>,
-    ) {
-        let request = proto::ResolveCompletionDocumentation {
-            project_id,
-            language_server_id: server_id.0 as u64,
-            lsp_completion: serde_json::to_string(&completion).unwrap().into_bytes(),
-        };
-
-        let Some(response) = client
-            .request(request)
-            .await
-            .context("completion documentation resolve proto request")
-            .log_err()
-        else {
-            return;
-        };
-
-        if response.text.is_empty() {
-            let mut completions = completions.write();
-            let completion = &mut completions[completion_index];
-            completion.documentation = Some(Documentation::Undocumented);
-        }
-
-        let documentation = if response.is_markdown {
-            Documentation::MultiLineMarkdown(
-                markdown::parse_markdown(&response.text, &language_registry, None).await,
-            )
-        } else if response.text.lines().count() <= 1 {
-            Documentation::SingleLine(response.text)
-        } else {
-            Documentation::MultiLinePlainText(response.text)
-        };
-
-        let mut completions = completions.write();
-        let completion = &mut completions[completion_index];
-        completion.documentation = Some(documentation);
-    }
-
-    async fn resolve_completion_documentation_local(
-        server: Arc<lsp::LanguageServer>,
-        completions: Arc<RwLock<Box<[Completion]>>>,
-        completion_index: usize,
-        completion: lsp::CompletionItem,
-        language_registry: Arc<LanguageRegistry>,
-    ) {
-        let can_resolve = server
-            .capabilities()
-            .completion_provider
-            .as_ref()
-            .and_then(|options| options.resolve_provider)
-            .unwrap_or(false);
-        if !can_resolve {
-            return;
-        }
-
-        let request = server.request::<lsp::request::ResolveCompletionItem>(completion);
-        let Some(completion_item) = request.await.log_err() else {
-            return;
-        };
-
-        if let Some(lsp_documentation) = completion_item.documentation {
-            let documentation = language::prepare_completion_documentation(
-                &lsp_documentation,
-                &language_registry,
-                None, // TODO: Try to reasonably work out which language the completion is for
-            )
-            .await;
 
-            let mut completions = completions.write();
-            let completion = &mut completions[completion_index];
-            completion.documentation = Some(documentation);
-        } else {
-            let mut completions = completions.write();
-            let completion = &mut completions[completion_index];
-            completion.documentation = Some(Documentation::Undocumented);
-        }
+        let resolve_task = project.update(cx, |project, cx| {
+            project.resolve_completions(vec![completion_index], self.completions.clone(), cx)
+        });
+        cx.spawn(move |this, mut cx| async move {
+            if let Some(true) = resolve_task.await.log_err() {
+                this.update(&mut cx, |_, cx| cx.notify()).ok();
+            }
+        })
+        .detach();
     }
 
     fn visible(&self) -> bool {
@@ -1565,6 +1398,7 @@ impl Editor {
             ime_transaction: Default::default(),
             active_diagnostics: None,
             soft_wrap_mode_override,
+            completion_provider: project.clone().map(|project| Box::new(project) as _),
             collaboration_hub: project.clone().map(|project| Box::new(project) as _),
             project,
             blink_manager: blink_manager.clone(),
@@ -1594,6 +1428,7 @@ impl Editor {
             keymap_context_layers: Default::default(),
             input_enabled: true,
             read_only: false,
+            use_autoclose: true,
             leader_peer_id: None,
             remote_id: None,
             hover_state: Default::default(),
@@ -1604,6 +1439,8 @@ impl Editor {
             pixel_position_of_newest_cursor: None,
             gutter_width: Default::default(),
             style: None,
+            show_cursor_names: false,
+            hovered_cursor: Default::default(),
             editor_actions: Default::default(),
             show_copilot_suggestions: mode == EditorMode::Full,
             _subscriptions: vec![
@@ -1795,6 +1632,10 @@ impl Editor {
         self.collaboration_hub = Some(hub);
     }
 
+    pub fn set_completion_provider(&mut self, hub: Box<dyn CompletionProvider>) {
+        self.completion_provider = Some(hub);
+    }
+
     pub fn placeholder_text(&self) -> Option<&str> {
         self.placeholder_text.as_deref()
     }
@@ -1869,6 +1710,10 @@ impl Editor {
         self.read_only = read_only;
     }
 
+    pub fn set_use_autoclose(&mut self, autoclose: bool) {
+        self.use_autoclose = autoclose;
+    }
+
     pub fn set_show_copilot_suggestions(&mut self, show_copilot_suggestions: bool) {
         self.show_copilot_suggestions = show_copilot_suggestions;
     }
@@ -2467,7 +2312,12 @@ impl Editor {
                                         ),
                                         &bracket_pair.start[..prefix_len],
                                     ));
-                            if following_text_allows_autoclose && preceding_text_matches_prefix {
+                            let autoclose = self.use_autoclose
+                                && snapshot.settings_at(selection.start, cx).use_autoclose;
+                            if autoclose
+                                && following_text_allows_autoclose
+                                && preceding_text_matches_prefix
+                            {
                                 let anchor = snapshot.anchor_before(selection.end);
                                 new_selections.push((selection.map(|_| anchor), text.len()));
                                 new_autoclose_regions.push((
@@ -3241,9 +3091,7 @@ impl Editor {
             return;
         }
 
-        let project = if let Some(project) = self.project.clone() {
-            project
-        } else {
+        let Some(provider) = self.completion_provider.as_ref() else {
             return;
         };
 
@@ -3259,9 +3107,7 @@ impl Editor {
         };
 
         let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
-        let completions = project.update(cx, |project, cx| {
-            project.completions(&buffer, buffer_position, cx)
-        });
+        let completions = provider.completions(&buffer, buffer_position, cx);
 
         let id = post_inc(&mut self.next_completion_id);
         let task = cx.spawn(|this, mut cx| {
@@ -3370,6 +3216,7 @@ impl Editor {
         let buffer_handle = completions_menu.buffer;
         let completions = completions_menu.completions.read();
         let completion = completions.get(mat.candidate_id)?;
+        cx.stop_propagation();
 
         let snippet;
         let text;
@@ -3466,15 +3313,13 @@ impl Editor {
             this.refresh_copilot_suggestions(true, cx);
         });
 
-        let project = self.project.clone()?;
-        let apply_edits = project.update(cx, |project, cx| {
-            project.apply_additional_edits_for_completion(
-                buffer_handle,
-                completion.clone(),
-                true,
-                cx,
-            )
-        });
+        let provider = self.completion_provider.as_ref()?;
+        let apply_edits = provider.apply_additional_edits_for_completion(
+            buffer_handle,
+            completion.clone(),
+            true,
+            cx,
+        );
         Some(cx.foreground_executor().spawn(async move {
             apply_edits.await?;
             Ok(())
@@ -3561,7 +3406,7 @@ impl Editor {
         let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?;
 
         let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
-        cx.update(|_, cx| {
+        cx.update(|cx| {
             entries.sort_unstable_by_key(|(buffer, _)| {
                 buffer.read(cx).file().map(|f| f.path().clone())
             });
@@ -3888,6 +3733,24 @@ impl Editor {
         self.update_visible_copilot_suggestion(cx);
     }
 
+    pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
+        self.show_cursor_names(cx);
+    }
+
+    fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
+        self.show_cursor_names = true;
+        cx.notify();
+        cx.spawn(|this, mut cx| async move {
+            cx.background_executor().timer(Duration::from_secs(3)).await;
+            this.update(&mut cx, |this, cx| {
+                this.show_cursor_names = false;
+                cx.notify()
+            })
+            .ok()
+        })
+        .detach();
+    }
+
     fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
         if self.has_active_copilot_suggestion(cx) {
             self.cycle_copilot_suggestions(Direction::Next, cx);
@@ -4065,7 +3928,7 @@ impl Editor {
         gutter_hovered: bool,
         _line_height: Pixels,
         _gutter_margin: Pixels,
-        cx: &mut ViewContext<Self>,
+        editor_view: View<Editor>,
     ) -> Vec<Option<IconButton>> {
         fold_data
             .iter()
@@ -4075,14 +3938,19 @@ impl Editor {
                     .map(|(fold_status, buffer_row, active)| {
                         (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
                             IconButton::new(ix as usize, ui::IconName::ChevronDown)
-                                .on_click(cx.listener(move |editor, _e, cx| match fold_status {
-                                    FoldStatus::Folded => {
-                                        editor.unfold_at(&UnfoldAt { buffer_row }, cx);
+                                .on_click({
+                                    let view = editor_view.clone();
+                                    move |_e, cx| {
+                                        view.update(cx, |editor, cx| match fold_status {
+                                            FoldStatus::Folded => {
+                                                editor.unfold_at(&UnfoldAt { buffer_row }, cx);
+                                            }
+                                            FoldStatus::Foldable => {
+                                                editor.fold_at(&FoldAt { buffer_row }, cx);
+                                            }
+                                        })
                                     }
-                                    FoldStatus::Foldable => {
-                                        editor.fold_at(&FoldAt { buffer_row }, cx);
-                                    }
-                                }))
+                                })
                                 .icon_color(ui::Color::Muted)
                                 .icon_size(ui::IconSize::Small)
                                 .selected(fold_status == FoldStatus::Folded)
@@ -8992,6 +8860,7 @@ impl Editor {
             cx.focus(&rename_editor_focus_handle);
         } else {
             self.blink_manager.update(cx, BlinkManager::enable);
+            self.show_cursor_names(cx);
             self.buffer.update(cx, |buffer, cx| {
                 buffer.finalize_last_transaction(cx);
                 if self.leader_peer_id.is_none() {
@@ -9043,6 +8912,7 @@ pub trait CollaborationHub {
         &self,
         cx: &'a AppContext,
     ) -> &'a HashMap<u64, ParticipantIndex>;
+    fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
 }
 
 impl CollaborationHub for Model<Project> {
@@ -9056,6 +8926,74 @@ impl CollaborationHub for Model<Project> {
     ) -> &'a HashMap<u64, ParticipantIndex> {
         self.read(cx).user_store().read(cx).participant_indices()
     }
+
+    fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
+        let this = self.read(cx);
+        let user_ids = this.collaborators().values().map(|c| c.user_id);
+        this.user_store().read_with(cx, |user_store, cx| {
+            user_store.participant_names(user_ids, cx)
+        })
+    }
+}
+
+pub trait CompletionProvider {
+    fn completions(
+        &self,
+        buffer: &Model<Buffer>,
+        buffer_position: text::Anchor,
+        cx: &mut ViewContext<Editor>,
+    ) -> Task<Result<Vec<Completion>>>;
+
+    fn resolve_completions(
+        &self,
+        completion_indices: Vec<usize>,
+        completions: Arc<RwLock<Box<[Completion]>>>,
+        cx: &mut ViewContext<Editor>,
+    ) -> Task<Result<bool>>;
+
+    fn apply_additional_edits_for_completion(
+        &self,
+        buffer: Model<Buffer>,
+        completion: Completion,
+        push_to_history: bool,
+        cx: &mut ViewContext<Editor>,
+    ) -> Task<Result<Option<language::Transaction>>>;
+}
+
+impl CompletionProvider for Model<Project> {
+    fn completions(
+        &self,
+        buffer: &Model<Buffer>,
+        buffer_position: text::Anchor,
+        cx: &mut ViewContext<Editor>,
+    ) -> Task<Result<Vec<Completion>>> {
+        self.update(cx, |project, cx| {
+            project.completions(&buffer, buffer_position, cx)
+        })
+    }
+
+    fn resolve_completions(
+        &self,
+        completion_indices: Vec<usize>,
+        completions: Arc<RwLock<Box<[Completion]>>>,
+        cx: &mut ViewContext<Editor>,
+    ) -> Task<Result<bool>> {
+        self.update(cx, |project, cx| {
+            project.resolve_completions(completion_indices, completions, cx)
+        })
+    }
+
+    fn apply_additional_edits_for_completion(
+        &self,
+        buffer: Model<Buffer>,
+        completion: Completion,
+        push_to_history: bool,
+        cx: &mut ViewContext<Editor>,
+    ) -> Task<Result<Option<language::Transaction>>> {
+        self.update(cx, |project, cx| {
+            project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
+        })
+    }
 }
 
 fn inlay_hint_settings(
@@ -9107,6 +9045,7 @@ impl EditorSnapshot {
         collaboration_hub: &dyn CollaborationHub,
         cx: &'a AppContext,
     ) -> impl 'a + Iterator<Item = RemoteSelection> {
+        let participant_names = collaboration_hub.user_names(cx);
         let participant_indices = collaboration_hub.user_participant_indices(cx);
         let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
         let collaborators_by_replica_id = collaborators_by_peer_id
@@ -9118,6 +9057,7 @@ impl EditorSnapshot {
             .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
                 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
                 let participant_index = participant_indices.get(&collaborator.user_id).copied();
+                let user_name = participant_names.get(&collaborator.user_id).cloned();
                 Some(RemoteSelection {
                     replica_id,
                     selection,
@@ -9125,6 +9065,7 @@ impl EditorSnapshot {
                     line_mode,
                     participant_index,
                     peer_id: collaborator.peer_id,
+                    user_name,
                 })
             })
     }
@@ -9144,6 +9085,34 @@ impl EditorSnapshot {
     pub fn scroll_position(&self) -> gpui::Point<f32> {
         self.scroll_anchor.scroll_position(&self.display_snapshot)
     }
+
+    pub fn gutter_dimensions(
+        &self,
+        font_id: FontId,
+        font_size: Pixels,
+        em_width: Pixels,
+        max_line_number_width: Pixels,
+        cx: &AppContext,
+    ) -> GutterDimensions {
+        if self.show_gutter {
+            let descent = cx.text_system().descent(font_id, font_size);
+            let gutter_padding_factor = 4.0;
+            let gutter_padding = (em_width * gutter_padding_factor).round();
+            // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
+            let min_width_for_number_on_gutter = em_width * 4.0;
+            let gutter_width =
+                max_line_number_width.max(min_width_for_number_on_gutter) + gutter_padding * 2.0;
+            let gutter_margin = -descent;
+
+            GutterDimensions {
+                padding: gutter_padding,
+                width: gutter_width,
+                margin: gutter_margin,
+            }
+        } else {
+            GutterDimensions::default()
+        }
+    }
 }
 
 impl Deref for EditorSnapshot {
@@ -9258,7 +9227,7 @@ impl Render for Editor {
     }
 }
 
-impl InputHandler for Editor {
+impl ViewInputHandler for Editor {
     fn text_for_range(
         &mut self,
         range_utf16: Range<usize>,
@@ -9655,10 +9624,10 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> Ren
                     .size(ButtonSize::Compact)
                     .style(ButtonStyle::Transparent)
                     .visible_on_hover(group_id)
-                    .on_click(cx.listener({
+                    .on_click({
                         let message = diagnostic.message.clone();
-                        move |_, _, cx| cx.write_to_clipboard(ClipboardItem::new(message.clone()))
-                    }))
+                        move |_click, cx| cx.write_to_clipboard(ClipboardItem::new(message.clone()))
+                    })
                     .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
             )
             .into_any_element()

crates/editor/src/element.rs ๐Ÿ”—

@@ -25,12 +25,12 @@ use collections::{BTreeMap, HashMap};
 use git::diff::DiffHunkStatus;
 use gpui::{
     div, fill, outline, overlay, point, px, quad, relative, size, transparent_black, Action,
-    AnchorCorner, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Corners,
-    CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity, Hsla,
-    InteractiveBounds, InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton,
-    MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta,
-    ScrollWheelEvent, ShapedLine, SharedString, Size, StackingOrder, StatefulInteractiveElement,
-    Style, Styled, TextRun, TextStyle, View, ViewContext, WindowContext,
+    AnchorCorner, AnyElement, AvailableSpace, Bounds, ContentMask, Corners, CursorStyle,
+    DispatchPhase, Edges, Element, ElementInputHandler, Entity, Hsla, InteractiveBounds,
+    InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton, MouseDownEvent,
+    MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine,
+    SharedString, Size, StackingOrder, StatefulInteractiveElement, Style, Styled, TextRun,
+    TextStyle, View, ViewContext, WindowContext,
 };
 use itertools::Itertools;
 use language::language_settings::ShowWhitespaceSetting;
@@ -64,6 +64,7 @@ struct SelectionLayout {
     is_local: bool,
     range: Range<DisplayPoint>,
     active_rows: Range<u32>,
+    user_name: Option<SharedString>,
 }
 
 impl SelectionLayout {
@@ -74,6 +75,7 @@ impl SelectionLayout {
         map: &DisplaySnapshot,
         is_newest: bool,
         is_local: bool,
+        user_name: Option<SharedString>,
     ) -> Self {
         let point_selection = selection.map(|p| p.to_point(&map.buffer_snapshot));
         let display_selection = point_selection.map(|p| p.to_display_point(map));
@@ -113,6 +115,7 @@ impl SelectionLayout {
             is_local,
             range,
             active_rows,
+            user_name,
         }
     }
 }
@@ -324,9 +327,10 @@ impl EditorElement {
         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);
     }
 
-    fn register_key_listeners(&self, cx: &mut WindowContext) {
+    fn register_key_listeners(&self, cx: &mut ElementContext) {
         cx.on_key_event({
             let editor = self.editor.clone();
             move |event: &ModifiersChangedEvent, phase, cx| {
@@ -564,6 +568,7 @@ impl EditorElement {
                         cx,
                     );
                     hover_at(editor, Some(point), cx);
+                    Self::update_visible_cursor(editor, point, cx);
                 }
                 None => {
                     update_inlay_link_and_hover_points(
@@ -585,12 +590,45 @@ impl EditorElement {
         }
     }
 
+    fn update_visible_cursor(
+        editor: &mut Editor,
+        point: DisplayPoint,
+        cx: &mut ViewContext<Editor>,
+    ) {
+        let snapshot = editor.snapshot(cx);
+        let Some(hub) = editor.collaboration_hub() else {
+            return;
+        };
+        let range = DisplayPoint::new(point.row(), point.column().saturating_sub(1))
+            ..DisplayPoint::new(
+                point.row(),
+                (point.column() + 1).min(snapshot.line_len(point.row())),
+            );
+
+        let range = snapshot
+            .buffer_snapshot
+            .anchor_at(range.start.to_point(&snapshot.display_snapshot), Bias::Left)
+            ..snapshot
+                .buffer_snapshot
+                .anchor_at(range.end.to_point(&snapshot.display_snapshot), Bias::Right);
+
+        let Some(selection) = snapshot.remote_selections_in_range(&range, hub, cx).next() else {
+            editor.hovered_cursor.take();
+            return;
+        };
+        editor.hovered_cursor.replace(crate::HoveredCursor {
+            replica_id: selection.replica_id,
+            selection_id: selection.selection.id,
+        });
+        cx.notify()
+    }
+
     fn paint_background(
         &self,
         gutter_bounds: Bounds<Pixels>,
         text_bounds: Bounds<Pixels>,
         layout: &LayoutState,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let bounds = gutter_bounds.union(&text_bounds);
         let scroll_top =
@@ -673,7 +711,7 @@ impl EditorElement {
         &mut self,
         bounds: Bounds<Pixels>,
         layout: &mut LayoutState,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let line_height = layout.position_map.line_height;
 
@@ -744,7 +782,7 @@ impl EditorElement {
         });
     }
 
-    fn paint_diff_hunks(bounds: Bounds<Pixels>, layout: &LayoutState, cx: &mut WindowContext) {
+    fn paint_diff_hunks(bounds: Bounds<Pixels>, layout: &LayoutState, cx: &mut ElementContext) {
         let line_height = layout.position_map.line_height;
 
         let scroll_position = layout.position_map.snapshot.scroll_position();
@@ -848,7 +886,7 @@ impl EditorElement {
         &mut self,
         text_bounds: Bounds<Pixels>,
         layout: &mut LayoutState,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let start_row = layout.visible_display_row_range.start;
         let content_origin = text_bounds.origin + point(layout.gutter_margin, Pixels::ZERO);
@@ -982,8 +1020,10 @@ impl EditorElement {
                 let corner_radius = 0.15 * layout.position_map.line_height;
                 let mut invisible_display_ranges = SmallVec::<[Range<DisplayPoint>; 32]>::new();
 
-                for (selection_style, selections) in &layout.selections {
-                    for selection in selections {
+                for (participant_ix, (selection_style, selections)) in
+                    layout.selections.iter().enumerate()
+                {
+                    for selection in selections.into_iter() {
                         self.paint_highlighted_range(
                             selection.range.clone(),
                             selection_style.selection,
@@ -1064,6 +1104,7 @@ impl EditorElement {
                                         ))
                                     });
                                 }
+
                                 cursors.push(Cursor {
                                     color: selection_style.cursor,
                                     block_width,
@@ -1071,6 +1112,14 @@ impl EditorElement {
                                     line_height: layout.position_map.line_height,
                                     shape: selection.cursor_shape,
                                     block_text,
+                                    cursor_name: selection.user_name.clone().map(|name| {
+                                        CursorName {
+                                            string: name,
+                                            color: self.style.background,
+                                            is_top_row: cursor_position.row() == 0,
+                                            z_index: (participant_ix % 256).try_into().unwrap(),
+                                        }
+                                    }),
                                 });
                             }
                         }
@@ -1104,7 +1153,7 @@ impl EditorElement {
         &mut self,
         text_bounds: Bounds<Pixels>,
         layout: &mut LayoutState,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let content_origin = text_bounds.origin + point(layout.gutter_margin, Pixels::ZERO);
         let start_row = layout.visible_display_row_range.start;
@@ -1169,9 +1218,11 @@ impl EditorElement {
                         popover_origin.x = popover_origin.x + x_out_of_bounds;
                     }
 
-                    cx.break_content_mask(|cx| {
-                        hover_popover.draw(popover_origin, available_space, cx)
-                    });
+                    if cx.was_top_layer(&popover_origin, cx.stacking_order()) {
+                        cx.break_content_mask(|cx| {
+                            hover_popover.draw(popover_origin, available_space, cx)
+                        });
+                    }
 
                     current_y = popover_origin.y - HOVER_POPOVER_GAP;
                 }
@@ -1217,7 +1268,7 @@ impl EditorElement {
         &mut self,
         bounds: Bounds<Pixels>,
         layout: &mut LayoutState,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         if layout.mode != EditorMode::Full {
             return;
@@ -1461,7 +1512,7 @@ impl EditorElement {
         layout: &LayoutState,
         content_origin: gpui::Point<Pixels>,
         bounds: Bounds<Pixels>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let start_row = layout.visible_display_row_range.start;
         let end_row = layout.visible_display_row_range.end;
@@ -1513,7 +1564,7 @@ impl EditorElement {
         &mut self,
         bounds: Bounds<Pixels>,
         layout: &mut LayoutState,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let scroll_position = layout.position_map.snapshot.scroll_position();
         let scroll_left = scroll_position.x * layout.position_map.em_width;
@@ -1763,7 +1814,7 @@ impl EditorElement {
         }
     }
 
-    fn compute_layout(&mut self, bounds: Bounds<Pixels>, cx: &mut WindowContext) -> LayoutState {
+    fn compute_layout(&mut self, bounds: Bounds<Pixels>, cx: &mut ElementContext) -> LayoutState {
         self.editor.update(cx, |editor, cx| {
             let snapshot = editor.snapshot(cx);
             let style = self.style.clone();
@@ -1783,30 +1834,16 @@ impl EditorElement {
                 .unwrap()
                 .width;
 
-            let gutter_padding;
-            let gutter_width;
-            let gutter_margin;
-            if snapshot.show_gutter {
-                let descent = cx.text_system().descent(font_id, font_size);
+            let gutter_dimensions = snapshot.gutter_dimensions(font_id, font_size, em_width, self.max_line_number_width(&snapshot, cx), cx);
 
-                let gutter_padding_factor = 3.5;
-                gutter_padding = (em_width * gutter_padding_factor).round();
-                gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0;
-                gutter_margin = -descent;
-            } else {
-                gutter_padding = Pixels::ZERO;
-                gutter_width = Pixels::ZERO;
-                gutter_margin = Pixels::ZERO;
-            };
-
-            editor.gutter_width = gutter_width;
+            editor.gutter_width = gutter_dimensions.width;
 
-            let text_width = bounds.size.width - gutter_width;
+            let text_width = bounds.size.width - gutter_dimensions.width;
             let overscroll = size(em_width, px(0.));
             let _snapshot = {
                 editor.set_visible_line_count((bounds.size.height / line_height).into(), cx);
 
-                let editor_width = text_width - gutter_margin - overscroll.width - em_width;
+                let editor_width = text_width - gutter_dimensions.margin - overscroll.width - em_width;
                 let wrap_width = match editor.soft_wrap_mode(cx) {
                     SoftWrap::None => (MAX_LINE_LEN / 2) as f32 * em_advance,
                     SoftWrap::EditorWidth => editor_width,
@@ -1826,7 +1863,7 @@ impl EditorElement {
                 .map(|(guide, active)| (self.column_pixels(*guide, cx), *active))
                 .collect::<SmallVec<[_; 2]>>();
 
-            let gutter_size = size(gutter_width, bounds.size.height);
+            let gutter_size = size(gutter_dimensions.width, bounds.size.height);
             let text_size = size(text_width, bounds.size.height);
 
             let autoscroll_horizontally =
@@ -1889,6 +1926,7 @@ impl EditorElement {
                         &snapshot.display_snapshot,
                         is_newest,
                         true,
+                        None,
                     );
                     if is_newest {
                         newest_selection_head = Some(layout.head);
@@ -1949,6 +1987,7 @@ impl EditorElement {
                     if Some(selection.peer_id) == editor.leader_peer_id {
                         continue;
                     }
+                    let is_shown = editor.show_cursor_names || editor.hovered_cursor.as_ref().is_some_and(|c| c.replica_id == selection.replica_id && c.selection_id == selection.selection.id);
 
                     remote_selections
                         .entry(selection.replica_id)
@@ -1961,6 +2000,11 @@ impl EditorElement {
                             &snapshot.display_snapshot,
                             false,
                             false,
+                            if is_shown {
+                                selection.user_name
+                            } else {
+                                None
+                            },
                         ));
                 }
 
@@ -1992,6 +2036,7 @@ impl EditorElement {
                     &snapshot.display_snapshot,
                     true,
                     true,
+                    None,
                 )
                 .head
             });
@@ -2022,22 +2067,26 @@ impl EditorElement {
                 .width;
             let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.width;
 
-            let (scroll_width, blocks) = cx.with_element_id(Some("editor_blocks"), |cx| {
+            let editor_view = cx.view().clone();
+            let (scroll_width, blocks) = cx.with_element_context(|cx| {
+             cx.with_element_id(Some("editor_blocks"), |cx| {
                 self.layout_blocks(
                     start_row..end_row,
                     &snapshot,
                     bounds.size.width,
                     scroll_width,
-                    gutter_padding,
-                    gutter_width,
+                    gutter_dimensions.padding,
+                    gutter_dimensions.width,
                     em_width,
-                    gutter_width + gutter_margin,
+                    gutter_dimensions.width + gutter_dimensions.margin,
                     line_height,
                     &style,
                     &line_layouts,
                     editor,
+                    editor_view,
                     cx,
                 )
+            })
             });
 
             let scroll_max = point(
@@ -2069,7 +2118,13 @@ impl EditorElement {
             if let Some(newest_selection_head) = newest_selection_head {
                 if (start_row..end_row).contains(&newest_selection_head.row()) {
                     if editor.context_menu_visible() {
-                        let max_height = (12. * line_height).min((bounds.size.height - line_height) / 2.);
+                        let max_height = cmp::min(
+                            12. * line_height,
+                            cmp::max(
+                                3. * line_height,
+                                (bounds.size.height - line_height) / 2.,
+                            )
+                        );
                         context_menu =
                             editor.render_context_menu(newest_selection_head, &self.style, max_height, cx);
                     }
@@ -2107,15 +2162,19 @@ impl EditorElement {
                 cx,
             );
 
-            let fold_indicators = cx.with_element_id(Some("gutter_fold_indicators"), |cx| {
+            let editor_view = cx.view().clone();
+            let fold_indicators = cx.with_element_context(|cx| {
+
+                cx.with_element_id(Some("gutter_fold_indicators"), |_cx| {
                 editor.render_fold_indicators(
                     fold_statuses,
                     &style,
                     editor.gutter_hovered,
                     line_height,
-                    gutter_margin,
-                    cx,
+                    gutter_dimensions.margin,
+                    editor_view,
                 )
+            })
             });
 
             let invisible_symbol_font_size = font_size / 2.;
@@ -2167,13 +2226,13 @@ impl EditorElement {
                 visible_display_row_range: start_row..end_row,
                 wrap_guides,
                 gutter_size,
-                gutter_padding,
+                gutter_padding: gutter_dimensions.padding,
                 text_size,
                 scrollbar_row_range,
                 show_scrollbars,
                 is_singleton,
                 max_row,
-                gutter_margin,
+                gutter_margin: gutter_dimensions.margin,
                 active_rows,
                 highlighted_rows,
                 highlighted_ranges,
@@ -2206,7 +2265,8 @@ impl EditorElement {
         style: &EditorStyle,
         line_layouts: &[LineWithInvisibles],
         editor: &mut Editor,
-        cx: &mut ViewContext<Editor>,
+        editor_view: View<Editor>,
+        cx: &mut ElementContext,
     ) -> (Pixels, Vec<BlockLayout>) {
         let mut block_id = 0;
         let (fixed_blocks, non_fixed_blocks) = snapshot
@@ -2220,7 +2280,7 @@ impl EditorElement {
                             available_space: Size<AvailableSpace>,
                             block_id: usize,
                             editor: &mut Editor,
-                            cx: &mut ViewContext<Editor>| {
+                            cx: &mut ElementContext| {
             let mut element = match block {
                 TransformBlock::Custom(block) => {
                     let align_to = block
@@ -2239,13 +2299,14 @@ impl EditorElement {
                         };
 
                     block.render(&mut BlockContext {
-                        view_context: cx,
+                        context: cx,
                         anchor_x,
                         gutter_padding,
                         line_height,
                         gutter_width,
                         em_width,
                         block_id,
+                        view: editor_view.clone(),
                         editor_style: &self.style,
                     })
                 }
@@ -2437,7 +2498,7 @@ impl EditorElement {
         &mut self,
         interactive_bounds: &InteractiveBounds,
         layout: &LayoutState,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         cx.on_mouse_event({
             let position_map = layout.position_map.clone();
@@ -2497,7 +2558,7 @@ impl EditorElement {
         gutter_bounds: Bounds<Pixels>,
         text_bounds: Bounds<Pixels>,
         layout: &LayoutState,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let interactive_bounds = InteractiveBounds {
             bounds: bounds.intersect(&cx.content_mask().bounds),
@@ -2720,7 +2781,7 @@ impl LineWithInvisibles {
         content_origin: gpui::Point<Pixels>,
         whitespace_setting: ShowWhitespaceSetting,
         selection_ranges: &[Range<DisplayPoint>],
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let line_height = layout.position_map.line_height;
         let line_y = line_height * row as f32 - layout.position_map.scroll_position.y;
@@ -2754,7 +2815,7 @@ impl LineWithInvisibles {
         row: u32,
         line_height: Pixels,
         whitespace_setting: ShowWhitespaceSetting,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let allowed_invisibles_regions = match whitespace_setting {
             ShowWhitespaceSetting::None => return,
@@ -2803,7 +2864,7 @@ impl Element for EditorElement {
     fn request_layout(
         &mut self,
         _element_state: Option<Self::State>,
-        cx: &mut gpui::WindowContext,
+        cx: &mut gpui::ElementContext,
     ) -> (gpui::LayoutId, Self::State) {
         cx.with_view_id(self.editor.entity_id(), |cx| {
             self.editor.update(cx, |editor, cx| {
@@ -2815,34 +2876,36 @@ impl Element for EditorElement {
                         let mut style = Style::default();
                         style.size.width = relative(1.).into();
                         style.size.height = self.style.text.line_height_in_pixels(rem_size).into();
-                        cx.request_layout(&style, None)
+                        cx.with_element_context(|cx| cx.request_layout(&style, None))
                     }
                     EditorMode::AutoHeight { max_lines } => {
                         let editor_handle = cx.view().clone();
                         let max_line_number_width =
                             self.max_line_number_width(&editor.snapshot(cx), cx);
-                        cx.request_measured_layout(
-                            Style::default(),
-                            move |known_dimensions, _, cx| {
-                                editor_handle
-                                    .update(cx, |editor, cx| {
-                                        compute_auto_height_layout(
-                                            editor,
-                                            max_lines,
-                                            max_line_number_width,
-                                            known_dimensions,
-                                            cx,
-                                        )
-                                    })
-                                    .unwrap_or_default()
-                            },
-                        )
+                        cx.with_element_context(|cx| {
+                            cx.request_measured_layout(
+                                Style::default(),
+                                move |known_dimensions, _, cx| {
+                                    editor_handle
+                                        .update(cx, |editor, cx| {
+                                            compute_auto_height_layout(
+                                                editor,
+                                                max_lines,
+                                                max_line_number_width,
+                                                known_dimensions,
+                                                cx,
+                                            )
+                                        })
+                                        .unwrap_or_default()
+                                },
+                            )
+                        })
                     }
                     EditorMode::Full => {
                         let mut style = Style::default();
                         style.size.width = relative(1.).into();
                         style.size.height = relative(1.).into();
-                        cx.request_layout(&style, None)
+                        cx.with_element_context(|cx| cx.request_layout(&style, None))
                     }
                 };
 
@@ -2855,7 +2918,7 @@ impl Element for EditorElement {
         &mut self,
         bounds: Bounds<gpui::Pixels>,
         _element_state: &mut Self::State,
-        cx: &mut gpui::WindowContext,
+        cx: &mut gpui::ElementContext,
     ) {
         let editor = self.editor.clone();
 
@@ -2884,9 +2947,10 @@ impl Element for EditorElement {
                         self.register_key_listeners(cx);
 
                         cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
-                            let input_handler =
-                                ElementInputHandler::new(bounds, self.editor.clone(), cx);
-                            cx.handle_input(&focus_handle, input_handler);
+                            cx.handle_input(
+                                &focus_handle,
+                                ElementInputHandler::new(bounds, self.editor.clone()),
+                            );
 
                             self.paint_background(gutter_bounds, text_bounds, &layout, cx);
                             if layout.gutter_size.width > Pixels::ZERO {
@@ -3097,6 +3161,15 @@ pub struct Cursor {
     color: Hsla,
     shape: CursorShape,
     block_text: Option<ShapedLine>,
+    cursor_name: Option<CursorName>,
+}
+
+#[derive(Debug)]
+pub struct CursorName {
+    string: SharedString,
+    color: Hsla,
+    is_top_row: bool,
+    z_index: u16,
 }
 
 impl Cursor {
@@ -3107,6 +3180,7 @@ impl Cursor {
         color: Hsla,
         shape: CursorShape,
         block_text: Option<ShapedLine>,
+        cursor_name: Option<CursorName>,
     ) -> Cursor {
         Cursor {
             origin,
@@ -3115,6 +3189,7 @@ impl Cursor {
             color,
             shape,
             block_text,
+            cursor_name,
         }
     }
 
@@ -3125,7 +3200,7 @@ impl Cursor {
         }
     }
 
-    pub fn paint(&self, origin: gpui::Point<Pixels>, cx: &mut WindowContext) {
+    pub fn paint(&self, origin: gpui::Point<Pixels>, cx: &mut ElementContext) {
         let bounds = match self.shape {
             CursorShape::Bar => Bounds {
                 origin: self.origin + origin,
@@ -3150,6 +3225,31 @@ impl Cursor {
             fill(bounds, self.color)
         };
 
+        if let Some(name) = &self.cursor_name {
+            let text_size = self.line_height / 1.5;
+
+            let name_origin = if name.is_top_row {
+                point(bounds.right() - px(1.), bounds.top())
+            } else {
+                point(bounds.left(), bounds.top() - text_size / 2. - px(1.))
+            };
+            cx.with_z_index(name.z_index, |cx| {
+                div()
+                    .bg(self.color)
+                    .text_size(text_size)
+                    .px_0p5()
+                    .line_height(text_size + px(2.))
+                    .text_color(name.color)
+                    .child(name.string.clone())
+                    .into_any_element()
+                    .draw(
+                        name_origin,
+                        size(AvailableSpace::MinContent, AvailableSpace::MinContent),
+                        cx,
+                    )
+            })
+        }
+
         cx.paint_quad(cursor);
 
         if let Some(block_text) = &self.block_text {
@@ -3180,7 +3280,7 @@ pub struct HighlightedRangeLine {
 }
 
 impl HighlightedRange {
-    pub fn paint(&self, bounds: Bounds<Pixels>, cx: &mut WindowContext) {
+    pub fn paint(&self, bounds: Bounds<Pixels>, cx: &mut ElementContext) {
         if self.lines.len() >= 2 && self.lines[0].start_x > self.lines[1].end_x {
             self.paint_lines(self.start_y, &self.lines[0..1], bounds, cx);
             self.paint_lines(
@@ -3199,7 +3299,7 @@ impl HighlightedRange {
         start_y: Pixels,
         lines: &[HighlightedRangeLine],
         _bounds: Bounds<Pixels>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         if lines.is_empty() {
             return;
@@ -3417,14 +3517,16 @@ mod tests {
             .unwrap();
         let state = cx
             .update_window(window.into(), |view, cx| {
-                cx.with_view_id(view.entity_id(), |cx| {
-                    element.compute_layout(
-                        Bounds {
-                            origin: point(px(500.), px(500.)),
-                            size: size(px(500.), px(500.)),
-                        },
-                        cx,
-                    )
+                cx.with_element_context(|cx| {
+                    cx.with_view_id(view.entity_id(), |cx| {
+                        element.compute_layout(
+                            Bounds {
+                                origin: point(px(500.), px(500.)),
+                                size: size(px(500.), px(500.)),
+                            },
+                            cx,
+                        )
+                    })
                 })
             })
             .unwrap();
@@ -3511,14 +3613,16 @@ mod tests {
 
         let state = cx
             .update_window(window.into(), |view, cx| {
-                cx.with_view_id(view.entity_id(), |cx| {
-                    element.compute_layout(
-                        Bounds {
-                            origin: point(px(500.), px(500.)),
-                            size: size(px(500.), px(500.)),
-                        },
-                        cx,
-                    )
+                cx.with_element_context(|cx| {
+                    cx.with_view_id(view.entity_id(), |cx| {
+                        element.compute_layout(
+                            Bounds {
+                                origin: point(px(500.), px(500.)),
+                                size: size(px(500.), px(500.)),
+                            },
+                            cx,
+                        )
+                    })
                 })
             })
             .unwrap();
@@ -3575,14 +3679,16 @@ mod tests {
         let mut element = EditorElement::new(&editor, style);
         let state = cx
             .update_window(window.into(), |view, cx| {
-                cx.with_view_id(view.entity_id(), |cx| {
-                    element.compute_layout(
-                        Bounds {
-                            origin: point(px(500.), px(500.)),
-                            size: size(px(500.), px(500.)),
-                        },
-                        cx,
-                    )
+                cx.with_element_context(|cx| {
+                    cx.with_view_id(view.entity_id(), |cx| {
+                        element.compute_layout(
+                            Bounds {
+                                origin: point(px(500.), px(500.)),
+                                size: size(px(500.), px(500.)),
+                            },
+                            cx,
+                        )
+                    })
                 })
             })
             .unwrap();
@@ -3600,8 +3706,10 @@ mod tests {
 
         // Don't panic.
         let bounds = Bounds::<Pixels>::new(Default::default(), size);
-        cx.update_window(window.into(), |_, cx| element.paint(bounds, &mut (), cx))
-            .unwrap()
+        cx.update_window(window.into(), |_, cx| {
+            cx.with_element_context(|cx| element.paint(bounds, &mut (), cx))
+        })
+        .unwrap()
     }
 
     #[gpui::test]
@@ -3776,13 +3884,15 @@ mod tests {
             .unwrap();
         let layout_state = cx
             .update_window(window.into(), |_, cx| {
-                element.compute_layout(
-                    Bounds {
-                        origin: point(px(500.), px(500.)),
-                        size: size(px(500.), px(500.)),
-                    },
-                    cx,
-                )
+                cx.with_element_context(|cx| {
+                    element.compute_layout(
+                        Bounds {
+                            origin: point(px(500.), px(500.)),
+                            size: size(px(500.), px(500.)),
+                        },
+                        cx,
+                    )
+                })
             })
             .unwrap();
 
@@ -3837,24 +3947,14 @@ fn compute_auto_height_layout(
         .width;
 
     let mut snapshot = editor.snapshot(cx);
-    let gutter_width;
-    let gutter_margin;
-    if snapshot.show_gutter {
-        let descent = cx.text_system().descent(font_id, font_size);
-        let gutter_padding_factor = 3.5;
-        let gutter_padding = (em_width * gutter_padding_factor).round();
-        gutter_width = max_line_number_width + gutter_padding * 2.0;
-        gutter_margin = -descent;
-    } else {
-        gutter_width = Pixels::ZERO;
-        gutter_margin = Pixels::ZERO;
-    };
+    let gutter_dimensions =
+        snapshot.gutter_dimensions(font_id, font_size, em_width, max_line_number_width, cx);
 
-    editor.gutter_width = gutter_width;
-    let text_width = width - gutter_width;
+    editor.gutter_width = gutter_dimensions.width;
+    let text_width = width - gutter_dimensions.width;
     let overscroll = size(em_width, px(0.));
 
-    let editor_width = text_width - gutter_margin - overscroll.width - em_width;
+    let editor_width = text_width - gutter_dimensions.margin - overscroll.width - em_width;
     if editor.set_wrap_width(Some(editor_width), cx) {
         snapshot = editor.snapshot(cx);
     }

crates/editor/src/hover_popover.rs ๐Ÿ”—

@@ -247,7 +247,7 @@ fn show_hover(
             };
 
             // query the LSP for hover info
-            let hover_request = cx.update(|_, cx| {
+            let hover_request = cx.update(|cx| {
                 project.update(cx, |project, cx| {
                     project.hover(&buffer, buffer_position, cx)
                 })
@@ -545,6 +545,7 @@ impl DiagnosticPopover {
 
         div()
             .id("diagnostic")
+            .elevation_2(cx)
             .overflow_y_scroll()
             .px_2()
             .py_1()

crates/fs/src/repository.rs ๐Ÿ”—

@@ -72,7 +72,7 @@ impl GitRepository for LibGitRepository {
             // This check is required because index.get_path() unwraps internally :(
             check_path_to_repo_path_errors(relative_file_path)?;
 
-            let oid = match index.get_path(&relative_file_path, STAGE_NORMAL) {
+            let oid = match index.get_path(relative_file_path, STAGE_NORMAL) {
                 Some(entry) => entry.id,
                 None => return Ok(None),
             };
@@ -81,7 +81,7 @@ impl GitRepository for LibGitRepository {
             Ok(Some(String::from_utf8(content)?))
         }
 
-        match logic(&self, relative_file_path) {
+        match logic(self, relative_file_path) {
             Ok(value) => return value,
             Err(err) => log::error!("Error loading head text: {:?}", err),
         }
@@ -199,7 +199,7 @@ impl GitRepository for LibGitRepository {
 
 fn matches_index(repo: &LibGitRepository, path: &RepoPath, mtime: SystemTime) -> bool {
     if let Some(index) = repo.index().log_err() {
-        if let Some(entry) = index.get_path(&path, 0) {
+        if let Some(entry) = index.get_path(path, 0) {
             if let Some(mtime) = mtime.duration_since(SystemTime::UNIX_EPOCH).log_err() {
                 if entry.mtime.seconds() == mtime.as_secs() as i32
                     && entry.mtime.nanoseconds() == mtime.subsec_nanos()

crates/git/src/diff.rs ๐Ÿ”—

@@ -165,7 +165,7 @@ impl BufferDiff {
         let mut tree = SumTree::new();
 
         let buffer_text = buffer.as_rope().to_string();
-        let patch = Self::diff(&diff_base, &buffer_text);
+        let patch = Self::diff(diff_base, &buffer_text);
 
         if let Some(patch) = patch {
             let mut divergence = 0;

crates/gpui/Cargo.toml ๐Ÿ”—

@@ -3,7 +3,7 @@ name = "gpui"
 version = "0.1.0"
 edition = "2021"
 authors = ["Nathan Sobo <nathan@zed.dev>"]
-description = "The next version of Zed's GPU-accelerated UI framework"
+description = "Zed's GPU-accelerated UI framework"
 publish = false
 
 [features]

crates/gpui/src/action.rs ๐Ÿ”—

@@ -40,14 +40,25 @@ use std::any::{Any, TypeId};
 /// register_action!(Paste);
 /// ```
 pub trait Action: 'static {
+    /// Clone the action into a new box
     fn boxed_clone(&self) -> Box<dyn Action>;
+
+    /// Cast the action to the any type
     fn as_any(&self) -> &dyn Any;
+
+    /// Do a partial equality check on this action and the other
     fn partial_eq(&self, action: &dyn Action) -> bool;
+
+    /// Get the name of this action, for displaying in UI
     fn name(&self) -> &str;
 
+    /// Get the name of this action for debugging
     fn debug_name() -> &'static str
     where
         Self: Sized;
+
+    /// Build this action from a JSON value. This is used to construct actions from the keymap.
+    /// A value of `{}` will be passed for actions that don't have any parameters.
     fn build(value: serde_json::Value) -> Result<Box<dyn Action>>
     where
         Self: Sized;
@@ -62,6 +73,7 @@ impl std::fmt::Debug for dyn Action {
 }
 
 impl dyn Action {
+    /// Get the type id of this action
     pub fn type_id(&self) -> TypeId {
         self.as_any().type_id()
     }
@@ -170,6 +182,7 @@ impl ActionRegistry {
 macro_rules! actions {
     ($namespace:path, [ $($name:ident),* $(,)? ]) => {
         $(
+            /// The `$name` action see [`gpui::actions!`]
             #[derive(::std::cmp::PartialEq, ::std::clone::Clone, ::std::default::Default, ::std::fmt::Debug, gpui::private::serde_derive::Deserialize)]
             #[serde(crate = "gpui::private::serde")]
             pub struct $name;

crates/gpui/src/app.rs ๐Ÿ”—

@@ -1,5 +1,3 @@
-#![deny(missing_docs)]
-
 mod async_context;
 mod entity_map;
 mod model_context;
@@ -864,7 +862,7 @@ impl AppContext {
             .unwrap()
     }
 
-    /// Set the value of the global of the given type.
+    /// Sets the value of the global of the given type.
     pub fn set_global<G: Any>(&mut self, global: G) {
         let global_type = TypeId::of::<G>();
         self.push_effect(Effect::NotifyGlobalObservers { global_type });
@@ -889,7 +887,7 @@ impl AppContext {
             .unwrap()
     }
 
-    /// Update the global of the given type with a closure. Unlike `global_mut`, this method provides
+    /// Updates the global of the given type with a closure. Unlike `global_mut`, this method provides
     /// your closure with mutable access to the `AppContext` and the global simultaneously.
     pub fn update_global<G: 'static, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R {
         self.update(|cx| {
@@ -1106,7 +1104,7 @@ impl AppContext {
             .contains_key(&action.as_any().type_id())
     }
 
-    /// Set the menu bar for this application. This will replace any existing menu bar.
+    /// Sets the menu bar for this application. This will replace any existing menu bar.
     pub fn set_menus(&mut self, menus: Vec<Menu>) {
         self.platform.set_menus(menus, &self.keymap.lock());
     }
@@ -1190,7 +1188,7 @@ impl Context for AppContext {
         })
     }
 
-    /// Update the entity referenced by the given model. The function is passed a mutable reference to the
+    /// Updates the entity referenced by the given model. The function is passed a mutable reference to the
     /// entity along with a `ModelContext` for the entity.
     fn update_model<T: 'static, R>(
         &mut self,

crates/gpui/src/app/async_context.rs ๐Ÿ”—

@@ -213,7 +213,12 @@ impl AsyncWindowContext {
     }
 
     /// A convenience method for [WindowContext::update()]
-    pub fn update<R>(
+    pub fn update<R>(&mut self, update: impl FnOnce(&mut WindowContext) -> R) -> Result<R> {
+        self.app.update_window(self.window, |_, cx| update(cx))
+    }
+
+    /// A convenience method for [WindowContext::update()]
+    pub fn update_root<R>(
         &mut self,
         update: impl FnOnce(AnyView, &mut WindowContext) -> R,
     ) -> Result<R> {

crates/gpui/src/app/entity_map.rs ๐Ÿ”—

@@ -104,7 +104,7 @@ impl EntityMap {
         }
     }
 
-    /// Return an entity after moving it to the stack.
+    /// 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());
@@ -391,7 +391,7 @@ impl<T: 'static> Model<T> {
         cx.read_model(self, f)
     }
 
-    /// Update the entity referenced by this model with the given function.
+    /// Updates the entity referenced by this model with the given function.
     ///
     /// The update function receives a context appropriate for its environment.
     /// When updating in an `AppContext`, it receives a `ModelContext`.
@@ -571,7 +571,7 @@ impl<T: 'static> WeakModel<T> {
         Model::upgrade_from(self)
     }
 
-    /// Update the entity referenced by this model with the given function if
+    /// Updates 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 update<C, R>(

crates/gpui/src/app/model_context.rs ๐Ÿ”—

@@ -189,7 +189,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
         }
     }
 
-    /// Update the given global
+    /// Updates the given global
     pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
     where
         G: 'static,

crates/gpui/src/app/test_context.rs ๐Ÿ”—

@@ -1,5 +1,3 @@
-#![deny(missing_docs)]
-
 use crate::{
     Action, AnyElement, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
     AvailableSpace, BackgroundExecutor, Bounds, ClipboardItem, Context, Entity, EventEmitter,
@@ -352,7 +350,7 @@ impl TestAppContext {
     }
 
     /// Returns the `TestWindow` backing the given handle.
-    pub fn test_window(&self, window: AnyWindowHandle) -> TestWindow {
+    pub(crate) fn test_window(&self, window: AnyWindowHandle) -> TestWindow {
         self.app
             .borrow_mut()
             .windows
@@ -578,7 +576,7 @@ impl<'a> VisualTestContext {
         self.cx.update_window(self.window, |_, cx| f(cx)).unwrap()
     }
 
-    /// Create a new VisualTestContext. You would typically shadow the passed in
+    /// Creates a new VisualTestContext. You would typically shadow the passed in
     /// TestAppContext with this, as this is typically more useful.
     /// `let cx = VisualTestContext::from_window(window, cx);`
     pub fn from_window(window: AnyWindowHandle, cx: &TestAppContext) -> Self {
@@ -642,8 +640,11 @@ impl<'a> VisualTestContext {
                 .as_ref()
                 .expect("Can't draw to this window without a root view")
                 .entity_id();
-            cx.with_view_id(entity_id, |cx| {
-                f(cx).draw(origin, space, cx);
+
+            cx.with_element_context(|cx| {
+                cx.with_view_id(entity_id, |cx| {
+                    f(cx).draw(origin, space, cx);
+                })
             });
 
             cx.refresh();

crates/gpui/src/assets.rs ๐Ÿ”—

@@ -8,8 +8,12 @@ use std::{
     sync::atomic::{AtomicUsize, Ordering::SeqCst},
 };
 
+/// A source of assets for this app to use.
 pub trait AssetSource: 'static + Send + Sync {
+    /// Load the given asset from the source path.
     fn load(&self, path: &str) -> Result<Cow<[u8]>>;
+
+    /// List the assets at the given path.
     fn list(&self, path: &str) -> Result<Vec<SharedString>>;
 }
 
@@ -26,15 +30,19 @@ impl AssetSource for () {
     }
 }
 
+/// A unique identifier for the image cache
 #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
 pub struct ImageId(usize);
 
+/// A cached and processed image.
 pub struct ImageData {
+    /// The ID associated with this image
     pub id: ImageId,
     data: ImageBuffer<Bgra<u8>, Vec<u8>>,
 }
 
 impl ImageData {
+    /// Create a new image from the given data.
     pub fn new(data: ImageBuffer<Bgra<u8>, Vec<u8>>) -> Self {
         static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
 
@@ -44,10 +52,12 @@ impl ImageData {
         }
     }
 
+    /// Convert this image into a byte slice.
     pub fn as_bytes(&self) -> &[u8] {
         &self.data
     }
 
+    /// Get the size of this image, in pixels
     pub fn size(&self) -> Size<DevicePixels> {
         let (width, height) = self.data.dimensions();
         size(width.into(), height.into())

crates/gpui/src/color.rs ๐Ÿ”—

@@ -2,6 +2,7 @@ use anyhow::bail;
 use serde::de::{self, Deserialize, Deserializer, Visitor};
 use std::fmt;
 
+/// Convert an RGB hex color code number to a color type
 pub fn rgb<C: From<Rgba>>(hex: u32) -> C {
     let r = ((hex >> 16) & 0xFF) as f32 / 255.0;
     let g = ((hex >> 8) & 0xFF) as f32 / 255.0;
@@ -9,6 +10,7 @@ pub fn rgb<C: From<Rgba>>(hex: u32) -> C {
     Rgba { r, g, b, a: 1.0 }.into()
 }
 
+/// Convert an RGBA hex color code number to [`Rgba`]
 pub fn rgba(hex: u32) -> Rgba {
     let r = ((hex >> 24) & 0xFF) as f32 / 255.0;
     let g = ((hex >> 16) & 0xFF) as f32 / 255.0;
@@ -17,11 +19,16 @@ pub fn rgba(hex: u32) -> Rgba {
     Rgba { r, g, b, a }
 }
 
+/// An RGBA color
 #[derive(PartialEq, Clone, Copy, Default)]
 pub struct Rgba {
+    /// The red component of the color, in the range 0.0 to 1.0
     pub r: f32,
+    /// The green component of the color, in the range 0.0 to 1.0
     pub g: f32,
+    /// The blue component of the color, in the range 0.0 to 1.0
     pub b: f32,
+    /// The alpha component of the color, in the range 0.0 to 1.0
     pub a: f32,
 }
 
@@ -32,6 +39,8 @@ impl fmt::Debug for Rgba {
 }
 
 impl Rgba {
+    /// Create a new [`Rgba`] color by blending this and another color together
+    /// TODO!(docs): find the source for this algorithm
     pub fn blend(&self, other: Rgba) -> Self {
         if other.a >= 1.0 {
             other
@@ -165,12 +174,20 @@ impl TryFrom<&'_ str> for Rgba {
     }
 }
 
+/// An HSLA color
 #[derive(Default, Copy, Clone, Debug)]
 #[repr(C)]
 pub struct Hsla {
+    /// Hue, in a range from 0 to 1
     pub h: f32,
+
+    /// Saturation, in a range from 0 to 1
     pub s: f32,
+
+    /// Lightness, in a range from 0 to 1
     pub l: f32,
+
+    /// Alpha, in a range from 0 to 1
     pub a: f32,
 }
 
@@ -203,38 +220,9 @@ impl Ord for Hsla {
     }
 }
 
-impl Hsla {
-    pub fn to_rgb(self) -> Rgba {
-        self.into()
-    }
-
-    pub fn red() -> Self {
-        red()
-    }
-
-    pub fn green() -> Self {
-        green()
-    }
-
-    pub fn blue() -> Self {
-        blue()
-    }
-
-    pub fn black() -> Self {
-        black()
-    }
-
-    pub fn white() -> Self {
-        white()
-    }
-
-    pub fn transparent_black() -> Self {
-        transparent_black()
-    }
-}
-
 impl Eq for Hsla {}
 
+/// Construct an [`Hsla`] object from plain values
 pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
     Hsla {
         h: h.clamp(0., 1.),
@@ -244,6 +232,7 @@ pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
     }
 }
 
+/// Pure black in [`Hsla`]
 pub fn black() -> Hsla {
     Hsla {
         h: 0.,
@@ -253,6 +242,7 @@ pub fn black() -> Hsla {
     }
 }
 
+/// Transparent black in [`Hsla`]
 pub fn transparent_black() -> Hsla {
     Hsla {
         h: 0.,
@@ -262,6 +252,7 @@ pub fn transparent_black() -> Hsla {
     }
 }
 
+/// Pure white in [`Hsla`]
 pub fn white() -> Hsla {
     Hsla {
         h: 0.,
@@ -271,6 +262,7 @@ pub fn white() -> Hsla {
     }
 }
 
+/// The color red in [`Hsla`]
 pub fn red() -> Hsla {
     Hsla {
         h: 0.,
@@ -280,6 +272,7 @@ pub fn red() -> Hsla {
     }
 }
 
+/// The color blue in [`Hsla`]
 pub fn blue() -> Hsla {
     Hsla {
         h: 0.6,
@@ -289,6 +282,7 @@ pub fn blue() -> Hsla {
     }
 }
 
+/// The color green in [`Hsla`]
 pub fn green() -> Hsla {
     Hsla {
         h: 0.33,
@@ -298,6 +292,7 @@ pub fn green() -> Hsla {
     }
 }
 
+/// The color yellow in [`Hsla`]
 pub fn yellow() -> Hsla {
     Hsla {
         h: 0.16,
@@ -308,6 +303,41 @@ pub fn yellow() -> Hsla {
 }
 
 impl Hsla {
+    /// Converts this HSLA color to an RGBA color.
+    pub fn to_rgb(self) -> Rgba {
+        self.into()
+    }
+
+    /// The color red
+    pub fn red() -> Self {
+        red()
+    }
+
+    /// The color green
+    pub fn green() -> Self {
+        green()
+    }
+
+    /// The color blue
+    pub fn blue() -> Self {
+        blue()
+    }
+
+    /// The color black
+    pub fn black() -> Self {
+        black()
+    }
+
+    /// The color white
+    pub fn white() -> Self {
+        white()
+    }
+
+    /// The color transparent black
+    pub fn transparent_black() -> Self {
+        transparent_black()
+    }
+
     /// Returns true if the HSLA color is fully transparent, false otherwise.
     pub fn is_transparent(&self) -> bool {
         self.a == 0.0
@@ -339,6 +369,7 @@ impl Hsla {
         }
     }
 
+    /// Returns a new HSLA color with the same hue, and lightness, but with no saturation.
     pub fn grayscale(&self) -> Self {
         Hsla {
             h: self.h,

crates/gpui/src/element.rs ๐Ÿ”—

@@ -1,26 +1,69 @@
+//! Elements are the workhorses of GPUI. They are responsible for laying out and painting all of
+//! the contents of a window. Elements form a tree and are laid out according to the web layout
+//! standards as implemented by [taffy](https://github.com/DioxusLabs/taffy). Most of the time,
+//! you won't need to interact with this module or these APIs directly. Elements provide their
+//! own APIs and GPUI, or other element implementation, uses the APIs in this module to convert
+//! that element tree into the pixels you see on the screen.
+//!
+//! # Element Basics
+//!
+//! Elements are constructed by calling [`Render::render()`] on the root view of the window, which
+//! which recursively constructs the element tree from the current state of the application,.
+//! These elements are then laid out by Taffy, and painted to the screen according to their own
+//! implementation of [`Element::paint()`]. Before the start of the next frame, the entire element
+//! tree and any callbacks they have registered with GPUI are dropped and the process repeats.
+//!
+//! But some state is too simple and voluminous to store in every view that needs it, e.g.
+//! whether a hover has been started or not. For this, GPUI provides the [`Element::State`], associated type.
+//! If an element returns an [`ElementId`] from [`IntoElement::element_id()`], and that element id
+//! appears in the same place relative to other views and ElementIds in the frame, then the previous
+//! frame's state will be passed to the element's layout and paint methods.
+//!
+//! # Implementing your own elements
+//!
+//! Elements are intended to be the low level, imperative API to GPUI. They are responsible for upholding,
+//! or breaking, GPUI's features as they deem necessary. As an example, most GPUI elements are expected
+//! to stay in the bounds that their parent element gives them. But with [`WindowContext::break_content_mask`],
+//! you can ignore this restriction and paint anywhere inside of the window's bounds. This is useful for overlays
+//! and popups and anything else that shows up 'on top' of other elements.
+//! With great power, comes great responsibility.
+//!
+//! However, most of the time, you won't need to implement your own elements. GPUI provides a number of
+//! elements that should cover most common use cases out of the box and it's recommended that you use those
+//! to construct `components`, using the [`RenderOnce`] trait and the `#[derive(IntoElement)]` macro. Only implement
+//! elements when you need to take manual control of the layout and painting process, such as when using
+//! your own custom layout algorithm or rendering a code editor.
+
 use crate::{
-    ArenaBox, AvailableSpace, BorrowWindow, Bounds, ElementId, LayoutId, Pixels, Point, Size,
-    ViewContext, WindowContext, ELEMENT_ARENA,
+    util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, ElementContext, ElementId, LayoutId,
+    Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA,
 };
 use derive_more::{Deref, DerefMut};
 pub(crate) use smallvec::SmallVec;
-use std::{any::Any, fmt::Debug};
+use std::{any::Any, fmt::Debug, ops::DerefMut};
 
 /// Implemented by types that participate in laying out and painting the contents of a window.
-/// Elements form a tree and are laid out according to web-based layout rules.
-/// Rather than calling methods on implementers of this trait directly, you'll usually call `into_any` to convert  them into an AnyElement, which manages state internally.
-/// You can create custom elements by implementing this trait.
+/// Elements form a tree and are laid out according to web-based layout rules, as implemented by Taffy.
+/// You can create custom elements by implementing this trait, see the module-level documentation
+/// for more details.
 pub trait Element: 'static + IntoElement {
+    /// The type of state to store for this element between frames. See the module-level documentation
+    /// for details.
     type State: 'static;
 
+    /// Before an element can be painted, we need to know where it's going to be and how big it is.
+    /// Use this method to request a layout from Taffy and initialize the element's state.
     fn request_layout(
         &mut self,
         state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State);
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext);
+    /// Once layout has been completed, this method will be called to paint the element to the screen.
+    /// The state argument is the same state that was returned from [`Element::request_layout()`].
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext);
 
+    /// Convert this element into a dynamically-typed [`AnyElement`].
     fn into_any(self) -> AnyElement {
         AnyElement::new(self)
     }
@@ -29,6 +72,7 @@ pub trait Element: 'static + IntoElement {
 /// Implemented by any type that can be converted into an element.
 pub trait IntoElement: Sized {
     /// The specific type of element into which the implementing type is converted.
+    /// Useful for converting other types into elements automatically, like Strings
     type Element: Element;
 
     /// The [`ElementId`] of self once converted into an [`Element`].
@@ -51,8 +95,8 @@ pub trait IntoElement: Sized {
         self,
         origin: Point<Pixels>,
         available_space: Size<T>,
-        cx: &mut WindowContext,
-        f: impl FnOnce(&mut <Self::Element as Element>::State, &mut WindowContext) -> R,
+        cx: &mut ElementContext,
+        f: impl FnOnce(&mut <Self::Element as Element>::State, &mut ElementContext) -> R,
     ) -> R
     where
         T: Clone + Default + Debug + Into<AvailableSpace>,
@@ -77,41 +121,14 @@ pub trait IntoElement: Sized {
             })
         }
     }
-
-    /// Convert self to another type by calling the given closure. Useful in rendering code.
-    fn map<U>(self, f: impl FnOnce(Self) -> U) -> U
-    where
-        Self: Sized,
-        U: IntoElement,
-    {
-        f(self)
-    }
-
-    /// Conditionally chain onto self with the given closure. Useful in rendering code.
-    fn when(self, condition: bool, then: impl FnOnce(Self) -> Self) -> Self
-    where
-        Self: Sized,
-    {
-        self.map(|this| if condition { then(this) } else { this })
-    }
-
-    /// Conditionally chain onto self with the given closure if the given option is Some.
-    /// The contents of the option are provided to the closure.
-    fn when_some<T>(self, option: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self
-    where
-        Self: Sized,
-    {
-        self.map(|this| {
-            if let Some(value) = option {
-                then(this, value)
-            } else {
-                this
-            }
-        })
-    }
 }
 
+impl<T: IntoElement> FluentBuilder for T {}
+
+/// An object that can be drawn to the screen. This is the trait that distinguishes `Views` from
+/// 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;
 }
 
@@ -122,35 +139,49 @@ impl Render for () {
 }
 
 /// You can derive [`IntoElement`] on any type that implements this trait.
-/// It is used to allow views to be expressed in terms of abstract data.
+/// It is used to construct reusable `components` out of plain data. Think of
+/// components as a recipe for a certain pattern of elements. RenderOnce allows
+/// you to invoke this pattern, without breaking the fluent builder pattern of
+/// the element APIs.
 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;
 }
 
+/// This is a helper trait to provide a uniform interface for constructing elements that
+/// can accept any number of any kind of child elements
 pub trait ParentElement {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]>;
+    /// Extend this element's children with the given child elements.
+    fn extend(&mut self, elements: impl Iterator<Item = AnyElement>);
 
+    /// Add a single child element to this element.
     fn child(mut self, child: impl IntoElement) -> Self
     where
         Self: Sized,
     {
-        self.children_mut().push(child.into_element().into_any());
+        self.extend(std::iter::once(child.into_element().into_any()));
         self
     }
 
+    /// Add multiple child elements to this element.
     fn children(mut self, children: impl IntoIterator<Item = impl IntoElement>) -> Self
     where
         Self: Sized,
     {
-        self.children_mut()
-            .extend(children.into_iter().map(|child| child.into_any_element()));
+        self.extend(children.into_iter().map(|child| child.into_any_element()));
         self
     }
 }
 
+/// An element for rendering components. An implementation detail of the [`IntoElement`] derive macro
+/// for [`RenderOnce`]
+#[doc(hidden)]
 pub struct Component<C: RenderOnce>(Option<C>);
 
 impl<C: RenderOnce> Component<C> {
+    /// Create a new component from the given RenderOnce type.
     pub fn new(component: C) -> Self {
         Component(Some(component))
     }
@@ -162,14 +193,19 @@ impl<C: RenderOnce> Element for Component<C> {
     fn request_layout(
         &mut self,
         _: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
-        let mut element = self.0.take().unwrap().render(cx).into_any_element();
+        let mut element = self
+            .0
+            .take()
+            .unwrap()
+            .render(cx.deref_mut())
+            .into_any_element();
         let layout_id = element.request_layout(cx);
         (layout_id, element)
     }
 
-    fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut ElementContext) {
         element.paint(cx)
     }
 }
@@ -186,31 +222,33 @@ impl<C: RenderOnce> IntoElement for Component<C> {
     }
 }
 
+/// A globally unique identifier for an element, used to track state across frames.
 #[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
-pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
+pub(crate) struct GlobalElementId(SmallVec<[ElementId; 32]>);
 
 trait ElementObject {
     fn element_id(&self) -> Option<ElementId>;
 
-    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId;
+    fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId;
 
-    fn paint(&mut self, cx: &mut WindowContext);
+    fn paint(&mut self, cx: &mut ElementContext);
 
     fn measure(
         &mut self,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> Size<Pixels>;
 
     fn draw(
         &mut self,
         origin: Point<Pixels>,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     );
 }
 
-pub struct DrawableElement<E: Element> {
+/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
+pub(crate) struct DrawableElement<E: Element> {
     element: Option<E>,
     phase: ElementDrawPhase<E::State>,
 }
@@ -243,7 +281,7 @@ impl<E: Element> DrawableElement<E> {
         self.element.as_ref()?.element_id()
     }
 
-    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
+    fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
         let (layout_id, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id()
         {
             let layout_id = cx.with_element_state(id, |element_state, cx| {
@@ -265,7 +303,7 @@ impl<E: Element> DrawableElement<E> {
         layout_id
     }
 
-    fn paint(mut self, cx: &mut WindowContext) -> Option<E::State> {
+    fn paint(mut self, cx: &mut ElementContext) -> Option<E::State> {
         match self.phase {
             ElementDrawPhase::LayoutRequested {
                 layout_id,
@@ -310,7 +348,7 @@ impl<E: Element> DrawableElement<E> {
     fn measure(
         &mut self,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> Size<Pixels> {
         if matches!(&self.phase, ElementDrawPhase::Start) {
             self.request_layout(cx);
@@ -351,7 +389,7 @@ impl<E: Element> DrawableElement<E> {
         mut self,
         origin: Point<Pixels>,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> Option<E::State> {
         self.measure(available_space, cx);
         cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
@@ -367,18 +405,18 @@ where
         self.as_ref().unwrap().element_id()
     }
 
-    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
+    fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
         DrawableElement::request_layout(self.as_mut().unwrap(), cx)
     }
 
-    fn paint(&mut self, cx: &mut WindowContext) {
+    fn paint(&mut self, cx: &mut ElementContext) {
         DrawableElement::paint(self.take().unwrap(), cx);
     }
 
     fn measure(
         &mut self,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> Size<Pixels> {
         DrawableElement::measure(self.as_mut().unwrap(), available_space, cx)
     }
@@ -387,16 +425,17 @@ where
         &mut self,
         origin: Point<Pixels>,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         DrawableElement::draw(self.take().unwrap(), origin, available_space, cx);
     }
 }
 
+/// A dynamically typed element that can be used to store any element type.
 pub struct AnyElement(ArenaBox<dyn ElementObject>);
 
 impl AnyElement {
-    pub fn new<E>(element: E) -> Self
+    pub(crate) fn new<E>(element: E) -> Self
     where
         E: 'static + Element,
         E::State: Any,
@@ -407,11 +446,14 @@ impl AnyElement {
         AnyElement(element)
     }
 
-    pub fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
+    /// 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 ElementContext) -> LayoutId {
         self.0.request_layout(cx)
     }
 
-    pub fn paint(&mut self, cx: &mut WindowContext) {
+    /// Paints the element stored in this `AnyElement`.
+    pub fn paint(&mut self, cx: &mut ElementContext) {
         self.0.paint(cx)
     }
 
@@ -419,7 +461,7 @@ impl AnyElement {
     pub fn measure(
         &mut self,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> Size<Pixels> {
         self.0.measure(available_space, cx)
     }
@@ -429,11 +471,12 @@ impl AnyElement {
         &mut self,
         origin: Point<Pixels>,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         self.0.draw(origin, available_space, cx)
     }
 
+    /// Returns the element ID of the element stored in this `AnyElement`, if any.
     pub fn inner_id(&self) -> Option<ElementId> {
         self.0.element_id()
     }
@@ -445,13 +488,13 @@ impl Element for AnyElement {
     fn request_layout(
         &mut self,
         _: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         let layout_id = self.request_layout(cx);
         (layout_id, ())
     }
 
-    fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut ElementContext) {
         self.paint(cx)
     }
 }
@@ -493,7 +536,7 @@ impl Element for () {
     fn request_layout(
         &mut self,
         _state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         (cx.request_layout(&crate::Style::default(), None), ())
     }
@@ -502,7 +545,7 @@ impl Element for () {
         &mut self,
         _bounds: Bounds<Pixels>,
         _state: &mut Self::State,
-        _cx: &mut WindowContext,
+        _cx: &mut ElementContext,
     ) {
     }
 }

crates/gpui/src/elements/canvas.rs ๐Ÿ”—

@@ -1,16 +1,20 @@
 use refineable::Refineable as _;
 
-use crate::{Bounds, Element, IntoElement, Pixels, Style, StyleRefinement, Styled, WindowContext};
+use crate::{Bounds, Element, ElementContext, IntoElement, Pixels, Style, StyleRefinement, Styled};
 
-pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut WindowContext)) -> Canvas {
+/// Construct a canvas element with the given paint callback.
+/// Useful for adding short term custom drawing to a view.
+pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut ElementContext)) -> Canvas {
     Canvas {
         paint_callback: Some(Box::new(callback)),
         style: StyleRefinement::default(),
     }
 }
 
+/// A canvas element, meant for accessing the low level paint API without defining a whole
+/// custom element
 pub struct Canvas {
-    paint_callback: Option<Box<dyn FnOnce(&Bounds<Pixels>, &mut WindowContext)>>,
+    paint_callback: Option<Box<dyn FnOnce(&Bounds<Pixels>, &mut ElementContext)>>,
     style: StyleRefinement,
 }
 
@@ -32,7 +36,7 @@ impl Element for Canvas {
     fn request_layout(
         &mut self,
         _: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (crate::LayoutId, Self::State) {
         let mut style = Style::default();
         style.refine(&self.style);
@@ -40,7 +44,7 @@ impl Element for Canvas {
         (layout_id, style)
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, style: &mut Style, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, style: &mut Style, cx: &mut ElementContext) {
         style.paint(bounds, cx, |cx| {
             (self.paint_callback.take().unwrap())(&bounds, cx)
         });

crates/gpui/src/elements/div.rs ๐Ÿ”—

@@ -1,6 +1,30 @@
+//! Div is the central, reusable element that most GPUI trees will be built from.
+//! It functions as a container for other elements, and provides a number of
+//! useful features for laying out and styling its children as well as binding
+//! mouse events and action handlers. It is meant to be similar to the HTML <div>
+//! element, but for GPUI.
+//!
+//! # Build your own div
+//!
+//! GPUI does not directly provide APIs for stateful, multi step events like `click`
+//! and `drag`. We want GPUI users to be able to build their own abstractions for
+//! their own needs. However, as a UI framework, we're also obliged to provide some
+//! building blocks to make the process of building your own elements easier.
+//! For this we have the [`Interactivity`] and the [`StyleRefinement`] structs, as well
+//! as several associated traits. Together, these provide the full suite of Dom-like events
+//! and Tailwind-like styling that you can use to build your own custom elements. Div is
+//! constructed by combining these two systems into an all-in-one element.
+//!
+//! # Capturing and bubbling
+//!
+//! Note that while event dispatch in GPUI uses similar names and concepts to the web
+//! even API, the details are very different. See the documentation in [TODO!(docs)
+//! DOCUMENT EVENT DISPATCH SOMEWHERE IN WINDOW CONTEXT] for more details
+//!
+
 use crate::{
-    point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext,
-    BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusHandle, IntoElement,
+    point, px, size, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, Bounds,
+    ClickEvent, DispatchPhase, Element, ElementContext, ElementId, FocusHandle, IntoElement,
     IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent,
     MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent,
     SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility,
@@ -17,6 +41,7 @@ use std::{
     fmt::Debug,
     marker::PhantomData,
     mem,
+    ops::DerefMut,
     rc::Rc,
     time::Duration,
 };
@@ -24,20 +49,30 @@ use taffy::style::Overflow;
 use util::ResultExt;
 
 const DRAG_THRESHOLD: f64 = 2.;
-const TOOLTIP_DELAY: Duration = Duration::from_millis(500);
+pub(crate) const TOOLTIP_DELAY: Duration = Duration::from_millis(500);
 
+/// The styling information for a given group.
 pub struct GroupStyle {
+    /// The identifier for this group.
     pub group: SharedString,
+
+    /// The specific style refinement that this group would apply
+    /// to its children.
     pub style: Box<StyleRefinement>,
 }
 
+/// An event for when a drag is moving over this element, with the given state type.
 pub struct DragMoveEvent<T> {
+    /// The mouse move event that triggered this drag move event.
     pub event: MouseMoveEvent,
+
+    /// The bounds of this element.
     pub bounds: Bounds<Pixels>,
     drag: PhantomData<T>,
 }
 
 impl<T: 'static> DragMoveEvent<T> {
+    /// Returns the drag state for this event.
     pub fn drag<'b>(&self, cx: &'b AppContext) -> &'b T {
         cx.active_drag
             .as_ref()
@@ -47,6 +82,10 @@ impl<T: 'static> DragMoveEvent<T> {
 }
 
 impl Interactivity {
+    /// Bind the given callback to the mouse down event for the given mouse button, during the bubble phase
+    /// The imperative API equivalent of [`InteractiveElement::on_mouse_down`]
+    ///
+    /// See [`ViewContext::listener()`] to get access to the view state from this callback
     pub fn on_mouse_down(
         &mut self,
         button: MouseButton,
@@ -63,6 +102,10 @@ impl Interactivity {
             }));
     }
 
+    /// Bind the given callback to the mouse down event for any button, during the capture phase
+    /// The imperative API equivalent of [`InteractiveElement::capture_any_mouse_down`]
+    ///
+    /// See [`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,
@@ -75,6 +118,10 @@ impl Interactivity {
             }));
     }
 
+    /// Bind the given callback to the mouse down event for any button, during the bubble phase
+    /// the imperative API equivalent to [`InteractiveElement::on_any_mouse_down()`]
+    ///
+    /// See [`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,
@@ -87,6 +134,10 @@ impl Interactivity {
             }));
     }
 
+    /// Bind the given callback to the mouse up event for the given button, during the bubble phase
+    /// the imperative API equivalent to [`InteractiveElement::on_mouse_up()`]
+    ///
+    /// See [`ViewContext::listener()`] to get access to a view's state from this callback
     pub fn on_mouse_up(
         &mut self,
         button: MouseButton,
@@ -103,6 +154,10 @@ impl Interactivity {
             }));
     }
 
+    /// Bind the given callback to the mouse up event for any button, during the capture phase
+    /// the imperative API equivalent to [`InteractiveElement::capture_any_mouse_up()`]
+    ///
+    /// See [`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,
@@ -115,6 +170,10 @@ impl Interactivity {
             }));
     }
 
+    /// Bind the given callback to the mouse up event for any button, during the bubble phase
+    /// the imperative API equivalent to [`InteractiveElement::on_any_mouse_up()`]
+    ///
+    /// See [`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,
@@ -127,6 +186,11 @@ impl Interactivity {
             }));
     }
 
+    /// Bind the given callback to the mouse down event, on any button, during the capture phase,
+    /// when the mouse is outside of the bounds of this element.
+    /// The imperative API equivalent to [`InteractiveElement::on_mouse_down_out()`]
+    ///
+    /// See [`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,
@@ -140,6 +204,11 @@ impl Interactivity {
             }));
     }
 
+    /// Bind the given callback to the mouse up event, for the given button, during the capture phase,
+    /// when the mouse is outside of the bounds of this element.
+    /// The imperative API equivalent to [`InteractiveElement::on_mouse_up_out()`]
+    ///
+    /// See [`ViewContext::listener()`] to get access to a view's state from this callback
     pub fn on_mouse_up_out(
         &mut self,
         button: MouseButton,
@@ -156,6 +225,10 @@ impl Interactivity {
             }));
     }
 
+    /// Bind the given callback to the mouse move event, during the bubble phase
+    /// The imperative API equivalent to [`InteractiveElement::on_mouse_move()`]
+    ///
+    /// See [`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,
@@ -168,6 +241,13 @@ impl Interactivity {
             }));
     }
 
+    /// Bind the given callback to the mouse drag event of the given type. Note that this
+    /// will be called for all move events, inside or outside of this element, as long as the
+    /// drag was started with this element under the mouse. Useful for implementing draggable
+    /// UIs that don't conform to a drag and drop style interaction, like resizing.
+    /// The imperative API equivalent to [`InteractiveElement::on_drag_move()`]
+    ///
+    /// See [`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,
@@ -194,6 +274,10 @@ impl Interactivity {
             }));
     }
 
+    /// Bind the given callback to scroll wheel events during the bubble phase
+    /// The imperative API equivalent to [`InteractiveElement::on_scroll_wheel()`]
+    ///
+    /// See [`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,
@@ -206,6 +290,10 @@ impl Interactivity {
             }));
     }
 
+    /// Bind the given callback to an action dispatch during the capture phase
+    /// The imperative API equivalent to [`InteractiveElement::capture_action()`]
+    ///
+    /// See [`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,
@@ -221,6 +309,10 @@ impl Interactivity {
         ));
     }
 
+    /// Bind the given callback to an action dispatch during the bubble phase
+    /// The imperative API equivalent to [`InteractiveElement::on_action()`]
+    ///
+    /// See [`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) {
         self.action_listeners.push((
             TypeId::of::<A>(),
@@ -233,6 +325,12 @@ impl Interactivity {
         ));
     }
 
+    /// Bind the given callback to an action dispatch, based on a dynamic action parameter
+    /// instead of a type parameter. Useful for component libraries that want to expose
+    /// action bindings to their users.
+    /// The imperative API equivalent to [`InteractiveElement::on_boxed_action()`]
+    ///
+    /// See [`ViewContext::listener()`] to get access to a view's state from this callback
     pub fn on_boxed_action(
         &mut self,
         action: &dyn Action,
@@ -249,6 +347,10 @@ impl Interactivity {
         ));
     }
 
+    /// Bind the given callback to key down events during the bubble phase
+    /// The imperative API equivalent to [`InteractiveElement::on_key_down()`]
+    ///
+    /// See [`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) {
         self.key_down_listeners
             .push(Box::new(move |event, phase, cx| {
@@ -258,6 +360,10 @@ impl Interactivity {
             }));
     }
 
+    /// Bind the given callback to key down events during the capture phase
+    /// The imperative API equivalent to [`InteractiveElement::capture_key_down()`]
+    ///
+    /// See [`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,
@@ -270,6 +376,10 @@ impl Interactivity {
             }));
     }
 
+    /// Bind the given callback to key up events during the bubble phase
+    /// The imperative API equivalent to [`InteractiveElement::on_key_up()`]
+    ///
+    /// See [`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) {
         self.key_up_listeners
             .push(Box::new(move |event, phase, cx| {
@@ -279,6 +389,10 @@ impl Interactivity {
             }));
     }
 
+    /// Bind the given callback to key up events during the capture phase
+    /// The imperative API equivalent to [`InteractiveElement::on_key_up()`]
+    ///
+    /// See [`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) {
         self.key_up_listeners
             .push(Box::new(move |event, phase, cx| {
@@ -288,6 +402,10 @@ impl Interactivity {
             }));
     }
 
+    /// 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()`] 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) {
         self.drop_listeners.push((
             TypeId::of::<T>(),
@@ -297,10 +415,16 @@ impl Interactivity {
         ));
     }
 
+    /// 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) {
         self.can_drop_predicate = Some(Box::new(predicate));
     }
 
+    /// Bind the given callback to click events of this element
+    /// The imperative API equivalent to [`InteractiveElement::on_click()`]
+    ///
+    /// See [`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)
     where
         Self: Sized,
@@ -309,6 +433,12 @@ impl Interactivity {
             .push(Box::new(move |event, cx| listener(event, cx)));
     }
 
+    /// On drag initiation, this callback will be used to create a new view to render the dragged value for a
+    /// drag and drop operation. This API should also be used as the equivalent of 'on drag start' with
+    /// the [`Self::on_drag_move()`] API
+    /// The imperative API equivalent to [`InteractiveElement::on_drag()`]
+    ///
+    /// See [`ViewContext::listener()`] to get access to a view's state from this callback
     pub fn on_drag<T, W>(
         &mut self,
         value: T,
@@ -328,6 +458,11 @@ impl Interactivity {
         ));
     }
 
+    /// Bind the given callback on the hover start and end events of this element. Note that the boolean
+    /// passed to the callback is true when the hover starts and false when it ends.
+    /// The imperative API equivalent to [`InteractiveElement::on_drag()`]
+    ///
+    /// See [`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)
     where
         Self: Sized,
@@ -339,6 +474,8 @@ impl Interactivity {
         self.hover_listener = Some(Box::new(listener));
     }
 
+    /// 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)
     where
         Self: Sized,
@@ -350,31 +487,43 @@ impl Interactivity {
         self.tooltip_builder = Some(Rc::new(build_tooltip));
     }
 
+    /// Block the mouse from interacting with this element or any of it's children
+    /// The imperative API equivalent to [`InteractiveElement::block_mouse()`]
     pub fn block_mouse(&mut self) {
         self.block_mouse = true;
     }
 }
 
+/// A trait for elements that want to use the standard GPUI event handlers that don't
+/// require any state.
 pub trait InteractiveElement: Sized {
+    /// Retrieve the interactivity state associated with this element
     fn interactivity(&mut self) -> &mut Interactivity;
 
+    /// Assign this element to a group of elements that can be styled together
     fn group(mut self, group: impl Into<SharedString>) -> Self {
         self.interactivity().group = Some(group.into());
         self
     }
 
+    /// Assign this elements
     fn id(mut self, id: impl Into<ElementId>) -> Stateful<Self> {
         self.interactivity().element_id = Some(id.into());
 
         Stateful { element: self }
     }
 
+    /// 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 it's focused styles.
     fn track_focus(mut self, focus_handle: &FocusHandle) -> Focusable<Self> {
         self.interactivity().focusable = true;
         self.interactivity().tracked_focus_handle = Some(focus_handle.clone());
         Focusable { element: self }
     }
 
+    /// Set the keymap context for this element. This will be used to determine
+    /// which action to dispatch from the keymap.
     fn key_context<C, E>(mut self, key_context: C) -> Self
     where
         C: TryInto<KeyContext, Error = E>,
@@ -386,6 +535,7 @@ pub trait InteractiveElement: Sized {
         self
     }
 
+    /// Apply the given style to this element when the mouse hovers over it
     fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
         debug_assert!(
             self.interactivity().hover_style.is_none(),
@@ -395,6 +545,7 @@ pub trait InteractiveElement: Sized {
         self
     }
 
+    /// Apply the given style to this element when the mouse hovers over a group member
     fn group_hover(
         mut self,
         group_name: impl Into<SharedString>,
@@ -407,6 +558,10 @@ pub trait InteractiveElement: Sized {
         self
     }
 
+    /// Bind the given callback to the mouse down event for the given mouse button,
+    /// the fluent API equivalent to [`Interactivity::on_mouse_down()`]
+    ///
+    /// See [`ViewContext::listener()`] to get access to the view state from this callback
     fn on_mouse_down(
         mut self,
         button: MouseButton,
@@ -417,17 +572,27 @@ pub trait InteractiveElement: Sized {
     }
 
     #[cfg(any(test, feature = "test-support"))]
+    /// Set a key that can be used to look up this element's bounds
+    /// in the [`VisualTestContext::debug_bounds()`] map
+    /// This is a noop in release builds
     fn debug_selector(mut self, f: impl FnOnce() -> String) -> Self {
         self.interactivity().debug_selector = Some(f());
         self
     }
 
     #[cfg(not(any(test, feature = "test-support")))]
+    /// Set a key that can be used to look up this element's bounds
+    /// in the [`VisualTestContext::debug_bounds()`] map
+    /// This is a noop in release builds
     #[inline]
     fn debug_selector(self, _: impl FnOnce() -> String) -> Self {
         self
     }
 
+    /// Bind the given callback to the mouse down event for any button, during the capture phase
+    /// the fluent API equivalent to [`Interactivity::capture_any_mouse_down()`]
+    ///
+    /// See [`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,
@@ -436,6 +601,10 @@ pub trait InteractiveElement: Sized {
         self
     }
 
+    /// Bind the given callback to the mouse down event for any button, during the capture phase
+    /// the fluent API equivalent to [`Interactivity::on_any_mouse_down()`]
+    ///
+    /// See [`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,
@@ -444,6 +613,10 @@ pub trait InteractiveElement: Sized {
         self
     }
 
+    /// Bind the given callback to the mouse up event for the given button, during the bubble phase
+    /// the fluent API equivalent to [`Interactivity::on_mouse_up()`]
+    ///
+    /// See [`ViewContext::listener()`] to get access to a view's state from this callback
     fn on_mouse_up(
         mut self,
         button: MouseButton,
@@ -453,6 +626,10 @@ pub trait InteractiveElement: Sized {
         self
     }
 
+    /// Bind the given callback to the mouse up event for any button, during the capture phase
+    /// the fluent API equivalent to [`Interactivity::capture_any_mouse_up()`]
+    ///
+    /// See [`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,
@@ -461,6 +638,11 @@ pub trait InteractiveElement: Sized {
         self
     }
 
+    /// Bind the given callback to the mouse down event, on any button, during the capture phase,
+    /// when the mouse is outside of the bounds of this element.
+    /// The fluent API equivalent to [`Interactivity::on_mouse_down_out()`]
+    ///
+    /// See [`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,
@@ -469,6 +651,11 @@ pub trait InteractiveElement: Sized {
         self
     }
 
+    /// Bind the given callback to the mouse up event, for the given button, during the capture phase,
+    /// when the mouse is outside of the bounds of this element.
+    /// The fluent API equivalent to [`Interactivity::on_mouse_up_out()`]
+    ///
+    /// See [`ViewContext::listener()`] to get access to a view's state from this callback
     fn on_mouse_up_out(
         mut self,
         button: MouseButton,
@@ -478,6 +665,10 @@ pub trait InteractiveElement: Sized {
         self
     }
 
+    /// Bind the given callback to the mouse move event, during the bubble phase
+    /// The fluent API equivalent to [`Interactivity::on_mouse_move()`]
+    ///
+    /// See [`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,
@@ -486,6 +677,13 @@ pub trait InteractiveElement: Sized {
         self
     }
 
+    /// Bind the given callback to the mouse drag event of the given type. Note that this
+    /// will be called for all move events, inside or outside of this element, as long as the
+    /// drag was started with this element under the mouse. Useful for implementing draggable
+    /// UIs that don't conform to a drag and drop style interaction, like resizing.
+    /// The fluent API equivalent to [`Interactivity::on_drag_move()`]
+    ///
+    /// See [`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,
@@ -497,6 +695,10 @@ pub trait InteractiveElement: Sized {
         self
     }
 
+    /// Bind the given callback to scroll wheel events during the bubble phase
+    /// The fluent API equivalent to [`Interactivity::on_scroll_wheel()`]
+    ///
+    /// See [`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,
@@ -506,6 +708,9 @@ pub trait InteractiveElement: Sized {
     }
 
     /// Capture the given action, before normal action dispatch can fire
+    /// The fluent API equivalent to [`Interactivity::on_scroll_wheel()`]
+    ///
+    /// See [`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,
@@ -514,12 +719,21 @@ pub trait InteractiveElement: Sized {
         self
     }
 
-    /// Add a listener for the given action, fires during the bubble event phase
+    /// Bind the given callback to an action dispatch during the bubble phase
+    /// The fluent API equivalent to [`Interactivity::on_action()`]
+    ///
+    /// See [`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 {
         self.interactivity().on_action(listener);
         self
     }
 
+    /// Bind the given callback to an action dispatch, based on a dynamic action parameter
+    /// instead of a type parameter. Useful for component libraries that want to expose
+    /// action bindings to their users.
+    /// The fluent API equivalent to [`Interactivity::on_boxed_action()`]
+    ///
+    /// See [`ViewContext::listener()`] to get access to a view's state from this callback
     fn on_boxed_action(
         mut self,
         action: &dyn Action,
@@ -529,6 +743,10 @@ pub trait InteractiveElement: Sized {
         self
     }
 
+    /// Bind the given callback to key down events during the bubble phase
+    /// The fluent API equivalent to [`Interactivity::on_key_down()`]
+    ///
+    /// See [`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,
@@ -537,6 +755,10 @@ pub trait InteractiveElement: Sized {
         self
     }
 
+    /// Bind the given callback to key down events during the capture phase
+    /// The fluent API equivalent to [`Interactivity::capture_key_down()`]
+    ///
+    /// See [`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,
@@ -545,11 +767,19 @@ pub trait InteractiveElement: Sized {
         self
     }
 
+    /// Bind the given callback to key up events during the bubble phase
+    /// The fluent API equivalent to [`Interactivity::on_key_up()`]
+    ///
+    /// See [`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 {
         self.interactivity().on_key_up(listener);
         self
     }
 
+    /// Bind the given callback to key up events during the capture phase
+    /// The fluent API equivalent to [`Interactivity::capture_key_up()`]
+    ///
+    /// See [`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,
@@ -558,6 +788,7 @@ pub trait InteractiveElement: Sized {
         self
     }
 
+    /// Apply the given style when the given data type is dragged over this element
     fn drag_over<S: 'static>(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
         self.interactivity()
             .drag_over_styles
@@ -565,6 +796,7 @@ pub trait InteractiveElement: Sized {
         self
     }
 
+    /// Apply the given style when the given data type is dragged over this element's group
     fn group_drag_over<S: 'static>(
         mut self,
         group_name: impl Into<SharedString>,
@@ -580,11 +812,17 @@ pub trait InteractiveElement: Sized {
         self
     }
 
+    /// Bind the given callback to drop events of the given type, whether or not the drag started on this element
+    /// The fluent API equivalent to [`Interactivity::on_drop()`]
+    ///
+    /// See [`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 {
         self.interactivity().on_drop(listener);
         self
     }
 
+    /// Use the given predicate to determine whether or not a drop event should be dispatched to this element
+    /// The fluent API equivalent to [`Interactivity::can_drop()`]
     fn can_drop(
         mut self,
         predicate: impl Fn(&dyn Any, &mut WindowContext) -> bool + 'static,
@@ -593,39 +831,49 @@ pub trait InteractiveElement: Sized {
         self
     }
 
+    /// Block the mouse from interacting with this element or any of it's children
+    /// The fluent API equivalent to [`Interactivity::block_mouse()`]
     fn block_mouse(mut self) -> Self {
         self.interactivity().block_mouse();
         self
     }
 }
 
+/// A trait for elements that want to use the standard GPUI interactivity features
+/// that require state.
 pub trait StatefulInteractiveElement: InteractiveElement {
+    /// Set this element to focusable.
     fn focusable(mut self) -> Focusable<Self> {
         self.interactivity().focusable = true;
         Focusable { element: self }
     }
 
+    /// Set the overflow x and y to scroll.
     fn overflow_scroll(mut self) -> Self {
         self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
         self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
         self
     }
 
+    /// Set the overflow x to scroll.
     fn overflow_x_scroll(mut self) -> Self {
         self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
         self
     }
 
+    /// Set the overflow y to scroll.
     fn overflow_y_scroll(mut self) -> Self {
         self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
         self
     }
 
+    /// Track the scroll state of this element with the given handle.
     fn track_scroll(mut self, scroll_handle: &ScrollHandle) -> Self {
         self.interactivity().scroll_handle = Some(scroll_handle.clone());
         self
     }
 
+    /// Set the given styles to be applied when this element is active.
     fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
     where
         Self: Sized,
@@ -634,6 +882,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
         self
     }
 
+    /// Set the given styles to be applied when this element's group is active.
     fn group_active(
         mut self,
         group_name: impl Into<SharedString>,
@@ -649,6 +898,10 @@ pub trait StatefulInteractiveElement: InteractiveElement {
         self
     }
 
+    /// Bind the given callback to click events of this element
+    /// The fluent API equivalent to [`Interactivity::on_click()`]
+    ///
+    /// See [`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
     where
         Self: Sized,
@@ -657,6 +910,12 @@ pub trait StatefulInteractiveElement: InteractiveElement {
         self
     }
 
+    /// On drag initiation, this callback will be used to create a new view to render the dragged value for a
+    /// drag and drop operation. This API should also be used as the equivalent of 'on drag start' with
+    /// the [`Self::on_drag_move()`] API
+    /// The fluent API equivalent to [`Interactivity::on_drag()`]
+    ///
+    /// See [`ViewContext::listener()`] to get access to a view's state from this callback
     fn on_drag<T, W>(
         mut self,
         value: T,
@@ -671,6 +930,11 @@ pub trait StatefulInteractiveElement: InteractiveElement {
         self
     }
 
+    /// Bind the given callback on the hover start and end events of this element. Note that the boolean
+    /// passed to the callback is true when the hover starts and false when it ends.
+    /// The fluent API equivalent to [`Interactivity::on_hover()`]
+    ///
+    /// See [`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
     where
         Self: Sized,
@@ -679,6 +943,8 @@ pub trait StatefulInteractiveElement: InteractiveElement {
         self
     }
 
+    /// 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
     where
         Self: Sized,
@@ -688,7 +954,9 @@ pub trait StatefulInteractiveElement: InteractiveElement {
     }
 }
 
+/// A trait for providing focus related APIs to interactive elements
 pub trait FocusableElement: InteractiveElement {
+    /// Set the given styles to be applied when this element, specifically, is focused.
     fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
     where
         Self: Sized,
@@ -697,6 +965,7 @@ pub trait FocusableElement: InteractiveElement {
         self
     }
 
+    /// Set the given styles to be applied when this element is inside another element that is focused.
     fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
     where
         Self: Sized,
@@ -706,35 +975,36 @@ pub trait FocusableElement: InteractiveElement {
     }
 }
 
-pub type MouseDownListener =
+pub(crate) type MouseDownListener =
     Box<dyn Fn(&MouseDownEvent, &InteractiveBounds, DispatchPhase, &mut WindowContext) + 'static>;
-pub type MouseUpListener =
+pub(crate) type MouseUpListener =
     Box<dyn Fn(&MouseUpEvent, &InteractiveBounds, DispatchPhase, &mut WindowContext) + 'static>;
 
-pub type MouseMoveListener =
+pub(crate) type MouseMoveListener =
     Box<dyn Fn(&MouseMoveEvent, &InteractiveBounds, DispatchPhase, &mut WindowContext) + 'static>;
 
-pub type ScrollWheelListener =
+pub(crate) type ScrollWheelListener =
     Box<dyn Fn(&ScrollWheelEvent, &InteractiveBounds, DispatchPhase, &mut WindowContext) + 'static>;
 
-pub type ClickListener = Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>;
+pub(crate) type ClickListener = Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>;
 
-pub type DragListener = Box<dyn Fn(&dyn Any, &mut WindowContext) -> AnyView + 'static>;
+pub(crate) type DragListener = Box<dyn Fn(&dyn Any, &mut WindowContext) -> AnyView + 'static>;
 
 type DropListener = Box<dyn Fn(&dyn Any, &mut WindowContext) + 'static>;
 
 type CanDropPredicate = Box<dyn Fn(&dyn Any, &mut WindowContext) -> bool + 'static>;
 
-pub type TooltipBuilder = Rc<dyn Fn(&mut WindowContext) -> AnyView + 'static>;
+pub(crate) type TooltipBuilder = Rc<dyn Fn(&mut WindowContext) -> AnyView + 'static>;
 
-pub type KeyDownListener = Box<dyn Fn(&KeyDownEvent, DispatchPhase, &mut WindowContext) + 'static>;
+pub(crate) type KeyDownListener =
+    Box<dyn Fn(&KeyDownEvent, DispatchPhase, &mut WindowContext) + 'static>;
 
-pub type KeyUpListener = Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut WindowContext) + 'static>;
+pub(crate) type KeyUpListener =
+    Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut WindowContext) + 'static>;
 
-pub type DragEventListener = Box<dyn Fn(&MouseMoveEvent, &mut WindowContext) + 'static>;
-
-pub type ActionListener = Box<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
+pub(crate) type ActionListener = Box<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
 
+/// Construct a new [`Div`] element
 #[track_caller]
 pub fn div() -> Div {
     #[cfg(debug_assertions)]
@@ -753,6 +1023,7 @@ pub fn div() -> Div {
     }
 }
 
+/// A [`Div`] element, the all-in-one element for building complex UIs in GPUI
 pub struct Div {
     interactivity: Interactivity,
     children: SmallVec<[AnyElement; 2]>,
@@ -771,8 +1042,8 @@ impl InteractiveElement for Div {
 }
 
 impl ParentElement for Div {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
-        &mut self.children
+    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
+        self.children.extend(elements)
     }
 }
 
@@ -782,7 +1053,7 @@ impl Element for Div {
     fn request_layout(
         &mut self,
         element_state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         let mut child_layout_ids = SmallVec::new();
         let (layout_id, interactive_state) = self.interactivity.layout(
@@ -812,7 +1083,7 @@ impl Element for Div {
         &mut self,
         bounds: Bounds<Pixels>,
         element_state: &mut Self::State,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let mut child_min = point(Pixels::MAX, Pixels::MAX);
         let mut child_max = Point::default();
@@ -875,12 +1146,14 @@ impl IntoElement for Div {
     }
 }
 
+/// The state a div needs to keep track of between frames.
 pub struct DivState {
     child_layout_ids: SmallVec<[LayoutId; 2]>,
     interactive_state: InteractiveElementState,
 }
 
 impl DivState {
+    /// Is the div currently being clicked on?
     pub fn is_active(&self) -> bool {
         self.interactive_state
             .pending_mouse_down
@@ -889,56 +1162,67 @@ impl DivState {
     }
 }
 
+/// The interactivity struct. Powers all of the general-purpose
+/// interactivity in the `Div` element.
 #[derive(Default)]
 pub struct Interactivity {
+    /// The element ID of the element
     pub element_id: Option<ElementId>,
-    pub key_context: Option<KeyContext>,
-    pub focusable: bool,
-    pub tracked_focus_handle: Option<FocusHandle>,
-    pub scroll_handle: Option<ScrollHandle>,
-    pub group: Option<SharedString>,
+    pub(crate) key_context: Option<KeyContext>,
+    pub(crate) focusable: bool,
+    pub(crate) tracked_focus_handle: Option<FocusHandle>,
+    pub(crate) scroll_handle: Option<ScrollHandle>,
+    pub(crate) group: Option<SharedString>,
+    /// The base style of the element, before any modifications are applied
+    /// by focus, active, etc.
     pub base_style: Box<StyleRefinement>,
-    pub focus_style: Option<Box<StyleRefinement>>,
-    pub in_focus_style: Option<Box<StyleRefinement>>,
-    pub hover_style: Option<Box<StyleRefinement>>,
-    pub group_hover_style: Option<GroupStyle>,
-    pub active_style: Option<Box<StyleRefinement>>,
-    pub group_active_style: Option<GroupStyle>,
-    pub drag_over_styles: Vec<(TypeId, StyleRefinement)>,
-    pub group_drag_over_styles: Vec<(TypeId, GroupStyle)>,
-    pub mouse_down_listeners: Vec<MouseDownListener>,
-    pub mouse_up_listeners: Vec<MouseUpListener>,
-    pub mouse_move_listeners: Vec<MouseMoveListener>,
-    pub scroll_wheel_listeners: Vec<ScrollWheelListener>,
-    pub key_down_listeners: Vec<KeyDownListener>,
-    pub key_up_listeners: Vec<KeyUpListener>,
-    pub action_listeners: Vec<(TypeId, ActionListener)>,
-    pub drop_listeners: Vec<(TypeId, DropListener)>,
-    pub can_drop_predicate: Option<CanDropPredicate>,
-    pub click_listeners: Vec<ClickListener>,
-    pub drag_listener: Option<(Box<dyn Any>, DragListener)>,
-    pub hover_listener: Option<Box<dyn Fn(&bool, &mut WindowContext)>>,
-    pub tooltip_builder: Option<TooltipBuilder>,
-    pub block_mouse: bool,
+    pub(crate) focus_style: Option<Box<StyleRefinement>>,
+    pub(crate) in_focus_style: Option<Box<StyleRefinement>>,
+    pub(crate) hover_style: Option<Box<StyleRefinement>>,
+    pub(crate) group_hover_style: Option<GroupStyle>,
+    pub(crate) active_style: Option<Box<StyleRefinement>>,
+    pub(crate) group_active_style: Option<GroupStyle>,
+    pub(crate) drag_over_styles: Vec<(TypeId, StyleRefinement)>,
+    pub(crate) group_drag_over_styles: Vec<(TypeId, GroupStyle)>,
+    pub(crate) mouse_down_listeners: Vec<MouseDownListener>,
+    pub(crate) mouse_up_listeners: Vec<MouseUpListener>,
+    pub(crate) mouse_move_listeners: Vec<MouseMoveListener>,
+    pub(crate) scroll_wheel_listeners: Vec<ScrollWheelListener>,
+    pub(crate) key_down_listeners: Vec<KeyDownListener>,
+    pub(crate) key_up_listeners: Vec<KeyUpListener>,
+    pub(crate) action_listeners: Vec<(TypeId, ActionListener)>,
+    pub(crate) drop_listeners: Vec<(TypeId, DropListener)>,
+    pub(crate) can_drop_predicate: Option<CanDropPredicate>,
+    pub(crate) click_listeners: Vec<ClickListener>,
+    pub(crate) drag_listener: Option<(Box<dyn Any>, DragListener)>,
+    pub(crate) hover_listener: Option<Box<dyn Fn(&bool, &mut WindowContext)>>,
+    pub(crate) tooltip_builder: Option<TooltipBuilder>,
+    pub(crate) block_mouse: bool,
 
     #[cfg(debug_assertions)]
-    pub location: Option<core::panic::Location<'static>>,
+    pub(crate) location: Option<core::panic::Location<'static>>,
 
     #[cfg(any(test, feature = "test-support"))]
-    pub debug_selector: Option<String>,
+    pub(crate) debug_selector: Option<String>,
 }
 
+/// The bounds and depth of an element in the computed element tree.
 #[derive(Clone, Debug)]
 pub struct InteractiveBounds {
+    /// The 2D bounds of the element
     pub bounds: Bounds<Pixels>,
+    /// The 'stacking order', or depth, for this element
     pub stacking_order: StackingOrder,
 }
 
 impl InteractiveBounds {
+    /// Checks whether this point was inside these bounds, and that these bounds where the topmost layer
     pub fn visibly_contains(&self, point: &Point<Pixels>, cx: &WindowContext) -> bool {
         self.bounds.contains(point) && cx.was_top_layer(point, &self.stacking_order)
     }
 
+    /// Checks whether this point was inside these bounds, and that these bounds where the topmost layer
+    /// under an active drag
     pub fn drag_target_contains(&self, point: &Point<Pixels>, cx: &WindowContext) -> bool {
         self.bounds.contains(point)
             && cx.was_top_layer_under_active_drag(point, &self.stacking_order)
@@ -946,11 +1230,12 @@ impl InteractiveBounds {
 }
 
 impl Interactivity {
+    /// Layout this element according to this interactivity state's configured styles
     pub fn layout(
         &mut self,
         element_state: Option<InteractiveElementState>,
-        cx: &mut WindowContext,
-        f: impl FnOnce(Style, &mut WindowContext) -> LayoutId,
+        cx: &mut ElementContext,
+        f: impl FnOnce(Style, &mut ElementContext) -> LayoutId,
     ) -> (LayoutId, InteractiveElementState) {
         let mut element_state = element_state.unwrap_or_default();
 
@@ -984,13 +1269,21 @@ impl Interactivity {
         (layout_id, element_state)
     }
 
+    /// Paint this element according to this interactivity state's configured styles
+    /// and bind the element's mouse and keyboard events.
+    ///
+    /// content_size is the size of the content of the element, which may be larger than the
+    /// element's bounds if the element is scrollable.
+    ///
+    /// the final computed style will be passed to the provided function, along
+    /// with the current scroll offset
     pub fn paint(
         &mut self,
         bounds: Bounds<Pixels>,
         content_size: Size<Pixels>,
         element_state: &mut InteractiveElementState,
-        cx: &mut WindowContext,
-        f: impl FnOnce(&Style, Point<Pixels>, &mut WindowContext),
+        cx: &mut ElementContext,
+        f: impl FnOnce(&Style, Point<Pixels>, &mut ElementContext),
     ) {
         let style = self.compute_style(Some(bounds), element_state, cx);
         let z_index = style.z_index.unwrap_or(0);
@@ -1003,7 +1296,7 @@ impl Interactivity {
                 .insert(debug_selector.clone(), bounds);
         }
 
-        let paint_hover_group_handler = |cx: &mut WindowContext| {
+        let paint_hover_group_handler = |cx: &mut ElementContext| {
             let hover_group_bounds = self
                 .group_hover_style
                 .as_ref()

crates/gpui/src/elements/img.rs ๐Ÿ”—

@@ -1,19 +1,23 @@
 use std::sync::Arc;
 
 use crate::{
-    point, size, BorrowWindow, Bounds, DevicePixels, Element, ImageData, InteractiveElement,
+    point, size, Bounds, DevicePixels, Element, ElementContext, ImageData, InteractiveElement,
     InteractiveElementState, Interactivity, IntoElement, LayoutId, Pixels, SharedUrl, Size,
-    StyleRefinement, Styled, WindowContext,
+    StyleRefinement, Styled,
 };
 use futures::FutureExt;
 use media::core_video::CVImageBuffer;
 use util::ResultExt;
 
+/// A source of image content.
 #[derive(Clone, Debug)]
 pub enum ImageSource {
     /// Image content will be loaded from provided URI at render time.
     Uri(SharedUrl),
+    /// Cached image data
     Data(Arc<ImageData>),
+    // TODO: move surface definitions into mac platform module
+    /// A CoreVideo image buffer
     Surface(CVImageBuffer),
 }
 
@@ -47,12 +51,14 @@ impl From<CVImageBuffer> for ImageSource {
     }
 }
 
+/// An image element.
 pub struct Img {
     interactivity: Interactivity,
     source: ImageSource,
     grayscale: bool,
 }
 
+/// Create a new image element.
 pub fn img(source: impl Into<ImageSource>) -> Img {
     Img {
         interactivity: Interactivity::default(),
@@ -62,6 +68,7 @@ pub fn img(source: impl Into<ImageSource>) -> Img {
 }
 
 impl Img {
+    /// Set the image to be displayed in grayscale.
     pub fn grayscale(mut self, grayscale: bool) -> Self {
         self.grayscale = grayscale;
         self
@@ -74,7 +81,7 @@ impl Element for Img {
     fn request_layout(
         &mut self,
         element_state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         self.interactivity
             .layout(element_state, cx, |style, cx| cx.request_layout(&style, []))
@@ -84,7 +91,7 @@ impl Element for Img {
         &mut self,
         bounds: Bounds<Pixels>,
         element_state: &mut Self::State,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let source = self.source.clone();
         self.interactivity.paint(

crates/gpui/src/elements/list.rs ๐Ÿ”—

@@ -1,13 +1,22 @@
+//! A list element that can be used to render a large number of differently sized elements
+//! efficiently. Clients of this API need to ensure that elements outside of the scrolled
+//! area do not change their height for this element to function correctly. In order to minimize
+//! re-renders, this element's state is stored intrusively on your own views, so that your code
+//! can coordinate directly with the list element's cached state.
+//!
+//! If all of your elements are the same height, see [`UniformList`] for a simpler API
+
 use crate::{
-    point, px, AnyElement, AvailableSpace, BorrowAppContext, BorrowWindow, Bounds, ContentMask,
-    DispatchPhase, Element, IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style,
-    StyleRefinement, Styled, WindowContext,
+    point, px, AnyElement, AvailableSpace, Bounds, ContentMask, DispatchPhase, Element,
+    IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style, StyleRefinement, Styled,
+    WindowContext,
 };
 use collections::VecDeque;
 use refineable::Refineable as _;
 use std::{cell::RefCell, ops::Range, rc::Rc};
 use sum_tree::{Bias, SumTree};
 
+/// Construct a new list element
 pub fn list(state: ListState) -> List {
     List {
         state,
@@ -15,11 +24,13 @@ pub fn list(state: ListState) -> List {
     }
 }
 
+/// A list element
 pub struct List {
     state: ListState,
     style: StyleRefinement,
 }
 
+/// The list state that views must hold on behalf of the list element.
 #[derive(Clone)]
 pub struct ListState(Rc<RefCell<StateInner>>);
 
@@ -35,15 +46,24 @@ struct StateInner {
     scroll_handler: Option<Box<dyn FnMut(&ListScrollEvent, &mut WindowContext)>>,
 }
 
+/// Whether the list is scrolling from top to bottom or bottom to top.
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
 pub enum ListAlignment {
+    /// The list is scrolling from top to bottom, like most lists.
     Top,
+    /// The list is scrolling from bottom to top, like a chat log.
     Bottom,
 }
 
+/// A scroll event that has been converted to be in terms of the list's items.
 pub struct ListScrollEvent {
+    /// The range of items currently visible in the list, after applying the scroll event.
     pub visible_range: Range<usize>,
+
+    /// The number of items that are currently visible in the list, after applying the scroll event.
     pub count: usize,
+
+    /// Whether the list has been scrolled.
     pub is_scrolled: bool,
 }
 
@@ -74,6 +94,11 @@ struct UnrenderedCount(usize);
 struct Height(Pixels);
 
 impl ListState {
+    /// Construct a new list state, for storage on a view.
+    ///
+    /// the overdraw parameter controls how much extra space is rendered
+    /// above and below the visible area. This can help ensure that the list
+    /// doesn't flicker or pop in when scrolling.
     pub fn new<F>(
         element_count: usize,
         orientation: ListAlignment,
@@ -111,10 +136,13 @@ impl ListState {
             .extend((0..element_count).map(|_| ListItem::Unrendered), &());
     }
 
+    /// The number of items in this list.
     pub fn item_count(&self) -> usize {
         self.0.borrow().items.summary().count
     }
 
+    /// Register with the list state that the items in `old_range` have been replaced
+    /// by `count` new items that must be recalculated.
     pub fn splice(&self, old_range: Range<usize>, count: usize) {
         let state = &mut *self.0.borrow_mut();
 
@@ -141,6 +169,7 @@ impl ListState {
         state.items = new_heights;
     }
 
+    /// 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,
@@ -148,10 +177,12 @@ impl ListState {
         self.0.borrow_mut().scroll_handler = Some(Box::new(handler))
     }
 
+    /// Get the current scroll offset, in terms of the list's items.
     pub fn logical_scroll_top(&self) -> ListOffset {
         self.0.borrow().logical_scroll_top()
     }
 
+    /// Scroll the list to the given offset
     pub fn scroll_to(&self, mut scroll_top: ListOffset) {
         let state = &mut *self.0.borrow_mut();
         let item_count = state.items.summary().count;
@@ -163,6 +194,7 @@ impl ListState {
         state.logical_scroll_top = Some(scroll_top);
     }
 
+    /// Scroll the list to the given item, such that the item is fully visible.
     pub fn scroll_to_reveal_item(&self, ix: usize) {
         let state = &mut *self.0.borrow_mut();
 
@@ -193,7 +225,8 @@ impl ListState {
         state.logical_scroll_top = Some(scroll_top);
     }
 
-    /// Get the bounds for the given item in window coordinates.
+    /// Get the bounds for the given item in window coordinates, if it's
+    /// been rendered.
     pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
         let state = &*self.0.borrow();
 
@@ -310,9 +343,13 @@ impl std::fmt::Debug for ListItem {
     }
 }
 
+/// An offset into the list's items, in terms of the item index and the number
+/// of pixels off the top left of the item.
 #[derive(Debug, Clone, Copy, Default)]
 pub struct ListOffset {
+    /// The index of an item in the list
     pub item_ix: usize,
+    /// The number of pixels to offset from the item index.
     pub offset_in_item: Pixels,
 }
 
@@ -322,7 +359,7 @@ impl Element for List {
     fn request_layout(
         &mut self,
         _state: Option<Self::State>,
-        cx: &mut crate::WindowContext,
+        cx: &mut crate::ElementContext,
     ) -> (crate::LayoutId, Self::State) {
         let mut style = Style::default();
         style.refine(&self.style);
@@ -336,7 +373,7 @@ impl Element for List {
         &mut self,
         bounds: Bounds<crate::Pixels>,
         _state: &mut Self::State,
-        cx: &mut crate::WindowContext,
+        cx: &mut crate::ElementContext,
     ) {
         let state = &mut *self.state.0.borrow_mut();
 

crates/gpui/src/elements/overlay.rs ๐Ÿ”—

@@ -2,14 +2,17 @@ use smallvec::SmallVec;
 use taffy::style::{Display, Position};
 
 use crate::{
-    point, AnyElement, BorrowWindow, Bounds, Element, IntoElement, LayoutId, ParentElement, Pixels,
-    Point, Size, Style, WindowContext,
+    point, AnyElement, Bounds, Element, ElementContext, IntoElement, LayoutId, ParentElement,
+    Pixels, Point, Size, Style,
 };
 
+/// The state that the overlay element uses to track its children.
 pub struct OverlayState {
     child_layout_ids: SmallVec<[LayoutId; 4]>,
 }
 
+/// An overlay element that can be used to display UI that
+/// floats on top of other UI elements.
 pub struct Overlay {
     children: SmallVec<[AnyElement; 2]>,
     anchor_corner: AnchorCorner,
@@ -60,8 +63,8 @@ impl Overlay {
 }
 
 impl ParentElement for Overlay {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
-        &mut self.children
+    fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
+        self.children.extend(elements)
     }
 }
 
@@ -71,7 +74,7 @@ impl Element for Overlay {
     fn request_layout(
         &mut self,
         _: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (crate::LayoutId, Self::State) {
         let child_layout_ids = self
             .children
@@ -94,7 +97,7 @@ impl Element for Overlay {
         &mut self,
         bounds: crate::Bounds<crate::Pixels>,
         element_state: &mut Self::State,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         if element_state.child_layout_ids.is_empty() {
             return;
@@ -191,15 +194,21 @@ enum Axis {
     Vertical,
 }
 
+/// Which algorithm to use when fitting the overlay to be inside the window.
 #[derive(Copy, Clone, PartialEq)]
 pub enum OverlayFitMode {
+    /// Snap the overlay to the window edge
     SnapToWindow,
+    /// Switch which corner anchor this overlay is attached to
     SwitchAnchor,
 }
 
+/// Which algorithm to use when positioning the overlay.
 #[derive(Copy, Clone, PartialEq)]
 pub enum OverlayPositionMode {
+    /// Position the overlay relative to the window
     Window,
+    /// Position the overlay relative to its parent
     Local,
 }
 
@@ -226,11 +235,16 @@ impl OverlayPositionMode {
     }
 }
 
+/// Which corner of the overlay should be considered the anchor.
 #[derive(Clone, Copy, PartialEq, Eq)]
 pub enum AnchorCorner {
+    /// The top left corner
     TopLeft,
+    /// The top right corner
     TopRight,
+    /// The bottom left corner
     BottomLeft,
+    /// The bottom right corner
     BottomRight,
 }
 
@@ -255,6 +269,7 @@ impl AnchorCorner {
         Bounds { origin, size }
     }
 
+    /// Get the point corresponding to this anchor corner in `bounds`.
     pub fn corner(&self, bounds: Bounds<Pixels>) -> Point<Pixels> {
         match self {
             Self::TopLeft => bounds.origin,

crates/gpui/src/elements/svg.rs ๐Ÿ”—

@@ -1,14 +1,16 @@
 use crate::{
-    Bounds, Element, ElementId, InteractiveElement, InteractiveElementState, Interactivity,
-    IntoElement, LayoutId, Pixels, SharedString, StyleRefinement, Styled, WindowContext,
+    Bounds, Element, ElementContext, ElementId, InteractiveElement, InteractiveElementState,
+    Interactivity, IntoElement, LayoutId, Pixels, SharedString, StyleRefinement, Styled,
 };
 use util::ResultExt;
 
+/// An SVG element.
 pub struct Svg {
     interactivity: Interactivity,
     path: Option<SharedString>,
 }
 
+/// Create a new SVG element.
 pub fn svg() -> Svg {
     Svg {
         interactivity: Interactivity::default(),
@@ -17,6 +19,7 @@ pub fn svg() -> Svg {
 }
 
 impl Svg {
+    /// Set the path to the SVG file for this element.
     pub fn path(mut self, path: impl Into<SharedString>) -> Self {
         self.path = Some(path.into());
         self
@@ -29,7 +32,7 @@ impl Element for Svg {
     fn request_layout(
         &mut self,
         element_state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         self.interactivity.layout(element_state, cx, |style, cx| {
             cx.request_layout(&style, None)
@@ -40,7 +43,7 @@ impl Element for Svg {
         &mut self,
         bounds: Bounds<Pixels>,
         element_state: &mut Self::State,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) where
         Self: Sized,
     {

crates/gpui/src/elements/text.rs ๐Ÿ”—

@@ -1,12 +1,19 @@
 use crate::{
-    Bounds, DispatchPhase, Element, ElementId, HighlightStyle, IntoElement, LayoutId,
-    MouseDownEvent, MouseUpEvent, Pixels, Point, SharedString, Size, TextRun, TextStyle,
-    WhiteSpace, WindowContext, WrappedLine,
+    ActiveTooltip, AnyTooltip, AnyView, Bounds, DispatchPhase, Element, ElementContext, ElementId,
+    HighlightStyle, IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
+    Point, SharedString, Size, TextRun, TextStyle, WhiteSpace, WindowContext, WrappedLine,
+    TOOLTIP_DELAY,
 };
 use anyhow::anyhow;
 use parking_lot::{Mutex, MutexGuard};
 use smallvec::SmallVec;
-use std::{cell::Cell, mem, ops::Range, rc::Rc, sync::Arc};
+use std::{
+    cell::{Cell, RefCell},
+    mem,
+    ops::Range,
+    rc::Rc,
+    sync::Arc,
+};
 use util::ResultExt;
 
 impl Element for &'static str {
@@ -15,14 +22,14 @@ impl Element for &'static str {
     fn request_layout(
         &mut self,
         _: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         let mut state = TextState::default();
         let layout_id = state.layout(SharedString::from(*self), None, cx);
         (layout_id, state)
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut ElementContext) {
         state.paint(bounds, self, cx)
     }
 }
@@ -45,14 +52,14 @@ impl Element for SharedString {
     fn request_layout(
         &mut self,
         _: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         let mut state = TextState::default();
         let layout_id = state.layout(self.clone(), None, cx);
         (layout_id, state)
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut ElementContext) {
         let text_str: &str = self.as_ref();
         state.paint(bounds, text_str, cx)
     }
@@ -81,6 +88,7 @@ pub struct StyledText {
 }
 
 impl StyledText {
+    /// Construct a new styled text element from the given string.
     pub fn new(text: impl Into<SharedString>) -> Self {
         StyledText {
             text: text.into(),
@@ -88,6 +96,8 @@ impl StyledText {
         }
     }
 
+    /// Set the styling attributes for the given text, as well as
+    /// as any ranges of text that have had their style customized.
     pub fn with_highlights(
         mut self,
         default_style: &TextStyle,
@@ -121,14 +131,14 @@ impl Element for StyledText {
     fn request_layout(
         &mut self,
         _: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         let mut state = TextState::default();
         let layout_id = state.layout(self.text.clone(), self.runs.take(), cx);
         (layout_id, state)
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
         state.paint(bounds, &self.text, cx)
     }
 }
@@ -145,6 +155,7 @@ impl IntoElement for StyledText {
     }
 }
 
+#[doc(hidden)]
 #[derive(Default, Clone)]
 pub struct TextState(Arc<Mutex<Option<TextStateInner>>>);
 
@@ -164,7 +175,7 @@ impl TextState {
         &mut self,
         text: SharedString,
         runs: Option<Vec<TextRun>>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> LayoutId {
         let text_style = cx.text_style();
         let font_size = text_style.font_size.to_pixels(cx.rem_size());
@@ -239,7 +250,7 @@ impl TextState {
         layout_id
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, text: &str, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, text: &str, cx: &mut ElementContext) {
         let element_state = self.lock();
         let element_state = element_state
             .as_ref()
@@ -284,11 +295,14 @@ impl TextState {
     }
 }
 
+/// A text element that can be interacted with.
 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>>>,
     clickable_ranges: Vec<Range<usize>>,
 }
 
@@ -297,21 +311,30 @@ struct InteractiveTextClickEvent {
     mouse_up_index: usize,
 }
 
+#[doc(hidden)]
 pub struct InteractiveTextState {
     text_state: TextState,
     mouse_down_index: Rc<Cell<Option<usize>>>,
+    hovered_index: Rc<Cell<Option<usize>>>,
+    active_tooltip: Rc<RefCell<Option<ActiveTooltip>>>,
 }
 
+/// InteractiveTest is a wrapper around StyledText that adds mouse interactions.
 impl InteractiveText {
+    /// Creates a new InteractiveText from the given text.
     pub fn new(id: impl Into<ElementId>, text: StyledText) -> Self {
         Self {
             element_id: id.into(),
             text,
             click_listener: None,
+            hover_listener: None,
+            tooltip_builder: None,
             clickable_ranges: Vec::new(),
         }
     }
 
+    /// on_click is called when the user clicks on one of the given ranges, passing the index of
+    /// the clicked range.
     pub fn on_click(
         mut self,
         ranges: Vec<Range<usize>>,
@@ -328,6 +351,25 @@ impl InteractiveText {
         self.clickable_ranges = ranges;
         self
     }
+
+    /// on_hover is called when the mouse moves over a character within the text, passing the
+    /// 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,
+    ) -> Self {
+        self.hover_listener = Some(Box::new(listener));
+        self
+    }
+
+    /// 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,
+    ) -> Self {
+        self.tooltip_builder = Some(Rc::new(builder));
+        self
+    }
 }
 
 impl Element for InteractiveText {
@@ -336,16 +378,21 @@ impl Element for InteractiveText {
     fn request_layout(
         &mut self,
         state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         if let Some(InteractiveTextState {
-            mouse_down_index, ..
+            mouse_down_index,
+            hovered_index,
+            active_tooltip,
+            ..
         }) = state
         {
             let (layout_id, text_state) = self.text.request_layout(None, cx);
             let element_state = InteractiveTextState {
                 text_state,
                 mouse_down_index,
+                hovered_index,
+                active_tooltip,
             };
             (layout_id, element_state)
         } else {
@@ -353,12 +400,14 @@ impl Element for InteractiveText {
             let element_state = InteractiveTextState {
                 text_state,
                 mouse_down_index: Rc::default(),
+                hovered_index: Rc::default(),
+                active_tooltip: Rc::default(),
             };
             (layout_id, element_state)
         }
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
         if let Some(click_listener) = self.click_listener.take() {
             let mouse_position = cx.mouse_position();
             if let Some(ix) = state.text_state.index_for_position(bounds, mouse_position) {
@@ -408,6 +457,83 @@ impl Element for InteractiveText {
                 });
             }
         }
+        if let Some(hover_listener) = self.hover_listener.take() {
+            let text_state = state.text_state.clone();
+            let hovered_index = state.hovered_index.clone();
+            cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
+                if phase == DispatchPhase::Bubble {
+                    let current = hovered_index.get();
+                    let updated = text_state.index_for_position(bounds, event.position);
+                    if current != updated {
+                        hovered_index.set(updated);
+                        hover_listener(updated, event.clone(), cx);
+                        cx.refresh();
+                    }
+                }
+            });
+        }
+        if let Some(tooltip_builder) = self.tooltip_builder.clone() {
+            let active_tooltip = state.active_tooltip.clone();
+            let pending_mouse_down = state.mouse_down_index.clone();
+            let text_state = state.text_state.clone();
+
+            cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
+                let position = text_state.index_for_position(bounds, event.position);
+                let is_hovered = position.is_some() && pending_mouse_down.get().is_none();
+                if !is_hovered {
+                    active_tooltip.take();
+                    return;
+                }
+                let position = position.unwrap();
+
+                if phase != DispatchPhase::Bubble {
+                    return;
+                }
+
+                if active_tooltip.borrow().is_none() {
+                    let task = cx.spawn({
+                        let active_tooltip = active_tooltip.clone();
+                        let tooltip_builder = tooltip_builder.clone();
+
+                        move |mut cx| async move {
+                            cx.background_executor().timer(TOOLTIP_DELAY).await;
+                            cx.update(|cx| {
+                                let new_tooltip =
+                                    tooltip_builder(position, cx).map(|tooltip| ActiveTooltip {
+                                        tooltip: Some(AnyTooltip {
+                                            view: tooltip,
+                                            cursor_offset: cx.mouse_position(),
+                                        }),
+                                        _task: None,
+                                    });
+                                *active_tooltip.borrow_mut() = new_tooltip;
+                                cx.refresh();
+                            })
+                            .ok();
+                        }
+                    });
+                    *active_tooltip.borrow_mut() = Some(ActiveTooltip {
+                        tooltip: None,
+                        _task: Some(task),
+                    });
+                }
+            });
+
+            let active_tooltip = state.active_tooltip.clone();
+            cx.on_mouse_event(move |_: &MouseDownEvent, _, _| {
+                active_tooltip.take();
+            });
+
+            if let Some(tooltip) = state
+                .active_tooltip
+                .clone()
+                .borrow()
+                .as_ref()
+                .and_then(|at| at.tooltip.clone())
+            {
+                cx.set_tooltip(tooltip);
+            }
+        }
 
         self.text.paint(bounds, &mut state.text_state, cx)
     }

crates/gpui/src/elements/uniform_list.rs ๐Ÿ”—

@@ -1,5 +1,11 @@
+//! A scrollable list of elements with uniform height, optimized for large lists.
+//! Rather than use the full taffy layout system, uniform_list simply measures
+//! the first element and then lays out all remaining elements in a line based on that
+//! measurement. This is much faster than the full layout system, but only works for
+//! elements with uniform height.
+
 use crate::{
-    point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Element,
+    point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementContext,
     ElementId, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId,
     Pixels, Render, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
 };
@@ -53,6 +59,7 @@ where
     }
 }
 
+/// A list element for efficiently laying out and displaying a list of uniform-height elements.
 pub struct UniformList {
     id: ElementId,
     item_count: usize,
@@ -63,18 +70,22 @@ pub struct UniformList {
     scroll_handle: Option<UniformListScrollHandle>,
 }
 
+/// A handle for controlling the scroll position of a uniform list.
+/// This should be stored in your view and passed to the uniform_list on each frame.
 #[derive(Clone, Default)]
 pub struct UniformListScrollHandle {
     deferred_scroll_to_item: Rc<RefCell<Option<usize>>>,
 }
 
 impl UniformListScrollHandle {
+    /// Create a new scroll handle to bind to a uniform list.
     pub fn new() -> Self {
         Self {
             deferred_scroll_to_item: Rc::new(RefCell::new(None)),
         }
     }
 
+    /// Scroll the list to the given item index.
     pub fn scroll_to_item(&mut self, ix: usize) {
         self.deferred_scroll_to_item.replace(Some(ix));
     }
@@ -86,6 +97,7 @@ impl Styled for UniformList {
     }
 }
 
+#[doc(hidden)]
 #[derive(Default)]
 pub struct UniformListState {
     interactive: InteractiveElementState,
@@ -98,7 +110,7 @@ impl Element for UniformList {
     fn request_layout(
         &mut self,
         state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         let max_items = self.item_count;
         let item_size = state
@@ -146,7 +158,7 @@ impl Element for UniformList {
         &mut self,
         bounds: Bounds<crate::Pixels>,
         element_state: &mut Self::State,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let style =
             self.interactivity
@@ -262,12 +274,13 @@ impl IntoElement for UniformList {
 }
 
 impl UniformList {
+    /// Selects a specific list item for measurement.
     pub fn with_width_from_item(mut self, item_index: Option<usize>) -> Self {
         self.item_to_measure_index = item_index.unwrap_or(0);
         self
     }
 
-    fn measure_item(&self, list_width: Option<Pixels>, cx: &mut WindowContext) -> Size<Pixels> {
+    fn measure_item(&self, list_width: Option<Pixels>, cx: &mut ElementContext) -> Size<Pixels> {
         if self.item_count == 0 {
             return Size::default();
         }
@@ -284,6 +297,7 @@ impl UniformList {
         item_to_measure.measure(available_space, cx)
     }
 
+    /// Track and render scroll state of this list with reference to the given scroll handle.
     pub fn track_scroll(mut self, handle: UniformListScrollHandle) -> Self {
         self.scroll_handle = Some(handle);
         self

crates/gpui/src/executor.rs ๐Ÿ”—

@@ -21,11 +21,15 @@ use waker_fn::waker_fn;
 #[cfg(any(test, feature = "test-support"))]
 use rand::rngs::StdRng;
 
+/// A pointer to the executor that is currently running,
+/// for spawning background tasks.
 #[derive(Clone)]
 pub struct BackgroundExecutor {
     dispatcher: Arc<dyn PlatformDispatcher>,
 }
 
+/// A pointer to the executor that is currently running,
+/// for spawning tasks on the main thread.
 #[derive(Clone)]
 pub struct ForegroundExecutor {
     dispatcher: Arc<dyn PlatformDispatcher>,
@@ -37,16 +41,19 @@ pub struct ForegroundExecutor {
 /// It implements [`Future`] so you can `.await` on it.
 ///
 /// If you drop a task it will be cancelled immediately. Calling [`Task::detach`] allows
-/// the task to continue running in the background, but with no way to return a value.
+/// the task to continue running, but with no way to return a value.
 #[must_use]
 #[derive(Debug)]
 pub enum Task<T> {
+    /// A task that is ready to return a value
     Ready(Option<T>),
+
+    /// A task that is currently running.
     Spawned(async_task::Task<T>),
 }
 
 impl<T> Task<T> {
-    /// Create a new task that will resolve with the value
+    /// Creates a new task that will resolve with the value
     pub fn ready(val: T) -> Self {
         Task::Ready(Some(val))
     }
@@ -68,7 +75,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: &mut AppContext) {
+    pub fn detach_and_log_err(self, cx: &AppContext) {
         let location = core::panic::Location::caller();
         cx.foreground_executor()
             .spawn(self.log_tracked_err(*location))
@@ -87,6 +94,8 @@ impl<T> Future for Task<T> {
     }
 }
 
+/// A task label is an opaque identifier that you can use to
+/// refer to a task in tests.
 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
 pub struct TaskLabel(NonZeroUsize);
 
@@ -97,6 +106,7 @@ impl Default for TaskLabel {
 }
 
 impl TaskLabel {
+    /// Construct a new task label.
     pub fn new() -> Self {
         static NEXT_TASK_LABEL: AtomicUsize = AtomicUsize::new(1);
         Self(NEXT_TASK_LABEL.fetch_add(1, SeqCst).try_into().unwrap())
@@ -363,6 +373,7 @@ impl BackgroundExecutor {
 
 /// ForegroundExecutor runs things on the main thread.
 impl ForegroundExecutor {
+    /// Creates a new ForegroundExecutor from the given PlatformDispatcher.
     pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
         Self {
             dispatcher,
@@ -411,13 +422,14 @@ impl<'a> Scope<'a> {
         }
     }
 
+    /// Spawn a future into this scope.
     pub fn spawn<F>(&mut self, f: F)
     where
         F: Future<Output = ()> + Send + 'a,
     {
         let tx = self.tx.clone().unwrap();
 
-        // Safety: The 'a lifetime is guaranteed to outlive any of these futures because
+        // SAFETY: The 'a lifetime is guaranteed to outlive any of these futures because
         // dropping this `Scope` blocks until all of the futures have resolved.
         let f = unsafe {
             mem::transmute::<

crates/gpui/src/geometry.rs ๐Ÿ”—

@@ -497,6 +497,20 @@ where
     }
 }
 
+impl<T> Add for Size<T>
+where
+    T: Add<Output = T> + Clone + Default + Debug,
+{
+    type Output = Size<T>;
+
+    fn add(self, rhs: Self) -> Self::Output {
+        Size {
+            width: self.width + rhs.width,
+            height: self.height + rhs.height,
+        }
+    }
+}
+
 impl<T, Rhs> Mul<Rhs> for Size<T>
 where
     T: Mul<Rhs, Output = Rhs> + Clone + Default + Debug,

crates/gpui/src/gpui.rs ๐Ÿ”—

@@ -1,3 +1,31 @@
+//! # Welcome to GPUI!
+//!
+//! GPUI is a hybrid immediate and retained mode, GPU accelerated, UI framework
+//! for Rust, designed to support a wide variety of applications. GPUI is currently
+//! being actively developed and improved for the [Zed code editor](https://zed.dev/), and new versions
+//! will have breaking changes. You'll probably need to use the latest stable version
+//! of rust to use GPUI.
+//!
+//! # Getting started with GPUI
+//!
+//! TODO!(docs): Write a code sample showing how to create a window and render a simple
+//! div
+//!
+//! # Drawing interesting things
+//!
+//! TODO!(docs): Expand demo to show how to draw a more interesting scene, with
+//! a counter to store state and a button to increment it.
+//!
+//! # Interacting with your application state
+//!
+//! TODO!(docs): Expand demo to show GPUI entity interactions, like subscriptions and entities
+//! maybe make a network request to show async stuff?
+//!
+//! # Conclusion
+//!
+//! TODO!(docs): Wrap up with a conclusion and links to other places? Zed / GPUI website?
+//! Discord for chatting about it? Other tutorials or references?
+
 #[macro_use]
 mod action;
 mod app;
@@ -58,10 +86,10 @@ pub use elements::*;
 pub use executor::*;
 pub use geometry::*;
 pub use gpui_macros::{register_action, test, IntoElement, Render};
-pub use image_cache::*;
+use image_cache::*;
 pub use input::*;
 pub use interactive::*;
-pub use key_dispatch::*;
+use key_dispatch::*;
 pub use keymap::*;
 pub use platform::*;
 pub use refineable::*;
@@ -73,7 +101,7 @@ pub use smol::Timer;
 pub use style::*;
 pub use styled::*;
 pub use subscription::*;
-pub use svg_renderer::*;
+use svg_renderer::*;
 pub use taffy::{AvailableSpace, LayoutId};
 #[cfg(any(test, feature = "test-support"))]
 pub use test::*;
@@ -82,20 +110,23 @@ pub use util::arc_cow::ArcCow;
 pub use view::*;
 pub use window::*;
 
-use std::{
-    any::{Any, TypeId},
-    borrow::BorrowMut,
-};
+use std::{any::Any, borrow::BorrowMut};
 use taffy::TaffyLayoutEngine;
 
+/// The context trait, allows the different contexts in GPUI to be used
+/// interchangeably for certain operations.
 pub trait Context {
+    /// 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>(
         &mut self,
         build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
     ) -> Self::Result<Model<T>>;
 
+    /// Update a model in the app context.
     fn update_model<T, R>(
         &mut self,
         handle: &Model<T>,
@@ -104,6 +135,7 @@ pub trait Context {
     where
         T: 'static;
 
+    /// Read a model from the app context.
     fn read_model<T, R>(
         &self,
         handle: &Model<T>,
@@ -112,10 +144,12 @@ pub trait Context {
     where
         T: 'static;
 
+    /// 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;
 
+    /// Read a window off of the application context.
     fn read_window<T, R>(
         &self,
         window: &WindowHandle<T>,
@@ -125,7 +159,10 @@ pub trait Context {
         T: 'static;
 }
 
+/// 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,
@@ -133,12 +170,14 @@ pub trait VisualContext: Context {
     where
         V: 'static + Render;
 
+    /// Update a view with the given callback
     fn update_view<V: 'static, R>(
         &mut self,
         view: &View<V>,
         update: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R,
     ) -> Self::Result<R>;
 
+    /// 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,
@@ -146,38 +185,42 @@ pub trait VisualContext: Context {
     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>
     where
         Self: Sized;
 }
 
+/// A trait for tying together the types of a GPUI entity and the events it can
+/// emit.
 pub trait EventEmitter<E: Any>: 'static {}
 
-pub enum GlobalKey {
-    Numeric(usize),
-    View(EntityId),
-    Type(TypeId),
-}
-
+/// A helper trait for auto-implementing certain methods on contexts that
+/// can be used interchangeably.
 pub trait BorrowAppContext {
-    fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
-    where
-        F: FnOnce(&mut Self) -> R;
-
+    /// Set a global value on the context.
     fn set_global<T: 'static>(&mut self, global: T);
 }
 
@@ -185,26 +228,14 @@ impl<C> BorrowAppContext for C
 where
     C: BorrowMut<AppContext>,
 {
-    fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
-    where
-        F: FnOnce(&mut Self) -> R,
-    {
-        if let Some(style) = style {
-            self.borrow_mut().push_text_style(style);
-            let result = f(self);
-            self.borrow_mut().pop_text_style();
-            result
-        } else {
-            f(self)
-        }
-    }
-
     fn set_global<G: 'static>(&mut self, global: G) {
         self.borrow_mut().set_global(global)
     }
 }
 
+/// A flatten equivalent for anyhow `Result`s.
 pub trait Flatten<T> {
+    /// Convert this type into a simple `Result<T>`.
     fn flatten(self) -> Result<T>;
 }
 

crates/gpui/src/image_cache.rs ๐Ÿ”—

@@ -11,12 +11,12 @@ use thiserror::Error;
 use util::http::{self, HttpClient};
 
 #[derive(PartialEq, Eq, Hash, Clone)]
-pub struct RenderImageParams {
+pub(crate) struct RenderImageParams {
     pub(crate) image_id: ImageId,
 }
 
 #[derive(Debug, Error, Clone)]
-pub enum Error {
+pub(crate) enum Error {
     #[error("http error: {0}")]
     Client(#[from] http::Error),
     #[error("IO error: {0}")]
@@ -42,7 +42,7 @@ impl From<ImageError> for Error {
     }
 }
 
-pub struct ImageCache {
+pub(crate) struct ImageCache {
     client: Arc<dyn HttpClient>,
     images: Arc<Mutex<HashMap<SharedUrl, FetchImageFuture>>>,
 }

crates/gpui/src/input.rs ๐Ÿ”—

@@ -1,24 +1,35 @@
-use crate::{
-    AsyncWindowContext, Bounds, Pixels, PlatformInputHandler, View, ViewContext, WindowContext,
-};
+use crate::{Bounds, InputHandler, Pixels, View, ViewContext, WindowContext};
 use std::ops::Range;
 
 /// Implement this trait to allow views to handle textual input when implementing an editor, field, etc.
 ///
-/// Once your view `V` implements this trait, you can use it to construct an [`ElementInputHandler<V>`].
+/// Once your view implements this trait, you can use it to construct an [`ElementInputHandler<V>`].
 /// This input handler can then be assigned during paint by calling [`WindowContext::handle_input`].
-pub trait InputHandler: 'static + Sized {
+///
+/// See [`InputHandler`] for details on how to implement each method.
+pub trait ViewInputHandler: 'static + Sized {
+    /// See [`InputHandler::text_for_range`] for details
     fn text_for_range(&mut self, range: Range<usize>, cx: &mut ViewContext<Self>)
         -> Option<String>;
+
+    /// See [`InputHandler::selected_text_range`] for details
     fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
+
+    /// See [`InputHandler::marked_text_range`] for details
     fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
+
+    /// See [`InputHandler::unmark_text`] for details
     fn unmark_text(&mut self, cx: &mut ViewContext<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>,
     );
+
+    /// See [`InputHandler::replace_and_mark_text_in_range`] for details
     fn replace_and_mark_text_in_range(
         &mut self,
         range: Option<Range<usize>>,
@@ -26,6 +37,8 @@ pub trait InputHandler: 'static + Sized {
         new_selected_range: Option<Range<usize>>,
         cx: &mut ViewContext<Self>,
     );
+
+    /// See [`InputHandler::bounds_for_range`] for details
     fn bounds_for_range(
         &mut self,
         range_utf16: Range<usize>,
@@ -39,7 +52,6 @@ pub trait InputHandler: 'static + Sized {
 pub struct ElementInputHandler<V> {
     view: View<V>,
     element_bounds: Bounds<Pixels>,
-    cx: AsyncWindowContext,
 }
 
 impl<V: 'static> ElementInputHandler<V> {
@@ -47,45 +59,42 @@ impl<V: 'static> ElementInputHandler<V> {
     /// containing view.
     ///
     /// [element_paint]: crate::Element::paint
-    pub fn new(element_bounds: Bounds<Pixels>, view: View<V>, cx: &mut WindowContext) -> Self {
+    pub fn new(element_bounds: Bounds<Pixels>, view: View<V>) -> Self {
         ElementInputHandler {
             view,
             element_bounds,
-            cx: cx.to_async(),
         }
     }
 }
 
-impl<V: InputHandler> PlatformInputHandler for ElementInputHandler<V> {
-    fn selected_text_range(&mut self) -> Option<Range<usize>> {
+impl<V: ViewInputHandler> InputHandler for ElementInputHandler<V> {
+    fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>> {
         self.view
-            .update(&mut self.cx, |view, cx| view.selected_text_range(cx))
-            .ok()
-            .flatten()
+            .update(cx, |view, cx| view.selected_text_range(cx))
     }
 
-    fn marked_text_range(&mut self) -> Option<Range<usize>> {
-        self.view
-            .update(&mut self.cx, |view, cx| view.marked_text_range(cx))
-            .ok()
-            .flatten()
+    fn marked_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>> {
+        self.view.update(cx, |view, cx| view.marked_text_range(cx))
     }
 
-    fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String> {
+    fn text_for_range(
+        &mut self,
+        range_utf16: Range<usize>,
+        cx: &mut WindowContext,
+    ) -> Option<String> {
         self.view
-            .update(&mut self.cx, |view, cx| {
-                view.text_for_range(range_utf16, cx)
-            })
-            .ok()
-            .flatten()
+            .update(cx, |view, cx| view.text_for_range(range_utf16, cx))
     }
 
-    fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str) {
-        self.view
-            .update(&mut self.cx, |view, cx| {
-                view.replace_text_in_range(replacement_range, text, cx)
-            })
-            .ok();
+    fn replace_text_in_range(
+        &mut self,
+        replacement_range: Option<Range<usize>>,
+        text: &str,
+        cx: &mut WindowContext,
+    ) {
+        self.view.update(cx, |view, cx| {
+            view.replace_text_in_range(replacement_range, text, cx)
+        });
     }
 
     fn replace_and_mark_text_in_range(
@@ -93,26 +102,24 @@ impl<V: InputHandler> PlatformInputHandler for ElementInputHandler<V> {
         range_utf16: Option<Range<usize>>,
         new_text: &str,
         new_selected_range: Option<Range<usize>>,
+        cx: &mut WindowContext,
     ) {
-        self.view
-            .update(&mut self.cx, |view, cx| {
-                view.replace_and_mark_text_in_range(range_utf16, new_text, new_selected_range, cx)
-            })
-            .ok();
+        self.view.update(cx, |view, cx| {
+            view.replace_and_mark_text_in_range(range_utf16, new_text, new_selected_range, cx)
+        });
     }
 
-    fn unmark_text(&mut self) {
-        self.view
-            .update(&mut self.cx, |view, cx| view.unmark_text(cx))
-            .ok();
+    fn unmark_text(&mut self, cx: &mut WindowContext) {
+        self.view.update(cx, |view, cx| view.unmark_text(cx));
     }
 
-    fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>> {
-        self.view
-            .update(&mut self.cx, |view, cx| {
-                view.bounds_for_range(range_utf16, self.element_bounds, cx)
-            })
-            .ok()
-            .flatten()
+    fn bounds_for_range(
+        &mut self,
+        range_utf16: Range<usize>,
+        cx: &mut WindowContext,
+    ) -> Option<Bounds<Pixels>> {
+        self.view.update(cx, |view, cx| {
+            view.bounds_for_range(range_utf16, self.element_bounds, cx)
+        })
     }
 }

crates/gpui/src/interactive.rs ๐Ÿ”—

@@ -4,15 +4,25 @@ use crate::{
 use smallvec::SmallVec;
 use std::{any::Any, fmt::Debug, ops::Deref, path::PathBuf};
 
+/// An event from a platform input source.
 pub trait InputEvent: Sealed + 'static {
+    /// Convert this event into the platform input enum.
     fn to_platform_input(self) -> PlatformInput;
 }
+
+/// A key event from the platform.
 pub trait KeyEvent: InputEvent {}
+
+/// A mouse event from the platform.
 pub trait MouseEvent: InputEvent {}
 
+/// The key down event equivalent for the platform.
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct KeyDownEvent {
+    /// The keystroke that was generated.
     pub keystroke: Keystroke,
+
+    /// Whether the key is currently held down.
     pub is_held: bool,
 }
 
@@ -24,8 +34,10 @@ impl InputEvent for KeyDownEvent {
 }
 impl KeyEvent for KeyDownEvent {}
 
+/// The key up event equivalent for the platform.
 #[derive(Clone, Debug)]
 pub struct KeyUpEvent {
+    /// The keystroke that was released.
     pub keystroke: Keystroke,
 }
 
@@ -37,8 +49,10 @@ impl InputEvent for KeyUpEvent {
 }
 impl KeyEvent for KeyUpEvent {}
 
+/// The modifiers changed event equivalent for the platform.
 #[derive(Clone, Debug, Default)]
 pub struct ModifiersChangedEvent {
+    /// The new state of the modifier keys
     pub modifiers: Modifiers,
 }
 
@@ -62,17 +76,28 @@ impl Deref for ModifiersChangedEvent {
 /// Based on the winit enum of the same name.
 #[derive(Clone, Copy, Debug, Default)]
 pub enum TouchPhase {
+    /// The touch started.
     Started,
+    /// The touch event is moving.
     #[default]
     Moved,
+    /// The touch phase has ended
     Ended,
 }
 
+/// A mouse down event from the platform
 #[derive(Clone, Debug, Default)]
 pub struct MouseDownEvent {
+    /// Which mouse button was pressed.
     pub button: MouseButton,
+
+    /// The position of the mouse on the window.
     pub position: Point<Pixels>,
+
+    /// The modifiers that were held down when the mouse was pressed.
     pub modifiers: Modifiers,
+
+    /// The number of times the button has been clicked.
     pub click_count: usize,
 }
 
@@ -84,11 +109,19 @@ impl InputEvent for MouseDownEvent {
 }
 impl MouseEvent for MouseDownEvent {}
 
+/// A mouse up event from the platform
 #[derive(Clone, Debug, Default)]
 pub struct MouseUpEvent {
+    /// Which mouse button was released.
     pub button: MouseButton,
+
+    /// The position of the mouse on the window.
     pub position: Point<Pixels>,
+
+    /// The modifiers that were held down when the mouse was released.
     pub modifiers: Modifiers,
+
+    /// The number of times the button has been clicked.
     pub click_count: usize,
 }
 
@@ -100,21 +133,34 @@ impl InputEvent for MouseUpEvent {
 }
 impl MouseEvent for MouseUpEvent {}
 
+/// A click event, generated when a mouse button is pressed and released.
 #[derive(Clone, Debug, Default)]
 pub struct ClickEvent {
+    /// The mouse event when the button was pressed.
     pub down: MouseDownEvent,
+
+    /// The mouse event when the button was released.
     pub up: MouseUpEvent,
 }
 
+/// An enum representing the mouse button that was pressed.
 #[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
 pub enum MouseButton {
+    /// The left mouse button.
     Left,
+
+    /// The right mouse button.
     Right,
+
+    /// The middle mouse button.
     Middle,
+
+    /// A navigation button, such as back or forward.
     Navigate(NavigationDirection),
 }
 
 impl MouseButton {
+    /// Get all the mouse buttons in a list.
     pub fn all() -> Vec<Self> {
         vec![
             MouseButton::Left,
@@ -132,9 +178,13 @@ impl Default for MouseButton {
     }
 }
 
+/// A navigation direction, such as back or forward.
 #[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
 pub enum NavigationDirection {
+    /// The back button.
     Back,
+
+    /// The forward button.
     Forward,
 }
 
@@ -144,10 +194,16 @@ impl Default for NavigationDirection {
     }
 }
 
+/// A mouse move event from the platform
 #[derive(Clone, Debug, Default)]
 pub struct MouseMoveEvent {
+    /// The position of the mouse on the window.
     pub position: Point<Pixels>,
+
+    /// The mouse button that was pressed, if any.
     pub pressed_button: Option<MouseButton>,
+
+    /// The modifiers that were held down when the mouse was moved.
     pub modifiers: Modifiers,
 }
 
@@ -160,16 +216,25 @@ impl InputEvent for MouseMoveEvent {
 impl MouseEvent for MouseMoveEvent {}
 
 impl MouseMoveEvent {
+    /// Returns true if the left mouse button is currently held down.
     pub fn dragging(&self) -> bool {
         self.pressed_button == Some(MouseButton::Left)
     }
 }
 
+/// A mouse wheel event from the platform
 #[derive(Clone, Debug, Default)]
 pub struct ScrollWheelEvent {
+    /// The position of the mouse on the window.
     pub position: Point<Pixels>,
+
+    /// The change in scroll wheel position for this event.
     pub delta: ScrollDelta,
+
+    /// The modifiers that were held down when the mouse was moved.
     pub modifiers: Modifiers,
+
+    /// The phase of the touch event.
     pub touch_phase: TouchPhase,
 }
 
@@ -189,9 +254,12 @@ impl Deref for ScrollWheelEvent {
     }
 }
 
+/// The scroll delta for a scroll wheel event.
 #[derive(Clone, Copy, Debug)]
 pub enum ScrollDelta {
+    /// An exact scroll delta in pixels.
     Pixels(Point<Pixels>),
+    /// An inexact scroll delta in lines.
     Lines(Point<f32>),
 }
 
@@ -202,6 +270,7 @@ impl Default for ScrollDelta {
 }
 
 impl ScrollDelta {
+    /// Returns true if this is a precise scroll delta in pixels.
     pub fn precise(&self) -> bool {
         match self {
             ScrollDelta::Pixels(_) => true,
@@ -209,6 +278,7 @@ impl ScrollDelta {
         }
     }
 
+    /// Converts this scroll event into exact pixels.
     pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
         match self {
             ScrollDelta::Pixels(delta) => *delta,
@@ -216,6 +286,7 @@ impl ScrollDelta {
         }
     }
 
+    /// Combines two scroll deltas into one.
     pub fn coalesce(self, other: ScrollDelta) -> ScrollDelta {
         match (self, other) {
             (ScrollDelta::Pixels(px_a), ScrollDelta::Pixels(px_b)) => {
@@ -231,10 +302,15 @@ impl ScrollDelta {
     }
 }
 
+/// A mouse exit event from the platform, generated when the mouse leaves the window.
+/// The position generated should be just outside of the window's bounds.
 #[derive(Clone, Debug, Default)]
 pub struct MouseExitEvent {
+    /// The position of the mouse relative to the window.
     pub position: Point<Pixels>,
+    /// The mouse button that was pressed, if any.
     pub pressed_button: Option<MouseButton>,
+    /// The modifiers that were held down when the mouse was moved.
     pub modifiers: Modifiers,
 }
 
@@ -254,10 +330,12 @@ impl Deref for MouseExitEvent {
     }
 }
 
+/// A collection of paths from the platform, such as from a file drop.
 #[derive(Debug, Clone, Default)]
 pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>);
 
 impl ExternalPaths {
+    /// Convert this collection of paths into a slice.
     pub fn paths(&self) -> &[PathBuf] {
         &self.0
     }
@@ -269,18 +347,27 @@ impl Render for ExternalPaths {
     }
 }
 
+/// A file drop event from the platform, generated when files are dragged and dropped onto the window.
 #[derive(Debug, Clone)]
 pub enum FileDropEvent {
+    /// The files have entered the window.
     Entered {
+        /// The position of the mouse relative to the window.
         position: Point<Pixels>,
+        /// The paths of the files that are being dragged.
         paths: ExternalPaths,
     },
+    /// The files are being dragged over the window
     Pending {
+        /// The position of the mouse relative to the window.
         position: Point<Pixels>,
     },
+    /// The files have been dropped onto the window.
     Submit {
+        /// The position of the mouse relative to the window.
         position: Point<Pixels>,
     },
+    /// The user has stopped dragging the files over the window.
     Exited,
 }
 
@@ -292,40 +379,31 @@ impl InputEvent for FileDropEvent {
 }
 impl MouseEvent for FileDropEvent {}
 
+/// An enum corresponding to all kinds of platform input events.
 #[derive(Clone, Debug)]
 pub enum PlatformInput {
+    /// A key was pressed.
     KeyDown(KeyDownEvent),
+    /// A key was released.
     KeyUp(KeyUpEvent),
+    /// The keyboard modifiers were changed.
     ModifiersChanged(ModifiersChangedEvent),
+    /// The mouse was pressed.
     MouseDown(MouseDownEvent),
+    /// The mouse was released.
     MouseUp(MouseUpEvent),
+    /// The mouse was moved.
     MouseMove(MouseMoveEvent),
+    /// The mouse exited the window.
     MouseExited(MouseExitEvent),
+    /// The scroll wheel was used.
     ScrollWheel(ScrollWheelEvent),
+    /// Files were dragged and dropped onto the window.
     FileDrop(FileDropEvent),
 }
 
 impl PlatformInput {
-    pub fn position(&self) -> Option<Point<Pixels>> {
-        match self {
-            PlatformInput::KeyDown { .. } => None,
-            PlatformInput::KeyUp { .. } => None,
-            PlatformInput::ModifiersChanged { .. } => None,
-            PlatformInput::MouseDown(event) => Some(event.position),
-            PlatformInput::MouseUp(event) => Some(event.position),
-            PlatformInput::MouseMove(event) => Some(event.position),
-            PlatformInput::MouseExited(event) => Some(event.position),
-            PlatformInput::ScrollWheel(event) => Some(event.position),
-            PlatformInput::FileDrop(FileDropEvent::Exited) => None,
-            PlatformInput::FileDrop(
-                FileDropEvent::Entered { position, .. }
-                | FileDropEvent::Pending { position, .. }
-                | FileDropEvent::Submit { position, .. },
-            ) => Some(*position),
-        }
-    }
-
-    pub fn mouse_event(&self) -> Option<&dyn Any> {
+    pub(crate) fn mouse_event(&self) -> Option<&dyn Any> {
         match self {
             PlatformInput::KeyDown { .. } => None,
             PlatformInput::KeyUp { .. } => None,
@@ -339,7 +417,7 @@ impl PlatformInput {
         }
     }
 
-    pub fn keyboard_event(&self) -> Option<&dyn Any> {
+    pub(crate) fn keyboard_event(&self) -> Option<&dyn Any> {
         match self {
             PlatformInput::KeyDown(event) => Some(event),
             PlatformInput::KeyUp(event) => Some(event),

crates/gpui/src/key_dispatch.rs ๐Ÿ”—

@@ -1,6 +1,57 @@
+/// KeyDispatch is where GPUI deals with binding actions to key events.
+///
+/// The key pieces to making a key binding work are to define an action,
+/// implement a method that takes that action as a type parameter,
+/// and then to register the action during render on a focused node
+/// with a keymap context:
+///
+/// ```rust
+/// actions!(editor,[Undo, Redo]);;
+///
+/// impl Editor {
+///   fn undo(&mut self, _: &Undo, _cx: &mut ViewContext<Self>) { ... }
+///   fn redo(&mut self, _: &Redo, _cx: &mut ViewContext<Self>) { ... }
+/// }
+///
+/// impl Render for Editor {
+///   fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+///     div()
+///       .track_focus(&self.focus_handle)
+///       .keymap_context("Editor")
+///       .on_action(cx.listener(Editor::undo))
+///       .on_action(cx.listener(Editor::redo))
+///     ...
+///    }
+/// }
+///```
+///
+/// The keybindings themselves are managed independently by calling cx.bind_keys().
+/// (Though mostly when developing Zed itself, you just need to add a new line to
+///  assets/keymaps/default.json).
+///
+/// ```rust
+/// cx.bind_keys([
+///   KeyBinding::new("cmd-z", Editor::undo, Some("Editor")),
+///   KeyBinding::new("cmd-shift-z", Editor::redo, Some("Editor")),
+/// ])
+/// ```
+///
+/// With all of this in place, GPUI will ensure that if you have an Editor that contains
+/// the focus, hitting cmd-z will Undo.
+///
+/// In real apps, it is a little more complicated than this, because typically you have
+/// several nested views that each register keyboard handlers. In this case action matching
+/// bubbles up from the bottom. For example in Zed, the Workspace is the top-level view, which contains Pane's, which contain Editors. If there are conflicting keybindings defined
+/// then the Editor's bindings take precedence over the Pane's bindings, which take precedence over the Workspace.
+///
+/// In GPUI, keybindings are not limited to just single keystrokes, you can define
+/// sequences by separating the keys with a space:
+///
+///  KeyBinding::new("cmd-k left", pane::SplitLeft, Some("Pane"))
+///
 use crate::{
-    Action, ActionRegistry, DispatchPhase, EntityId, FocusId, KeyBinding, KeyContext, KeyMatch,
-    Keymap, Keystroke, KeystrokeMatcher, WindowContext,
+    Action, ActionRegistry, DispatchPhase, ElementContext, EntityId, FocusId, KeyBinding,
+    KeyContext, Keymap, KeymatchResult, Keystroke, KeystrokeMatcher, WindowContext,
 };
 use collections::FxHashMap;
 use parking_lot::Mutex;
@@ -13,7 +64,7 @@ use std::{
 };
 
 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
-pub struct DispatchNodeId(usize);
+pub(crate) struct DispatchNodeId(usize);
 
 pub(crate) struct DispatchTree {
     node_stack: Vec<DispatchNodeId>,
@@ -36,7 +87,7 @@ pub(crate) struct DispatchNode {
     parent: Option<DispatchNodeId>,
 }
 
-type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>;
+type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut ElementContext)>;
 
 #[derive(Clone)]
 pub(crate) struct DispatchActionListener {
@@ -272,30 +323,51 @@ impl DispatchTree {
             .collect()
     }
 
+    // dispatch_key pushses the next keystroke into any key binding matchers.
+    // any matching bindings are returned in the order that they should be dispatched:
+    // * First by length of binding (so if you have a binding for "b" and "ab", the "ab" binding fires first)
+    // * Secondly by depth in the tree (so if Editor has a binding for "b" and workspace a
+    // binding for "b", the Editor action fires first).
     pub fn dispatch_key(
         &mut self,
         keystroke: &Keystroke,
-        context: &[KeyContext],
-    ) -> Vec<Box<dyn Action>> {
-        if !self.keystroke_matchers.contains_key(context) {
-            let keystroke_contexts = context.iter().cloned().collect();
-            self.keystroke_matchers.insert(
-                keystroke_contexts,
-                KeystrokeMatcher::new(self.keymap.clone()),
-            );
-        }
+        dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
+    ) -> KeymatchResult {
+        let mut bindings = SmallVec::<[KeyBinding; 1]>::new();
+        let mut pending = false;
+
+        let mut context_stack: SmallVec<[KeyContext; 4]> = SmallVec::new();
+        for node_id in dispatch_path {
+            let node = self.node(*node_id);
 
-        let keystroke_matcher = self.keystroke_matchers.get_mut(context).unwrap();
-        if let KeyMatch::Some(actions) = keystroke_matcher.match_keystroke(keystroke, context) {
-            // Clear all pending keystrokes when an action has been found.
-            for keystroke_matcher in self.keystroke_matchers.values_mut() {
-                keystroke_matcher.clear_pending();
+            if let Some(context) = node.context.clone() {
+                context_stack.push(context);
             }
+        }
 
-            actions
-        } else {
-            vec![]
+        while !context_stack.is_empty() {
+            let keystroke_matcher = self
+                .keystroke_matchers
+                .entry(context_stack.clone())
+                .or_insert_with(|| KeystrokeMatcher::new(self.keymap.clone()));
+
+            let result = keystroke_matcher.match_keystroke(keystroke, &context_stack);
+            pending = result.pending || pending;
+            for new_binding in result.bindings {
+                match bindings
+                    .iter()
+                    .position(|el| el.keystrokes.len() < new_binding.keystrokes.len())
+                {
+                    Some(idx) => {
+                        bindings.insert(idx, new_binding);
+                    }
+                    None => bindings.push(new_binding),
+                }
+            }
+            context_stack.pop();
         }
+
+        KeymatchResult { bindings, pending }
     }
 
     pub fn has_pending_keystrokes(&self) -> bool {

crates/gpui/src/keymap/binding.rs ๐Ÿ”—

@@ -2,6 +2,7 @@ use crate::{Action, KeyBindingContextPredicate, KeyMatch, Keystroke};
 use anyhow::Result;
 use smallvec::SmallVec;
 
+/// A keybinding and it's associated metadata, from the keymap.
 pub struct KeyBinding {
     pub(crate) action: Box<dyn Action>,
     pub(crate) keystrokes: SmallVec<[Keystroke; 2]>,
@@ -19,10 +20,12 @@ impl Clone for KeyBinding {
 }
 
 impl KeyBinding {
+    /// Construct a new keybinding from the given data.
     pub fn new<A: Action>(keystrokes: &str, action: A, context_predicate: Option<&str>) -> Self {
         Self::load(keystrokes, Box::new(action), context_predicate).unwrap()
     }
 
+    /// Load a keybinding from the given raw data.
     pub fn load(keystrokes: &str, action: Box<dyn Action>, context: Option<&str>) -> Result<Self> {
         let context = if let Some(context) = context {
             Some(KeyBindingContextPredicate::parse(context)?)
@@ -42,11 +45,12 @@ impl KeyBinding {
         })
     }
 
+    /// Check if the given keystrokes match this binding.
     pub fn match_keystrokes(&self, pending_keystrokes: &[Keystroke]) -> KeyMatch {
         if self.keystrokes.as_ref().starts_with(pending_keystrokes) {
             // If the binding is completed, push it onto the matches list
             if self.keystrokes.as_ref().len() == pending_keystrokes.len() {
-                KeyMatch::Some(vec![self.action.boxed_clone()])
+                KeyMatch::Matched
             } else {
                 KeyMatch::Pending
             }
@@ -55,10 +59,12 @@ impl KeyBinding {
         }
     }
 
+    /// Get the keystrokes associated with this binding
     pub fn keystrokes(&self) -> &[Keystroke] {
         self.keystrokes.as_slice()
     }
 
+    /// Get the action associated with this binding
     pub fn action(&self) -> &dyn Action {
         self.action.as_ref()
     }

crates/gpui/src/keymap/context.rs ๐Ÿ”—

@@ -3,6 +3,10 @@ use anyhow::{anyhow, Result};
 use smallvec::SmallVec;
 use std::fmt;
 
+/// A datastructure for resolving whether an action should be dispatched
+/// at this point in the element tree. Contains a set of identifiers
+/// and/or key value pairs representing the current context for the
+/// keymap.
 #[derive(Clone, Default, Eq, PartialEq, Hash)]
 pub struct KeyContext(SmallVec<[ContextEntry; 1]>);
 
@@ -21,6 +25,11 @@ impl<'a> TryFrom<&'a str> for KeyContext {
 }
 
 impl KeyContext {
+    /// Parse a key context from a string.
+    /// The key context format is very simple:
+    /// - either a single identifier, such as `StatusBar`
+    /// - or a key value pair, such as `mode = visible`
+    /// - separated by whitespace, such as `StatusBar mode = visible`
     pub fn parse(source: &str) -> Result<Self> {
         let mut context = Self::default();
         let source = skip_whitespace(source);
@@ -53,14 +62,17 @@ impl KeyContext {
         Self::parse_expr(source, context)
     }
 
+    /// Check if this context is empty.
     pub fn is_empty(&self) -> bool {
         self.0.is_empty()
     }
 
+    /// Clear this context.
     pub fn clear(&mut self) {
         self.0.clear();
     }
 
+    /// Extend this context with another context.
     pub fn extend(&mut self, other: &Self) {
         for entry in &other.0 {
             if !self.contains(&entry.key) {
@@ -69,6 +81,7 @@ impl KeyContext {
         }
     }
 
+    /// Add an identifier to this context, if it's not already in this context.
     pub fn add<I: Into<SharedString>>(&mut self, identifier: I) {
         let key = identifier.into();
 
@@ -77,6 +90,7 @@ impl KeyContext {
         }
     }
 
+    /// Set a key value pair in this context, if it's not already set.
     pub fn set<S1: Into<SharedString>, S2: Into<SharedString>>(&mut self, key: S1, value: S2) {
         let key = key.into();
         if !self.contains(&key) {
@@ -87,10 +101,12 @@ impl KeyContext {
         }
     }
 
+    /// Check if this context contains a given identifier or key.
     pub fn contains(&self, key: &str) -> bool {
         self.0.iter().any(|entry| entry.key.as_ref() == key)
     }
 
+    /// Get the associated value for a given identifier or key.
     pub fn get(&self, key: &str) -> Option<&SharedString> {
         self.0
             .iter()
@@ -117,20 +133,31 @@ impl fmt::Debug for KeyContext {
     }
 }
 
+/// A datastructure for resolving whether an action should be dispatched
+/// Representing a small language for describing which contexts correspond
+/// to which actions.
 #[derive(Clone, Debug, Eq, PartialEq, Hash)]
 pub enum KeyBindingContextPredicate {
+    /// A predicate that will match a given identifier.
     Identifier(SharedString),
+    /// A predicate that will match a given key-value pair.
     Equal(SharedString, SharedString),
+    /// A predicate that will match a given key-value pair not being present.
     NotEqual(SharedString, SharedString),
+    /// A predicate that will match a given predicate appearing below another predicate.
+    /// in the element tree
     Child(
         Box<KeyBindingContextPredicate>,
         Box<KeyBindingContextPredicate>,
     ),
+    /// Predicate that will invert another predicate.
     Not(Box<KeyBindingContextPredicate>),
+    /// A predicate that will match if both of its children match.
     And(
         Box<KeyBindingContextPredicate>,
         Box<KeyBindingContextPredicate>,
     ),
+    /// A predicate that will match if either of its children match.
     Or(
         Box<KeyBindingContextPredicate>,
         Box<KeyBindingContextPredicate>,
@@ -138,6 +165,34 @@ pub enum KeyBindingContextPredicate {
 }
 
 impl KeyBindingContextPredicate {
+    /// Parse a string in the same format as the keymap's context field.
+    ///
+    /// A basic equivalence check against a set of identifiers can performed by
+    /// simply writing a string:
+    ///
+    /// `StatusBar` -> A predicate that will match a context with the identifier `StatusBar`
+    ///
+    /// You can also specify a key-value pair:
+    ///
+    /// `mode == visible` -> A predicate that will match a context with the key `mode`
+    ///                      with the value `visible`
+    ///
+    /// And a logical operations combining these two checks:
+    ///
+    /// `StatusBar && mode == visible` -> A predicate that will match a context with the
+    ///                                   identifier `StatusBar` and the key `mode`
+    ///                                   with the value `visible`
+    ///
+    ///
+    /// There is also a special child `>` operator that will match a predicate that is
+    /// below another predicate:
+    ///
+    /// `StatusBar > mode == visible` -> A predicate that will match a context identifier `StatusBar`
+    ///                                  and a child context that has the key `mode` with the
+    ///                                  value `visible`
+    ///
+    /// This syntax supports `!=`, `||` and `&&` as logical operators.
+    /// You can also preface an operation or check with a `!` to negate it.
     pub fn parse(source: &str) -> Result<Self> {
         let source = skip_whitespace(source);
         let (predicate, rest) = Self::parse_expr(source, 0)?;
@@ -148,6 +203,7 @@ impl KeyBindingContextPredicate {
         }
     }
 
+    /// Eval a predicate against a set of contexts, arranged from lowest to highest.
     pub fn eval(&self, contexts: &[KeyContext]) -> bool {
         let Some(context) = contexts.last() else {
             return false;

crates/gpui/src/keymap/keymap.rs ๐Ÿ”—

@@ -6,9 +6,12 @@ use std::{
     collections::HashMap,
 };
 
+/// An opaque identifier of which version of the keymap is currently active.
+/// The keymap's version is changed whenever bindings are added or removed.
 #[derive(Copy, Clone, Eq, PartialEq, Default)]
 pub struct KeymapVersion(usize);
 
+/// A collection of key bindings for the user's application.
 #[derive(Default)]
 pub struct Keymap {
     bindings: Vec<KeyBinding>,
@@ -19,16 +22,19 @@ pub struct Keymap {
 }
 
 impl Keymap {
+    /// Create a new keymap with the given bindings.
     pub fn new(bindings: Vec<KeyBinding>) -> Self {
         let mut this = Self::default();
         this.add_bindings(bindings);
         this
     }
 
+    /// Get the current version of the keymap.
     pub fn version(&self) -> KeymapVersion {
         self.version
     }
 
+    /// Add more bindings to the keymap.
     pub fn add_bindings<T: IntoIterator<Item = KeyBinding>>(&mut self, bindings: T) {
         let no_action_id = (NoAction {}).type_id();
 
@@ -51,6 +57,7 @@ impl Keymap {
         self.version.0 += 1;
     }
 
+    /// Reset this keymap to its initial state.
     pub fn clear(&mut self) {
         self.bindings.clear();
         self.binding_indices_by_action_id.clear();
@@ -77,6 +84,7 @@ impl Keymap {
             .filter(move |binding| binding.action().partial_eq(action))
     }
 
+    /// Check if the given binding is enabled, given a certain key context.
     pub fn binding_enabled(&self, binding: &KeyBinding, context: &[KeyContext]) -> bool {
         // If binding has a context predicate, it must match the current context,
         if let Some(predicate) = &binding.context_predicate {

crates/gpui/src/keymap/matcher.rs ๐Ÿ”—

@@ -1,13 +1,19 @@
-use crate::{Action, KeyContext, Keymap, KeymapVersion, Keystroke};
+use crate::{KeyBinding, KeyContext, Keymap, KeymapVersion, Keystroke};
 use parking_lot::Mutex;
+use smallvec::SmallVec;
 use std::sync::Arc;
 
-pub struct KeystrokeMatcher {
+pub(crate) struct KeystrokeMatcher {
     pending_keystrokes: Vec<Keystroke>,
     keymap: Arc<Mutex<Keymap>>,
     keymap_version: KeymapVersion,
 }
 
+pub struct KeymatchResult {
+    pub bindings: SmallVec<[KeyBinding; 1]>,
+    pub pending: bool,
+}
+
 impl KeystrokeMatcher {
     pub fn new(keymap: Arc<Mutex<Keymap>>) -> Self {
         let keymap_version = keymap.lock().version();
@@ -18,10 +24,6 @@ impl KeystrokeMatcher {
         }
     }
 
-    pub fn clear_pending(&mut self) {
-        self.pending_keystrokes.clear();
-    }
-
     pub fn has_pending_keystrokes(&self) -> bool {
         !self.pending_keystrokes.is_empty()
     }
@@ -35,11 +37,11 @@ impl KeystrokeMatcher {
     /// - KeyMatch::Complete(matches) =>
     ///         One or more bindings have received the necessary key presses.
     ///         Bindings added later will take precedence over earlier bindings.
-    pub fn match_keystroke(
+    pub(crate) fn match_keystroke(
         &mut self,
         keystroke: &Keystroke,
         context_stack: &[KeyContext],
-    ) -> KeyMatch {
+    ) -> KeymatchResult {
         let keymap = self.keymap.lock();
         // Clear pending keystrokes if the keymap has changed since the last matched keystroke.
         if keymap.version() != self.keymap_version {
@@ -48,7 +50,7 @@ impl KeystrokeMatcher {
         }
 
         let mut pending_key = None;
-        let mut found_actions = Vec::new();
+        let mut bindings = SmallVec::new();
 
         for binding in keymap.bindings().rev() {
             if !keymap.binding_enabled(binding, context_stack) {
@@ -58,8 +60,8 @@ impl KeystrokeMatcher {
             for candidate in keystroke.match_candidates() {
                 self.pending_keystrokes.push(candidate.clone());
                 match binding.match_keystrokes(&self.pending_keystrokes) {
-                    KeyMatch::Some(mut actions) => {
-                        found_actions.append(&mut actions);
+                    KeyMatch::Matched => {
+                        bindings.push(binding.clone());
                     }
                     KeyMatch::Pending => {
                         pending_key.get_or_insert(candidate);
@@ -70,399 +72,31 @@ impl KeystrokeMatcher {
             }
         }
 
-        if !found_actions.is_empty() {
-            self.pending_keystrokes.clear();
-            return KeyMatch::Some(found_actions);
+        if bindings.len() == 0 && pending_key.is_none() && self.pending_keystrokes.len() > 0 {
+            drop(keymap);
+            self.pending_keystrokes.remove(0);
+            return self.match_keystroke(keystroke, context_stack);
         }
 
-        if let Some(pending_key) = pending_key {
+        let pending = if let Some(pending_key) = pending_key {
             self.pending_keystrokes.push(pending_key);
-            KeyMatch::Pending
+            true
         } else {
             self.pending_keystrokes.clear();
-            KeyMatch::None
-        }
+            false
+        };
+
+        KeymatchResult { bindings, pending }
     }
 }
 
-#[derive(Debug)]
+/// The result of matching a keystroke against a given keybinding.
+/// - KeyMatch::None => No match is valid for this key given any pending keystrokes.
+/// - KeyMatch::Pending => There exist bindings that is still waiting for more keys.
+/// - KeyMatch::Some(matches) => One or more bindings have received the necessary key presses.
+#[derive(Debug, PartialEq)]
 pub enum KeyMatch {
     None,
     Pending,
-    Some(Vec<Box<dyn Action>>),
-}
-
-impl KeyMatch {
-    pub fn is_some(&self) -> bool {
-        matches!(self, KeyMatch::Some(_))
-    }
-
-    pub fn matches(self) -> Option<Vec<Box<dyn Action>>> {
-        match self {
-            KeyMatch::Some(matches) => Some(matches),
-            _ => None,
-        }
-    }
-}
-
-impl PartialEq for KeyMatch {
-    fn eq(&self, other: &Self) -> bool {
-        match (self, other) {
-            (KeyMatch::None, KeyMatch::None) => true,
-            (KeyMatch::Pending, KeyMatch::Pending) => true,
-            (KeyMatch::Some(a), KeyMatch::Some(b)) => {
-                if a.len() != b.len() {
-                    return false;
-                }
-
-                for (a, b) in a.iter().zip(b.iter()) {
-                    if !a.partial_eq(b.as_ref()) {
-                        return false;
-                    }
-                }
-
-                true
-            }
-            _ => false,
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-
-    use serde_derive::Deserialize;
-
-    use super::*;
-    use crate::{self as gpui, KeyBindingContextPredicate, Modifiers};
-    use crate::{actions, KeyBinding};
-
-    #[test]
-    fn test_keymap_and_view_ordering() {
-        actions!(test, [EditorAction, ProjectPanelAction]);
-
-        let mut editor = KeyContext::default();
-        editor.add("Editor");
-
-        let mut project_panel = KeyContext::default();
-        project_panel.add("ProjectPanel");
-
-        // Editor 'deeper' in than project panel
-        let dispatch_path = vec![project_panel, editor];
-
-        // But editor actions 'higher' up in keymap
-        let keymap = Keymap::new(vec![
-            KeyBinding::new("left", EditorAction, Some("Editor")),
-            KeyBinding::new("left", ProjectPanelAction, Some("ProjectPanel")),
-        ]);
-
-        let mut matcher = KeystrokeMatcher::new(Arc::new(Mutex::new(keymap)));
-
-        let matches = matcher
-            .match_keystroke(&Keystroke::parse("left").unwrap(), &dispatch_path)
-            .matches()
-            .unwrap();
-
-        assert!(matches[0].partial_eq(&EditorAction));
-        assert!(matches.get(1).is_none());
-    }
-
-    #[test]
-    fn test_multi_keystroke_match() {
-        actions!(test, [B, AB, C, D, DA, E, EF]);
-
-        let mut context1 = KeyContext::default();
-        context1.add("1");
-
-        let mut context2 = KeyContext::default();
-        context2.add("2");
-
-        let dispatch_path = vec![context2, context1];
-
-        let keymap = Keymap::new(vec![
-            KeyBinding::new("a b", AB, Some("1")),
-            KeyBinding::new("b", B, Some("2")),
-            KeyBinding::new("c", C, Some("2")),
-            KeyBinding::new("d", D, Some("1")),
-            KeyBinding::new("d", D, Some("2")),
-            KeyBinding::new("d a", DA, Some("2")),
-        ]);
-
-        let mut matcher = KeystrokeMatcher::new(Arc::new(Mutex::new(keymap)));
-
-        // Binding with pending prefix always takes precedence
-        assert_eq!(
-            matcher.match_keystroke(&Keystroke::parse("a").unwrap(), &dispatch_path),
-            KeyMatch::Pending,
-        );
-        // B alone doesn't match because a was pending, so AB is returned instead
-        assert_eq!(
-            matcher.match_keystroke(&Keystroke::parse("b").unwrap(), &dispatch_path),
-            KeyMatch::Some(vec![Box::new(AB)]),
-        );
-        assert!(!matcher.has_pending_keystrokes());
-
-        // Without an a prefix, B is dispatched like expected
-        assert_eq!(
-            matcher.match_keystroke(&Keystroke::parse("b").unwrap(), &dispatch_path[0..1]),
-            KeyMatch::Some(vec![Box::new(B)]),
-        );
-        assert!(!matcher.has_pending_keystrokes());
-
-        // If a is prefixed, C will not be dispatched because there
-        // was a pending binding for it
-        assert_eq!(
-            matcher.match_keystroke(&Keystroke::parse("a").unwrap(), &dispatch_path),
-            KeyMatch::Pending,
-        );
-        assert_eq!(
-            matcher.match_keystroke(&Keystroke::parse("c").unwrap(), &dispatch_path),
-            KeyMatch::None,
-        );
-        assert!(!matcher.has_pending_keystrokes());
-
-        // If a single keystroke matches multiple bindings in the tree
-        // only one of them is returned.
-        assert_eq!(
-            matcher.match_keystroke(&Keystroke::parse("d").unwrap(), &dispatch_path),
-            KeyMatch::Some(vec![Box::new(D)]),
-        );
-    }
-
-    #[test]
-    fn test_keystroke_parsing() {
-        assert_eq!(
-            Keystroke::parse("ctrl-p").unwrap(),
-            Keystroke {
-                key: "p".into(),
-                modifiers: Modifiers {
-                    control: true,
-                    alt: false,
-                    shift: false,
-                    command: false,
-                    function: false,
-                },
-                ime_key: None,
-            }
-        );
-
-        assert_eq!(
-            Keystroke::parse("alt-shift-down").unwrap(),
-            Keystroke {
-                key: "down".into(),
-                modifiers: Modifiers {
-                    control: false,
-                    alt: true,
-                    shift: true,
-                    command: false,
-                    function: false,
-                },
-                ime_key: None,
-            }
-        );
-
-        assert_eq!(
-            Keystroke::parse("shift-cmd--").unwrap(),
-            Keystroke {
-                key: "-".into(),
-                modifiers: Modifiers {
-                    control: false,
-                    alt: false,
-                    shift: true,
-                    command: true,
-                    function: false,
-                },
-                ime_key: None,
-            }
-        );
-    }
-
-    #[test]
-    fn test_context_predicate_parsing() {
-        use KeyBindingContextPredicate::*;
-
-        assert_eq!(
-            KeyBindingContextPredicate::parse("a && (b == c || d != e)").unwrap(),
-            And(
-                Box::new(Identifier("a".into())),
-                Box::new(Or(
-                    Box::new(Equal("b".into(), "c".into())),
-                    Box::new(NotEqual("d".into(), "e".into())),
-                ))
-            )
-        );
-
-        assert_eq!(
-            KeyBindingContextPredicate::parse("!a").unwrap(),
-            Not(Box::new(Identifier("a".into())),)
-        );
-    }
-
-    #[test]
-    fn test_context_predicate_eval() {
-        let predicate = KeyBindingContextPredicate::parse("a && b || c == d").unwrap();
-
-        let mut context = KeyContext::default();
-        context.add("a");
-        assert!(!predicate.eval(&[context]));
-
-        let mut context = KeyContext::default();
-        context.add("a");
-        context.add("b");
-        assert!(predicate.eval(&[context]));
-
-        let mut context = KeyContext::default();
-        context.add("a");
-        context.set("c", "x");
-        assert!(!predicate.eval(&[context]));
-
-        let mut context = KeyContext::default();
-        context.add("a");
-        context.set("c", "d");
-        assert!(predicate.eval(&[context]));
-
-        let predicate = KeyBindingContextPredicate::parse("!a").unwrap();
-        assert!(predicate.eval(&[KeyContext::default()]));
-    }
-
-    #[test]
-    fn test_context_child_predicate_eval() {
-        let predicate = KeyBindingContextPredicate::parse("a && b > c").unwrap();
-        let contexts = [
-            context_set(&["a", "b"]),
-            context_set(&["c", "d"]), // match this context
-            context_set(&["e", "f"]),
-        ];
-
-        assert!(!predicate.eval(&contexts[..=0]));
-        assert!(predicate.eval(&contexts[..=1]));
-        assert!(!predicate.eval(&contexts[..=2]));
-
-        let predicate = KeyBindingContextPredicate::parse("a && b > c && !d > e").unwrap();
-        let contexts = [
-            context_set(&["a", "b"]),
-            context_set(&["c", "d"]),
-            context_set(&["e"]),
-            context_set(&["a", "b"]),
-            context_set(&["c"]),
-            context_set(&["e"]), // only match this context
-            context_set(&["f"]),
-        ];
-
-        assert!(!predicate.eval(&contexts[..=0]));
-        assert!(!predicate.eval(&contexts[..=1]));
-        assert!(!predicate.eval(&contexts[..=2]));
-        assert!(!predicate.eval(&contexts[..=3]));
-        assert!(!predicate.eval(&contexts[..=4]));
-        assert!(predicate.eval(&contexts[..=5]));
-        assert!(!predicate.eval(&contexts[..=6]));
-
-        fn context_set(names: &[&str]) -> KeyContext {
-            let mut keymap = KeyContext::default();
-            names.iter().for_each(|name| keymap.add(name.to_string()));
-            keymap
-        }
-    }
-
-    #[test]
-    fn test_matcher() {
-        #[derive(Clone, Deserialize, PartialEq, Eq, Debug)]
-        pub struct A(pub String);
-        impl_actions!(test, [A]);
-        actions!(test, [B, Ab, Dollar, Quote, Ess, Backtick]);
-
-        #[derive(Clone, Debug, Eq, PartialEq)]
-        struct ActionArg {
-            a: &'static str,
-        }
-
-        let keymap = Keymap::new(vec![
-            KeyBinding::new("a", A("x".to_string()), Some("a")),
-            KeyBinding::new("b", B, Some("a")),
-            KeyBinding::new("a b", Ab, Some("a || b")),
-            KeyBinding::new("$", Dollar, Some("a")),
-            KeyBinding::new("\"", Quote, Some("a")),
-            KeyBinding::new("alt-s", Ess, Some("a")),
-            KeyBinding::new("ctrl-`", Backtick, Some("a")),
-        ]);
-
-        let mut context_a = KeyContext::default();
-        context_a.add("a");
-
-        let mut context_b = KeyContext::default();
-        context_b.add("b");
-
-        let mut matcher = KeystrokeMatcher::new(Arc::new(Mutex::new(keymap)));
-
-        // Basic match
-        assert_eq!(
-            matcher.match_keystroke(&Keystroke::parse("a").unwrap(), &[context_a.clone()]),
-            KeyMatch::Some(vec![Box::new(A("x".to_string()))])
-        );
-        matcher.clear_pending();
-
-        // Multi-keystroke match
-        assert_eq!(
-            matcher.match_keystroke(&Keystroke::parse("a").unwrap(), &[context_b.clone()]),
-            KeyMatch::Pending
-        );
-        assert_eq!(
-            matcher.match_keystroke(&Keystroke::parse("b").unwrap(), &[context_b.clone()]),
-            KeyMatch::Some(vec![Box::new(Ab)])
-        );
-        matcher.clear_pending();
-
-        // Failed matches don't interfere with matching subsequent keys
-        assert_eq!(
-            matcher.match_keystroke(&Keystroke::parse("x").unwrap(), &[context_a.clone()]),
-            KeyMatch::None
-        );
-        assert_eq!(
-            matcher.match_keystroke(&Keystroke::parse("a").unwrap(), &[context_a.clone()]),
-            KeyMatch::Some(vec![Box::new(A("x".to_string()))])
-        );
-        matcher.clear_pending();
-
-        let mut context_c = KeyContext::default();
-        context_c.add("c");
-
-        assert_eq!(
-            matcher.match_keystroke(
-                &Keystroke::parse("a").unwrap(),
-                &[context_c.clone(), context_b.clone()]
-            ),
-            KeyMatch::Pending
-        );
-        assert_eq!(
-            matcher.match_keystroke(&Keystroke::parse("b").unwrap(), &[context_b.clone()]),
-            KeyMatch::Some(vec![Box::new(Ab)])
-        );
-
-        // handle Czech $ (option + 4 key)
-        assert_eq!(
-            matcher.match_keystroke(&Keystroke::parse("alt-รง->$").unwrap(), &[context_a.clone()]),
-            KeyMatch::Some(vec![Box::new(Dollar)])
-        );
-
-        // handle Brazilian quote (quote key then space key)
-        assert_eq!(
-            matcher.match_keystroke(
-                &Keystroke::parse("space->\"").unwrap(),
-                &[context_a.clone()]
-            ),
-            KeyMatch::Some(vec![Box::new(Quote)])
-        );
-
-        // handle ctrl+` on a brazilian keyboard
-        assert_eq!(
-            matcher.match_keystroke(&Keystroke::parse("ctrl-->`").unwrap(), &[context_a.clone()]),
-            KeyMatch::Some(vec![Box::new(Backtick)])
-        );
-
-        // handle alt-s on a US keyboard
-        assert_eq!(
-            matcher.match_keystroke(&Keystroke::parse("alt-s->รŸ").unwrap(), &[context_a.clone()]),
-            KeyMatch::Some(vec![Box::new(Ess)])
-        );
-    }
+    Matched,
 }

crates/gpui/src/platform.rs ๐Ÿ”—

@@ -6,10 +6,10 @@ mod mac;
 mod test;
 
 use crate::{
-    Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId, FontMetrics,
-    FontRun, ForegroundExecutor, GlobalPixels, GlyphId, Keymap, LineLayout, Pixels, PlatformInput,
-    Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result, Scene, SharedString,
-    Size, TaskLabel,
+    Action, AnyWindowHandle, AsyncWindowContext, BackgroundExecutor, Bounds, DevicePixels, Font,
+    FontId, FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, Keymap, LineLayout,
+    Pixels, PlatformInput, Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result,
+    Scene, SharedString, Size, TaskLabel, WindowContext,
 };
 use anyhow::anyhow;
 use async_task::Runnable;
@@ -34,9 +34,9 @@ use uuid::Uuid;
 pub use app_menu::*;
 pub use keystroke::*;
 #[cfg(target_os = "macos")]
-pub use mac::*;
+pub(crate) use mac::*;
 #[cfg(any(test, feature = "test-support"))]
-pub use test::*;
+pub(crate) use test::*;
 use time::UtcOffset;
 
 #[cfg(target_os = "macos")]
@@ -69,11 +69,10 @@ pub(crate) trait Platform: 'static {
     fn set_display_link_output_callback(
         &self,
         display_id: DisplayId,
-        callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>,
+        callback: Box<dyn FnMut() + Send>,
     );
     fn start_display_link(&self, display_id: DisplayId);
     fn stop_display_link(&self, display_id: DisplayId);
-    // fn add_status_item(&self, _handle: AnyWindowHandle) -> Box<dyn PlatformWindow>;
 
     fn open_url(&self, url: &str);
     fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>);
@@ -149,8 +148,8 @@ pub(crate) trait PlatformWindow {
     fn mouse_position(&self) -> Point<Pixels>;
     fn modifiers(&self) -> Modifiers;
     fn as_any_mut(&mut self) -> &mut dyn Any;
-    fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>);
-    fn take_input_handler(&mut self) -> Option<Box<dyn PlatformInputHandler>>;
+    fn set_input_handler(&mut self, input_handler: PlatformInputHandler);
+    fn take_input_handler(&mut self) -> Option<PlatformInputHandler>;
     fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize>;
     fn activate(&self);
     fn set_title(&mut self, title: &str);
@@ -325,30 +324,175 @@ impl From<TileId> for etagere::AllocId {
     }
 }
 
-pub trait PlatformInputHandler: 'static {
-    fn selected_text_range(&mut self) -> Option<Range<usize>>;
-    fn marked_text_range(&mut self) -> Option<Range<usize>>;
-    fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String>;
-    fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str);
+pub(crate) struct PlatformInputHandler {
+    cx: AsyncWindowContext,
+    handler: Box<dyn InputHandler>,
+}
+
+impl PlatformInputHandler {
+    pub fn new(cx: AsyncWindowContext, handler: Box<dyn InputHandler>) -> Self {
+        Self { cx, handler }
+    }
+
+    fn selected_text_range(&mut self) -> Option<Range<usize>> {
+        self.cx
+            .update(|cx| self.handler.selected_text_range(cx))
+            .ok()
+            .flatten()
+    }
+
+    fn marked_text_range(&mut self) -> Option<Range<usize>> {
+        self.cx
+            .update(|cx| self.handler.marked_text_range(cx))
+            .ok()
+            .flatten()
+    }
+
+    fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String> {
+        self.cx
+            .update(|cx| self.handler.text_for_range(range_utf16, cx))
+            .ok()
+            .flatten()
+    }
+
+    fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str) {
+        self.cx
+            .update(|cx| {
+                self.handler
+                    .replace_text_in_range(replacement_range, text, cx);
+            })
+            .ok();
+    }
+
+    fn replace_and_mark_text_in_range(
+        &mut self,
+        range_utf16: Option<Range<usize>>,
+        new_text: &str,
+        new_selected_range: Option<Range<usize>>,
+    ) {
+        self.cx
+            .update(|cx| {
+                self.handler.replace_and_mark_text_in_range(
+                    range_utf16,
+                    new_text,
+                    new_selected_range,
+                    cx,
+                )
+            })
+            .ok();
+    }
+
+    fn unmark_text(&mut self) {
+        self.cx.update(|cx| self.handler.unmark_text(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))
+            .ok()
+            .flatten()
+    }
+
+    pub(crate) fn flush_pending_input(&mut self, input: &str, cx: &mut WindowContext) {
+        let Some(range) = self.handler.selected_text_range(cx) else {
+            return;
+        };
+        self.handler.replace_text_in_range(Some(range), &input, cx);
+    }
+}
+
+/// Zed's interface for handling text input from the platform's IME system
+/// This is currently a 1:1 exposure of the NSTextInputClient API:
+///
+/// <https://developer.apple.com/documentation/appkit/nstextinputclient>
+pub trait InputHandler: 'static {
+    /// Get the range of the user's currently selected text, if any
+    /// Corresponds to [selectedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438242-selectedrange)
+    ///
+    /// Return value is in terms of UTF-16 characters, from 0 to the length of the document
+    fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>>;
+
+    /// 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>>;
+
+    /// 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)
+    ///
+    /// range_utf16 is in terms of UTF-16 characters
+    fn text_for_range(
+        &mut self,
+        range_utf16: Range<usize>,
+        cx: &mut WindowContext,
+    ) -> Option<String>;
+
+    /// Replace the text in the given document range with the given text
+    /// Corresponds to [insertText(_:replacementRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438258-inserttext)
+    ///
+    /// replacement_range is in terms of UTF-16 characters
+    fn replace_text_in_range(
+        &mut self,
+        replacement_range: Option<Range<usize>>,
+        text: &str,
+        cx: &mut WindowContext,
+    );
+
+    /// Replace the text in the given document range with the given text,
+    /// and mark the given text as part of of an IME 'composing' state
+    /// Corresponds to [setMarkedText(_:selectedRange:replacementRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438246-setmarkedtext)
+    ///
+    /// range_utf16 is in terms of UTF-16 characters
+    /// new_selected_range is in terms of UTF-16 characters
     fn replace_and_mark_text_in_range(
         &mut self,
         range_utf16: Option<Range<usize>>,
         new_text: &str,
         new_selected_range: Option<Range<usize>>,
+        cx: &mut WindowContext,
     );
-    fn unmark_text(&mut self);
-    fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>>;
+
+    /// 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);
+
+    /// 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)
+    ///
+    /// This is used for positioning the IME candidate window
+    fn bounds_for_range(
+        &mut self,
+        range_utf16: Range<usize>,
+        cx: &mut WindowContext,
+    ) -> Option<Bounds<Pixels>>;
 }
 
+/// The variables that can be configured when creating a new window
 #[derive(Debug)]
 pub struct WindowOptions {
+    /// The initial bounds of the window
     pub bounds: WindowBounds,
+
+    /// The titlebar configuration of the window
     pub titlebar: Option<TitlebarOptions>,
+
+    /// Whether the window should be centered on the screen
     pub center: bool,
+
+    /// Whether the window should be focused when created
     pub focus: bool,
+
+    /// Whether the window should be shown when created
     pub show: bool,
+
+    /// The kind of window to create
     pub kind: WindowKind,
+
+    /// Whether the window should be movable by the user
     pub is_movable: bool,
+
+    /// The display to create the window on
     pub display_id: Option<DisplayId>,
 }
 
@@ -371,46 +515,67 @@ impl Default for WindowOptions {
     }
 }
 
+/// The options that can be configured for a window's titlebar
 #[derive(Debug, Default)]
 pub struct TitlebarOptions {
+    /// The initial title of the window
     pub title: Option<SharedString>,
-    pub appears_transparent: bool,
-    pub traffic_light_position: Option<Point<Pixels>>,
-}
 
-#[derive(Copy, Clone, Debug)]
-pub enum Appearance {
-    Light,
-    VibrantLight,
-    Dark,
-    VibrantDark,
-}
+    /// Whether the titlebar should appear transparent
+    pub appears_transparent: bool,
 
-impl Default for Appearance {
-    fn default() -> Self {
-        Self::Light
-    }
+    /// The position of the macOS traffic light buttons
+    pub traffic_light_position: Option<Point<Pixels>>,
 }
 
+/// The kind of window to create
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum WindowKind {
+    /// A normal application window
     Normal,
+
+    /// A window that appears above all other windows, usually used for alerts or popups
+    /// use sparingly!
     PopUp,
 }
 
+/// Which bounds algorithm to use for the initial size a window
 #[derive(Copy, Clone, Debug, PartialEq, Default)]
 pub enum WindowBounds {
+    /// The window should be full screen, on macOS this corresponds to the full screen feature
     Fullscreen,
+
+    /// Make the window as large as the current display's size.
     #[default]
     Maximized,
+
+    /// Set the window to the given size in pixels
     Fixed(Bounds<GlobalPixels>),
 }
 
+/// The appearance of the window, as defined by the operating system
+/// On macOS, this corresponds to named [NSAppearance](https://developer.apple.com/documentation/appkit/nsappearance)
+/// values
 #[derive(Copy, Clone, Debug)]
 pub enum WindowAppearance {
+    /// A light appearance
+    ///
+    /// on macOS, this corresponds to the `aqua` appearance
     Light,
+
+    /// A light appearance with vibrant colors
+    ///
+    /// on macOS, this corresponds to the `NSAppearanceNameVibrantLight` appearance
     VibrantLight,
+
+    /// A dark appearance
+    ///
+    /// on macOS, this corresponds to the `darkAqua` appearance
     Dark,
+
+    /// A dark appearance with vibrant colors
+    ///
+    /// on macOS, this corresponds to the `NSAppearanceNameVibrantDark` appearance
     VibrantDark,
 }
 
@@ -420,40 +585,102 @@ impl Default for WindowAppearance {
     }
 }
 
+/// The options that can be configured for a file dialog prompt
 #[derive(Copy, Clone, Debug)]
 pub struct PathPromptOptions {
+    /// Should the prompt allow files to be selected?
     pub files: bool,
+    /// Should the prompt allow directories to be selected?
     pub directories: bool,
+    /// Should the prompt allow multiple files to be selected?
     pub multiple: bool,
 }
 
+/// What kind of prompt styling to show
 #[derive(Copy, Clone, Debug)]
 pub enum PromptLevel {
+    /// A prompt that is shown when the user should be notified of something
     Info,
+
+    /// A prompt that is shown when the user needs to be warned of a potential problem
     Warning,
+
+    /// A prompt that is shown when a critical problem has occurred
     Critical,
 }
 
 /// The style of the cursor (pointer)
 #[derive(Copy, Clone, Debug)]
 pub enum CursorStyle {
+    /// The default cursor
     Arrow,
+
+    /// A text input cursor
+    /// corresponds to the CSS cursor value `text`
     IBeam,
+
+    /// A crosshair cursor
+    /// corresponds to the CSS cursor value `crosshair`
     Crosshair,
+
+    /// A closed hand cursor
+    /// corresponds to the CSS cursor value `grabbing`
     ClosedHand,
+
+    /// An open hand cursor
+    /// corresponds to the CSS cursor value `grab`
     OpenHand,
+
+    /// A pointing hand cursor
+    /// corresponds to the CSS cursor value `pointer`
     PointingHand,
+
+    /// A resize left cursor
+    /// corresponds to the CSS cursor value `w-resize`
     ResizeLeft,
+
+    /// A resize right cursor
+    /// corresponds to the CSS cursor value `e-resize`
     ResizeRight,
+
+    /// A resize cursor to the left and right
+    /// corresponds to the CSS cursor value `col-resize`
     ResizeLeftRight,
+
+    /// A resize up cursor
+    /// corresponds to the CSS cursor value `n-resize`
     ResizeUp,
+
+    /// A resize down cursor
+    /// corresponds to the CSS cursor value `s-resize`
     ResizeDown,
+
+    /// A resize cursor directing up and down
+    /// corresponds to the CSS cursor value `row-resize`
     ResizeUpDown,
+
+    /// A cursor indicating that something will disappear if moved here
+    /// Does not correspond to a CSS cursor value
     DisappearingItem,
+
+    /// A text input cursor for vertical layout
+    /// corresponds to the CSS cursor value `vertical-text`
     IBeamCursorForVerticalLayout,
+
+    /// A cursor indicating that the operation is not allowed
+    /// corresponds to the CSS cursor value `not-allowed`
     OperationNotAllowed,
+
+    /// A cursor indicating that the operation will result in a link
+    /// corresponds to the CSS cursor value `alias`
     DragLink,
+
+    /// A cursor indicating that the operation will result in a copy
+    /// corresponds to the CSS cursor value `copy`
     DragCopy,
+
+    /// A cursor indicating that the operation will result in a context menu
+    /// corresponds to the CSS cursor value `context-menu`
     ContextualMenu,
 }
 
@@ -463,6 +690,7 @@ impl Default for CursorStyle {
     }
 }
 
+/// A datastructure representing a semantic version number
 #[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Serialize)]
 pub struct SemanticVersion {
     major: usize,
@@ -501,6 +729,7 @@ impl Display for SemanticVersion {
     }
 }
 
+/// A clipboard item that should be copied to the clipboard
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct ClipboardItem {
     pub(crate) text: String,
@@ -508,6 +737,7 @@ pub struct ClipboardItem {
 }
 
 impl ClipboardItem {
+    /// Create a new clipboard item with the given text
     pub fn new(text: String) -> Self {
         Self {
             text,
@@ -515,15 +745,18 @@ impl ClipboardItem {
         }
     }
 
+    /// Create a new clipboard item with the given text and metadata
     pub fn with_metadata<T: Serialize>(mut self, metadata: T) -> Self {
         self.metadata = Some(serde_json::to_string(&metadata).unwrap());
         self
     }
 
+    /// Get the text of the clipboard item
     pub fn text(&self) -> &String {
         &self.text
     }
 
+    /// Get the metadata of the clipboard item
     pub fn metadata<T>(&self) -> Option<T>
     where
         T: for<'a> Deserialize<'a>,

crates/gpui/src/platform/app_menu.rs ๐Ÿ”—

@@ -1,30 +1,49 @@
 use crate::{Action, AppContext, Platform};
 use util::ResultExt;
 
+/// A menu of the application, either a main menu or a submenu
 pub struct Menu<'a> {
+    /// The name of the menu
     pub name: &'a str,
+
+    /// The items in the menu
     pub items: Vec<MenuItem<'a>>,
 }
 
+/// The different kinds of items that can be in a menu
 pub enum MenuItem<'a> {
+    /// A separator between items
     Separator,
+
+    /// A submenu
     Submenu(Menu<'a>),
+
+    /// An action that can be performed
     Action {
+        /// The name of this menu item
         name: &'a str,
+
+        /// the action to perform when this menu item is selected
         action: Box<dyn Action>,
+
+        /// The OS Action that corresponds to this action, if any
+        /// See [`OsAction`] for more information
         os_action: Option<OsAction>,
     },
 }
 
 impl<'a> MenuItem<'a> {
+    /// Creates a new menu item that is a separator
     pub fn separator() -> Self {
         Self::Separator
     }
 
+    /// Creates a new menu item that is a submenu
     pub fn submenu(menu: Menu<'a>) -> Self {
         Self::Submenu(menu)
     }
 
+    /// Creates a new menu item that invokes an action
     pub fn action(name: &'a str, action: impl Action) -> Self {
         Self::Action {
             name,
@@ -33,6 +52,7 @@ impl<'a> MenuItem<'a> {
         }
     }
 
+    /// Creates a new menu item that invokes an action and has an OS action
     pub fn os_action(name: &'a str, action: impl Action, os_action: OsAction) -> Self {
         Self::Action {
             name,
@@ -42,13 +62,31 @@ impl<'a> MenuItem<'a> {
     }
 }
 
+// TODO: As part of the global selections refactor, these should
+// be moved to GPUI-provided actions that make this association
+// without leaking the platform details to GPUI users
+
+/// OS actions are actions that are recognized by the operating system
+/// This allows the operating system to provide specialized behavior for
+/// these actions
 #[derive(Copy, Clone, Eq, PartialEq)]
 pub enum OsAction {
+    /// The 'cut' action
     Cut,
+
+    /// The 'copy' action
     Copy,
+
+    /// The 'paste' action
     Paste,
+
+    /// The 'select all' action
     SelectAll,
+
+    /// The 'undo' action
     Undo,
+
+    /// The 'redo' action
     Redo,
 }
 

crates/gpui/src/platform/keystroke.rs ๐Ÿ”—

@@ -3,51 +3,60 @@ use serde::Deserialize;
 use smallvec::SmallVec;
 use std::fmt::Write;
 
+/// A keystroke and associated metadata generated by the platform
 #[derive(Clone, Debug, Eq, PartialEq, Default, Deserialize, Hash)]
 pub struct Keystroke {
+    /// the state of the modifier keys at the time the keystroke was generated
     pub modifiers: Modifiers,
+
     /// key is the character printed on the key that was pressed
     /// e.g. for option-s, key is "s"
     pub key: String,
+
     /// ime_key is the character inserted by the IME engine when that key was pressed.
     /// e.g. for option-s, ime_key is "รŸ"
     pub ime_key: Option<String>,
 }
 
 impl Keystroke {
-    // When matching a key we cannot know whether the user intended to type
-    // the ime_key or the key. On some non-US keyboards keys we use in our
-    // bindings are behind option (for example `$` is typed `alt-รง` on a Czech keyboard),
-    // and on some keyboards the IME handler converts a sequence of keys into a
-    // specific character (for example `"` is typed as `" space` on a brazilian keyboard).
-    pub fn match_candidates(&self) -> SmallVec<[Keystroke; 2]> {
+    /// When matching a key we cannot know whether the user intended to type
+    /// the ime_key or the key itself. On some non-US keyboards keys we use in our
+    /// bindings are behind option (for example `$` is typed `alt-รง` on a Czech keyboard),
+    /// and on some keyboards the IME handler converts a sequence of keys into a
+    /// specific character (for example `"` is typed as `" space` on a brazilian keyboard).
+    ///
+    /// This method generates a list of potential keystroke candidates that could be matched
+    /// against when resolving a keybinding.
+    pub(crate) fn match_candidates(&self) -> SmallVec<[Keystroke; 2]> {
         let mut possibilities = SmallVec::new();
         match self.ime_key.as_ref() {
-            None => possibilities.push(self.clone()),
             Some(ime_key) => {
-                possibilities.push(Keystroke {
-                    modifiers: Modifiers {
-                        control: self.modifiers.control,
-                        alt: false,
-                        shift: false,
-                        command: false,
-                        function: false,
-                    },
-                    key: ime_key.to_string(),
-                    ime_key: None,
-                });
+                if ime_key != &self.key {
+                    possibilities.push(Keystroke {
+                        modifiers: Modifiers {
+                            control: self.modifiers.control,
+                            alt: false,
+                            shift: false,
+                            command: false,
+                            function: false,
+                        },
+                        key: ime_key.to_string(),
+                        ime_key: None,
+                    });
+                }
                 possibilities.push(Keystroke {
                     ime_key: None,
                     ..self.clone()
                 });
             }
+            None => possibilities.push(self.clone()),
         }
         possibilities
     }
 
     /// key syntax is:
     /// [ctrl-][alt-][shift-][cmd-][fn-]key[->ime_key]
-    /// ime_key is only used for generating test events,
+    /// ime_key syntax is only used for generating test events,
     /// when matching a key with an ime_key set will be matched without it.
     pub fn parse(source: &str) -> anyhow::Result<Self> {
         let mut control = false;
@@ -135,16 +144,29 @@ impl std::fmt::Display for Keystroke {
     }
 }
 
+/// The state of the modifier keys at some point in time
 #[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Deserialize, Hash)]
 pub struct Modifiers {
+    /// The control key
     pub control: bool,
+
+    /// The alt key
+    /// Sometimes also known as the 'meta' key
     pub alt: bool,
+
+    /// The shift key
     pub shift: bool,
+
+    /// The command key, on macos
+    /// the windows key, on windows
     pub command: bool,
+
+    /// The function key
     pub function: bool,
 }
 
 impl Modifiers {
+    /// Returns true if any modifier key is pressed
     pub fn modified(&self) -> bool {
         self.control || self.alt || self.shift || self.command || self.function
     }

crates/gpui/src/platform/mac.rs ๐Ÿ”—

@@ -21,13 +21,13 @@ use metal_renderer::*;
 use objc::runtime::{BOOL, NO, YES};
 use std::ops::Range;
 
-pub use dispatcher::*;
-pub use display::*;
-pub use display_linker::*;
-pub use metal_atlas::*;
-pub use platform::*;
-pub use text_system::*;
-pub use window::*;
+pub(crate) use dispatcher::*;
+pub(crate) use display::*;
+pub(crate) use display_linker::*;
+pub(crate) use metal_atlas::*;
+pub(crate) use platform::*;
+pub(crate) use text_system::*;
+pub(crate) use window::*;
 
 trait BoolExt {
     fn to_objc(self) -> BOOL;

crates/gpui/src/platform/mac/dispatcher.rs ๐Ÿ”—

@@ -24,7 +24,7 @@ pub(crate) fn dispatch_get_main_queue() -> dispatch_queue_t {
     unsafe { &_dispatch_main_q as *const _ as dispatch_queue_t }
 }
 
-pub struct MacDispatcher {
+pub(crate) struct MacDispatcher {
     parker: Arc<Mutex<Parker>>,
 }
 

crates/gpui/src/platform/mac/display.rs ๐Ÿ”—

@@ -3,18 +3,15 @@ use anyhow::Result;
 use cocoa::{
     appkit::NSScreen,
     base::{id, nil},
-    foundation::{NSDictionary, NSString},
+    foundation::{NSDictionary, NSPoint, NSRect, NSSize, NSString},
 };
 use core_foundation::uuid::{CFUUIDGetUUIDBytes, CFUUIDRef};
-use core_graphics::{
-    display::{CGDirectDisplayID, CGDisplayBounds, CGGetActiveDisplayList},
-    geometry::{CGPoint, CGRect, CGSize},
-};
+use core_graphics::display::{CGDirectDisplayID, CGDisplayBounds, CGGetActiveDisplayList};
 use objc::{msg_send, sel, sel_impl};
 use uuid::Uuid;
 
 #[derive(Debug)]
-pub struct MacDisplay(pub(crate) CGDirectDisplayID);
+pub(crate) struct MacDisplay(pub(crate) CGDirectDisplayID);
 
 unsafe impl Send for MacDisplay {}
 
@@ -24,11 +21,6 @@ impl MacDisplay {
         Self::all().find(|screen| screen.id() == id)
     }
 
-    /// Get the screen with the given persistent [`Uuid`].
-    pub fn find_by_uuid(uuid: Uuid) -> Option<Self> {
-        Self::all().find(|screen| screen.uuid().ok() == Some(uuid))
-    }
-
     /// Get the primary screen - the one with the menu bar, and whose bottom left
     /// corner is at the origin of the AppKit coordinate system.
     pub fn primary() -> Self {
@@ -77,14 +69,14 @@ extern "C" {
     fn CGDisplayCreateUUIDFromDisplayID(display: CGDirectDisplayID) -> CFUUIDRef;
 }
 
-/// Convert the given rectangle from CoreGraphics' native coordinate space to GPUI's coordinate space.
+/// Convert the given rectangle from Cocoa's coordinate space to GPUI's coordinate space.
 ///
-/// CoreGraphics' coordinate space has its origin at the bottom left of the primary screen,
+/// Cocoa's coordinate space has its origin at the bottom left of the primary screen,
 /// with the Y axis pointing upwards.
 ///
 /// Conversely, in GPUI's coordinate system, the origin is placed at the top left of the primary
-/// screen, with the Y axis pointing downwards.
-pub(crate) fn display_bounds_from_native(rect: CGRect) -> Bounds<GlobalPixels> {
+/// screen, with the Y axis pointing downwards (matching CoreGraphics)
+pub(crate) fn global_bounds_from_ns_rect(rect: NSRect) -> Bounds<GlobalPixels> {
     let primary_screen_size = unsafe { CGDisplayBounds(MacDisplay::primary().id().0) }.size;
 
     Bounds {
@@ -101,22 +93,22 @@ pub(crate) fn display_bounds_from_native(rect: CGRect) -> Bounds<GlobalPixels> {
     }
 }
 
-/// Convert the given rectangle from GPUI's coordinate system to CoreGraphics' native coordinate space.
+/// Convert the given rectangle from GPUI's coordinate system to Cocoa's native coordinate space.
 ///
-/// CoreGraphics' coordinate space has its origin at the bottom left of the primary screen,
+/// Cocoa's coordinate space has its origin at the bottom left of the primary screen,
 /// with the Y axis pointing upwards.
 ///
 /// Conversely, in GPUI's coordinate system, the origin is placed at the top left of the primary
-/// screen, with the Y axis pointing downwards.
-pub(crate) fn display_bounds_to_native(bounds: Bounds<GlobalPixels>) -> CGRect {
+/// screen, with the Y axis pointing downwards (matching CoreGraphics)
+pub(crate) fn global_bounds_to_ns_rect(bounds: Bounds<GlobalPixels>) -> NSRect {
     let primary_screen_height = MacDisplay::primary().bounds().size.height;
 
-    CGRect::new(
-        &CGPoint::new(
+    NSRect::new(
+        NSPoint::new(
             bounds.origin.x.into(),
             (primary_screen_height - bounds.origin.y - bounds.size.height).into(),
         ),
-        &CGSize::new(bounds.size.width.into(), bounds.size.height.into()),
+        NSSize::new(bounds.size.width.into(), bounds.size.height.into()),
     )
 }
 
@@ -155,8 +147,20 @@ impl PlatformDisplay for MacDisplay {
 
     fn bounds(&self) -> Bounds<GlobalPixels> {
         unsafe {
-            let native_bounds = CGDisplayBounds(self.0);
-            display_bounds_from_native(native_bounds)
+            // CGDisplayBounds is in "global display" coordinates, where 0 is
+            // the top left of the primary display.
+            let bounds = CGDisplayBounds(self.0);
+
+            Bounds {
+                origin: point(
+                    GlobalPixels(bounds.origin.x as f32),
+                    GlobalPixels(bounds.origin.y as f32),
+                ),
+                size: size(
+                    GlobalPixels(bounds.size.width as f32),
+                    GlobalPixels(bounds.size.height as f32),
+                ),
+            }
         }
     }
 }

crates/gpui/src/platform/mac/display_linker.rs ๐Ÿ”—

@@ -7,8 +7,6 @@ use std::{
 use crate::DisplayId;
 use collections::HashMap;
 use parking_lot::Mutex;
-pub use sys::CVSMPTETime as SmtpeTime;
-pub use sys::CVTimeStamp as VideoTimestamp;
 
 pub(crate) struct MacDisplayLinker {
     links: HashMap<DisplayId, MacDisplayLink>,
@@ -27,13 +25,13 @@ impl MacDisplayLinker {
     }
 }
 
-type OutputCallback = Mutex<Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>>;
+type OutputCallback = Mutex<Box<dyn FnMut() + Send>>;
 
 impl MacDisplayLinker {
     pub fn set_output_callback(
         &mut self,
         display_id: DisplayId,
-        output_callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>,
+        output_callback: Box<dyn FnMut() + Send>,
     ) {
         if let Some(mut system_link) = unsafe { sys::DisplayLink::on_display(display_id.0) } {
             let callback = Arc::new(Mutex::new(output_callback));
@@ -81,11 +79,11 @@ unsafe extern "C" fn trampoline(
     _flags_out: *mut i64,
     user_data: *mut c_void,
 ) -> i32 {
-    if let Some((current_time, output_time)) = current_time.as_ref().zip(output_time.as_ref()) {
+    if let Some((_current_time, _output_time)) = current_time.as_ref().zip(output_time.as_ref()) {
         let output_callback: Weak<OutputCallback> =
             Weak::from_raw(user_data as *mut OutputCallback);
         if let Some(output_callback) = output_callback.upgrade() {
-            (output_callback.lock())(current_time, output_time)
+            (output_callback.lock())()
         }
         mem::forget(output_callback);
     }
@@ -126,7 +124,7 @@ mod sys {
 
     #[repr(C)]
     #[derive(Clone, Copy)]
-    pub struct CVTimeStamp {
+    pub(crate) struct CVTimeStamp {
         pub version: u32,
         pub video_time_scale: i32,
         pub video_time: i64,
@@ -154,7 +152,7 @@ mod sys {
 
     #[repr(C)]
     #[derive(Clone, Copy, Default)]
-    pub struct CVSMPTETime {
+    pub(crate) struct CVSMPTETime {
         pub subframes: i16,
         pub subframe_divisor: i16,
         pub counter: u32,

crates/gpui/src/platform/mac/events.rs ๐Ÿ”—

@@ -83,7 +83,10 @@ unsafe fn read_modifiers(native_event: id) -> Modifiers {
 }
 
 impl PlatformInput {
-    pub unsafe fn from_native(native_event: id, window_height: Option<Pixels>) -> Option<Self> {
+    pub(crate) unsafe fn from_native(
+        native_event: id,
+        window_height: Option<Pixels>,
+    ) -> Option<Self> {
         let event_type = native_event.eventType();
 
         // Filter out event types that aren't in the NSEventType enum.

crates/gpui/src/platform/mac/metal_atlas.rs ๐Ÿ”—

@@ -10,10 +10,10 @@ use metal::Device;
 use parking_lot::Mutex;
 use std::borrow::Cow;
 
-pub struct MetalAtlas(Mutex<MetalAtlasState>);
+pub(crate) struct MetalAtlas(Mutex<MetalAtlasState>);
 
 impl MetalAtlas {
-    pub fn new(device: Device) -> Self {
+    pub(crate) fn new(device: Device) -> Self {
         MetalAtlas(Mutex::new(MetalAtlasState {
             device: AssertSend(device),
             monochrome_textures: Default::default(),

crates/gpui/src/platform/mac/platform.rs ๐Ÿ”—

@@ -3,7 +3,7 @@ use crate::{
     Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
     ForegroundExecutor, Keymap, MacDispatcher, MacDisplay, MacDisplayLinker, MacTextSystem,
     MacWindow, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay, PlatformInput,
-    PlatformTextSystem, PlatformWindow, Result, SemanticVersion, VideoTimestamp, WindowOptions,
+    PlatformTextSystem, PlatformWindow, Result, SemanticVersion, WindowOptions,
 };
 use anyhow::anyhow;
 use block::ConcreteBlock;
@@ -139,9 +139,9 @@ unsafe fn build_classes() {
     }
 }
 
-pub struct MacPlatform(Mutex<MacPlatformState>);
+pub(crate) struct MacPlatform(Mutex<MacPlatformState>);
 
-pub struct MacPlatformState {
+pub(crate) struct MacPlatformState {
     background_executor: BackgroundExecutor,
     foreground_executor: ForegroundExecutor,
     text_system: Arc<MacTextSystem>,
@@ -169,7 +169,7 @@ impl Default for MacPlatform {
 }
 
 impl MacPlatform {
-    pub fn new() -> Self {
+    pub(crate) fn new() -> Self {
         let dispatcher = Arc::new(MacDispatcher::new());
         Self(Mutex::new(MacPlatformState {
             background_executor: BackgroundExecutor::new(dispatcher.clone()),
@@ -475,10 +475,6 @@ impl Platform for MacPlatform {
         }
     }
 
-    // fn add_status_item(&self, _handle: AnyWindowHandle) -> Box<dyn platform::Window> {
-    //     Box::new(StatusItem::add(self.fonts()))
-    // }
-
     fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
         MacDisplay::all()
             .map(|screen| Rc::new(screen) as Rc<_>)
@@ -504,7 +500,7 @@ impl Platform for MacPlatform {
     fn set_display_link_output_callback(
         &self,
         display_id: DisplayId,
-        callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>,
+        callback: Box<dyn FnMut() + Send>,
     ) {
         self.0
             .lock()

crates/gpui/src/platform/mac/text_system.rs ๐Ÿ”—

@@ -41,7 +41,7 @@ use super::open_type;
 #[allow(non_upper_case_globals)]
 const kCGImageAlphaOnly: u32 = 7;
 
-pub struct MacTextSystem(RwLock<MacTextSystemState>);
+pub(crate) struct MacTextSystem(RwLock<MacTextSystemState>);
 
 struct MacTextSystemState {
     memory_source: MemSource,
@@ -54,7 +54,7 @@ struct MacTextSystemState {
 }
 
 impl MacTextSystem {
-    pub fn new() -> Self {
+    pub(crate) fn new() -> Self {
         Self(RwLock::new(MacTextSystemState {
             memory_source: MemSource::empty(),
             system_source: SystemSource::new(),

crates/gpui/src/platform/mac/window.rs ๐Ÿ”—

@@ -1,9 +1,9 @@
-use super::{display_bounds_from_native, ns_string, MacDisplay, MetalRenderer, NSRange};
+use super::{global_bounds_from_ns_rect, ns_string, MacDisplay, MetalRenderer, NSRange};
 use crate::{
-    display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, ExternalPaths,
-    FileDropEvent, ForegroundExecutor, GlobalPixels, KeyDownEvent, Keystroke, Modifiers,
-    ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
-    PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
+    global_bounds_to_ns_rect, platform::PlatformInputHandler, point, px, size, AnyWindowHandle,
+    Bounds, ExternalPaths, FileDropEvent, ForegroundExecutor, GlobalPixels, KeyDownEvent,
+    Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent,
+    MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point,
     PromptLevel, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions,
 };
 use block::ConcreteBlock;
@@ -220,7 +220,7 @@ unsafe fn build_classes() {
     };
 }
 
-pub fn convert_mouse_position(position: NSPoint, window_height: Pixels) -> Point<Pixels> {
+pub(crate) fn convert_mouse_position(position: NSPoint, window_height: Pixels) -> Point<Pixels> {
     point(
         px(position.x as f32),
         // MacOS screen coordinates are relative to bottom left
@@ -327,7 +327,7 @@ struct MacWindowState {
     should_close_callback: Option<Box<dyn FnMut() -> bool>>,
     close_callback: Option<Box<dyn FnOnce()>>,
     appearance_changed_callback: Option<Box<dyn FnMut()>>,
-    input_handler: Option<Box<dyn PlatformInputHandler>>,
+    input_handler: Option<PlatformInputHandler>,
     pending_key_down: Option<(KeyDownEvent, Option<InsertText>)>,
     last_key_equivalent: Option<KeyDownEvent>,
     synthetic_drag_counter: usize,
@@ -411,10 +411,8 @@ impl MacWindowState {
     }
 
     fn frame(&self) -> Bounds<GlobalPixels> {
-        unsafe {
-            let frame = NSWindow::frame(self.native_window);
-            display_bounds_from_native(mem::transmute::<NSRect, CGRect>(frame))
-        }
+        let frame = unsafe { NSWindow::frame(self.native_window) };
+        global_bounds_from_ns_rect(frame)
     }
 
     fn content_size(&self) -> Size<Pixels> {
@@ -448,7 +446,7 @@ impl MacWindowState {
 
 unsafe impl Send for MacWindowState {}
 
-pub struct MacWindow(Arc<Mutex<MacWindowState>>);
+pub(crate) struct MacWindow(Arc<Mutex<MacWindowState>>);
 
 impl MacWindow {
     pub fn open(
@@ -516,25 +514,6 @@ impl MacWindow {
                     NSArray::arrayWithObject(nil, NSFilenamesPboardType)
             ];
 
-            let screen = native_window.screen();
-            match options.bounds {
-                WindowBounds::Fullscreen => {
-                    native_window.toggleFullScreen_(nil);
-                }
-                WindowBounds::Maximized => {
-                    native_window.setFrame_display_(screen.visibleFrame(), YES);
-                }
-                WindowBounds::Fixed(bounds) => {
-                    let display_bounds = display.bounds();
-                    let frame = if bounds.intersects(&display_bounds) {
-                        display_bounds_to_native(bounds)
-                    } else {
-                        display_bounds_to_native(display_bounds)
-                    };
-                    native_window.setFrame_display_(mem::transmute::<CGRect, NSRect>(frame), YES);
-                }
-            }
-
             let native_view: id = msg_send![VIEW_CLASS, alloc];
             let native_view = NSView::init(native_view);
 
@@ -656,6 +635,27 @@ impl MacWindow {
                 native_window.orderFront_(nil);
             }
 
+            let screen = native_window.screen();
+            match options.bounds {
+                WindowBounds::Fullscreen => {
+                    // We need to toggle full screen asynchronously as doing so may
+                    // call back into the platform handlers.
+                    window.toggle_full_screen()
+                }
+                WindowBounds::Maximized => {
+                    native_window.setFrame_display_(screen.visibleFrame(), YES);
+                }
+                WindowBounds::Fixed(bounds) => {
+                    let display_bounds = display.bounds();
+                    let frame = if bounds.intersects(&display_bounds) {
+                        global_bounds_to_ns_rect(bounds)
+                    } else {
+                        global_bounds_to_ns_rect(display_bounds)
+                    };
+                    native_window.setFrame_display_(frame, YES);
+                }
+            }
+
             window.0.lock().move_traffic_light();
             pool.drain();
 
@@ -764,11 +764,11 @@ impl PlatformWindow for MacWindow {
         self
     }
 
-    fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>) {
+    fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
         self.0.as_ref().lock().input_handler = Some(input_handler);
     }
 
-    fn take_input_handler(&mut self) -> Option<Box<dyn PlatformInputHandler>> {
+    fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
         self.0.as_ref().lock().input_handler.take()
     }
 
@@ -1003,9 +1003,21 @@ impl PlatformWindow for MacWindow {
 }
 
 fn get_scale_factor(native_window: id) -> f32 {
-    unsafe {
+    let factor = unsafe {
         let screen: id = msg_send![native_window, screen];
         NSScreen::backingScaleFactor(screen) as f32
+    };
+
+    // We are not certain what triggers this, but it seems that sometimes
+    // this method would return 0 (https://github.com/zed-industries/community/issues/2422)
+    // It seems most likely that this would happen if the window has no screen
+    // (if it is off-screen), though we'd expect to see viewDidChangeBackingProperties before
+    // it was rendered for real.
+    // Regardless, attempt to avoid the issue here.
+    if factor == 0.0 {
+        2.
+    } else {
+        factor
     }
 }
 
@@ -1542,9 +1554,7 @@ extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NS
                 replacement_range,
                 text: text.to_string(),
             });
-            if text.to_string().to_ascii_lowercase() != pending_key_down.0.keystroke.key {
-                pending_key_down.0.keystroke.ime_key = Some(text.to_string());
-            }
+            pending_key_down.0.keystroke.ime_key = Some(text.to_string());
             window_state.lock().pending_key_down = Some(pending_key_down);
         }
     }
@@ -1761,13 +1771,13 @@ fn drag_event_position(window_state: &Mutex<MacWindowState>, dragging_info: id)
 
 fn with_input_handler<F, R>(window: &Object, f: F) -> Option<R>
 where
-    F: FnOnce(&mut dyn PlatformInputHandler) -> R,
+    F: FnOnce(&mut PlatformInputHandler) -> R,
 {
     let window_state = unsafe { get_window_state(window) };
     let mut lock = window_state.as_ref().lock();
     if let Some(mut input_handler) = lock.input_handler.take() {
         drop(lock);
-        let result = f(input_handler.as_mut());
+        let result = f(&mut input_handler);
         window_state.lock().input_handler = Some(input_handler);
         Some(result)
     } else {

crates/gpui/src/platform/mac/window_appearance.rs ๐Ÿ”—

@@ -8,7 +8,7 @@ use objc::{msg_send, sel, sel_impl};
 use std::ffi::CStr;
 
 impl WindowAppearance {
-    pub unsafe fn from_native(appearance: id) -> Self {
+    pub(crate) unsafe fn from_native(appearance: id) -> Self {
         let name: id = msg_send![appearance, name];
         if name == NSAppearanceNameVibrantLight {
             Self::VibrantLight

crates/gpui/src/platform/test.rs ๐Ÿ”—

@@ -3,7 +3,7 @@ mod display;
 mod platform;
 mod window;
 
-pub use dispatcher::*;
-pub use display::*;
-pub use platform::*;
-pub use window::*;
+pub(crate) use dispatcher::*;
+pub(crate) use display::*;
+pub(crate) use platform::*;
+pub(crate) use window::*;

crates/gpui/src/platform/test/dispatcher.rs ๐Ÿ”—

@@ -18,6 +18,7 @@ use util::post_inc;
 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
 struct TestDispatcherId(usize);
 
+#[doc(hidden)]
 pub struct TestDispatcher {
     id: TestDispatcherId,
     state: Arc<Mutex<TestDispatcherState>>,

crates/gpui/src/platform/test/display.rs ๐Ÿ”—

@@ -3,7 +3,7 @@ use anyhow::{Ok, Result};
 use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Point};
 
 #[derive(Debug)]
-pub struct TestDisplay {
+pub(crate) struct TestDisplay {
     id: DisplayId,
     uuid: uuid::Uuid,
     bounds: Bounds<GlobalPixels>,

crates/gpui/src/platform/test/platform.rs ๐Ÿ”—

@@ -15,7 +15,7 @@ use std::{
 };
 
 /// TestPlatform implements the Platform trait for use in tests.
-pub struct TestPlatform {
+pub(crate) struct TestPlatform {
     background_executor: BackgroundExecutor,
     foreground_executor: ForegroundExecutor,
 
@@ -178,20 +178,9 @@ impl Platform for TestPlatform {
     fn set_display_link_output_callback(
         &self,
         _display_id: DisplayId,
-        mut callback: Box<dyn FnMut(&crate::VideoTimestamp, &crate::VideoTimestamp) + Send>,
+        mut callback: Box<dyn FnMut() + Send>,
     ) {
-        let timestamp = crate::VideoTimestamp {
-            version: 0,
-            video_time_scale: 0,
-            video_time: 0,
-            host_time: 0,
-            rate_scalar: 0.0,
-            video_refresh_period: 0,
-            smpte_time: crate::SmtpeTime::default(),
-            flags: 0,
-            reserved: 0,
-        };
-        callback(&timestamp, &timestamp)
+        callback()
     }
 
     fn start_display_link(&self, _display_id: DisplayId) {}

crates/gpui/src/platform/test/window.rs ๐Ÿ”—

@@ -10,7 +10,7 @@ use std::{
     sync::{self, Arc},
 };
 
-pub struct TestWindowState {
+pub(crate) struct TestWindowState {
     pub(crate) bounds: WindowBounds,
     pub(crate) handle: AnyWindowHandle,
     display: Rc<dyn PlatformDisplay>,
@@ -23,11 +23,11 @@ pub struct TestWindowState {
     active_status_change_callback: Option<Box<dyn FnMut(bool)>>,
     resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
     moved_callback: Option<Box<dyn FnMut()>>,
-    input_handler: Option<Box<dyn PlatformInputHandler>>,
+    input_handler: Option<PlatformInputHandler>,
 }
 
 #[derive(Clone)]
-pub struct TestWindow(pub(crate) Arc<Mutex<TestWindowState>>);
+pub(crate) struct TestWindow(pub(crate) Arc<Mutex<TestWindowState>>);
 
 impl TestWindow {
     pub fn new(
@@ -96,7 +96,19 @@ impl TestWindow {
         result
     }
 
-    pub fn simulate_keystroke(&mut self, keystroke: Keystroke, is_held: bool) {
+    pub fn simulate_keystroke(&mut self, mut keystroke: Keystroke, is_held: bool) {
+        if keystroke.ime_key.is_none()
+            && !keystroke.modifiers.command
+            && !keystroke.modifiers.control
+            && !keystroke.modifiers.function
+        {
+            keystroke.ime_key = Some(if keystroke.modifiers.shift {
+                keystroke.key.to_ascii_uppercase().clone()
+            } else {
+                keystroke.key.clone()
+            })
+        }
+
         if self.simulate_input(PlatformInput::KeyDown(KeyDownEvent {
             keystroke: keystroke.clone(),
             is_held,
@@ -112,14 +124,12 @@ impl TestWindow {
             );
         };
         drop(lock);
-        let text = keystroke.ime_key.unwrap_or(keystroke.key);
-        input_handler.replace_text_in_range(None, &text);
+        if let Some(text) = keystroke.ime_key.as_ref() {
+            input_handler.replace_text_in_range(None, &text);
+        }
 
         self.0.lock().input_handler = Some(input_handler);
     }
-    pub fn edited(&self) -> bool {
-        self.0.lock().edited
-    }
 }
 
 impl PlatformWindow for TestWindow {
@@ -163,11 +173,11 @@ impl PlatformWindow for TestWindow {
         self
     }
 
-    fn set_input_handler(&mut self, input_handler: Box<dyn crate::PlatformInputHandler>) {
+    fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
         self.0.lock().input_handler = Some(input_handler);
     }
 
-    fn take_input_handler(&mut self) -> Option<Box<dyn PlatformInputHandler>> {
+    fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
         self.0.lock().input_handler.take()
     }
 
@@ -269,12 +279,12 @@ impl PlatformWindow for TestWindow {
     }
 }
 
-pub struct TestAtlasState {
+pub(crate) struct TestAtlasState {
     next_id: u32,
     tiles: HashMap<AtlasKey, AtlasTile>,
 }
 
-pub struct TestAtlas(Mutex<TestAtlasState>);
+pub(crate) struct TestAtlas(Mutex<TestAtlasState>);
 
 impl TestAtlas {
     pub fn new() -> Self {

crates/gpui/src/prelude.rs ๐Ÿ”—

@@ -1,5 +1,9 @@
+//! The GPUI prelude is a collection of traits and types that are widely used
+//! throughout the library. It is recommended to import this prelude into your
+//! application to avoid having to import each trait individually.
+
 pub use crate::{
-    BorrowAppContext, BorrowWindow, Context, Element, FocusableElement, InteractiveElement,
-    IntoElement, ParentElement, Refineable, Render, RenderOnce, StatefulInteractiveElement, Styled,
-    VisualContext,
+    util::FluentBuilder, BorrowAppContext, BorrowWindow, Context, Element, FocusableElement,
+    InteractiveElement, IntoElement, ParentElement, Refineable, Render, RenderOnce,
+    StatefulInteractiveElement, Styled, VisualContext,
 };

crates/gpui/src/scene.rs ๐Ÿ”—

@@ -10,12 +10,12 @@ pub(crate) type PointF = Point<f32>;
 #[allow(non_camel_case_types, unused)]
 pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
 
-pub type LayerId = u32;
-pub type DrawOrder = u32;
+pub(crate) type LayerId = u32;
+pub(crate) type DrawOrder = u32;
 
 #[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Hash)]
 #[repr(C)]
-pub struct ViewId {
+pub(crate) struct ViewId {
     low_bits: u32,
     high_bits: u32,
 }
@@ -38,7 +38,7 @@ impl From<ViewId> for EntityId {
 }
 
 #[derive(Default)]
-pub struct Scene {
+pub(crate) struct Scene {
     layers_by_order: BTreeMap<StackingOrder, LayerId>,
     orders_by_layer: BTreeMap<LayerId, StackingOrder>,
     pub(crate) shadows: Vec<Shadow>,
@@ -153,49 +153,49 @@ impl Scene {
         for shadow in prev_scene.shadows.drain(..) {
             if views.contains(&shadow.view_id.into()) {
                 let order = &prev_scene.orders_by_layer[&shadow.layer_id];
-                self.insert(&order, shadow);
+                self.insert(order, shadow);
             }
         }
 
         for quad in prev_scene.quads.drain(..) {
             if views.contains(&quad.view_id.into()) {
                 let order = &prev_scene.orders_by_layer[&quad.layer_id];
-                self.insert(&order, quad);
+                self.insert(order, quad);
             }
         }
 
         for path in prev_scene.paths.drain(..) {
             if views.contains(&path.view_id.into()) {
                 let order = &prev_scene.orders_by_layer[&path.layer_id];
-                self.insert(&order, path);
+                self.insert(order, path);
             }
         }
 
         for underline in prev_scene.underlines.drain(..) {
             if views.contains(&underline.view_id.into()) {
                 let order = &prev_scene.orders_by_layer[&underline.layer_id];
-                self.insert(&order, underline);
+                self.insert(order, underline);
             }
         }
 
         for sprite in prev_scene.monochrome_sprites.drain(..) {
             if views.contains(&sprite.view_id.into()) {
                 let order = &prev_scene.orders_by_layer[&sprite.layer_id];
-                self.insert(&order, sprite);
+                self.insert(order, sprite);
             }
         }
 
         for sprite in prev_scene.polychrome_sprites.drain(..) {
             if views.contains(&sprite.view_id.into()) {
                 let order = &prev_scene.orders_by_layer[&sprite.layer_id];
-                self.insert(&order, sprite);
+                self.insert(order, sprite);
             }
         }
 
         for surface in prev_scene.surfaces.drain(..) {
             if views.contains(&surface.view_id.into()) {
                 let order = &prev_scene.orders_by_layer[&surface.layer_id];
-                self.insert(&order, surface);
+                self.insert(order, surface);
             }
         }
     }
@@ -429,7 +429,7 @@ impl<'a> Iterator for BatchIterator<'a> {
 }
 
 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
-pub enum PrimitiveKind {
+pub(crate) enum PrimitiveKind {
     Shadow,
     #[default]
     Quad,
@@ -495,7 +495,7 @@ pub(crate) enum PrimitiveBatch<'a> {
 
 #[derive(Default, Debug, Clone, Eq, PartialEq)]
 #[repr(C)]
-pub struct Quad {
+pub(crate) struct Quad {
     pub view_id: ViewId,
     pub layer_id: LayerId,
     pub order: DrawOrder,
@@ -527,7 +527,7 @@ impl From<Quad> for Primitive {
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 #[repr(C)]
-pub struct Underline {
+pub(crate) struct Underline {
     pub view_id: ViewId,
     pub layer_id: LayerId,
     pub order: DrawOrder,
@@ -558,7 +558,7 @@ impl From<Underline> for Primitive {
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 #[repr(C)]
-pub struct Shadow {
+pub(crate) struct Shadow {
     pub view_id: ViewId,
     pub layer_id: LayerId,
     pub order: DrawOrder,
@@ -655,7 +655,7 @@ impl From<PolychromeSprite> for Primitive {
 }
 
 #[derive(Clone, Debug, Eq, PartialEq)]
-pub struct Surface {
+pub(crate) struct Surface {
     pub view_id: ViewId,
     pub layer_id: LayerId,
     pub order: DrawOrder,
@@ -685,6 +685,7 @@ impl From<Surface> for Primitive {
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 pub(crate) struct PathId(pub(crate) usize);
 
+/// A line made up of a series of vertices and control points.
 #[derive(Debug)]
 pub struct Path<P: Clone + Default + Debug> {
     pub(crate) id: PathId,
@@ -701,6 +702,7 @@ pub struct Path<P: Clone + Default + Debug> {
 }
 
 impl Path<Pixels> {
+    /// Create a new path with the given starting point.
     pub fn new(start: Point<Pixels>) -> Self {
         Self {
             id: PathId(0),
@@ -720,6 +722,7 @@ impl Path<Pixels> {
         }
     }
 
+    /// Scale this path by the given factor.
     pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
         Path {
             id: self.id,
@@ -740,6 +743,7 @@ impl Path<Pixels> {
         }
     }
 
+    /// Draw a straight line from the current point to the given point.
     pub fn line_to(&mut self, to: Point<Pixels>) {
         self.contour_count += 1;
         if self.contour_count > 1 {
@@ -751,6 +755,7 @@ impl Path<Pixels> {
         self.current = to;
     }
 
+    /// Draw a curve from the current point to the given point, using the given control point.
     pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {
         self.contour_count += 1;
         if self.contour_count > 1 {
@@ -833,7 +838,7 @@ impl From<Path<ScaledPixels>> for Primitive {
 
 #[derive(Clone, Debug)]
 #[repr(C)]
-pub struct PathVertex<P: Clone + Default + Debug> {
+pub(crate) struct PathVertex<P: Clone + Default + Debug> {
     pub(crate) xy_position: Point<P>,
     pub(crate) st_position: Point<f32>,
     pub(crate) content_mask: ContentMask<P>,
@@ -850,4 +855,4 @@ impl PathVertex<Pixels> {
 }
 
 #[derive(Copy, Clone, Debug)]
-pub struct AtlasId(pub(crate) usize);
+pub(crate) struct AtlasId(pub(crate) usize);

crates/gpui/src/shared_string.rs ๐Ÿ”—

@@ -3,6 +3,8 @@ use serde::{Deserialize, Serialize};
 use std::{borrow::Borrow, sync::Arc};
 use util::arc_cow::ArcCow;
 
+/// A shared string is an immutable string that can be cheaply cloned in GPUI
+/// tasks. Essentially an abstraction over an Arc<str> and &'static str,
 #[derive(Deref, DerefMut, Eq, PartialEq, Hash, Clone)]
 pub struct SharedString(ArcCow<'static, str>);
 

crates/gpui/src/style.rs ๐Ÿ”—

@@ -1,10 +1,10 @@
 use std::{iter, mem, ops::Range};
 
 use crate::{
-    black, phi, point, quad, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds,
-    ContentMask, Corners, CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement,
-    Font, FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba,
-    SharedString, Size, SizeRefinement, Styled, TextRun, WindowContext,
+    black, phi, point, quad, rems, AbsoluteLength, Bounds, ContentMask, Corners, CornersRefinement,
+    CursorStyle, DefiniteLength, Edges, EdgesRefinement, ElementContext, Font, FontFeatures,
+    FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba, SharedString, Size,
+    SizeRefinement, Styled, TextRun,
 };
 use collections::HashSet;
 use refineable::{Cascade, Refineable};
@@ -110,7 +110,7 @@ pub struct Style {
     /// The mouse cursor style shown when the mouse pointer is over an element.
     pub mouse_cursor: Option<CursorStyle>,
 
-    pub z_index: Option<u8>,
+    pub z_index: Option<u16>,
 
     #[cfg(debug_assertions)]
     pub debug: bool,
@@ -308,61 +308,12 @@ impl Style {
         }
     }
 
-    pub fn apply_text_style<C, F, R>(&self, cx: &mut C, f: F) -> R
-    where
-        C: BorrowAppContext,
-        F: FnOnce(&mut C) -> R,
-    {
-        if self.text.is_some() {
-            cx.with_text_style(Some(self.text.clone()), f)
-        } else {
-            f(cx)
-        }
-    }
-
-    /// Apply overflow to content mask
-    pub fn apply_overflow<C, F, R>(&self, bounds: Bounds<Pixels>, cx: &mut C, f: F) -> R
-    where
-        C: BorrowWindow,
-        F: FnOnce(&mut C) -> R,
-    {
-        let current_mask = cx.content_mask();
-
-        let min = current_mask.bounds.origin;
-        let max = current_mask.bounds.lower_right();
-
-        let mask_bounds = match (
-            self.overflow.x == Overflow::Visible,
-            self.overflow.y == Overflow::Visible,
-        ) {
-            // x and y both visible
-            (true, true) => return f(cx),
-            // x visible, y hidden
-            (true, false) => Bounds::from_corners(
-                point(min.x, bounds.origin.y),
-                point(max.x, bounds.lower_right().y),
-            ),
-            // x hidden, y visible
-            (false, true) => Bounds::from_corners(
-                point(bounds.origin.x, min.y),
-                point(bounds.lower_right().x, max.y),
-            ),
-            // both hidden
-            (false, false) => bounds,
-        };
-        let mask = ContentMask {
-            bounds: mask_bounds,
-        };
-
-        cx.with_content_mask(Some(mask), f)
-    }
-
     /// Paints the background of an element styled with this style.
     pub fn paint(
         &self,
         bounds: Bounds<Pixels>,
-        cx: &mut WindowContext,
-        continuation: impl FnOnce(&mut WindowContext),
+        cx: &mut ElementContext,
+        continuation: impl FnOnce(&mut ElementContext),
     ) {
         #[cfg(debug_assertions)]
         if self.debug_below {
@@ -386,7 +337,7 @@ impl Style {
 
         let background_color = self.background.as_ref().and_then(Fill::color);
         if background_color.map_or(false, |color| !color.is_transparent()) {
-            cx.with_z_index(0, |cx| {
+            cx.with_z_index(1, |cx| {
                 let mut border_color = background_color.unwrap_or_default();
                 border_color.a = 0.;
                 cx.paint_quad(quad(
@@ -399,12 +350,12 @@ impl Style {
             });
         }
 
-        cx.with_z_index(0, |cx| {
+        cx.with_z_index(2, |cx| {
             continuation(cx);
         });
 
         if self.is_border_visible() {
-            cx.with_z_index(0, |cx| {
+            cx.with_z_index(3, |cx| {
                 let corner_radii = self.corner_radii.to_pixels(bounds.size, rem_size);
                 let border_widths = self.border_widths.to_pixels(rem_size);
                 let max_border_width = border_widths.max();

crates/gpui/src/styled.rs ๐Ÿ”—

@@ -1,18 +1,18 @@
 use crate::{
     self as gpui, hsla, point, px, relative, rems, AbsoluteLength, AlignItems, CursorStyle,
-    DefiniteLength, Display, Fill, FlexDirection, FontWeight, Hsla, JustifyContent, Length,
-    Position, SharedString, StyleRefinement, Visibility, WhiteSpace,
+    DefiniteLength, Fill, FlexDirection, FontWeight, Hsla, JustifyContent, Length, Position,
+    SharedString, StyleRefinement, Visibility, WhiteSpace,
 };
 use crate::{BoxShadow, TextStyleRefinement};
 use smallvec::{smallvec, SmallVec};
-use taffy::style::Overflow;
+use taffy::style::{Display, Overflow};
 
 pub trait Styled: Sized {
     fn style(&mut self) -> &mut StyleRefinement;
 
     gpui_macros::style_helpers!();
 
-    fn z_index(mut self, z_index: u8) -> Self {
+    fn z_index(mut self, z_index: u16) -> Self {
         self.style().z_index = Some(z_index);
         self
     }
@@ -66,18 +66,24 @@ pub trait Styled: Sized {
         self
     }
 
+    /// Sets the behavior of content that overflows the container to be hidden.
+    /// [Docs](https://tailwindcss.com/docs/overflow#hiding-content-that-overflows)
     fn overflow_hidden(mut self) -> Self {
         self.style().overflow.x = Some(Overflow::Hidden);
         self.style().overflow.y = Some(Overflow::Hidden);
         self
     }
 
-    fn overflow_hidden_x(mut self) -> Self {
+    /// Sets the behavior of content that overflows the container on the X axis to be hidden.
+    /// [Docs](https://tailwindcss.com/docs/overflow#hiding-content-that-overflows)
+    fn overflow_x_hidden(mut self) -> Self {
         self.style().overflow.x = Some(Overflow::Hidden);
         self
     }
 
-    fn overflow_hidden_y(mut self) -> Self {
+    /// Sets the behavior of content that overflows the container on the Y axis to be hidden.
+    /// [Docs](https://tailwindcss.com/docs/overflow#hiding-content-that-overflows)
+    fn overflow_y_hidden(mut self) -> Self {
         self.style().overflow.y = Some(Overflow::Hidden);
         self
     }
@@ -301,6 +307,13 @@ pub trait Styled: Sized {
         self
     }
 
+    /// Sets the initial size of flex items for this element.
+    /// [Docs](https://tailwindcss.com/docs/flex-basis)
+    fn flex_basis(mut self, basis: impl Into<Length>) -> Self {
+        self.style().flex_basis = Some(basis.into());
+        self
+    }
+
     /// Sets the element to allow a flex item to grow to fill any available space.
     /// [Docs](https://tailwindcss.com/docs/flex-grow)
     fn flex_grow(mut self) -> Self {
@@ -308,6 +321,20 @@ pub trait Styled: Sized {
         self
     }
 
+    /// Sets the element to allow a flex item to shrink if needed.
+    /// [Docs](https://tailwindcss.com/docs/flex-shrink)
+    fn flex_shrink(mut self) -> Self {
+        self.style().flex_shrink = Some(1.);
+        self
+    }
+
+    /// Sets the element to prevent a flex item from shrinking.
+    /// [Docs](https://tailwindcss.com/docs/flex-shrink#dont-shrink)
+    fn flex_shrink_0(mut self) -> Self {
+        self.style().flex_shrink = Some(0.);
+        self
+    }
+
     /// Sets the element to align flex items to the start of the container's cross axis.
     /// [Docs](https://tailwindcss.com/docs/align-items#start)
     fn items_start(mut self) -> Self {

crates/gpui/src/subscription.rs ๐Ÿ”—

@@ -147,12 +147,17 @@ where
     }
 }
 
+/// A handle to a subscription created by GPUI. When dropped, the subscription
+/// is cancelled and the callback will no longer be invoked.
 #[must_use]
 pub struct Subscription {
     unsubscribe: Option<Box<dyn FnOnce() + 'static>>,
 }
 
 impl Subscription {
+    /// Detaches the subscription from this handle. The callback will
+    /// continue to be invoked until the views or models it has been
+    /// subscribed to are dropped
     pub fn detach(mut self) {
         self.unsubscribe.take();
     }

crates/gpui/src/svg_renderer.rs ๐Ÿ”—

@@ -3,12 +3,12 @@ use anyhow::anyhow;
 use std::{hash::Hash, sync::Arc};
 
 #[derive(Clone, PartialEq, Hash, Eq)]
-pub struct RenderSvgParams {
+pub(crate) struct RenderSvgParams {
     pub(crate) path: SharedString,
     pub(crate) size: Size<DevicePixels>,
 }
 
-pub struct SvgRenderer {
+pub(crate) struct SvgRenderer {
     asset_source: Arc<dyn AssetSource>,
 }
 

crates/gpui/src/taffy.rs ๐Ÿ”—

@@ -229,6 +229,7 @@ impl TaffyLayoutEngine {
     }
 }
 
+/// A unique identifier for a layout node, generated when requesting a layout from Taffy
 #[derive(Copy, Clone, Eq, PartialEq, Debug)]
 #[repr(transparent)]
 pub struct LayoutId(NodeId);
@@ -440,6 +441,7 @@ where
     }
 }
 
+/// The space available for an element to be laid out in
 #[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
 pub enum AvailableSpace {
     /// The amount of space available is the specified number of pixels

crates/gpui/src/test.rs ๐Ÿ”—

@@ -34,6 +34,9 @@ use std::{
     panic::{self, RefUnwindSafe},
 };
 
+/// Run the given test function with the configured parameters.
+/// This is intended for use with the `gpui::test` macro
+/// and generally should not be used directly.
 pub fn run_test(
     mut num_iterations: u64,
     max_retries: usize,
@@ -78,6 +81,7 @@ pub fn run_test(
     }
 }
 
+/// A test struct for converting an observation callback into a stream.
 pub struct Observation<T> {
     rx: channel::Receiver<T>,
     _subscription: Subscription,

crates/gpui/src/text_system.rs ๐Ÿ”—

@@ -377,7 +377,7 @@ impl TextSystem {
         Ok(lines)
     }
 
-    pub fn finish_frame(&self, reused_views: &FxHashSet<EntityId>) {
+    pub(crate) fn finish_frame(&self, reused_views: &FxHashSet<EntityId>) {
         self.line_layout_cache.finish_frame(reused_views)
     }
 

crates/gpui/src/text_system/line.rs ๐Ÿ”—

@@ -1,6 +1,6 @@
 use crate::{
-    black, fill, point, px, size, BorrowWindow, Bounds, Hsla, LineLayout, Pixels, Point, Result,
-    SharedString, UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout,
+    black, fill, point, px, size, Bounds, ElementContext, Hsla, LineLayout, Pixels, Point, Result,
+    SharedString, UnderlineStyle, WrapBoundary, WrappedLineLayout,
 };
 use derive_more::{Deref, DerefMut};
 use smallvec::SmallVec;
@@ -24,6 +24,7 @@ pub struct ShapedLine {
 }
 
 impl ShapedLine {
+    /// The length of the line in utf-8 bytes.
     pub fn len(&self) -> usize {
         self.layout.len
     }
@@ -32,7 +33,7 @@ impl ShapedLine {
         &self,
         origin: Point<Pixels>,
         line_height: Pixels,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> Result<()> {
         paint_line(
             origin,
@@ -65,7 +66,7 @@ impl WrappedLine {
         &self,
         origin: Point<Pixels>,
         line_height: Pixels,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> Result<()> {
         paint_line(
             origin,
@@ -86,7 +87,7 @@ fn paint_line(
     line_height: Pixels,
     decoration_runs: &[DecorationRun],
     wrap_boundaries: &[WrapBoundary],
-    cx: &mut WindowContext<'_>,
+    cx: &mut ElementContext<'_>,
 ) -> Result<()> {
     let padding_top = (line_height - layout.ascent - layout.descent) / 2.;
     let baseline_offset = point(px(0.), padding_top + layout.ascent);

crates/gpui/src/util.rs ๐Ÿ”—

@@ -9,6 +9,58 @@ use smol::future::FutureExt;
 
 pub use util::*;
 
+/// A helper trait for building complex objects with imperative conditionals in a fluent style.
+pub trait FluentBuilder {
+    /// Imperatively modify self with the given closure.
+    fn map<U>(self, f: impl FnOnce(Self) -> U) -> U
+    where
+        Self: Sized,
+    {
+        f(self)
+    }
+
+    /// Conditionally modify self with the given closure.
+    fn when(self, condition: bool, then: impl FnOnce(Self) -> Self) -> Self
+    where
+        Self: Sized,
+    {
+        self.map(|this| if condition { then(this) } else { this })
+    }
+
+    /// Conditionally unwrap and modify self with the given closure, if the given option is Some.
+    fn when_some<T>(self, option: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self
+    where
+        Self: Sized,
+    {
+        self.map(|this| {
+            if let Some(value) = option {
+                then(this, value)
+            } else {
+                this
+            }
+        })
+    }
+
+    /// Conditionally modify self with one closure or another
+    fn when_else(
+        self,
+        condition: bool,
+        then: impl FnOnce(Self) -> Self,
+        otherwise: impl FnOnce(Self) -> Self,
+    ) -> Self
+    where
+        Self: Sized,
+    {
+        self.map(|this| {
+            if condition {
+                then(this)
+            } else {
+                otherwise(this)
+            }
+        })
+    }
+}
+
 #[cfg(any(test, feature = "test-support"))]
 pub async fn timeout<F, T>(timeout: Duration, f: F) -> Result<T, ()>
 where

crates/gpui/src/view.rs ๐Ÿ”—

@@ -1,10 +1,8 @@
-#![deny(missing_docs)]
-
 use crate::{
-    seal::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, BorrowWindow,
-    Bounds, ContentMask, Element, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView,
-    IntoElement, LayoutId, Model, Pixels, Point, Render, Size, StackingOrder, Style, TextStyle,
-    ViewContext, VisualContext, WeakModel, WindowContext,
+    seal::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, Bounds,
+    ContentMask, Element, ElementContext, ElementId, Entity, EntityId, Flatten, FocusHandle,
+    FocusableView, IntoElement, LayoutId, Model, Pixels, Point, Render, Size, StackingOrder, Style,
+    TextStyle, ViewContext, VisualContext, WeakModel,
 };
 use anyhow::{Context, Result};
 use std::{
@@ -25,6 +23,7 @@ impl<V> Sealed for View<V> {}
 #[doc(hidden)]
 pub struct AnyViewState {
     root_style: Style,
+    next_stacking_order_id: u16,
     cache_key: Option<ViewCacheKey>,
     element: Option<AnyElement>,
 }
@@ -64,7 +63,7 @@ impl<V: 'static> View<V> {
         Entity::downgrade(self)
     }
 
-    /// Update the view's state with the given function, which is passed a mutable reference and a context.
+    /// 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,
@@ -96,7 +95,7 @@ impl<V: Render> Element for View<V> {
     fn request_layout(
         &mut self,
         _state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         cx.with_view_id(self.entity_id(), |cx| {
             let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element());
@@ -105,7 +104,7 @@ impl<V: Render> Element for View<V> {
         })
     }
 
-    fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut ElementContext) {
         cx.paint_view(self.entity_id(), |cx| element.take().unwrap().paint(cx));
     }
 }
@@ -156,7 +155,7 @@ impl<V: 'static> WeakView<V> {
         Entity::upgrade_from(self)
     }
 
-    /// Update this view's state if it hasn't been released.
+    /// 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,
@@ -204,7 +203,7 @@ impl<V> Eq for WeakView<V> {}
 #[derive(Clone, Debug)]
 pub struct AnyView {
     model: AnyModel,
-    request_layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
+    request_layout: fn(&AnyView, &mut ElementContext) -> (LayoutId, AnyElement),
     cache: bool,
 }
 
@@ -252,7 +251,7 @@ impl AnyView {
         &self,
         origin: Point<Pixels>,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         cx.paint_view(self.entity_id(), |cx| {
             cx.with_absolute_element_offset(origin, |cx| {
@@ -280,7 +279,7 @@ impl Element for AnyView {
     fn request_layout(
         &mut self,
         state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         cx.with_view_id(self.entity_id(), |cx| {
             if self.cache {
@@ -294,6 +293,7 @@ impl Element for AnyView {
             let root_style = cx.layout_style(layout_id).unwrap().clone();
             let state = AnyViewState {
                 root_style,
+                next_stacking_order_id: 0,
                 cache_key: None,
                 element: Some(element),
             };
@@ -301,7 +301,7 @@ impl Element for AnyView {
         })
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
         cx.paint_view(self.entity_id(), |cx| {
             if !self.cache {
                 state.element.take().unwrap().paint(cx);
@@ -316,14 +316,19 @@ impl Element for AnyView {
                     && !cx.window.dirty_views.contains(&self.entity_id())
                     && !cx.window.refreshing
                 {
-                    cx.reuse_view();
+                    cx.reuse_view(state.next_stacking_order_id);
                     return;
                 }
             }
 
-            let mut element = (self.request_layout)(self, cx).1;
-            element.draw(bounds.origin, bounds.size.into(), cx);
+            if let Some(mut element) = state.element.take() {
+                element.paint(cx);
+            } else {
+                let mut element = (self.request_layout)(self, cx).1;
+                element.draw(bounds.origin, bounds.size.into(), cx);
+            }
 
+            state.next_stacking_order_id = cx.window.next_frame.next_stacking_order_id;
             state.cache_key = Some(ViewCacheKey {
                 bounds,
                 stacking_order: cx.stacking_order().clone(),
@@ -361,7 +366,7 @@ impl IntoElement for AnyView {
 /// A weak, dynamically-typed view handle that does not prevent the view from being released.
 pub struct AnyWeakView {
     model: AnyWeakModel,
-    layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
+    layout: fn(&AnyView, &mut ElementContext) -> (LayoutId, AnyElement),
 }
 
 impl AnyWeakView {
@@ -400,11 +405,11 @@ impl std::fmt::Debug for AnyWeakView {
 }
 
 mod any_view {
-    use crate::{AnyElement, AnyView, IntoElement, LayoutId, Render, WindowContext};
+    use crate::{AnyElement, AnyView, ElementContext, IntoElement, LayoutId, Render};
 
     pub(crate) fn request_layout<V: 'static + Render>(
         view: &AnyView,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, AnyElement) {
         let view = view.clone().downcast::<V>().unwrap();
         let mut element = view.update(cx, |view, cx| view.render(cx).into_any_element());

crates/gpui/src/window.rs ๐Ÿ”—

@@ -1,33 +1,26 @@
-#![deny(missing_docs)]
-
 use crate::{
-    px, size, transparent_black, Action, AnyDrag, AnyTooltip, AnyView, AppContext, Arena,
-    AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
-    DevicePixels, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect,
-    Entity, EntityId, EventEmitter, FileDropEvent, Flatten, FontId, GlobalElementId, GlyphId, Hsla,
-    ImageData, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, KeystrokeEvent, LayoutId,
-    Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent,
-    MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
-    PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render,
-    RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, Scene, Shadow,
-    SharedString, Size, Style, SubscriberSet, Subscription, Surface, TaffyLayoutEngine, Task,
-    Underline, UnderlineStyle, View, VisualContext, WeakView, WindowBounds, WindowOptions,
-    SUBPIXEL_VARIANTS,
+    px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, AsyncWindowContext,
+    AvailableSpace, Bounds, Context, Corners, CursorStyle, DispatchActionListener, DispatchNodeId,
+    DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten,
+    GlobalElementId, Hsla, KeyBinding, KeyContext, KeyDownEvent, KeyMatch, KeymatchResult,
+    Keystroke, KeystrokeEvent, Model, ModelContext, Modifiers, MouseButton, MouseMoveEvent,
+    MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point,
+    PromptLevel, Render, ScaledPixels, SharedString, Size, SubscriberSet, Subscription,
+    TaffyLayoutEngine, Task, View, VisualContext, WeakView, WindowBounds, WindowOptions,
 };
 use anyhow::{anyhow, Context as _, Result};
-use collections::{FxHashMap, FxHashSet};
+use collections::FxHashSet;
 use derive_more::{Deref, DerefMut};
 use futures::{
     channel::{mpsc, oneshot},
     StreamExt,
 };
-use media::core_video::CVImageBuffer;
 use parking_lot::RwLock;
 use slotmap::SlotMap;
 use smallvec::SmallVec;
 use std::{
     any::{Any, TypeId},
-    borrow::{Borrow, BorrowMut, Cow},
+    borrow::{Borrow, BorrowMut},
     cell::RefCell,
     collections::hash_map::Entry,
     fmt::{Debug, Display},
@@ -40,33 +33,30 @@ use std::{
         atomic::{AtomicUsize, Ordering::SeqCst},
         Arc,
     },
+    time::Duration,
 };
-use util::{post_inc, ResultExt};
+use util::{measure, ResultExt};
+
+mod element_cx;
+pub use element_cx::*;
 
-const ACTIVE_DRAG_Z_INDEX: u8 = 1;
+const ACTIVE_DRAG_Z_INDEX: u16 = 1;
 
 /// A global stacking order, which is created by stacking successive z-index values.
 /// Each z-index will always be interpreted in the context of its parent z-index.
-#[derive(Deref, DerefMut, Clone, Ord, PartialOrd, PartialEq, Eq, Default)]
-pub struct StackingOrder {
-    #[deref]
-    #[deref_mut]
-    context_stack: SmallVec<[u8; 64]>,
-    id: u32,
+#[derive(Debug, Deref, DerefMut, Clone, Ord, PartialOrd, PartialEq, Eq, Default)]
+pub struct StackingOrder(SmallVec<[StackingContext; 64]>);
+
+/// A single entry in a primitive's z-index stacking order
+#[derive(Clone, Ord, PartialOrd, PartialEq, Eq, Default)]
+pub struct StackingContext {
+    pub(crate) z_index: u16,
+    pub(crate) id: u16,
 }
 
-impl std::fmt::Debug for StackingOrder {
+impl std::fmt::Debug for StackingContext {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        let mut stacks = self.context_stack.iter().peekable();
-        write!(f, "[({}): ", self.id)?;
-        while let Some(z_index) = stacks.next() {
-            write!(f, "{z_index}")?;
-            if stacks.peek().is_some() {
-                write!(f, "->")?;
-            }
-        }
-        write!(f, "]")?;
-        Ok(())
+        write!(f, "{{{}.{}}} ", self.z_index, self.id)
     }
 }
 
@@ -100,7 +90,7 @@ impl DispatchPhase {
 }
 
 type AnyObserver = Box<dyn FnMut(&mut WindowContext) -> bool + 'static>;
-type AnyMouseListener = Box<dyn FnMut(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
+
 type AnyWindowFocusListener = Box<dyn FnMut(&FocusEvent, &mut WindowContext) -> bool + 'static>;
 
 struct FocusEvent {
@@ -260,8 +250,8 @@ pub struct Window {
     pub(crate) platform_window: Box<dyn PlatformWindow>,
     display_id: DisplayId,
     sprite_atlas: Arc<dyn PlatformAtlas>,
-    rem_size: Pixels,
-    viewport_size: Size<Pixels>,
+    pub(crate) rem_size: Pixels,
+    pub(crate) viewport_size: Size<Pixels>,
     layout_engine: Option<TaffyLayoutEngine>,
     pub(crate) root_view: Option<AnyView>,
     pub(crate) element_id_stack: GlobalElementId,
@@ -284,138 +274,54 @@ pub struct Window {
     activation_observers: SubscriberSet<(), AnyObserver>,
     pub(crate) focus: Option<FocusId>,
     focus_enabled: bool,
+    pending_input: Option<PendingInput>,
 
     #[cfg(any(test, feature = "test-support"))]
     pub(crate) focus_invalidated: bool,
 }
 
-pub(crate) struct ElementStateBox {
-    inner: Box<dyn Any>,
-    parent_view_id: EntityId,
-    #[cfg(debug_assertions)]
-    type_name: &'static str,
-}
-
-struct RequestedInputHandler {
-    view_id: EntityId,
-    handler: Option<Box<dyn PlatformInputHandler>>,
-}
-
-struct TooltipRequest {
-    view_id: EntityId,
-    tooltip: AnyTooltip,
-}
-
-pub(crate) struct Frame {
+#[derive(Default, Debug)]
+struct PendingInput {
+    keystrokes: SmallVec<[Keystroke; 1]>,
+    bindings: SmallVec<[KeyBinding; 1]>,
     focus: Option<FocusId>,
-    window_active: bool,
-    pub(crate) element_states: FxHashMap<GlobalElementId, ElementStateBox>,
-    mouse_listeners: FxHashMap<TypeId, Vec<(StackingOrder, EntityId, AnyMouseListener)>>,
-    pub(crate) dispatch_tree: DispatchTree,
-    pub(crate) scene: Scene,
-    pub(crate) depth_map: Vec<(StackingOrder, EntityId, Bounds<Pixels>)>,
-    pub(crate) z_index_stack: StackingOrder,
-    pub(crate) next_stacking_order_id: u32,
-    next_root_z_index: u8,
-    content_mask_stack: Vec<ContentMask<Pixels>>,
-    element_offset_stack: Vec<Point<Pixels>>,
-    requested_input_handler: Option<RequestedInputHandler>,
-    tooltip_request: Option<TooltipRequest>,
-    cursor_styles: FxHashMap<EntityId, CursorStyle>,
-    requested_cursor_style: Option<CursorStyle>,
-    pub(crate) view_stack: Vec<EntityId>,
-    pub(crate) reused_views: FxHashSet<EntityId>,
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub(crate) debug_bounds: collections::FxHashMap<String, Bounds<Pixels>>,
+    timer: Option<Task<()>>,
 }
 
-impl Frame {
-    fn new(dispatch_tree: DispatchTree) -> Self {
-        Frame {
-            focus: None,
-            window_active: false,
-            element_states: FxHashMap::default(),
-            mouse_listeners: FxHashMap::default(),
-            dispatch_tree,
-            scene: Scene::default(),
-            depth_map: Vec::new(),
-            z_index_stack: StackingOrder::default(),
-            next_stacking_order_id: 0,
-            next_root_z_index: 0,
-            content_mask_stack: Vec::new(),
-            element_offset_stack: Vec::new(),
-            requested_input_handler: None,
-            tooltip_request: None,
-            cursor_styles: FxHashMap::default(),
-            requested_cursor_style: None,
-            view_stack: Vec::new(),
-            reused_views: FxHashSet::default(),
+impl PendingInput {
+    fn is_noop(&self) -> bool {
+        self.bindings.is_empty() && (self.keystrokes.iter().all(|k| k.ime_key.is_none()))
+    }
 
-            #[cfg(any(test, feature = "test-support"))]
-            debug_bounds: FxHashMap::default(),
-        }
+    fn input(&self) -> String {
+        self.keystrokes
+            .iter()
+            .flat_map(|k| k.ime_key.clone())
+            .collect::<Vec<String>>()
+            .join("")
     }
 
-    fn clear(&mut self) {
-        self.element_states.clear();
-        self.mouse_listeners.values_mut().for_each(Vec::clear);
-        self.dispatch_tree.clear();
-        self.depth_map.clear();
-        self.next_stacking_order_id = 0;
-        self.next_root_z_index = 0;
-        self.reused_views.clear();
-        self.scene.clear();
-        self.requested_input_handler.take();
-        self.tooltip_request.take();
-        self.cursor_styles.clear();
-        self.requested_cursor_style.take();
-        debug_assert_eq!(self.view_stack.len(), 0);
-    }
-
-    fn focus_path(&self) -> SmallVec<[FocusId; 8]> {
-        self.focus
-            .map(|focus_id| self.dispatch_tree.focus_path(focus_id))
-            .unwrap_or_default()
-    }
-
-    fn finish(&mut self, prev_frame: &mut Self) {
-        // Reuse mouse listeners that didn't change since the last frame.
-        for (type_id, listeners) in &mut prev_frame.mouse_listeners {
-            let next_listeners = self.mouse_listeners.entry(*type_id).or_default();
-            for (order, view_id, listener) in listeners.drain(..) {
-                if self.reused_views.contains(&view_id) {
-                    next_listeners.push((order, view_id, listener));
-                }
-            }
+    fn used_by_binding(&self, binding: &KeyBinding) -> bool {
+        if self.keystrokes.is_empty() {
+            return true;
         }
-
-        // Reuse entries in the depth map that didn't change since the last frame.
-        for (order, view_id, bounds) in prev_frame.depth_map.drain(..) {
-            if self.reused_views.contains(&view_id) {
-                match self
-                    .depth_map
-                    .binary_search_by(|(level, _, _)| order.cmp(level))
-                {
-                    Ok(i) | Err(i) => self.depth_map.insert(i, (order, view_id, bounds)),
-                }
-            }
-        }
-
-        // Retain element states for views that didn't change since the last frame.
-        for (element_id, state) in prev_frame.element_states.drain() {
-            if self.reused_views.contains(&state.parent_view_id) {
-                self.element_states.entry(element_id).or_insert(state);
+        let keystroke = &self.keystrokes[0];
+        for candidate in keystroke.match_candidates() {
+            if binding.match_keystrokes(&[candidate]) == KeyMatch::Pending {
+                return true;
             }
         }
-
-        // Reuse geometry that didn't change since the last frame.
-        self.scene
-            .reuse_views(&self.reused_views, &mut prev_frame.scene);
-        self.scene.finish();
+        false
     }
 }
 
+pub(crate) struct ElementStateBox {
+    pub(crate) inner: Box<dyn Any>,
+    pub(crate) parent_view_id: EntityId,
+    #[cfg(debug_assertions)]
+    pub(crate) type_name: &'static str,
+}
+
 impl Window {
     pub(crate) fn new(
         handle: AnyWindowHandle,
@@ -434,7 +340,9 @@ impl Window {
         platform_window.on_request_frame(Box::new({
             let mut cx = cx.to_async();
             move || {
-                handle.update(&mut cx, |_, cx| cx.draw()).log_err();
+                measure("frame duration", || {
+                    handle.update(&mut cx, |_, cx| cx.draw()).log_err();
+                })
             }
         }));
         platform_window.on_resize(Box::new({
@@ -508,6 +416,7 @@ impl Window {
             activation_observers: SubscriberSet::new(),
             focus: None,
             focus_enabled: true,
+            pending_input: None,
 
             #[cfg(any(test, feature = "test-support"))]
             focus_invalidated: false,
@@ -721,7 +630,7 @@ impl<'a> WindowContext<'a> {
         subscription
     }
 
-    /// Create an `AsyncWindowContext`, which has a static lifetime and can be held across
+    /// Creates an [`AsyncWindowContext`], which has a static lifetime and can be held across
     /// await points in async code.
     pub fn to_async(&self) -> AsyncWindowContext {
         AsyncWindowContext::new(self.app.to_async(), self.window.handle)
@@ -737,7 +646,7 @@ impl<'a> WindowContext<'a> {
             let (tx, mut rx) = mpsc::unbounded::<()>();
             self.platform.set_display_link_output_callback(
                 display_id,
-                Box::new(move |_current_time, _output_time| _ = tx.unbounded_send(())),
+                Box::new(move || _ = tx.unbounded_send(())),
             );
 
             let consumer_task = self.app.spawn(|cx| async move {
@@ -794,7 +703,7 @@ impl<'a> WindowContext<'a> {
             .spawn(|app| f(AsyncWindowContext::new(app, self.window.handle)))
     }
 
-    /// Update the global of the given type. The given closure is given simultaneous mutable
+    /// Updates the global of the given type. The given closure is given simultaneous mutable
     /// access both to the global and the context.
     pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
     where
@@ -806,80 +715,6 @@ impl<'a> WindowContext<'a> {
         result
     }
 
-    #[must_use]
-    /// Add a node to the layout tree for the current frame. Takes the `Style` of the element for which
-    /// layout is being requested, along with the layout ids of any children. This method is called during
-    /// calls to the `Element::layout` trait method and enables any element to participate in layout.
-    pub fn request_layout(
-        &mut self,
-        style: &Style,
-        children: impl IntoIterator<Item = LayoutId>,
-    ) -> LayoutId {
-        self.app.layout_id_buffer.clear();
-        self.app.layout_id_buffer.extend(children);
-        let rem_size = self.rem_size();
-
-        self.window.layout_engine.as_mut().unwrap().request_layout(
-            style,
-            rem_size,
-            &self.app.layout_id_buffer,
-        )
-    }
-
-    /// Add a node to the layout tree for the current frame. Instead of taking a `Style` and children,
-    /// this variant takes a function that is invoked during layout so you can use arbitrary logic to
-    /// determine the element's size. One place this is used internally is when measuring text.
-    ///
-    /// The given closure is invoked at layout time with the known dimensions and available space and
-    /// returns a `Size`.
-    pub fn request_measured_layout<
-        F: FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut WindowContext) -> Size<Pixels>
-            + 'static,
-    >(
-        &mut self,
-        style: Style,
-        measure: F,
-    ) -> LayoutId {
-        let rem_size = self.rem_size();
-        self.window
-            .layout_engine
-            .as_mut()
-            .unwrap()
-            .request_measured_layout(style, rem_size, measure)
-    }
-
-    pub(crate) fn layout_style(&self, layout_id: LayoutId) -> Option<&Style> {
-        self.window
-            .layout_engine
-            .as_ref()
-            .unwrap()
-            .requested_style(layout_id)
-    }
-
-    /// Compute the layout for the given id within the given available space.
-    /// This method is called for its side effect, typically by the framework prior to painting.
-    /// After calling it, you can request the bounds of the given layout node id or any descendant.
-    pub fn compute_layout(&mut self, layout_id: LayoutId, available_space: Size<AvailableSpace>) {
-        let mut layout_engine = self.window.layout_engine.take().unwrap();
-        layout_engine.compute_layout(layout_id, available_space, self);
-        self.window.layout_engine = Some(layout_engine);
-    }
-
-    /// Obtain the bounds computed for the given LayoutId relative to the window. This method should not
-    /// be invoked until the paint phase begins, and will usually be invoked by GPUI itself automatically
-    /// in order to pass your element its `Bounds` automatically.
-    pub fn layout_bounds(&mut self, layout_id: LayoutId) -> Bounds<Pixels> {
-        let mut bounds = self
-            .window
-            .layout_engine
-            .as_mut()
-            .unwrap()
-            .layout_bounds(layout_id)
-            .map(Into::into);
-        bounds.origin += self.element_offset();
-        bounds
-    }
-
     fn window_bounds_changed(&mut self) {
         self.window.scale_factor = self.window.platform_window.scale_factor();
         self.window.viewport_size = self.window.platform_window.content_size();
@@ -913,7 +748,7 @@ impl<'a> WindowContext<'a> {
         self.window.platform_window.zoom();
     }
 
-    /// Update the window's title at the platform level.
+    /// Updates the window's title at the platform level.
     pub fn set_window_title(&mut self, title: &str) {
         self.window.platform_window.set_title(title);
     }
@@ -975,67 +810,6 @@ impl<'a> WindowContext<'a> {
         self.window.default_prevented
     }
 
-    /// Register a mouse event listener on the window for the next frame. The type of event
-    /// is determined by the first parameter of the given listener. When the next frame is rendered
-    /// the listener will be cleared.
-    pub fn on_mouse_event<Event: MouseEvent>(
-        &mut self,
-        mut handler: impl FnMut(&Event, DispatchPhase, &mut WindowContext) + 'static,
-    ) {
-        let view_id = self.parent_view_id();
-        let order = self.window.next_frame.z_index_stack.clone();
-        self.window
-            .next_frame
-            .mouse_listeners
-            .entry(TypeId::of::<Event>())
-            .or_default()
-            .push((
-                order,
-                view_id,
-                Box::new(
-                    move |event: &dyn Any, phase: DispatchPhase, cx: &mut WindowContext<'_>| {
-                        handler(event.downcast_ref().unwrap(), phase, cx)
-                    },
-                ),
-            ))
-    }
-
-    /// Register a key event listener on the window for the next frame. The type of event
-    /// is determined by the first parameter of the given listener. When the next frame is rendered
-    /// the listener will be cleared.
-    ///
-    /// This is a fairly low-level method, so prefer using event handlers on elements unless you have
-    /// a specific need to register a global listener.
-    pub fn on_key_event<Event: KeyEvent>(
-        &mut self,
-        listener: impl Fn(&Event, DispatchPhase, &mut WindowContext) + 'static,
-    ) {
-        self.window.next_frame.dispatch_tree.on_key_event(Rc::new(
-            move |event: &dyn Any, phase, cx: &mut WindowContext<'_>| {
-                if let Some(event) = event.downcast_ref::<Event>() {
-                    listener(event, phase, cx)
-                }
-            },
-        ));
-    }
-
-    /// Register an action listener on the window for the next frame. The type of action
-    /// is determined by the first parameter of the given listener. When the next frame is rendered
-    /// the listener will be cleared.
-    ///
-    /// This is a fairly low-level method, so prefer using action handlers on elements unless you have
-    /// a specific need to register a global listener.
-    pub fn on_action(
-        &mut self,
-        action_type: TypeId,
-        listener: impl Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static,
-    ) {
-        self.window
-            .next_frame
-            .dispatch_tree
-            .on_action(action_type, Rc::new(listener));
-    }
-
     /// Determine whether the given action is available along the dispatch path to the currently focused element.
     pub fn is_action_available(&self, action: &dyn Action) -> bool {
         let target = self
@@ -1063,42 +837,40 @@ impl<'a> WindowContext<'a> {
         self.window.modifiers
     }
 
-    /// Update the cursor style at the platform level.
-    pub fn set_cursor_style(&mut self, style: CursorStyle) {
-        let view_id = self.parent_view_id();
-        self.window.next_frame.cursor_styles.insert(view_id, style);
-        self.window.next_frame.requested_cursor_style = Some(style);
-    }
-
-    /// Set a tooltip to be rendered for the upcoming frame
-    pub fn set_tooltip(&mut self, tooltip: AnyTooltip) {
-        let view_id = self.parent_view_id();
-        self.window.next_frame.tooltip_request = Some(TooltipRequest { view_id, tooltip });
-    }
-
-    /// Called during painting to track which z-index is on top at each pixel position
-    pub fn add_opaque_layer(&mut self, bounds: Bounds<Pixels>) {
-        let stacking_order = self.window.next_frame.z_index_stack.clone();
-        let view_id = self.parent_view_id();
-        let depth_map = &mut self.window.next_frame.depth_map;
-        match depth_map.binary_search_by(|(level, _, _)| stacking_order.cmp(level)) {
-            Ok(i) | Err(i) => depth_map.insert(i, (stacking_order, view_id, bounds)),
-        }
-    }
-
     /// Returns true if there is no opaque layer containing the given point
-    /// on top of the given level. Layers whose level is an extension of the
-    /// level are not considered to be on top of the level.
-    pub fn was_top_layer(&self, point: &Point<Pixels>, level: &StackingOrder) -> bool {
-        for (opaque_level, _, bounds) in self.window.rendered_frame.depth_map.iter() {
-            if level >= opaque_level {
-                break;
+    /// on top of the given level. Layers who are extensions of the queried layer
+    /// are not considered to be on top of queried layer.
+    pub fn was_top_layer(&self, point: &Point<Pixels>, layer: &StackingOrder) -> bool {
+        // Precondition: the depth map is ordered from topmost to bottomost.
+
+        for (opaque_layer, _, bounds) in self.window.rendered_frame.depth_map.iter() {
+            if layer >= opaque_layer {
+                // The queried layer is either above or is the same as the this opaque layer.
+                // Anything after this point is guaranteed to be below the queried layer.
+                return true;
             }
 
-            if bounds.contains(point) && !opaque_level.starts_with(level) {
+            if !bounds.contains(point) {
+                // This opaque layer is above the queried layer but it doesn't contain
+                // the given position, so we can ignore it even if it's above.
+                continue;
+            }
+
+            // At this point, we've established that this opaque layer is on top of the queried layer
+            // and contains the position:
+            // - If the opaque layer is an extension of the queried layer, we don't want
+            // to consider the opaque layer to be on top and so we ignore it.
+            // - Else, we will bail early and say that the queried layer wasn't the top one.
+            let opaque_layer_is_extension_of_queried_layer = opaque_layer.len() >= layer.len()
+                && opaque_layer
+                    .iter()
+                    .zip(layer.iter())
+                    .all(|(a, b)| a.z_index == b.z_index);
+            if !opaque_layer_is_extension_of_queried_layer {
                 return false;
             }
         }
+
         true
     }
 
@@ -1111,11 +883,16 @@ impl<'a> WindowContext<'a> {
             if level >= opaque_level {
                 break;
             }
-            if opaque_level.starts_with(&[ACTIVE_DRAG_Z_INDEX]) {
+
+            if opaque_level
+                .first()
+                .map(|c| c.z_index == ACTIVE_DRAG_Z_INDEX)
+                .unwrap_or(false)
+            {
                 continue;
             }
 
-            if bounds.contains(point) && !opaque_level.starts_with(level) {
+            if bounds.contains(point) {
                 return false;
             }
         }
@@ -1127,371 +904,6 @@ impl<'a> WindowContext<'a> {
         &self.window.next_frame.z_index_stack
     }
 
-    /// Paint one or more drop shadows into the scene for the next frame at the current z-index.
-    pub fn paint_shadows(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        corner_radii: Corners<Pixels>,
-        shadows: &[BoxShadow],
-    ) {
-        let scale_factor = self.scale_factor();
-        let content_mask = self.content_mask();
-        let view_id = self.parent_view_id();
-        let window = &mut *self.window;
-        for shadow in shadows {
-            let mut shadow_bounds = bounds;
-            shadow_bounds.origin += shadow.offset;
-            shadow_bounds.dilate(shadow.spread_radius);
-            window.next_frame.scene.insert(
-                &window.next_frame.z_index_stack,
-                Shadow {
-                    view_id: view_id.into(),
-                    layer_id: 0,
-                    order: 0,
-                    bounds: shadow_bounds.scale(scale_factor),
-                    content_mask: content_mask.scale(scale_factor),
-                    corner_radii: corner_radii.scale(scale_factor),
-                    color: shadow.color,
-                    blur_radius: shadow.blur_radius.scale(scale_factor),
-                },
-            );
-        }
-    }
-
-    /// Paint one or more quads into the scene for the next frame at the current stacking context.
-    /// Quads are colored rectangular regions with an optional background, border, and corner radius.
-    /// see [`fill`], [`outline`], and [`quad`] to construct this type.
-    pub fn paint_quad(&mut self, quad: PaintQuad) {
-        let scale_factor = self.scale_factor();
-        let content_mask = self.content_mask();
-        let view_id = self.parent_view_id();
-
-        let window = &mut *self.window;
-        window.next_frame.scene.insert(
-            &window.next_frame.z_index_stack,
-            Quad {
-                view_id: view_id.into(),
-                layer_id: 0,
-                order: 0,
-                bounds: quad.bounds.scale(scale_factor),
-                content_mask: content_mask.scale(scale_factor),
-                background: quad.background,
-                border_color: quad.border_color,
-                corner_radii: quad.corner_radii.scale(scale_factor),
-                border_widths: quad.border_widths.scale(scale_factor),
-            },
-        );
-    }
-
-    /// Paint the given `Path` into the scene for the next frame at the current z-index.
-    pub fn paint_path(&mut self, mut path: Path<Pixels>, color: impl Into<Hsla>) {
-        let scale_factor = self.scale_factor();
-        let content_mask = self.content_mask();
-        let view_id = self.parent_view_id();
-
-        path.content_mask = content_mask;
-        path.color = color.into();
-        path.view_id = view_id.into();
-        let window = &mut *self.window;
-        window
-            .next_frame
-            .scene
-            .insert(&window.next_frame.z_index_stack, path.scale(scale_factor));
-    }
-
-    /// Paint an underline into the scene for the next frame at the current z-index.
-    pub fn paint_underline(
-        &mut self,
-        origin: Point<Pixels>,
-        width: Pixels,
-        style: &UnderlineStyle,
-    ) {
-        let scale_factor = self.scale_factor();
-        let height = if style.wavy {
-            style.thickness * 3.
-        } else {
-            style.thickness
-        };
-        let bounds = Bounds {
-            origin,
-            size: size(width, height),
-        };
-        let content_mask = self.content_mask();
-        let view_id = self.parent_view_id();
-
-        let window = &mut *self.window;
-        window.next_frame.scene.insert(
-            &window.next_frame.z_index_stack,
-            Underline {
-                view_id: view_id.into(),
-                layer_id: 0,
-                order: 0,
-                bounds: bounds.scale(scale_factor),
-                content_mask: content_mask.scale(scale_factor),
-                thickness: style.thickness.scale(scale_factor),
-                color: style.color.unwrap_or_default(),
-                wavy: style.wavy,
-            },
-        );
-    }
-
-    /// Paint a monochrome (non-emoji) glyph into the scene for the next frame at the current z-index.
-    /// The y component of the origin is the baseline of the glyph.
-    pub fn paint_glyph(
-        &mut self,
-        origin: Point<Pixels>,
-        font_id: FontId,
-        glyph_id: GlyphId,
-        font_size: Pixels,
-        color: Hsla,
-    ) -> Result<()> {
-        let scale_factor = self.scale_factor();
-        let glyph_origin = origin.scale(scale_factor);
-        let subpixel_variant = Point {
-            x: (glyph_origin.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
-            y: (glyph_origin.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
-        };
-        let params = RenderGlyphParams {
-            font_id,
-            glyph_id,
-            font_size,
-            subpixel_variant,
-            scale_factor,
-            is_emoji: false,
-        };
-
-        let raster_bounds = self.text_system().raster_bounds(&params)?;
-        if !raster_bounds.is_zero() {
-            let tile =
-                self.window
-                    .sprite_atlas
-                    .get_or_insert_with(&params.clone().into(), &mut || {
-                        let (size, bytes) = self.text_system().rasterize_glyph(&params)?;
-                        Ok((size, Cow::Owned(bytes)))
-                    })?;
-            let bounds = Bounds {
-                origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
-                size: tile.bounds.size.map(Into::into),
-            };
-            let content_mask = self.content_mask().scale(scale_factor);
-            let view_id = self.parent_view_id();
-            let window = &mut *self.window;
-            window.next_frame.scene.insert(
-                &window.next_frame.z_index_stack,
-                MonochromeSprite {
-                    view_id: view_id.into(),
-                    layer_id: 0,
-                    order: 0,
-                    bounds,
-                    content_mask,
-                    color,
-                    tile,
-                },
-            );
-        }
-        Ok(())
-    }
-
-    /// Paint an emoji glyph into the scene for the next frame at the current z-index.
-    /// The y component of the origin is the baseline of the glyph.
-    pub fn paint_emoji(
-        &mut self,
-        origin: Point<Pixels>,
-        font_id: FontId,
-        glyph_id: GlyphId,
-        font_size: Pixels,
-    ) -> Result<()> {
-        let scale_factor = self.scale_factor();
-        let glyph_origin = origin.scale(scale_factor);
-        let params = RenderGlyphParams {
-            font_id,
-            glyph_id,
-            font_size,
-            // We don't render emojis with subpixel variants.
-            subpixel_variant: Default::default(),
-            scale_factor,
-            is_emoji: true,
-        };
-
-        let raster_bounds = self.text_system().raster_bounds(&params)?;
-        if !raster_bounds.is_zero() {
-            let tile =
-                self.window
-                    .sprite_atlas
-                    .get_or_insert_with(&params.clone().into(), &mut || {
-                        let (size, bytes) = self.text_system().rasterize_glyph(&params)?;
-                        Ok((size, Cow::Owned(bytes)))
-                    })?;
-            let bounds = Bounds {
-                origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
-                size: tile.bounds.size.map(Into::into),
-            };
-            let content_mask = self.content_mask().scale(scale_factor);
-            let view_id = self.parent_view_id();
-            let window = &mut *self.window;
-
-            window.next_frame.scene.insert(
-                &window.next_frame.z_index_stack,
-                PolychromeSprite {
-                    view_id: view_id.into(),
-                    layer_id: 0,
-                    order: 0,
-                    bounds,
-                    corner_radii: Default::default(),
-                    content_mask,
-                    tile,
-                    grayscale: false,
-                },
-            );
-        }
-        Ok(())
-    }
-
-    /// Paint a monochrome SVG into the scene for the next frame at the current stacking context.
-    pub fn paint_svg(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        path: SharedString,
-        color: Hsla,
-    ) -> Result<()> {
-        let scale_factor = self.scale_factor();
-        let bounds = bounds.scale(scale_factor);
-        // Render the SVG at twice the size to get a higher quality result.
-        let params = RenderSvgParams {
-            path,
-            size: bounds
-                .size
-                .map(|pixels| DevicePixels::from((pixels.0 * 2.).ceil() as i32)),
-        };
-
-        let tile =
-            self.window
-                .sprite_atlas
-                .get_or_insert_with(&params.clone().into(), &mut || {
-                    let bytes = self.svg_renderer.render(&params)?;
-                    Ok((params.size, Cow::Owned(bytes)))
-                })?;
-        let content_mask = self.content_mask().scale(scale_factor);
-        let view_id = self.parent_view_id();
-
-        let window = &mut *self.window;
-        window.next_frame.scene.insert(
-            &window.next_frame.z_index_stack,
-            MonochromeSprite {
-                view_id: view_id.into(),
-                layer_id: 0,
-                order: 0,
-                bounds,
-                content_mask,
-                color,
-                tile,
-            },
-        );
-
-        Ok(())
-    }
-
-    /// Paint an image into the scene for the next frame at the current z-index.
-    pub fn paint_image(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        corner_radii: Corners<Pixels>,
-        data: Arc<ImageData>,
-        grayscale: bool,
-    ) -> Result<()> {
-        let scale_factor = self.scale_factor();
-        let bounds = bounds.scale(scale_factor);
-        let params = RenderImageParams { image_id: data.id };
-
-        let tile = self
-            .window
-            .sprite_atlas
-            .get_or_insert_with(&params.clone().into(), &mut || {
-                Ok((data.size(), Cow::Borrowed(data.as_bytes())))
-            })?;
-        let content_mask = self.content_mask().scale(scale_factor);
-        let corner_radii = corner_radii.scale(scale_factor);
-        let view_id = self.parent_view_id();
-
-        let window = &mut *self.window;
-        window.next_frame.scene.insert(
-            &window.next_frame.z_index_stack,
-            PolychromeSprite {
-                view_id: view_id.into(),
-                layer_id: 0,
-                order: 0,
-                bounds,
-                content_mask,
-                corner_radii,
-                tile,
-                grayscale,
-            },
-        );
-        Ok(())
-    }
-
-    /// Paint a surface into the scene for the next frame at the current z-index.
-    pub fn paint_surface(&mut self, bounds: Bounds<Pixels>, image_buffer: CVImageBuffer) {
-        let scale_factor = self.scale_factor();
-        let bounds = bounds.scale(scale_factor);
-        let content_mask = self.content_mask().scale(scale_factor);
-        let view_id = self.parent_view_id();
-        let window = &mut *self.window;
-        window.next_frame.scene.insert(
-            &window.next_frame.z_index_stack,
-            Surface {
-                view_id: view_id.into(),
-                layer_id: 0,
-                order: 0,
-                bounds,
-                content_mask,
-                image_buffer,
-            },
-        );
-    }
-
-    pub(crate) fn reuse_view(&mut self) {
-        let view_id = self.parent_view_id();
-        let grafted_view_ids = self
-            .window
-            .next_frame
-            .dispatch_tree
-            .reuse_view(view_id, &mut self.window.rendered_frame.dispatch_tree);
-        for view_id in grafted_view_ids {
-            assert!(self.window.next_frame.reused_views.insert(view_id));
-
-            // Reuse the previous input handler requested during painting of the reused view.
-            if self
-                .window
-                .rendered_frame
-                .requested_input_handler
-                .as_ref()
-                .map_or(false, |requested| requested.view_id == view_id)
-            {
-                self.window.next_frame.requested_input_handler =
-                    self.window.rendered_frame.requested_input_handler.take();
-            }
-
-            // Reuse the tooltip previously requested during painting of the reused view.
-            if self
-                .window
-                .rendered_frame
-                .tooltip_request
-                .as_ref()
-                .map_or(false, |requested| requested.view_id == view_id)
-            {
-                self.window.next_frame.tooltip_request =
-                    self.window.rendered_frame.tooltip_request.take();
-            }
-
-            // Reuse the cursor styles previously requested during painting of the reused view.
-            if let Some(style) = self.window.rendered_frame.cursor_styles.remove(&view_id) {
-                self.window.next_frame.cursor_styles.insert(view_id, style);
-                self.window.next_frame.requested_cursor_style = Some(style);
-            }
-        }
-    }
-
     /// Draw pixels to the display for this window based on the contents of its scene.
     pub(crate) fn draw(&mut self) {
         self.window.dirty = false;
@@ -1504,44 +916,55 @@ impl<'a> WindowContext<'a> {
 
         if let Some(requested_handler) = self.window.rendered_frame.requested_input_handler.as_mut()
         {
-            requested_handler.handler = self.window.platform_window.take_input_handler();
+            let input_handler = self.window.platform_window.take_input_handler();
+            requested_handler.handler = input_handler;
         }
 
         let root_view = self.window.root_view.take().unwrap();
-
-        self.with_z_index(0, |cx| {
-            cx.with_key_dispatch(Some(KeyContext::default()), None, |_, cx| {
-                for (action_type, action_listeners) in &cx.app.global_action_listeners {
-                    for action_listener in action_listeners.iter().cloned() {
-                        cx.window.next_frame.dispatch_tree.on_action(
-                            *action_type,
-                            Rc::new(move |action: &dyn Any, phase, cx: &mut WindowContext<'_>| {
-                                action_listener(action, phase, cx)
-                            }),
-                        )
+        self.with_element_context(|cx| {
+            cx.with_z_index(0, |cx| {
+                cx.with_key_dispatch(Some(KeyContext::default()), None, |_, cx| {
+                    // We need to use cx.cx here so we can utilize borrow splitting
+                    for (action_type, action_listeners) in &cx.cx.app.global_action_listeners {
+                        for action_listener in action_listeners.iter().cloned() {
+                            cx.cx.window.next_frame.dispatch_tree.on_action(
+                                *action_type,
+                                Rc::new(
+                                    move |action: &dyn Any, phase, cx: &mut WindowContext<'_>| {
+                                        action_listener(action, phase, cx)
+                                    },
+                                ),
+                            )
+                        }
                     }
-                }
 
-                let available_space = cx.window.viewport_size.map(Into::into);
-                root_view.draw(Point::default(), available_space, cx);
+                    let available_space = cx.window.viewport_size.map(Into::into);
+                    root_view.draw(Point::default(), available_space, cx);
+                })
             })
         });
 
         if let Some(active_drag) = self.app.active_drag.take() {
-            self.with_z_index(ACTIVE_DRAG_Z_INDEX, |cx| {
-                let offset = cx.mouse_position() - active_drag.cursor_offset;
-                let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent);
-                active_drag.view.draw(offset, available_space, cx);
+            self.with_element_context(|cx| {
+                cx.with_z_index(ACTIVE_DRAG_Z_INDEX, |cx| {
+                    let offset = cx.mouse_position() - active_drag.cursor_offset;
+                    let available_space =
+                        size(AvailableSpace::MinContent, AvailableSpace::MinContent);
+                    active_drag.view.draw(offset, available_space, cx);
+                })
             });
             self.active_drag = Some(active_drag);
         } else if let Some(tooltip_request) = self.window.next_frame.tooltip_request.take() {
-            self.with_z_index(1, |cx| {
-                let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent);
-                tooltip_request.tooltip.view.draw(
-                    tooltip_request.tooltip.cursor_offset,
-                    available_space,
-                    cx,
-                );
+            self.with_element_context(|cx| {
+                cx.with_z_index(1, |cx| {
+                    let available_space =
+                        size(AvailableSpace::MinContent, AvailableSpace::MinContent);
+                    tooltip_request.tooltip.view.draw(
+                        tooltip_request.tooltip.cursor_offset,
+                        available_space,
+                        cx,
+                    );
+                })
             });
             self.window.next_frame.tooltip_request = Some(tooltip_request);
         }

crates/gpui/src/window/element_cx.rs ๐Ÿ”—

@@ -0,0 +1,1136 @@
+use std::{
+    any::{Any, TypeId},
+    borrow::{Borrow, BorrowMut, Cow},
+    mem,
+    rc::Rc,
+    sync::Arc,
+};
+
+use anyhow::Result;
+use collections::{FxHashMap, FxHashSet};
+use derive_more::{Deref, DerefMut};
+use media::core_video::CVImageBuffer;
+use smallvec::SmallVec;
+use util::post_inc;
+
+use crate::{
+    prelude::*, size, AnyTooltip, AppContext, AvailableSpace, Bounds, BoxShadow, ContentMask,
+    Corners, CursorStyle, DevicePixels, DispatchPhase, DispatchTree, ElementId, ElementStateBox,
+    EntityId, FocusHandle, FocusId, FontId, GlobalElementId, GlyphId, Hsla, ImageData,
+    InputHandler, IsZero, KeyContext, KeyEvent, LayoutId, MonochromeSprite, MouseEvent, PaintQuad,
+    Path, Pixels, PlatformInputHandler, Point, PolychromeSprite, Quad, RenderGlyphParams,
+    RenderImageParams, RenderSvgParams, Scene, Shadow, SharedString, Size, StackingContext,
+    StackingOrder, Style, Surface, TextStyleRefinement, Underline, UnderlineStyle, Window,
+    WindowContext, SUBPIXEL_VARIANTS,
+};
+
+type AnyMouseListener = Box<dyn FnMut(&dyn Any, DispatchPhase, &mut ElementContext) + 'static>;
+
+pub(crate) struct RequestedInputHandler {
+    pub(crate) view_id: EntityId,
+    pub(crate) handler: Option<PlatformInputHandler>,
+}
+
+pub(crate) struct TooltipRequest {
+    pub(crate) view_id: EntityId,
+    pub(crate) tooltip: AnyTooltip,
+}
+
+pub(crate) struct Frame {
+    pub(crate) focus: Option<FocusId>,
+    pub(crate) window_active: bool,
+    pub(crate) element_states: FxHashMap<GlobalElementId, ElementStateBox>,
+    pub(crate) mouse_listeners: FxHashMap<TypeId, Vec<(StackingOrder, EntityId, AnyMouseListener)>>,
+    pub(crate) dispatch_tree: DispatchTree,
+    pub(crate) scene: Scene,
+    pub(crate) depth_map: Vec<(StackingOrder, EntityId, Bounds<Pixels>)>,
+    pub(crate) z_index_stack: StackingOrder,
+    pub(crate) next_stacking_order_id: u16,
+    pub(crate) next_root_z_index: u16,
+    pub(crate) content_mask_stack: Vec<ContentMask<Pixels>>,
+    pub(crate) element_offset_stack: Vec<Point<Pixels>>,
+    pub(crate) requested_input_handler: Option<RequestedInputHandler>,
+    pub(crate) tooltip_request: Option<TooltipRequest>,
+    pub(crate) cursor_styles: FxHashMap<EntityId, CursorStyle>,
+    pub(crate) requested_cursor_style: Option<CursorStyle>,
+    pub(crate) view_stack: Vec<EntityId>,
+    pub(crate) reused_views: FxHashSet<EntityId>,
+
+    #[cfg(any(test, feature = "test-support"))]
+    pub(crate) debug_bounds: collections::FxHashMap<String, Bounds<Pixels>>,
+}
+
+impl Frame {
+    pub(crate) fn new(dispatch_tree: DispatchTree) -> Self {
+        Frame {
+            focus: None,
+            window_active: false,
+            element_states: FxHashMap::default(),
+            mouse_listeners: FxHashMap::default(),
+            dispatch_tree,
+            scene: Scene::default(),
+            depth_map: Vec::new(),
+            z_index_stack: StackingOrder::default(),
+            next_stacking_order_id: 0,
+            next_root_z_index: 0,
+            content_mask_stack: Vec::new(),
+            element_offset_stack: Vec::new(),
+            requested_input_handler: None,
+            tooltip_request: None,
+            cursor_styles: FxHashMap::default(),
+            requested_cursor_style: None,
+            view_stack: Vec::new(),
+            reused_views: FxHashSet::default(),
+
+            #[cfg(any(test, feature = "test-support"))]
+            debug_bounds: FxHashMap::default(),
+        }
+    }
+
+    pub(crate) fn clear(&mut self) {
+        self.element_states.clear();
+        self.mouse_listeners.values_mut().for_each(Vec::clear);
+        self.dispatch_tree.clear();
+        self.depth_map.clear();
+        self.next_stacking_order_id = 0;
+        self.next_root_z_index = 0;
+        self.reused_views.clear();
+        self.scene.clear();
+        self.requested_input_handler.take();
+        self.tooltip_request.take();
+        self.cursor_styles.clear();
+        self.requested_cursor_style.take();
+        debug_assert_eq!(self.view_stack.len(), 0);
+    }
+
+    pub(crate) fn focus_path(&self) -> SmallVec<[FocusId; 8]> {
+        self.focus
+            .map(|focus_id| self.dispatch_tree.focus_path(focus_id))
+            .unwrap_or_default()
+    }
+
+    pub(crate) fn finish(&mut self, prev_frame: &mut Self) {
+        // Reuse mouse listeners that didn't change since the last frame.
+        for (type_id, listeners) in &mut prev_frame.mouse_listeners {
+            let next_listeners = self.mouse_listeners.entry(*type_id).or_default();
+            for (order, view_id, listener) in listeners.drain(..) {
+                if self.reused_views.contains(&view_id) {
+                    next_listeners.push((order, view_id, listener));
+                }
+            }
+        }
+
+        // Reuse entries in the depth map that didn't change since the last frame.
+        for (order, view_id, bounds) in prev_frame.depth_map.drain(..) {
+            if self.reused_views.contains(&view_id) {
+                match self
+                    .depth_map
+                    .binary_search_by(|(level, _, _)| order.cmp(level))
+                {
+                    Ok(i) | Err(i) => self.depth_map.insert(i, (order, view_id, bounds)),
+                }
+            }
+        }
+
+        // Retain element states for views that didn't change since the last frame.
+        for (element_id, state) in prev_frame.element_states.drain() {
+            if self.reused_views.contains(&state.parent_view_id) {
+                self.element_states.entry(element_id).or_insert(state);
+            }
+        }
+
+        // Reuse geometry that didn't change since the last frame.
+        self.scene
+            .reuse_views(&self.reused_views, &mut prev_frame.scene);
+        self.scene.finish();
+    }
+}
+
+/// This context is used for assisting in the implementation of the element trait
+#[derive(Deref, DerefMut)]
+pub struct ElementContext<'a> {
+    pub(crate) cx: WindowContext<'a>,
+}
+
+impl<'a> WindowContext<'a> {
+    pub fn with_element_context<R>(&mut self, f: impl FnOnce(&mut ElementContext) -> R) -> R {
+        f(&mut ElementContext {
+            cx: WindowContext::new(self.app, self.window),
+        })
+    }
+}
+
+impl<'a> Borrow<AppContext> for ElementContext<'a> {
+    fn borrow(&self) -> &AppContext {
+        self.cx.app
+    }
+}
+
+impl<'a> BorrowMut<AppContext> for ElementContext<'a> {
+    fn borrow_mut(&mut self) -> &mut AppContext {
+        self.cx.borrow_mut()
+    }
+}
+
+impl<'a> Borrow<WindowContext<'a>> for ElementContext<'a> {
+    fn borrow(&self) -> &WindowContext<'a> {
+        &self.cx
+    }
+}
+
+impl<'a> BorrowMut<WindowContext<'a>> for ElementContext<'a> {
+    fn borrow_mut(&mut self) -> &mut WindowContext<'a> {
+        &mut self.cx
+    }
+}
+
+impl<'a> Borrow<Window> for ElementContext<'a> {
+    fn borrow(&self) -> &Window {
+        self.cx.window
+    }
+}
+
+impl<'a> BorrowMut<Window> for ElementContext<'a> {
+    fn borrow_mut(&mut self) -> &mut Window {
+        self.cx.borrow_mut()
+    }
+}
+
+impl<'a> Context for ElementContext<'a> {
+    type Result<T> = <WindowContext<'a> as Context>::Result<T>;
+
+    fn new_model<T: 'static>(
+        &mut self,
+        build_model: impl FnOnce(&mut crate::ModelContext<'_, T>) -> T,
+    ) -> Self::Result<crate::Model<T>> {
+        self.cx.new_model(build_model)
+    }
+
+    fn update_model<T, R>(
+        &mut self,
+        handle: &crate::Model<T>,
+        update: impl FnOnce(&mut T, &mut crate::ModelContext<'_, T>) -> R,
+    ) -> Self::Result<R>
+    where
+        T: 'static,
+    {
+        self.cx.update_model(handle, update)
+    }
+
+    fn read_model<T, R>(
+        &self,
+        handle: &crate::Model<T>,
+        read: impl FnOnce(&T, &AppContext) -> R,
+    ) -> Self::Result<R>
+    where
+        T: 'static,
+    {
+        self.cx.read_model(handle, read)
+    }
+
+    fn update_window<T, F>(&mut self, window: crate::AnyWindowHandle, f: F) -> Result<T>
+    where
+        F: FnOnce(crate::AnyView, &mut WindowContext<'_>) -> T,
+    {
+        self.cx.update_window(window, f)
+    }
+
+    fn read_window<T, R>(
+        &self,
+        window: &crate::WindowHandle<T>,
+        read: impl FnOnce(crate::View<T>, &AppContext) -> R,
+    ) -> Result<R>
+    where
+        T: 'static,
+    {
+        self.cx.read_window(window, read)
+    }
+}
+
+impl<'a> VisualContext for ElementContext<'a> {
+    fn new_view<V>(
+        &mut self,
+        build_view: impl FnOnce(&mut crate::ViewContext<'_, V>) -> V,
+    ) -> Self::Result<crate::View<V>>
+    where
+        V: 'static + Render,
+    {
+        self.cx.new_view(build_view)
+    }
+
+    fn update_view<V: 'static, R>(
+        &mut self,
+        view: &crate::View<V>,
+        update: impl FnOnce(&mut V, &mut crate::ViewContext<'_, V>) -> R,
+    ) -> Self::Result<R> {
+        self.cx.update_view(view, update)
+    }
+
+    fn replace_root_view<V>(
+        &mut self,
+        build_view: impl FnOnce(&mut crate::ViewContext<'_, V>) -> V,
+    ) -> Self::Result<crate::View<V>>
+    where
+        V: 'static + Render,
+    {
+        self.cx.replace_root_view(build_view)
+    }
+
+    fn focus_view<V>(&mut self, view: &crate::View<V>) -> Self::Result<()>
+    where
+        V: crate::FocusableView,
+    {
+        self.cx.focus_view(view)
+    }
+
+    fn dismiss_view<V>(&mut self, view: &crate::View<V>) -> Self::Result<()>
+    where
+        V: crate::ManagedView,
+    {
+        self.cx.dismiss_view(view)
+    }
+}
+
+impl<'a> ElementContext<'a> {
+    pub(crate) fn reuse_view(&mut self, next_stacking_order_id: u16) {
+        let view_id = self.parent_view_id();
+        let grafted_view_ids = self
+            .cx
+            .window
+            .next_frame
+            .dispatch_tree
+            .reuse_view(view_id, &mut self.cx.window.rendered_frame.dispatch_tree);
+        for view_id in grafted_view_ids {
+            assert!(self.window.next_frame.reused_views.insert(view_id));
+
+            // Reuse the previous input handler requested during painting of the reused view.
+            if self
+                .window
+                .rendered_frame
+                .requested_input_handler
+                .as_ref()
+                .map_or(false, |requested| requested.view_id == view_id)
+            {
+                self.window.next_frame.requested_input_handler =
+                    self.window.rendered_frame.requested_input_handler.take();
+            }
+
+            // Reuse the tooltip previously requested during painting of the reused view.
+            if self
+                .window
+                .rendered_frame
+                .tooltip_request
+                .as_ref()
+                .map_or(false, |requested| requested.view_id == view_id)
+            {
+                self.window.next_frame.tooltip_request =
+                    self.window.rendered_frame.tooltip_request.take();
+            }
+
+            // Reuse the cursor styles previously requested during painting of the reused view.
+            if let Some(style) = self.window.rendered_frame.cursor_styles.remove(&view_id) {
+                self.window.next_frame.cursor_styles.insert(view_id, style);
+                self.window.next_frame.requested_cursor_style = Some(style);
+            }
+        }
+
+        debug_assert!(next_stacking_order_id >= self.window.next_frame.next_stacking_order_id);
+        self.window.next_frame.next_stacking_order_id = next_stacking_order_id;
+    }
+
+    pub fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
+    where
+        F: FnOnce(&mut Self) -> R,
+    {
+        if let Some(style) = style {
+            self.push_text_style(style);
+            let result = f(self);
+            self.pop_text_style();
+            result
+        } else {
+            f(self)
+        }
+    }
+
+    /// Updates the cursor style at the platform level.
+    pub fn set_cursor_style(&mut self, style: CursorStyle) {
+        let view_id = self.parent_view_id();
+        self.window.next_frame.cursor_styles.insert(view_id, style);
+        self.window.next_frame.requested_cursor_style = Some(style);
+    }
+
+    /// Sets a tooltip to be rendered for the upcoming frame
+    pub fn set_tooltip(&mut self, tooltip: AnyTooltip) {
+        let view_id = self.parent_view_id();
+        self.window.next_frame.tooltip_request = Some(TooltipRequest { view_id, tooltip });
+    }
+
+    /// Pushes the given element id onto the global stack and invokes the given closure
+    /// with a `GlobalElementId`, which disambiguates the given id in the context of its ancestor
+    /// ids. Because elements are discarded and recreated on each frame, the `GlobalElementId` is
+    /// used to associate state with identified elements across separate frames.
+    pub fn with_element_id<R>(
+        &mut self,
+        id: Option<impl Into<ElementId>>,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
+        if let Some(id) = id.map(Into::into) {
+            let window = self.window_mut();
+            window.element_id_stack.push(id);
+            let result = f(self);
+            let window: &mut Window = self.borrow_mut();
+            window.element_id_stack.pop();
+            result
+        } else {
+            f(self)
+        }
+    }
+
+    /// Invoke the given function with the given content mask after intersecting it
+    /// with the current mask.
+    pub fn with_content_mask<R>(
+        &mut self,
+        mask: Option<ContentMask<Pixels>>,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
+        if let Some(mask) = mask {
+            let mask = mask.intersect(&self.content_mask());
+            self.window_mut().next_frame.content_mask_stack.push(mask);
+            let result = f(self);
+            self.window_mut().next_frame.content_mask_stack.pop();
+            result
+        } else {
+            f(self)
+        }
+    }
+
+    /// Invoke the given function with the content mask reset to that
+    /// of the window.
+    pub fn break_content_mask<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
+        let mask = ContentMask {
+            bounds: Bounds {
+                origin: Point::default(),
+                size: self.window().viewport_size,
+            },
+        };
+
+        let new_root_z_index = post_inc(&mut self.window_mut().next_frame.next_root_z_index);
+        let new_stacking_order_id =
+            post_inc(&mut self.window_mut().next_frame.next_stacking_order_id);
+        let new_context = StackingContext {
+            z_index: new_root_z_index,
+            id: new_stacking_order_id,
+        };
+
+        let old_stacking_order = mem::take(&mut self.window_mut().next_frame.z_index_stack);
+
+        self.window_mut().next_frame.z_index_stack.push(new_context);
+        self.window_mut().next_frame.content_mask_stack.push(mask);
+        let result = f(self);
+        self.window_mut().next_frame.content_mask_stack.pop();
+        self.window_mut().next_frame.z_index_stack = old_stacking_order;
+
+        result
+    }
+
+    /// Called during painting to invoke the given closure in a new stacking context. The given
+    /// z-index is interpreted relative to the previous call to `stack`.
+    pub fn with_z_index<R>(&mut self, z_index: u16, f: impl FnOnce(&mut Self) -> R) -> R {
+        let new_stacking_order_id =
+            post_inc(&mut self.window_mut().next_frame.next_stacking_order_id);
+        let new_context = StackingContext {
+            z_index,
+            id: new_stacking_order_id,
+        };
+
+        self.window_mut().next_frame.z_index_stack.push(new_context);
+        let result = f(self);
+        self.window_mut().next_frame.z_index_stack.pop();
+
+        result
+    }
+
+    /// Updates the global element offset relative to the current offset. This is used to implement
+    /// scrolling.
+    pub fn with_element_offset<R>(
+        &mut self,
+        offset: Point<Pixels>,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
+        if offset.is_zero() {
+            return f(self);
+        };
+
+        let abs_offset = self.element_offset() + offset;
+        self.with_absolute_element_offset(abs_offset, f)
+    }
+
+    /// Updates the global element offset based on the given offset. This is used to implement
+    /// drag handles and other manual painting of elements.
+    pub fn with_absolute_element_offset<R>(
+        &mut self,
+        offset: Point<Pixels>,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
+        self.window_mut()
+            .next_frame
+            .element_offset_stack
+            .push(offset);
+        let result = f(self);
+        self.window_mut().next_frame.element_offset_stack.pop();
+        result
+    }
+
+    /// Obtain the current element offset.
+    pub fn element_offset(&self) -> Point<Pixels> {
+        self.window()
+            .next_frame
+            .element_offset_stack
+            .last()
+            .copied()
+            .unwrap_or_default()
+    }
+
+    /// Obtain the current content mask.
+    pub fn content_mask(&self) -> ContentMask<Pixels> {
+        self.window()
+            .next_frame
+            .content_mask_stack
+            .last()
+            .cloned()
+            .unwrap_or_else(|| ContentMask {
+                bounds: Bounds {
+                    origin: Point::default(),
+                    size: self.window().viewport_size,
+                },
+            })
+    }
+
+    /// The size of an em for the base font of the application. Adjusting this value allows the
+    /// UI to scale, just like zooming a web page.
+    pub fn rem_size(&self) -> Pixels {
+        self.window().rem_size
+    }
+
+    /// Updates or initializes state for an element with the given id that lives across multiple
+    /// frames. If an element with this ID existed in the rendered frame, its state will be passed
+    /// to the given closure. The state returned by the closure will be stored so it can be referenced
+    /// when drawing the next frame.
+    pub fn with_element_state<S, R>(
+        &mut self,
+        id: ElementId,
+        f: impl FnOnce(Option<S>, &mut Self) -> (R, S),
+    ) -> R
+    where
+        S: 'static,
+    {
+        self.with_element_id(Some(id), |cx| {
+                let global_id = cx.window().element_id_stack.clone();
+
+                if let Some(any) = cx
+                    .window_mut()
+                    .next_frame
+                    .element_states
+                    .remove(&global_id)
+                    .or_else(|| {
+                        cx.window_mut()
+                            .rendered_frame
+                            .element_states
+                            .remove(&global_id)
+                    })
+                {
+                    let ElementStateBox {
+                        inner,
+                        parent_view_id,
+                        #[cfg(debug_assertions)]
+                        type_name
+                    } = any;
+                    // Using the extra inner option to avoid needing to reallocate a new box.
+                    let mut state_box = inner
+                        .downcast::<Option<S>>()
+                        .map_err(|_| {
+                            #[cfg(debug_assertions)]
+                            {
+                                anyhow::anyhow!(
+                                    "invalid element state type for id, requested_type {:?}, actual type: {:?}",
+                                    std::any::type_name::<S>(),
+                                    type_name
+                                )
+                            }
+
+                            #[cfg(not(debug_assertions))]
+                            {
+                                anyhow::anyhow!(
+                                    "invalid element state type for id, requested_type {:?}",
+                                    std::any::type_name::<S>(),
+                                )
+                            }
+                        })
+                        .unwrap();
+
+                    // Actual: Option<AnyElement> <- View
+                    // Requested: () <- AnyElement
+                    let state = state_box
+                        .take()
+                        .expect("element state is already on the stack");
+                    let (result, state) = f(Some(state), cx);
+                    state_box.replace(state);
+                    cx.window_mut()
+                        .next_frame
+                        .element_states
+                        .insert(global_id, ElementStateBox {
+                            inner: state_box,
+                            parent_view_id,
+                            #[cfg(debug_assertions)]
+                            type_name
+                        });
+                    result
+                } else {
+                    let (result, state) = f(None, cx);
+                    let parent_view_id = cx.parent_view_id();
+                    cx.window_mut()
+                        .next_frame
+                        .element_states
+                        .insert(global_id,
+                            ElementStateBox {
+                                inner: Box::new(Some(state)),
+                                parent_view_id,
+                                #[cfg(debug_assertions)]
+                                type_name: std::any::type_name::<S>()
+                            }
+
+                        );
+                    result
+                }
+            })
+    }
+    /// Paint one or more drop shadows into the scene for the next frame at the current z-index.
+    pub fn paint_shadows(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        corner_radii: Corners<Pixels>,
+        shadows: &[BoxShadow],
+    ) {
+        let scale_factor = self.scale_factor();
+        let content_mask = self.content_mask();
+        let view_id = self.parent_view_id();
+        let window = &mut *self.window;
+        for shadow in shadows {
+            let mut shadow_bounds = bounds;
+            shadow_bounds.origin += shadow.offset;
+            shadow_bounds.dilate(shadow.spread_radius);
+            window.next_frame.scene.insert(
+                &window.next_frame.z_index_stack,
+                Shadow {
+                    view_id: view_id.into(),
+                    layer_id: 0,
+                    order: 0,
+                    bounds: shadow_bounds.scale(scale_factor),
+                    content_mask: content_mask.scale(scale_factor),
+                    corner_radii: corner_radii.scale(scale_factor),
+                    color: shadow.color,
+                    blur_radius: shadow.blur_radius.scale(scale_factor),
+                },
+            );
+        }
+    }
+
+    /// Paint one or more quads into the scene for the next frame at the current stacking context.
+    /// Quads are colored rectangular regions with an optional background, border, and corner radius.
+    /// see [`fill`], [`outline`], and [`quad`] to construct this type.
+    pub fn paint_quad(&mut self, quad: PaintQuad) {
+        let scale_factor = self.scale_factor();
+        let content_mask = self.content_mask();
+        let view_id = self.parent_view_id();
+
+        let window = &mut *self.window;
+        window.next_frame.scene.insert(
+            &window.next_frame.z_index_stack,
+            Quad {
+                view_id: view_id.into(),
+                layer_id: 0,
+                order: 0,
+                bounds: quad.bounds.scale(scale_factor),
+                content_mask: content_mask.scale(scale_factor),
+                background: quad.background,
+                border_color: quad.border_color,
+                corner_radii: quad.corner_radii.scale(scale_factor),
+                border_widths: quad.border_widths.scale(scale_factor),
+            },
+        );
+    }
+
+    /// Paint the given `Path` into the scene for the next frame at the current z-index.
+    pub fn paint_path(&mut self, mut path: Path<Pixels>, color: impl Into<Hsla>) {
+        let scale_factor = self.scale_factor();
+        let content_mask = self.content_mask();
+        let view_id = self.parent_view_id();
+
+        path.content_mask = content_mask;
+        path.color = color.into();
+        path.view_id = view_id.into();
+        let window = &mut *self.window;
+        window
+            .next_frame
+            .scene
+            .insert(&window.next_frame.z_index_stack, path.scale(scale_factor));
+    }
+
+    /// Paint an underline into the scene for the next frame at the current z-index.
+    pub fn paint_underline(
+        &mut self,
+        origin: Point<Pixels>,
+        width: Pixels,
+        style: &UnderlineStyle,
+    ) {
+        let scale_factor = self.scale_factor();
+        let height = if style.wavy {
+            style.thickness * 3.
+        } else {
+            style.thickness
+        };
+        let bounds = Bounds {
+            origin,
+            size: size(width, height),
+        };
+        let content_mask = self.content_mask();
+        let view_id = self.parent_view_id();
+
+        let window = &mut *self.window;
+        window.next_frame.scene.insert(
+            &window.next_frame.z_index_stack,
+            Underline {
+                view_id: view_id.into(),
+                layer_id: 0,
+                order: 0,
+                bounds: bounds.scale(scale_factor),
+                content_mask: content_mask.scale(scale_factor),
+                thickness: style.thickness.scale(scale_factor),
+                color: style.color.unwrap_or_default(),
+                wavy: style.wavy,
+            },
+        );
+    }
+
+    /// Paint a monochrome (non-emoji) glyph into the scene for the next frame at the current z-index.
+    /// The y component of the origin is the baseline of the glyph.
+    pub fn paint_glyph(
+        &mut self,
+        origin: Point<Pixels>,
+        font_id: FontId,
+        glyph_id: GlyphId,
+        font_size: Pixels,
+        color: Hsla,
+    ) -> Result<()> {
+        let scale_factor = self.scale_factor();
+        let glyph_origin = origin.scale(scale_factor);
+        let subpixel_variant = Point {
+            x: (glyph_origin.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
+            y: (glyph_origin.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
+        };
+        let params = RenderGlyphParams {
+            font_id,
+            glyph_id,
+            font_size,
+            subpixel_variant,
+            scale_factor,
+            is_emoji: false,
+        };
+
+        let raster_bounds = self.text_system().raster_bounds(&params)?;
+        if !raster_bounds.is_zero() {
+            let tile =
+                self.window
+                    .sprite_atlas
+                    .get_or_insert_with(&params.clone().into(), &mut || {
+                        let (size, bytes) = self.text_system().rasterize_glyph(&params)?;
+                        Ok((size, Cow::Owned(bytes)))
+                    })?;
+            let bounds = Bounds {
+                origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
+                size: tile.bounds.size.map(Into::into),
+            };
+            let content_mask = self.content_mask().scale(scale_factor);
+            let view_id = self.parent_view_id();
+            let window = &mut *self.window;
+            window.next_frame.scene.insert(
+                &window.next_frame.z_index_stack,
+                MonochromeSprite {
+                    view_id: view_id.into(),
+                    layer_id: 0,
+                    order: 0,
+                    bounds,
+                    content_mask,
+                    color,
+                    tile,
+                },
+            );
+        }
+        Ok(())
+    }
+
+    /// Paint an emoji glyph into the scene for the next frame at the current z-index.
+    /// The y component of the origin is the baseline of the glyph.
+    pub fn paint_emoji(
+        &mut self,
+        origin: Point<Pixels>,
+        font_id: FontId,
+        glyph_id: GlyphId,
+        font_size: Pixels,
+    ) -> Result<()> {
+        let scale_factor = self.scale_factor();
+        let glyph_origin = origin.scale(scale_factor);
+        let params = RenderGlyphParams {
+            font_id,
+            glyph_id,
+            font_size,
+            // We don't render emojis with subpixel variants.
+            subpixel_variant: Default::default(),
+            scale_factor,
+            is_emoji: true,
+        };
+
+        let raster_bounds = self.text_system().raster_bounds(&params)?;
+        if !raster_bounds.is_zero() {
+            let tile =
+                self.window
+                    .sprite_atlas
+                    .get_or_insert_with(&params.clone().into(), &mut || {
+                        let (size, bytes) = self.text_system().rasterize_glyph(&params)?;
+                        Ok((size, Cow::Owned(bytes)))
+                    })?;
+            let bounds = Bounds {
+                origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
+                size: tile.bounds.size.map(Into::into),
+            };
+            let content_mask = self.content_mask().scale(scale_factor);
+            let view_id = self.parent_view_id();
+            let window = &mut *self.window;
+
+            window.next_frame.scene.insert(
+                &window.next_frame.z_index_stack,
+                PolychromeSprite {
+                    view_id: view_id.into(),
+                    layer_id: 0,
+                    order: 0,
+                    bounds,
+                    corner_radii: Default::default(),
+                    content_mask,
+                    tile,
+                    grayscale: false,
+                },
+            );
+        }
+        Ok(())
+    }
+
+    /// Paint a monochrome SVG into the scene for the next frame at the current stacking context.
+    pub fn paint_svg(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        path: SharedString,
+        color: Hsla,
+    ) -> Result<()> {
+        let scale_factor = self.scale_factor();
+        let bounds = bounds.scale(scale_factor);
+        // Render the SVG at twice the size to get a higher quality result.
+        let params = RenderSvgParams {
+            path,
+            size: bounds
+                .size
+                .map(|pixels| DevicePixels::from((pixels.0 * 2.).ceil() as i32)),
+        };
+
+        let tile =
+            self.window
+                .sprite_atlas
+                .get_or_insert_with(&params.clone().into(), &mut || {
+                    let bytes = self.svg_renderer.render(&params)?;
+                    Ok((params.size, Cow::Owned(bytes)))
+                })?;
+        let content_mask = self.content_mask().scale(scale_factor);
+        let view_id = self.parent_view_id();
+
+        let window = &mut *self.window;
+        window.next_frame.scene.insert(
+            &window.next_frame.z_index_stack,
+            MonochromeSprite {
+                view_id: view_id.into(),
+                layer_id: 0,
+                order: 0,
+                bounds,
+                content_mask,
+                color,
+                tile,
+            },
+        );
+
+        Ok(())
+    }
+
+    /// Paint an image into the scene for the next frame at the current z-index.
+    pub fn paint_image(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        corner_radii: Corners<Pixels>,
+        data: Arc<ImageData>,
+        grayscale: bool,
+    ) -> Result<()> {
+        let scale_factor = self.scale_factor();
+        let bounds = bounds.scale(scale_factor);
+        let params = RenderImageParams { image_id: data.id };
+
+        let tile = self
+            .window
+            .sprite_atlas
+            .get_or_insert_with(&params.clone().into(), &mut || {
+                Ok((data.size(), Cow::Borrowed(data.as_bytes())))
+            })?;
+        let content_mask = self.content_mask().scale(scale_factor);
+        let corner_radii = corner_radii.scale(scale_factor);
+        let view_id = self.parent_view_id();
+
+        let window = &mut *self.window;
+        window.next_frame.scene.insert(
+            &window.next_frame.z_index_stack,
+            PolychromeSprite {
+                view_id: view_id.into(),
+                layer_id: 0,
+                order: 0,
+                bounds,
+                content_mask,
+                corner_radii,
+                tile,
+                grayscale,
+            },
+        );
+        Ok(())
+    }
+
+    /// Paint a surface into the scene for the next frame at the current z-index.
+    pub fn paint_surface(&mut self, bounds: Bounds<Pixels>, image_buffer: CVImageBuffer) {
+        let scale_factor = self.scale_factor();
+        let bounds = bounds.scale(scale_factor);
+        let content_mask = self.content_mask().scale(scale_factor);
+        let view_id = self.parent_view_id();
+        let window = &mut *self.window;
+        window.next_frame.scene.insert(
+            &window.next_frame.z_index_stack,
+            Surface {
+                view_id: view_id.into(),
+                layer_id: 0,
+                order: 0,
+                bounds,
+                content_mask,
+                image_buffer,
+            },
+        );
+    }
+
+    #[must_use]
+    /// Add a node to the layout tree for the current frame. Takes the `Style` of the element for which
+    /// layout is being requested, along with the layout ids of any children. This method is called during
+    /// calls to the `Element::layout` trait method and enables any element to participate in layout.
+    pub fn request_layout(
+        &mut self,
+        style: &Style,
+        children: impl IntoIterator<Item = LayoutId>,
+    ) -> LayoutId {
+        self.app.layout_id_buffer.clear();
+        self.app.layout_id_buffer.extend(children);
+        let rem_size = self.rem_size();
+
+        self.cx
+            .window
+            .layout_engine
+            .as_mut()
+            .unwrap()
+            .request_layout(style, rem_size, &self.cx.app.layout_id_buffer)
+    }
+
+    /// Add a node to the layout tree for the current frame. Instead of taking a `Style` and children,
+    /// this variant takes a function that is invoked during layout so you can use arbitrary logic to
+    /// determine the element's size. One place this is used internally is when measuring text.
+    ///
+    /// The given closure is invoked at layout time with the known dimensions and available space and
+    /// returns a `Size`.
+    pub fn request_measured_layout<
+        F: FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut WindowContext) -> Size<Pixels>
+            + 'static,
+    >(
+        &mut self,
+        style: Style,
+        measure: F,
+    ) -> LayoutId {
+        let rem_size = self.rem_size();
+        self.window
+            .layout_engine
+            .as_mut()
+            .unwrap()
+            .request_measured_layout(style, rem_size, measure)
+    }
+
+    /// Compute the layout for the given id within the given available space.
+    /// This method is called for its side effect, typically by the framework prior to painting.
+    /// After calling it, you can request the bounds of the given layout node id or any descendant.
+    pub fn compute_layout(&mut self, layout_id: LayoutId, available_space: Size<AvailableSpace>) {
+        let mut layout_engine = self.window.layout_engine.take().unwrap();
+        layout_engine.compute_layout(layout_id, available_space, self);
+        self.window.layout_engine = Some(layout_engine);
+    }
+
+    /// Obtain the bounds computed for the given LayoutId relative to the window. This method should not
+    /// be invoked until the paint phase begins, and will usually be invoked by GPUI itself automatically
+    /// in order to pass your element its `Bounds` automatically.
+    pub fn layout_bounds(&mut self, layout_id: LayoutId) -> Bounds<Pixels> {
+        let mut bounds = self
+            .window
+            .layout_engine
+            .as_mut()
+            .unwrap()
+            .layout_bounds(layout_id)
+            .map(Into::into);
+        bounds.origin += self.element_offset();
+        bounds
+    }
+
+    pub(crate) fn layout_style(&self, layout_id: LayoutId) -> Option<&Style> {
+        self.window
+            .layout_engine
+            .as_ref()
+            .unwrap()
+            .requested_style(layout_id)
+    }
+
+    /// Called during painting to track which z-index is on top at each pixel position
+    pub fn add_opaque_layer(&mut self, bounds: Bounds<Pixels>) {
+        let stacking_order = self.window.next_frame.z_index_stack.clone();
+        let view_id = self.parent_view_id();
+        let depth_map = &mut self.window.next_frame.depth_map;
+        match depth_map.binary_search_by(|(level, _, _)| stacking_order.cmp(level)) {
+            Ok(i) | Err(i) => depth_map.insert(i, (stacking_order, view_id, bounds)),
+        }
+    }
+
+    /// Invoke the given function with the given focus handle present on the key dispatch stack.
+    /// If you want an element to participate in key dispatch, use this method to push its key context and focus handle into the stack during paint.
+    pub fn with_key_dispatch<R>(
+        &mut self,
+        context: Option<KeyContext>,
+        focus_handle: Option<FocusHandle>,
+        f: impl FnOnce(Option<FocusHandle>, &mut Self) -> R,
+    ) -> R {
+        let window = &mut self.window;
+        let focus_id = focus_handle.as_ref().map(|handle| handle.id);
+        window
+            .next_frame
+            .dispatch_tree
+            .push_node(context.clone(), focus_id, None);
+
+        let result = f(focus_handle, self);
+
+        self.window.next_frame.dispatch_tree.pop_node();
+
+        result
+    }
+
+    /// Invoke the given function with the given view id present on the view stack.
+    /// This is a fairly low-level method used to layout views.
+    pub fn with_view_id<R>(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R {
+        let text_system = self.text_system().clone();
+        text_system.with_view(view_id, || {
+            if self.window.next_frame.view_stack.last() == Some(&view_id) {
+                return f(self);
+            } else {
+                self.window.next_frame.view_stack.push(view_id);
+                let result = f(self);
+                self.window.next_frame.view_stack.pop();
+                result
+            }
+        })
+    }
+
+    /// Invoke the given function with the given view id present on the view stack.
+    /// This is a fairly low-level method used to paint views.
+    pub fn paint_view<R>(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R {
+        let text_system = self.text_system().clone();
+        text_system.with_view(view_id, || {
+            if self.window.next_frame.view_stack.last() == Some(&view_id) {
+                return f(self);
+            } else {
+                self.window.next_frame.view_stack.push(view_id);
+                self.window
+                    .next_frame
+                    .dispatch_tree
+                    .push_node(None, None, Some(view_id));
+                let result = f(self);
+                self.window.next_frame.dispatch_tree.pop_node();
+                self.window.next_frame.view_stack.pop();
+                result
+            }
+        })
+    }
+
+    /// Sets an input handler, such as [`ElementInputHandler`][element_input_handler], which interfaces with the
+    /// platform to receive textual input with proper integration with concerns such
+    /// as IME interactions. This handler will be active for the upcoming frame until the following frame is
+    /// rendered.
+    ///
+    /// [element_input_handler]: crate::ElementInputHandler
+    pub fn handle_input(&mut self, focus_handle: &FocusHandle, input_handler: impl InputHandler) {
+        if focus_handle.is_focused(self) {
+            let view_id = self.parent_view_id();
+            self.window.next_frame.requested_input_handler = Some(RequestedInputHandler {
+                view_id,
+                handler: Some(PlatformInputHandler::new(
+                    self.to_async(),
+                    Box::new(input_handler),
+                )),
+            })
+        }
+    }
+
+    /// Register a mouse event listener on the window for the next frame. The type of event
+    /// is determined by the first parameter of the given listener. When the next frame is rendered
+    /// the listener will be cleared.
+    pub fn on_mouse_event<Event: MouseEvent>(
+        &mut self,
+        mut handler: impl FnMut(&Event, DispatchPhase, &mut ElementContext) + 'static,
+    ) {
+        let view_id = self.parent_view_id();
+        let order = self.window.next_frame.z_index_stack.clone();
+        self.window
+            .next_frame
+            .mouse_listeners
+            .entry(TypeId::of::<Event>())
+            .or_default()
+            .push((
+                order,
+                view_id,
+                Box::new(
+                    move |event: &dyn Any, phase: DispatchPhase, cx: &mut ElementContext<'_>| {
+                        handler(event.downcast_ref().unwrap(), phase, cx)
+                    },
+                ),
+            ))
+    }
+
+    /// Register a key event listener on the window for the next frame. The type of event
+    /// is determined by the first parameter of the given listener. When the next frame is rendered
+    /// the listener will be cleared.
+    ///
+    /// This is a fairly low-level method, so prefer using event handlers on elements unless you have
+    /// a specific need to register a global listener.
+    pub fn on_key_event<Event: KeyEvent>(
+        &mut self,
+        listener: impl Fn(&Event, DispatchPhase, &mut ElementContext) + 'static,
+    ) {
+        self.window.next_frame.dispatch_tree.on_key_event(Rc::new(
+            move |event: &dyn Any, phase, cx: &mut ElementContext<'_>| {
+                if let Some(event) = event.downcast_ref::<Event>() {
+                    listener(event, phase, cx)
+                }
+            },
+        ));
+    }
+}

crates/journal/src/journal.rs ๐Ÿ”—

@@ -102,7 +102,7 @@ pub fn new_journal_entry(app_state: Arc<AppState>, cx: &mut WindowContext) {
     cx.spawn(|mut cx| async move {
         let (journal_dir, entry_path) = create_entry.await?;
         let (workspace, _) = cx
-            .update(|_, cx| workspace::open_paths(&[journal_dir], &app_state, None, cx))?
+            .update(|cx| workspace::open_paths(&[journal_dir], &app_state, None, cx))?
             .await?;
 
         let opened = workspace

crates/language/src/buffer.rs ๐Ÿ”—

@@ -10,7 +10,7 @@ use crate::{
     markdown::parse_markdown,
     outline::OutlineItem,
     syntax_map::{
-        SyntaxLayerInfo, SyntaxMap, SyntaxMapCapture, SyntaxMapCaptures, SyntaxMapMatches,
+        SyntaxLayer, SyntaxMap, SyntaxMapCapture, SyntaxMapCaptures, SyntaxMapMatches,
         SyntaxSnapshot, ToTreeSitterPoint,
     },
     CodeLabel, LanguageScope, Outline,
@@ -42,7 +42,13 @@ use std::{
 };
 use sum_tree::TreeMap;
 use text::operation_queue::OperationQueue;
-pub use text::{Buffer as TextBuffer, BufferSnapshot as TextBufferSnapshot, *};
+use text::*;
+pub use text::{
+    Anchor, Bias, Buffer as TextBuffer, BufferSnapshot as TextBufferSnapshot, Edit, OffsetRangeExt,
+    OffsetUtf16, Patch, Point, PointUtf16, Rope, RopeFingerprint, Selection, SelectionGoal,
+    Subscription, TextDimension, TextSummary, ToOffset, ToOffsetUtf16, ToPoint, ToPointUtf16,
+    Transaction, TransactionId, Unclipped,
+};
 use theme::SyntaxTheme;
 #[cfg(any(test, feature = "test-support"))]
 use util::RandomCharIter;
@@ -54,15 +60,22 @@ pub use {tree_sitter_rust, tree_sitter_typescript};
 pub use lsp::DiagnosticSeverity;
 
 lazy_static! {
+    /// A label for the background task spawned by the buffer to compute
+    /// a diff against the contents of its file.
     pub static ref BUFFER_DIFF_TASK: TaskLabel = TaskLabel::new();
 }
 
+/// Indicate whether a [Buffer] has permissions to edit.
 #[derive(PartialEq, Clone, Copy, Debug)]
 pub enum Capability {
+    /// The buffer is a mutable replica.
     ReadWrite,
+    /// The buffer is a read-only replica.
     ReadOnly,
 }
 
+/// An in-memory representation of a source code file, including its text,
+/// syntax trees, git status, and diagnostics.
 pub struct Buffer {
     text: TextBuffer,
     diff_base: Option<String>,
@@ -99,9 +112,11 @@ pub struct Buffer {
     capability: Capability,
 }
 
+/// An immutable, cheaply cloneable representation of a fixed
+/// state of a buffer.
 pub struct BufferSnapshot {
     text: text::BufferSnapshot,
-    pub git_diff: git::diff::BufferDiff,
+    git_diff: git::diff::BufferDiff,
     pub(crate) syntax: SyntaxSnapshot,
     file: Option<Arc<dyn File>>,
     diagnostics: SmallVec<[(LanguageServerId, DiagnosticSet); 2]>,
@@ -114,25 +129,37 @@ pub struct BufferSnapshot {
     parse_count: usize,
 }
 
+/// The kind and amount of indentation in a particular line. For now,
+/// assumes that indentation is all the same character.
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
 pub struct IndentSize {
+    /// The number of bytes that comprise the indentation.
     pub len: u32,
+    /// The kind of whitespace used for indentation.
     pub kind: IndentKind,
 }
 
+/// A whitespace character that's used for indentation.
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
 pub enum IndentKind {
+    /// An ASCII space character.
     #[default]
     Space,
+    /// An ASCII tab character.
     Tab,
 }
 
+/// The shape of a selection cursor.
 #[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
 pub enum CursorShape {
+    /// A vertical bar
     #[default]
     Bar,
+    /// A block that surrounds the following character
     Block,
+    /// An underline that runs along the following character
     Underscore,
+    /// A box drawn around the following character
     Hollow,
 }
 
@@ -144,25 +171,40 @@ struct SelectionSet {
     lamport_timestamp: clock::Lamport,
 }
 
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct GroupId {
-    source: Arc<str>,
-    id: usize,
-}
-
+/// A diagnostic associated with a certain range of a buffer.
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub struct Diagnostic {
+    /// The name of the service that produced this diagnostic.
     pub source: Option<String>,
+    /// A machine-readable code that identifies this diagnostic.
     pub code: Option<String>,
+    /// Whether this diagnostic is a hint, warning, or error.
     pub severity: DiagnosticSeverity,
+    /// The human-readable message associated with this diagnostic.
     pub message: String,
+    /// An id that identifies the group to which this diagnostic belongs.
+    ///
+    /// When a language server produces a diagnostic with
+    /// one or more associated diagnostics, those diagnostics are all
+    /// assigned a single group id.
     pub group_id: usize,
-    pub is_valid: bool,
+    /// Whether this diagnostic is the primary diagnostic for its group.
+    ///
+    /// In a given group, the primary diagnostic is the top-level diagnostic
+    /// returned by the language server. The non-primary diagnostics are the
+    /// associated diagnostics.
     pub is_primary: bool,
+    /// Whether this diagnostic is considered to originate from an analysis of
+    /// files on disk, as opposed to any unsaved buffer contents. This is a
+    /// property of a given diagnostic source, and is configured for a given
+    /// language server via the [LspAdapter::disk_based_diagnostic_sources] method
+    /// for the language server.
     pub is_disk_based: bool,
+    /// Whether this diagnostic marks unnecessary code.
     pub is_unnecessary: bool,
 }
 
+/// TODO - move this into the `project` crate and make it private.
 pub async fn prepare_completion_documentation(
     documentation: &lsp::Documentation,
     language_registry: &Arc<LanguageRegistry>,
@@ -194,77 +236,127 @@ pub async fn prepare_completion_documentation(
     }
 }
 
+/// Documentation associated with a [Completion].
 #[derive(Clone, Debug)]
 pub enum Documentation {
+    /// There is no documentation for this completion.
     Undocumented,
+    /// A single line of documentation.
     SingleLine(String),
+    /// Multiple lines of plain text documentation.
     MultiLinePlainText(String),
+    /// Markdown documentation.
     MultiLineMarkdown(ParsedMarkdown),
 }
 
+/// A completion provided by a language server
 #[derive(Clone, Debug)]
 pub struct Completion {
+    /// The range of the buffer that will be replaced.
     pub old_range: Range<Anchor>,
+    /// The new text that will be inserted.
     pub new_text: String,
+    /// A label for this completion that is shown in the menu.
     pub label: CodeLabel,
+    /// The id of the language server that produced this completion.
     pub server_id: LanguageServerId,
+    /// The documentation for this completion.
     pub documentation: Option<Documentation>,
+    /// The raw completion provided by the language server.
     pub lsp_completion: lsp::CompletionItem,
 }
 
+/// A code action provided by a language server.
 #[derive(Clone, Debug)]
 pub struct CodeAction {
+    /// The id of the language server that produced this code action.
     pub server_id: LanguageServerId,
+    /// The range of the buffer where this code action is applicable.
     pub range: Range<Anchor>,
+    /// The raw code action provided by the language server.
     pub lsp_action: lsp::CodeAction,
 }
 
+/// An operation used to synchronize this buffer with its other replicas.
 #[derive(Clone, Debug, PartialEq)]
 pub enum Operation {
+    /// A text operation.
     Buffer(text::Operation),
 
+    /// An update to the buffer's diagnostics.
     UpdateDiagnostics {
+        /// The id of the language server that produced the new diagnostics.
         server_id: LanguageServerId,
+        /// The diagnostics.
         diagnostics: Arc<[DiagnosticEntry<Anchor>]>,
+        /// The buffer's lamport timestamp.
         lamport_timestamp: clock::Lamport,
     },
 
+    /// An update to the most recent selections in this buffer.
     UpdateSelections {
+        /// The selections.
         selections: Arc<[Selection<Anchor>]>,
+        /// The buffer's lamport timestamp.
         lamport_timestamp: clock::Lamport,
+        /// Whether the selections are in 'line mode'.
         line_mode: bool,
+        /// The [CursorShape] associated with these selections.
         cursor_shape: CursorShape,
     },
 
+    /// An update to the characters that should trigger autocompletion
+    /// for this buffer.
     UpdateCompletionTriggers {
+        /// The characters that trigger autocompletion.
         triggers: Vec<String>,
+        /// The buffer's lamport timestamp.
         lamport_timestamp: clock::Lamport,
     },
 }
 
+/// An event that occurs in a buffer.
 #[derive(Clone, Debug, PartialEq)]
 pub enum Event {
+    /// The buffer was changed in a way that must be
+    /// propagated to its other replicas.
     Operation(Operation),
+    /// The buffer was edited.
     Edited,
+    /// The buffer's `dirty` bit changed.
     DirtyChanged,
+    /// The buffer was saved.
     Saved,
+    /// The buffer's file was changed on disk.
     FileHandleChanged,
+    /// The buffer was reloaded.
     Reloaded,
+    /// The buffer's diff_base changed.
     DiffBaseChanged,
+    /// The buffer's language was changed.
     LanguageChanged,
+    /// The buffer's syntax trees were updated.
     Reparsed,
+    /// The buffer's diagnostics were updated.
     DiagnosticsUpdated,
+    /// The buffer gained or lost editing capabilities.
     CapabilityChanged,
+    /// The buffer was explicitly requested to close.
     Closed,
 }
 
+/// The file associated with a buffer.
 pub trait File: Send + Sync {
+    /// Returns the [LocalFile] associated with this file, if the
+    /// file is local.
     fn as_local(&self) -> Option<&dyn LocalFile>;
 
+    /// Returns whether this file is local.
     fn is_local(&self) -> bool {
         self.as_local().is_some()
     }
 
+    /// Returns the file's mtime.
     fn mtime(&self) -> SystemTime;
 
     /// Returns the path of this file relative to the worktree's root directory.
@@ -283,19 +375,25 @@ pub trait File: Send + Sync {
     /// This is needed for looking up project-specific settings.
     fn worktree_id(&self) -> usize;
 
+    /// Returns whether the file has been deleted.
     fn is_deleted(&self) -> bool;
 
+    /// 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) -> rpc::proto::File;
 }
 
+/// 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;
 
+    /// Loads the file's contents from disk.
     fn load(&self, cx: &AppContext) -> Task<Result<String>>;
 
+    /// Called when the buffer is reloaded from disk.
     fn buffer_reloaded(
         &self,
         buffer_id: u64,
@@ -307,6 +405,10 @@ pub trait LocalFile: File {
     );
 }
 
+/// The auto-indent behavior associated with an editing operation.
+/// For some editing operations, each affected line of text has its
+/// indentation recomputed. For other operations, the entire block
+/// of edited text is adjusted uniformly.
 #[derive(Clone, Debug)]
 pub enum AutoindentMode {
     /// Indent each line of inserted text.
@@ -354,6 +456,8 @@ struct BufferChunkHighlights<'a> {
     highlight_maps: Vec<HighlightMap>,
 }
 
+/// An iterator that yields chunks of a buffer's text, along with their
+/// syntax highlights and diagnostic status.
 pub struct BufferChunks<'a> {
     range: Range<usize>,
     chunks: text::Chunks<'a>,
@@ -366,16 +470,26 @@ pub struct BufferChunks<'a> {
     highlights: Option<BufferChunkHighlights<'a>>,
 }
 
+/// A chunk of a buffer's text, along with its syntax highlight and
+/// diagnostic status.
 #[derive(Clone, Copy, Debug, Default)]
 pub struct Chunk<'a> {
+    /// The text of the chunk.
     pub text: &'a str,
+    /// The syntax highlighting style of the chunk.
     pub syntax_highlight_id: Option<HighlightId>,
+    /// The highlight style that has been applied to this chunk in
+    /// the editor.
     pub highlight_style: Option<HighlightStyle>,
+    /// The severity of diagnostic associated with this chunk, if any.
     pub diagnostic_severity: Option<DiagnosticSeverity>,
+    /// Whether this chunk of text is marked as unnecessary.
     pub is_unnecessary: bool,
+    /// Whether this chunk of text was originally a tab character.
     pub is_tab: bool,
 }
 
+/// A set of edits to a given version of a buffer, computed asynchronously.
 pub struct Diff {
     pub(crate) base_version: clock::Global,
     line_ending: LineEnding,
@@ -390,24 +504,19 @@ pub(crate) struct DiagnosticEndpoint {
     is_unnecessary: bool,
 }
 
+/// A class of characters, used for characterizing a run of text.
 #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug)]
 pub enum CharKind {
+    /// Whitespace.
     Whitespace,
+    /// Punctuation.
     Punctuation,
+    /// Word.
     Word,
 }
 
-impl CharKind {
-    pub fn coerce_punctuation(self, treat_punctuation_as_word: bool) -> Self {
-        if treat_punctuation_as_word && self == CharKind::Punctuation {
-            CharKind::Word
-        } else {
-            self
-        }
-    }
-}
-
 impl Buffer {
+    /// Create a new buffer with the given base text.
     pub fn new<T: Into<String>>(replica_id: ReplicaId, id: u64, base_text: T) -> Self {
         Self::build(
             TextBuffer::new(replica_id, id, base_text.into()),
@@ -417,6 +526,7 @@ impl Buffer {
         )
     }
 
+    /// Create a new buffer that is a replica of a remote buffer.
     pub fn remote(
         remote_id: u64,
         replica_id: ReplicaId,
@@ -431,6 +541,8 @@ impl Buffer {
         )
     }
 
+    /// Create a new buffer that is a replica of a remote buffer, populating its
+    /// state from the given protobuf message.
     pub fn from_proto(
         replica_id: ReplicaId,
         capability: Capability,
@@ -457,6 +569,7 @@ impl Buffer {
         Ok(this)
     }
 
+    /// Serialize the buffer's state to a protobuf message.
     pub fn to_proto(&self) -> proto::BufferState {
         proto::BufferState {
             id: self.remote_id(),
@@ -470,6 +583,7 @@ impl Buffer {
         }
     }
 
+    /// Serialize as protobufs all of the changes to the buffer since the given version.
     pub fn serialize_ops(
         &self,
         since: Option<clock::Global>,
@@ -516,19 +630,23 @@ 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 {
         self.set_language(Some(language), cx);
         self
     }
 
+    /// Returns the [Capability] of this buffer.
     pub fn capability(&self) -> Capability {
         self.capability
     }
 
+    /// Whether this buffer can only be read.
     pub fn read_only(&self) -> bool {
         self.capability == Capability::ReadOnly
     }
 
+    /// Builds a [Buffer] with the given underlying [TextBuffer], diff base, [File] and [Capability].
     pub fn build(
         buffer: TextBuffer,
         diff_base: Option<String>,
@@ -573,6 +691,8 @@ impl Buffer {
         }
     }
 
+    /// Retrieve a snapshot of the buffer's current state. This is computationally
+    /// cheap, and allows reading from the buffer on a background thread.
     pub fn snapshot(&self) -> BufferSnapshot {
         let text = self.text.snapshot();
         let mut syntax_map = self.syntax_map.lock();
@@ -595,30 +715,38 @@ impl Buffer {
         }
     }
 
-    pub fn as_text_snapshot(&self) -> &text::BufferSnapshot {
+    #[cfg(test)]
+    pub(crate) fn as_text_snapshot(&self) -> &text::BufferSnapshot {
         &self.text
     }
 
+    /// Retrieve a snapshot of the buffer's raw text, without any
+    /// language-related state like the syntax tree or diagnostics.
     pub fn text_snapshot(&self) -> text::BufferSnapshot {
         self.text.snapshot()
     }
 
+    /// The file associated with the buffer, if any.
     pub fn file(&self) -> Option<&Arc<dyn File>> {
         self.file.as_ref()
     }
 
+    /// The version of the buffer that was last saved or reloaded from disk.
     pub fn saved_version(&self) -> &clock::Global {
         &self.saved_version
     }
 
+    /// The fingerprint of the buffer's text when the buffer was last saved or reloaded from disk.
     pub fn saved_version_fingerprint(&self) -> RopeFingerprint {
         self.file_fingerprint
     }
 
+    /// The mtime of the buffer's file when the buffer was last saved or reloaded from disk.
     pub fn saved_mtime(&self) -> SystemTime {
         self.saved_mtime
     }
 
+    /// Assign a language to the buffer.
     pub fn set_language(&mut self, language: Option<Arc<Language>>, cx: &mut ModelContext<Self>) {
         self.syntax_map.lock().clear();
         self.language = language;
@@ -626,17 +754,21 @@ impl Buffer {
         cx.emit(Event::LanguageChanged);
     }
 
+    /// Assign a language registry to the buffer. This allows the buffer to retrieve
+    /// other languages if parts of the buffer are written in different languages.
     pub fn set_language_registry(&mut self, language_registry: Arc<LanguageRegistry>) {
         self.syntax_map
             .lock()
             .set_language_registry(language_registry);
     }
 
+    /// Assign the buffer a new [Capability].
     pub fn set_capability(&mut self, capability: Capability, cx: &mut ModelContext<Self>) {
         self.capability = capability;
         cx.emit(Event::CapabilityChanged)
     }
 
+    /// This method is called to signal that the buffer has been saved.
     pub fn did_save(
         &mut self,
         version: clock::Global,
@@ -651,6 +783,7 @@ impl Buffer {
         cx.notify();
     }
 
+    /// Reloads the contents of the buffer from disk.
     pub fn reload(
         &mut self,
         cx: &mut ModelContext<Self>,
@@ -699,6 +832,7 @@ impl Buffer {
         rx
     }
 
+    /// This method is called to signal that the buffer has been reloaded.
     pub fn did_reload(
         &mut self,
         version: clock::Global,
@@ -725,6 +859,8 @@ impl Buffer {
         cx.notify();
     }
 
+    /// 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>) {
         let mut file_changed = false;
 
@@ -762,16 +898,20 @@ impl Buffer {
         }
     }
 
+    /// Returns the current diff base, see [Buffer::set_diff_base].
     pub fn diff_base(&self) -> Option<&str> {
         self.diff_base.as_deref()
     }
 
+    /// Sets the text that will be used to compute a Git diff
+    /// against the buffer text.
     pub fn set_diff_base(&mut self, diff_base: Option<String>, cx: &mut ModelContext<Self>) {
         self.diff_base = diff_base;
         self.git_diff_recalc(cx);
         cx.emit(Event::DiffBaseChanged);
     }
 
+    /// Recomputes the Git diff status.
     pub fn git_diff_recalc(&mut self, cx: &mut ModelContext<Self>) -> Option<Task<()>> {
         let diff_base = self.diff_base.clone()?; // TODO: Make this an Arc
         let snapshot = self.snapshot();
@@ -792,14 +932,12 @@ impl Buffer {
         }))
     }
 
-    pub fn close(&mut self, cx: &mut ModelContext<Self>) {
-        cx.emit(Event::Closed);
-    }
-
+    /// Returns the primary [Language] assigned to this [Buffer].
     pub fn language(&self) -> Option<&Arc<Language>> {
         self.language.as_ref()
     }
 
+    /// Returns the [Language] at the given location.
     pub fn language_at<D: ToOffset>(&self, position: D) -> Option<Arc<Language>> {
         let offset = position.to_offset(self);
         self.syntax_map
@@ -810,31 +948,39 @@ impl Buffer {
             .or_else(|| self.language.clone())
     }
 
+    /// The number of times the buffer was parsed.
     pub fn parse_count(&self) -> usize {
         self.parse_count
     }
 
+    /// The number of times selections were updated.
     pub fn selections_update_count(&self) -> usize {
         self.selections_update_count
     }
 
+    /// The number of times diagnostics were updated.
     pub fn diagnostics_update_count(&self) -> usize {
         self.diagnostics_update_count
     }
 
+    /// The number of times the underlying file was updated.
     pub fn file_update_count(&self) -> usize {
         self.file_update_count
     }
 
+    /// The number of times the git diff status was updated.
     pub fn git_diff_update_count(&self) -> usize {
         self.git_diff_update_count
     }
 
+    /// Whether the buffer is being parsed in the background.
     #[cfg(any(test, feature = "test-support"))]
     pub fn is_parsing(&self) -> bool {
         self.parsing_in_background
     }
 
+    /// Indicates whether the buffer contains any regions that may be
+    /// written in a language that hasn't been loaded yet.
     pub fn contains_unknown_injections(&self) -> bool {
         self.syntax_map.lock().contains_unknown_injections()
     }
@@ -941,6 +1087,7 @@ impl Buffer {
         cx.notify();
     }
 
+    /// Assign to the buffer a set of diagnostics created by a given language server.
     pub fn update_diagnostics(
         &mut self,
         server_id: LanguageServerId,
@@ -1170,9 +1317,9 @@ impl Buffer {
         self.edit(edits, None, cx);
     }
 
-    // Create a minimal edit that will cause the the given row to be indented
-    // with the given size. After applying this edit, the length of the line
-    // will always be at least `new_size.len`.
+    /// Create a minimal edit that will cause the the given row to be indented
+    /// with the given size. After applying this edit, the length of the line
+    /// will always be at least `new_size.len`.
     pub fn edit_for_indent_size_adjustment(
         row: u32,
         current_size: IndentSize,
@@ -1207,6 +1354,8 @@ 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> {
         let old_text = self.as_rope().clone();
         let base_version = self.version();
@@ -1278,7 +1427,7 @@ impl Buffer {
             })
     }
 
-    /// Spawn a background task that searches the buffer for any whitespace
+    /// 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> {
         let old_text = self.as_rope().clone();
@@ -1298,7 +1447,7 @@ impl Buffer {
         })
     }
 
-    /// Ensure that the buffer ends with a single newline character, and
+    /// 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>) {
         let len = self.len();
@@ -1319,7 +1468,7 @@ impl Buffer {
         self.edit([(offset..len, "\n")], None, cx);
     }
 
-    /// Apply a diff to the buffer. If the buffer has changed since the given diff was
+    /// 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> {
@@ -1358,11 +1507,14 @@ impl Buffer {
         self.end_transaction(cx)
     }
 
+    /// Checks if the buffer has unsaved changes.
     pub fn is_dirty(&self) -> bool {
         self.file_fingerprint != self.as_rope().fingerprint()
             || self.file.as_ref().map_or(false, |file| file.is_deleted())
     }
 
+    /// Checks if the buffer and its file have both changed since the buffer
+    /// was last saved or reloaded.
     pub fn has_conflict(&self) -> bool {
         self.file_fingerprint != self.as_rope().fingerprint()
             && self
@@ -1371,14 +1523,23 @@ impl Buffer {
                 .map_or(false, |file| file.mtime() > self.saved_mtime)
     }
 
+    /// Gets a [`Subscription`] that tracks all of the changes to the buffer's text.
     pub fn subscribe(&mut self) -> Subscription {
         self.text.subscribe()
     }
 
+    /// Starts a transaction, if one is not already in-progress. When undoing or
+    /// redoing edits, all of the edits performed within a transaction are undone
+    /// or redone together.
     pub fn start_transaction(&mut self) -> Option<TransactionId> {
         self.start_transaction_at(Instant::now())
     }
 
+    /// Starts a transaction, providing the current time. Subsequent transactions
+    /// that occur within a short period of time will be grouped together. This
+    /// is controlled by the buffer's undo grouping duration.
+    ///
+    /// See [`Buffer::set_group_interval`].
     pub fn start_transaction_at(&mut self, now: Instant) -> Option<TransactionId> {
         self.transaction_depth += 1;
         if self.was_dirty_before_starting_transaction.is_none() {
@@ -1387,10 +1548,16 @@ impl Buffer {
         self.text.start_transaction_at(now)
     }
 
+    /// Terminates the current transaction, if this is the outermost transaction.
     pub fn end_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
         self.end_transaction_at(Instant::now(), cx)
     }
 
+    /// Terminates the current transaction, providing the current time. Subsequent transactions
+    /// that occur within a short period of time will be grouped together. This
+    /// is controlled by the buffer's undo grouping duration.
+    ///
+    /// See [`Buffer::set_group_interval`].
     pub fn end_transaction_at(
         &mut self,
         now: Instant,
@@ -1411,26 +1578,33 @@ impl Buffer {
         }
     }
 
+    /// Manually add a transaction to the buffer's undo history.
     pub fn push_transaction(&mut self, transaction: Transaction, now: Instant) {
         self.text.push_transaction(transaction, now);
     }
 
+    /// Prevent the last transaction from being grouped with any subsequent transactions,
+    /// even if they occur with the buffer's undo grouping duration.
     pub fn finalize_last_transaction(&mut self) -> Option<&Transaction> {
         self.text.finalize_last_transaction()
     }
 
+    /// Manually group all changes since a given transaction.
     pub fn group_until_transaction(&mut self, transaction_id: TransactionId) {
         self.text.group_until_transaction(transaction_id);
     }
 
+    /// Manually remove a transaction from the buffer's undo history
     pub fn forget_transaction(&mut self, transaction_id: TransactionId) {
         self.text.forget_transaction(transaction_id);
     }
 
+    /// Manually merge two adjacent transactions in the buffer's undo history.
     pub fn merge_transactions(&mut self, transaction: TransactionId, destination: TransactionId) {
         self.text.merge_transactions(transaction, destination);
     }
 
+    /// Waits for the buffer to receive operations with the given timestamps.
     pub fn wait_for_edits(
         &mut self,
         edit_ids: impl IntoIterator<Item = clock::Lamport>,
@@ -1438,6 +1612,7 @@ impl Buffer {
         self.text.wait_for_edits(edit_ids)
     }
 
+    /// Waits for the buffer to receive the operations necessary for resolving the given anchors.
     pub fn wait_for_anchors(
         &mut self,
         anchors: impl IntoIterator<Item = Anchor>,
@@ -1445,14 +1620,18 @@ impl Buffer {
         self.text.wait_for_anchors(anchors)
     }
 
+    /// Waits for the buffer to receive operations up to the given version.
     pub fn wait_for_version(&mut self, version: clock::Global) -> impl Future<Output = Result<()>> {
         self.text.wait_for_version(version)
     }
 
+    /// Forces all futures returned by [`Buffer::wait_for_version`], [`Buffer::wait_for_edits`], or
+    /// [`Buffer::wait_for_version`] to resolve with an error.
     pub fn give_up_waiting(&mut self) {
         self.text.give_up_waiting();
     }
 
+    /// Stores a set of selections that should be broadcasted to all of the buffer's replicas.
     pub fn set_active_selections(
         &mut self,
         selections: Arc<[Selection<Anchor>]>,
@@ -1481,6 +1660,8 @@ 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>) {
         if self
             .remote_selections
@@ -1491,6 +1672,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>
     where
         T: Into<Arc<str>>,
@@ -1499,6 +1681,15 @@ impl Buffer {
         self.edit([(0..self.len(), text)], None, cx)
     }
 
+    /// Applies the given edits to the buffer. Each edit is specified as a range of text to
+    /// delete, and a string of text to insert at that location.
+    ///
+    /// If an [`AutoindentMode`] is provided, then the buffer will enqueue an auto-indent
+    /// request for the edited ranges, which will be processed when the buffer finishes
+    /// parsing.
+    ///
+    /// Parsing takes place at the end of a transaction, and may compute synchronously
+    /// or asynchronously, depending on the changes.
     pub fn edit<I, S, T>(
         &mut self,
         edits_iter: I,
@@ -1632,6 +1823,7 @@ impl Buffer {
         cx.notify();
     }
 
+    /// Applies the given remote operations to the buffer.
     pub fn apply_ops<I: IntoIterator<Item = Operation>>(
         &mut self,
         ops: I,
@@ -1779,11 +1971,13 @@ impl Buffer {
         cx.emit(Event::Operation(operation));
     }
 
+    /// Removes the selections for a given peer.
     pub fn remove_peer(&mut self, replica_id: ReplicaId, cx: &mut ModelContext<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> {
         let was_dirty = self.is_dirty();
         let old_version = self.version.clone();
@@ -1797,6 +1991,7 @@ impl Buffer {
         }
     }
 
+    /// Manually undoes a specific transaction in the buffer's undo history.
     pub fn undo_transaction(
         &mut self,
         transaction_id: TransactionId,
@@ -1813,6 +2008,7 @@ impl Buffer {
         }
     }
 
+    /// Manually undoes all changes after a given transaction in the buffer's undo history.
     pub fn undo_to_transaction(
         &mut self,
         transaction_id: TransactionId,
@@ -1832,6 +2028,7 @@ impl Buffer {
         undone
     }
 
+    /// Manually redoes a specific transaction in the buffer's redo history.
     pub fn redo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
         let was_dirty = self.is_dirty();
         let old_version = self.version.clone();
@@ -1845,6 +2042,7 @@ impl Buffer {
         }
     }
 
+    /// Manually undoes all changes until a given transaction in the buffer's redo history.
     pub fn redo_to_transaction(
         &mut self,
         transaction_id: TransactionId,
@@ -1864,6 +2062,7 @@ impl Buffer {
         redone
     }
 
+    /// Override current completion triggers with the user-provided completion triggers.
     pub fn set_completion_triggers(&mut self, triggers: Vec<String>, cx: &mut ModelContext<Self>) {
         self.completion_triggers = triggers.clone();
         self.completion_triggers_timestamp = self.text.lamport_clock.tick();
@@ -1877,11 +2076,14 @@ impl Buffer {
         cx.notify();
     }
 
+    /// Returns a list of strings which trigger a completion menu for this language.
+    /// Usually this is driven by LSP server which returns a list of trigger characters for completions.
     pub fn completion_triggers(&self) -> &[String] {
         &self.completion_triggers
     }
 }
 
+#[doc(hidden)]
 #[cfg(any(test, feature = "test-support"))]
 impl Buffer {
     pub fn edit_via_marked_text(
@@ -1954,10 +2156,12 @@ impl Deref for Buffer {
 }
 
 impl BufferSnapshot {
+    /// Returns [`IndentSize`] for a given line that respects user settings and /// language preferences.
     pub fn indent_size_for_line(&self, row: u32) -> IndentSize {
         indent_size_for_line(self, row)
     }
-
+    /// 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 {
         let settings = language_settings(self.language_at(position), self.file(), cx);
         if settings.hard_tabs {
@@ -1967,6 +2171,8 @@ impl BufferSnapshot {
         }
     }
 
+    /// Retrieve the suggested indent size for all of the given rows. The unit of indentation
+    /// is passed in as `single_indent_size`.
     pub fn suggested_indents(
         &self,
         rows: impl Iterator<Item = u32>,
@@ -2213,6 +2419,10 @@ impl BufferSnapshot {
         None
     }
 
+    /// Iterates over chunks of text in the given range of the buffer. Text is chunked
+    /// in an arbitrary way due to being stored in a [`rope::Rope`]. The text is also
+    /// returned in chunks where each chunk has a single syntax highlighting style and
+    /// diagnostic status.
     pub fn chunks<T: ToOffset>(&self, range: Range<T>, language_aware: bool) -> BufferChunks {
         let range = range.start.to_offset(self)..range.end.to_offset(self);
 
@@ -2249,7 +2459,9 @@ impl BufferSnapshot {
         BufferChunks::new(self.text.as_rope(), range, syntax, diagnostic_endpoints)
     }
 
-    pub fn for_each_line(&self, range: Range<Point>, mut callback: impl FnMut(u32, &str)) {
+    /// Invokes the given callback for each line of text in the given range of the buffer.
+    /// Uses callback to avoid allocating a string for each line.
+    fn for_each_line(&self, range: Range<Point>, mut callback: impl FnMut(u32, &str)) {
         let mut line = String::new();
         let mut row = range.start.row;
         for chunk in self
@@ -2268,11 +2480,12 @@ impl BufferSnapshot {
         }
     }
 
-    pub fn syntax_layers(&self) -> impl Iterator<Item = SyntaxLayerInfo> + '_ {
+    /// Iterates over every [`SyntaxLayer`] in the buffer.
+    pub fn syntax_layers(&self) -> impl Iterator<Item = SyntaxLayer> + '_ {
         self.syntax.layers_for_range(0..self.len(), &self.text)
     }
 
-    pub fn syntax_layer_at<D: ToOffset>(&self, position: D) -> Option<SyntaxLayerInfo> {
+    fn syntax_layer_at<D: ToOffset>(&self, position: D) -> Option<SyntaxLayer> {
         let offset = position.to_offset(self);
         self.syntax
             .layers_for_range(offset..offset, &self.text)
@@ -2280,12 +2493,14 @@ impl BufferSnapshot {
             .last()
     }
 
+    /// Returns the [Language] at the given location.
     pub fn language_at<D: ToOffset>(&self, position: D) -> Option<&Arc<Language>> {
         self.syntax_layer_at(position)
             .map(|info| info.language)
             .or(self.language.as_ref())
     }
 
+    /// Returns the settings for the language at the given location.
     pub fn settings_at<'a, D: ToOffset>(
         &self,
         position: D,
@@ -2294,6 +2509,7 @@ impl BufferSnapshot {
         language_settings(self.language_at(position), self.file.as_ref(), cx)
     }
 
+    /// Returns the [LanguageScope] at the given location.
     pub fn language_scope_at<D: ToOffset>(&self, position: D) -> Option<LanguageScope> {
         let offset = position.to_offset(self);
         let mut scope = None;
@@ -2338,6 +2554,8 @@ impl BufferSnapshot {
         })
     }
 
+    /// Returns a tuple of the range and character kind of the word
+    /// surrounding the given position.
     pub fn surrounding_word<T: ToOffset>(&self, start: T) -> (Range<usize>, Option<CharKind>) {
         let mut start = start.to_offset(self);
         let mut end = start;
@@ -2370,6 +2588,7 @@ impl BufferSnapshot {
         (start..end, word_kind)
     }
 
+    /// Returns the range for the closes syntax node enclosing the given range.
     pub fn range_for_syntax_ancestor<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
         let range = range.start.to_offset(self)..range.end.to_offset(self);
         let mut result: Option<Range<usize>> = None;
@@ -2438,11 +2657,19 @@ impl BufferSnapshot {
         result
     }
 
+    /// Returns the outline for the buffer.
+    ///
+    /// This method allows passing an optional [SyntaxTheme] to
+    /// syntax-highlight the returned symbols.
     pub fn outline(&self, theme: Option<&SyntaxTheme>) -> Option<Outline<Anchor>> {
         self.outline_items_containing(0..self.len(), true, theme)
             .map(Outline::new)
     }
 
+    /// Returns all the symbols that contain the given position.
+    ///
+    /// This method allows passing an optional [SyntaxTheme] to
+    /// syntax-highlight the returned symbols.
     pub fn symbols_containing<T: ToOffset>(
         &self,
         position: T,

crates/language/src/diagnostic_set.rs ๐Ÿ”—

@@ -9,20 +9,36 @@ use std::{
 use sum_tree::{self, Bias, SumTree};
 use text::{Anchor, FromAnchor, PointUtf16, ToOffset};
 
+/// A set of diagnostics associated with a given buffer, provided
+/// by a single language server.
+///
+/// The diagnostics are stored in a [SumTree], which allows this struct
+/// to be cheaply copied, and allows for efficient retrieval of the
+/// diagnostics that intersect a given range of the buffer.
 #[derive(Clone, Debug, Default)]
 pub struct DiagnosticSet {
     diagnostics: SumTree<DiagnosticEntry<Anchor>>,
 }
 
+/// A single diagnostic in a set. Generic over its range type, because
+/// the diagnostics are stored internally as [Anchor]s, but can be
+/// resolved to different coordinates types like [usize] byte offsets or
+/// [Point]s.
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub struct DiagnosticEntry<T> {
+    /// The range of the buffer where the diagnostic applies.
     pub range: Range<T>,
+    /// The information about the diagnostic.
     pub diagnostic: Diagnostic,
 }
 
+/// A group of related diagnostics, ordered by their start position
+/// in the buffer.
 #[derive(Debug)]
 pub struct DiagnosticGroup<T> {
+    /// The diagnostics.
     pub entries: Vec<DiagnosticEntry<T>>,
+    /// The index into `entries` where the primary diagnostic is stored.
     pub primary_ix: usize,
 }
 
@@ -36,7 +52,8 @@ pub struct Summary {
 }
 
 impl<T> DiagnosticEntry<T> {
-    // Used to provide diagnostic context to lsp codeAction request
+    /// Returns a raw LSP diagnostic ssed to provide diagnostic context to lsp
+    /// codeAction request
     pub fn to_lsp_diagnostic_stub(&self) -> lsp::Diagnostic {
         let code = self
             .diagnostic
@@ -53,6 +70,8 @@ impl<T> DiagnosticEntry<T> {
 }
 
 impl DiagnosticSet {
+    /// Constructs a [DiagnosticSet] from a sequence of entries, ordered by
+    /// their position in the buffer.
     pub fn from_sorted_entries<I>(iter: I, buffer: &text::BufferSnapshot) -> Self
     where
         I: IntoIterator<Item = DiagnosticEntry<Anchor>>,
@@ -62,6 +81,7 @@ impl DiagnosticSet {
         }
     }
 
+    /// Constructs a [DiagnosticSet] from a sequence of entries in an arbitrary order.
     pub fn new<I>(iter: I, buffer: &text::BufferSnapshot) -> Self
     where
         I: IntoIterator<Item = DiagnosticEntry<PointUtf16>>,
@@ -80,14 +100,18 @@ impl DiagnosticSet {
         }
     }
 
+    /// Returns the number of diagnostics in the set.
     pub fn len(&self) -> usize {
         self.diagnostics.summary().count
     }
 
+    /// Returns an iterator over the diagnostic entries in the set.
     pub fn iter(&self) -> impl Iterator<Item = &DiagnosticEntry<Anchor>> {
         self.diagnostics.iter()
     }
 
+    /// Returns an iterator over the diagnostic entries that intersect the
+    /// given range of the buffer.
     pub fn range<'a, T, O>(
         &'a self,
         range: Range<T>,
@@ -134,6 +158,7 @@ impl DiagnosticSet {
         })
     }
 
+    /// Adds all of this set's diagnostic groups to the given output vector.
     pub fn groups(
         &self,
         language_server_id: LanguageServerId,
@@ -169,10 +194,12 @@ impl DiagnosticSet {
                 .range
                 .start
                 .cmp(&group_b.entries[group_b.primary_ix].range.start, buffer)
-                .then_with(|| id_a.cmp(&id_b))
+                .then_with(|| id_a.cmp(id_b))
         });
     }
 
+    /// Returns all of the diagnostics in a particular diagnostic group,
+    /// in order of their position in the buffer.
     pub fn group<'a, O: FromAnchor>(
         &'a self,
         group_id: usize,
@@ -183,6 +210,7 @@ impl DiagnosticSet {
             .map(|entry| entry.resolve(buffer))
     }
 }
+
 impl sum_tree::Item for DiagnosticEntry<Anchor> {
     type Summary = Summary;
 
@@ -198,6 +226,7 @@ impl sum_tree::Item for DiagnosticEntry<Anchor> {
 }
 
 impl DiagnosticEntry<Anchor> {
+    /// Converts the [DiagnosticEntry] to a different buffer coordinate type.
     pub fn resolve<O: FromAnchor>(&self, buffer: &text::BufferSnapshot) -> DiagnosticEntry<O> {
         DiagnosticEntry {
             range: O::from_anchor(&self.range.start, buffer)

crates/language/src/highlight_map.rs ๐Ÿ”—

@@ -11,7 +11,7 @@ pub struct HighlightId(pub u32);
 const DEFAULT_SYNTAX_HIGHLIGHT_ID: HighlightId = HighlightId(u32::MAX);
 
 impl HighlightMap {
-    pub fn new(capture_names: &[&str], theme: &SyntaxTheme) -> Self {
+    pub(crate) fn new(capture_names: &[&str], theme: &SyntaxTheme) -> Self {
         // For each capture name in the highlight query, find the longest
         // key in the theme's syntax styles that matches all of the
         // dot-separated components of the capture name.
@@ -51,7 +51,7 @@ impl HighlightMap {
 }
 
 impl HighlightId {
-    pub fn is_default(&self) -> bool {
+    pub(crate) fn is_default(&self) -> bool {
         *self == DEFAULT_SYNTAX_HIGHLIGHT_ID
     }
 

crates/language/src/language.rs ๐Ÿ”—

@@ -1,3 +1,11 @@
+//! The `language` crate provides a large chunk of Zed's language-related
+//! features (the other big contributors being project and lsp crates that revolve around LSP features).
+//! Namely, this crate:
+//! - Provides [`Language`], [`Grammar`] and [`LanguageRegistry`] types that
+//! use Tree-sitter to provide syntax highlighting to the editor; note though that `language` doesn't perform the highlighting by itself. It only maps ranges in a buffer to colors. Treesitter is also used for buffer outlines (lists of symbols in a buffer)
+//! - Exposes [`LanguageConfig`] that describes how constructs (like brackets or line comments) should be handled by the editor for a source file of a particular language.
+//!
+//! Notably we do *not* assign a single language to a single file; in real world a single file can consist of multiple programming languages - HTML is a good example of that - and `language` crate tends to reflect that status quo in it's API.
 mod buffer;
 mod diagnostic_set;
 mod highlight_map;
@@ -54,10 +62,13 @@ pub use buffer::*;
 pub use diagnostic_set::DiagnosticEntry;
 pub use lsp::LanguageServerId;
 pub use outline::{Outline, OutlineItem};
-pub use syntax_map::{OwnedSyntaxLayerInfo, SyntaxLayerInfo};
+pub use syntax_map::{OwnedSyntaxLayer, SyntaxLayer};
 pub use text::LineEnding;
 pub use tree_sitter::{Parser, Tree};
 
+/// Initializes the `language` crate.
+///
+/// This should be called before making use of items from the create.
 pub fn init(cx: &mut AppContext) {
     language_settings::init(cx);
 }
@@ -90,7 +101,9 @@ thread_local! {
 }
 
 lazy_static! {
-    pub static ref NEXT_GRAMMAR_ID: AtomicUsize = Default::default();
+    pub(crate) static ref NEXT_GRAMMAR_ID: AtomicUsize = Default::default();
+    /// A shared grammar for plain text, exposed for reuse by downstream crates.
+    #[doc(hidden)]
     pub static ref PLAIN_TEXT: Arc<Language> = Arc::new(Language::new(
         LanguageConfig {
             name: "Plain Text".into(),
@@ -100,10 +113,14 @@ lazy_static! {
     ));
 }
 
+/// Types that represent a position in a buffer, and can be converted into
+/// an LSP position, to send to a language server.
 pub trait ToLspPosition {
+    /// Converts the value into an LSP position.
     fn to_lsp_position(self) -> lsp::Position;
 }
 
+/// A name of a language server.
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
 pub struct LanguageServerName(pub Arc<str>);
 
@@ -239,6 +256,8 @@ impl CachedLspAdapter {
     }
 }
 
+/// [`LspAdapterDelegate`] allows [`LspAdapter]` implementations to interface with the application
+// e.g. to display a notification or fetch data from the web.
 pub trait LspAdapterDelegate: Send + Sync {
     fn show_notification(&self, message: &str, cx: &mut AppContext);
     fn http_client(&self) -> Arc<dyn HttpClient>;
@@ -284,6 +303,10 @@ pub trait LspAdapter: 'static + Send + Sync {
         delegate: &dyn LspAdapterDelegate,
     ) -> Option<LanguageServerBinary>;
 
+    /// Returns true if a language server can be reinstalled.
+    /// If language server initialization fails, a reinstallation will be attempted unless the value returned from this method is false.
+    /// Implementations that rely on software already installed on user's system
+    /// should have [`can_be_reinstalled`] return false.
     fn can_be_reinstalled(&self) -> bool {
         true
     }
@@ -295,6 +318,9 @@ pub trait LspAdapter: 'static + Send + Sync {
 
     fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
 
+    /// A callback called for each [`lsp_types::CompletionItem`] obtained from LSP server.
+    /// Some LspAdapter implementations might want to modify the obtained item to
+    /// change how it's displayed.
     async fn process_completion(&self, _: &mut lsp::CompletionItem) {}
 
     async fn label_for_completion(
@@ -314,6 +340,7 @@ pub trait LspAdapter: 'static + Send + Sync {
         None
     }
 
+    /// Returns initialization options that are going to be sent to a LSP server as a part of [`lsp_types::InitializeParams`]
     async fn initialization_options(&self) -> Option<Value> {
         None
     }
@@ -322,6 +349,7 @@ pub trait LspAdapter: 'static + Send + Sync {
         futures::future::ready(serde_json::json!({})).boxed()
     }
 
+    /// Returns a list of code actions supported by a given LspAdapter
     fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
         Some(vec![
             CodeActionKind::EMPTY,
@@ -351,43 +379,69 @@ pub trait LspAdapter: 'static + Send + Sync {
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub struct CodeLabel {
+    /// The text to display.
     pub text: String,
+    /// Syntax highlighting runs.
     pub runs: Vec<(Range<usize>, HighlightId)>,
+    /// The portion of the text that should be used in fuzzy filtering.
     pub filter_range: Range<usize>,
 }
 
 #[derive(Clone, Deserialize)]
 pub struct LanguageConfig {
+    /// Human-readable name of the language.
     pub name: Arc<str>,
+    // The name of the grammar in a WASM bundle (experimental).
     pub grammar_name: Option<Arc<str>>,
+    /// Given a list of `LanguageConfig`'s, the language of a file can be determined based on the path extension matching any of the `path_suffixes`.
     pub path_suffixes: Vec<String>,
+    /// List of bracket types in a language.
     pub brackets: BracketPairConfig,
+    /// A regex pattern that determines whether the language should be assigned to a file or not.
     #[serde(default, deserialize_with = "deserialize_regex")]
     pub first_line_pattern: Option<Regex>,
+    /// If set to true, auto indentation uses last non empty line to determine
+    /// the indentation level for a new line.
     #[serde(default = "auto_indent_using_last_non_empty_line_default")]
     pub auto_indent_using_last_non_empty_line: bool,
+    /// A regex that is used to determine whether the indentation level should be
+    /// increased in the following line.
     #[serde(default, deserialize_with = "deserialize_regex")]
     pub increase_indent_pattern: Option<Regex>,
+    /// A regex that is used to determine whether the indentation level should be
+    /// decreased in the following line.
     #[serde(default, deserialize_with = "deserialize_regex")]
     pub decrease_indent_pattern: Option<Regex>,
+    /// A list of characters that trigger the automatic insertion of a closing
+    /// bracket when they immediately precede the point where an opening
+    /// bracket is inserted.
     #[serde(default)]
     pub autoclose_before: String,
-    #[serde(default)]
-    pub line_comment: Option<Arc<str>>,
+    /// A placeholder used internally by Semantic Index.
     #[serde(default)]
     pub collapsed_placeholder: String,
+    /// A line comment string that is inserted in e.g. `toggle comments` action.
+    #[serde(default)]
+    pub line_comment: Option<Arc<str>>,
+    /// Starting and closing characters of a block comment.
     #[serde(default)]
     pub block_comment: Option<(Arc<str>, Arc<str>)>,
+    /// A list of language servers that are allowed to run on subranges of a given language.
     #[serde(default)]
     pub scope_opt_in_language_servers: Vec<String>,
     #[serde(default)]
     pub overrides: HashMap<String, LanguageConfigOverride>,
+    /// A list of characters that Zed should treat as word characters for the
+    /// purpose of features that operate on word boundaries, like 'move to next word end'
+    /// or a whole-word search in buffer search.
     #[serde(default)]
     pub word_characters: HashSet<char>,
+    /// The name of a Prettier parser that should be used for this language.
     #[serde(default)]
     pub prettier_parser_name: Option<String>,
 }
 
+/// Tree-sitter language queries for a given language.
 #[derive(Debug, Default)]
 pub struct LanguageQueries {
     pub highlights: Option<Cow<'static, str>>,
@@ -399,6 +453,9 @@ pub struct LanguageQueries {
     pub overrides: Option<Cow<'static, str>>,
 }
 
+/// Represents a language for the given range. Some languages (e.g. HTML)
+/// interleave several languages together, thus a single buffer might actually contain
+/// several nested scopes.
 #[derive(Clone, Debug)]
 pub struct LanguageScope {
     language: Arc<Language>,
@@ -458,9 +515,9 @@ impl Default for LanguageConfig {
             block_comment: Default::default(),
             scope_opt_in_language_servers: Default::default(),
             overrides: Default::default(),
-            collapsed_placeholder: Default::default(),
             word_characters: Default::default(),
             prettier_parser_name: None,
+            collapsed_placeholder: Default::default(),
         }
     }
 }
@@ -478,6 +535,7 @@ fn deserialize_regex<'de, D: Deserializer<'de>>(d: D) -> Result<Option<Regex>, D
     }
 }
 
+#[doc(hidden)]
 #[cfg(any(test, feature = "test-support"))]
 pub struct FakeLspAdapter {
     pub name: &'static str,
@@ -489,9 +547,16 @@ pub struct FakeLspAdapter {
     pub prettier_plugins: Vec<&'static str>,
 }
 
+/// Configuration of handling bracket pairs for a given language.
+///
+/// This struct includes settings for defining which pairs of characters are considered brackets and
+/// also specifies any language-specific scopes where these pairs should be ignored for bracket matching purposes.
 #[derive(Clone, Debug, Default)]
 pub struct BracketPairConfig {
+    /// A list of character pairs that should be treated as brackets in the context of a given language.
     pub pairs: Vec<BracketPair>,
+    /// A list of tree-sitter scopes for which a given bracket should not be active.
+    /// N-th entry in `[Self::disabled_scopes_by_bracket_ix]` contains a list of disabled scopes for an n-th entry in `[Self::pairs]`
     pub disabled_scopes_by_bracket_ix: Vec<Vec<String>>,
 }
 
@@ -523,11 +588,18 @@ impl<'de> Deserialize<'de> for BracketPairConfig {
     }
 }
 
+/// Describes a single bracket pair and how an editor should react to e.g. inserting
+/// an opening bracket or to a newline character insertion inbetween `start` and `end` characters.
 #[derive(Clone, Debug, Default, Deserialize, PartialEq)]
 pub struct BracketPair {
+    /// Starting substring for a bracket.
     pub start: String,
+    /// Ending substring for a bracket.
     pub end: String,
+    /// True if `end` should be automatically inserted right after `start` characters.
     pub close: bool,
+    /// True if an extra newline should be inserted while the cursor is in the middle
+    /// of that bracket pair.
     pub newline: bool,
 }
 
@@ -780,7 +852,7 @@ impl LanguageRegistry {
         let mut state = self.state.write();
         state.theme = Some(theme.clone());
         for language in &state.languages {
-            language.set_theme(&theme.syntax());
+            language.set_theme(theme.syntax());
         }
     }
 
@@ -1094,7 +1166,7 @@ impl LanguageRegistryState {
 
     fn add(&mut self, language: Arc<Language>) {
         if let Some(theme) = self.theme.as_ref() {
-            language.set_theme(&theme.syntax());
+            language.set_theme(theme.syntax());
         }
         self.languages.push(language);
         self.version += 1;
@@ -1641,6 +1713,8 @@ impl LanguageScope {
         self.language.config.collapsed_placeholder.as_ref()
     }
 
+    /// Returns line prefix that is inserted in e.g. line continuations or
+    /// in `toggle comments` action.
     pub fn line_comment_prefix(&self) -> Option<&Arc<str>> {
         Override::as_option(
             self.config_override().map(|o| &o.line_comment),
@@ -1656,6 +1730,11 @@ impl LanguageScope {
         .map(|e| (&e.0, &e.1))
     }
 
+    /// Returns a list of language-specific word characters.
+    ///
+    /// By default, Zed treats alphanumeric characters (and '_') as word characters for
+    /// the purpose of actions like 'move to next word end` or whole-word search.
+    /// It additionally accounts for language's additional word characters.
     pub fn word_characters(&self) -> Option<&HashSet<char>> {
         Override::as_option(
             self.config_override().map(|o| &o.word_characters),
@@ -1663,6 +1742,8 @@ impl LanguageScope {
         )
     }
 
+    /// Returns a list of bracket pairs for a given language with an additional
+    /// piece of information about whether the particular bracket pair is currently active for a given language.
     pub fn brackets(&self) -> impl Iterator<Item = (&BracketPair, bool)> {
         let mut disabled_ids = self
             .config_override()

crates/language/src/language_settings.rs ๐Ÿ”—

@@ -1,3 +1,5 @@
+//! Provides `language`-related settings.
+
 use crate::{File, Language};
 use anyhow::Result;
 use collections::{HashMap, HashSet};
@@ -11,10 +13,12 @@ use serde::{Deserialize, Serialize};
 use settings::Settings;
 use std::{num::NonZeroU32, path::Path, sync::Arc};
 
+/// Initializes the language settings.
 pub fn init(cx: &mut AppContext) {
     AllLanguageSettings::register(cx);
 }
 
+/// Returns the settings for the specified language from the provided file.
 pub fn language_settings<'a>(
     language: Option<&Arc<Language>>,
     file: Option<&Arc<dyn File>>,
@@ -24,6 +28,7 @@ pub fn language_settings<'a>(
     all_language_settings(file, cx).language(language_name.as_deref())
 }
 
+/// Returns the settings for all languages from the provided file.
 pub fn all_language_settings<'a>(
     file: Option<&Arc<dyn File>>,
     cx: &'a AppContext,
@@ -32,51 +37,91 @@ pub fn all_language_settings<'a>(
     AllLanguageSettings::get(location, cx)
 }
 
+/// The settings for all languages.
 #[derive(Debug, Clone)]
 pub struct AllLanguageSettings {
+    /// The settings for GitHub Copilot.
     pub copilot: CopilotSettings,
     defaults: LanguageSettings,
     languages: HashMap<Arc<str>, LanguageSettings>,
 }
 
+/// The settings for a particular language.
 #[derive(Debug, Clone, Deserialize)]
 pub struct LanguageSettings {
+    /// How many columns a tab should occupy.
     pub tab_size: NonZeroU32,
+    /// Whether to indent lines using tab characters, as opposed to multiple
+    /// spaces.
     pub hard_tabs: bool,
+    /// How to soft-wrap long lines of text.
     pub soft_wrap: SoftWrap,
+    /// The column at which to soft-wrap lines, for buffers where soft-wrap
+    /// is enabled.
     pub preferred_line_length: u32,
+    /// Whether to show wrap guides in the editor. Setting this to true will
+    /// show a guide at the 'preferred_line_length' value if softwrap is set to
+    /// 'preferred_line_length', and will show any additional guides as specified
+    /// by the 'wrap_guides' setting.
     pub show_wrap_guides: bool,
+    /// Character counts at which to show wrap guides in the editor.
     pub wrap_guides: Vec<usize>,
+    /// Whether or not to perform a buffer format before saving.
     pub format_on_save: FormatOnSave,
+    /// Whether or not to remove any trailing whitespace from lines of a buffer
+    /// before saving it.
     pub remove_trailing_whitespace_on_save: bool,
+    /// Whether or not to ensure there's a single newline at the end of a buffer
+    /// when saving it.
     pub ensure_final_newline_on_save: bool,
+    /// How to perform a buffer format.
     pub formatter: Formatter,
+    /// Zed's Prettier integration settings.
+    /// If Prettier is enabled, Zed will use this its Prettier instance for any applicable file, if
+    /// the project has no other Prettier installed.
     pub prettier: HashMap<String, serde_json::Value>,
+    /// Whether to use language servers to provide code intelligence.
     pub enable_language_server: bool,
+    /// Controls whether Copilot provides suggestion immediately (true)
+    /// or waits for a `copilot::Toggle` (false).
     pub show_copilot_suggestions: bool,
+    /// Whether to show tabs and spaces in the editor.
     pub show_whitespaces: ShowWhitespaceSetting,
+    /// Whether to start a new line with a comment when a previous line is a comment as well.
     pub extend_comment_on_newline: bool,
+    /// Inlay hint related settings.
     pub inlay_hints: InlayHintSettings,
+    /// Whether to automatically close brackets.
+    pub use_autoclose: bool,
 }
 
+/// The settings for [GitHub Copilot](https://github.com/features/copilot).
 #[derive(Clone, Debug, Default)]
 pub struct CopilotSettings {
+    /// Whether Copilot is enabled.
     pub feature_enabled: bool,
+    /// A list of globs representing files that Copilot should be disabled for.
     pub disabled_globs: Vec<GlobMatcher>,
 }
 
+/// The settings for all languages.
 #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
 pub struct AllLanguageSettingsContent {
+    /// The settings for enabling/disabling features.
     #[serde(default)]
     pub features: Option<FeaturesContent>,
+    /// The settings for GitHub Copilot.
     #[serde(default)]
     pub copilot: Option<CopilotSettingsContent>,
+    /// The default language settings.
     #[serde(flatten)]
     pub defaults: LanguageSettingsContent,
+    /// The settings for individual languages.
     #[serde(default, alias = "language_overrides")]
     pub languages: HashMap<Arc<str>, LanguageSettingsContent>,
 }
 
+/// The settings for a particular language.
 #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
 pub struct LanguageSettingsContent {
     /// How many columns a tab should occupy.
@@ -138,7 +183,7 @@ pub struct LanguageSettingsContent {
     pub formatter: Option<Formatter>,
     /// Zed's Prettier integration settings.
     /// If Prettier is enabled, Zed will use this its Prettier instance for any applicable file, if
-    /// project has no other Prettier installed.
+    /// the project has no other Prettier installed.
     ///
     /// Default: {}
     #[serde(default)]
@@ -148,7 +193,7 @@ pub struct LanguageSettingsContent {
     /// Default: true
     #[serde(default)]
     pub enable_language_server: Option<bool>,
-    /// Controls whether copilot provides suggestion immediately (true)
+    /// Controls whether Copilot provides suggestion immediately (true)
     /// or waits for a `copilot::Toggle` (false).
     ///
     /// Default: true
@@ -165,20 +210,30 @@ pub struct LanguageSettingsContent {
     /// Inlay hint related settings.
     #[serde(default)]
     pub inlay_hints: Option<InlayHintSettings>,
+    /// Whether to automatically type closing characters for you. For example,
+    /// when you type (, Zed will automatically add a closing ) at the correct position.
+    ///
+    /// Default: true
+    pub use_autoclose: Option<bool>,
 }
 
+/// The contents of the GitHub Copilot settings.
 #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
 pub struct CopilotSettingsContent {
+    /// A list of globs representing files that Copilot should be disabled for.
     #[serde(default)]
     pub disabled_globs: Option<Vec<String>>,
 }
 
+/// The settings for enabling/disabling features.
 #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
 #[serde(rename_all = "snake_case")]
 pub struct FeaturesContent {
+    /// Whether the GitHub Copilot feature is enabled.
     pub copilot: Option<bool>,
 }
 
+/// Controls the soft-wrapping behavior in the editor.
 #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
 #[serde(rename_all = "snake_case")]
 pub enum SoftWrap {
@@ -190,29 +245,38 @@ pub enum SoftWrap {
     PreferredLineLength,
 }
 
+/// Controls the behavior of formatting files when they are saved.
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
 #[serde(rename_all = "snake_case")]
 pub enum FormatOnSave {
+    /// Files should be formatted on save.
     On,
+    /// Files should not be formatted on save.
     Off,
+    /// Files should be formatted using the current language server.
     LanguageServer,
+    /// The external program to use to format the files on save.
     External {
+        /// The external program to run.
         command: Arc<str>,
+        /// The arguments to pass to the program.
         arguments: Arc<[String]>,
     },
 }
 
+/// Controls how whitespace should be displayedin the editor.
 #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
 #[serde(rename_all = "snake_case")]
 pub enum ShowWhitespaceSetting {
-    /// Draw tabs and spaces only for the selected text.
+    /// Draw whitespace only for the selected text.
     Selection,
-    /// Do not draw any tabs or spaces
+    /// Do not draw any tabs or spaces.
     None,
-    /// Draw all invisible symbols
+    /// Draw all invisible symbols.
     All,
 }
 
+/// Controls which formatter should be used when formatting code.
 #[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
 #[serde(rename_all = "snake_case")]
 pub enum Formatter {
@@ -226,11 +290,14 @@ pub enum Formatter {
     Prettier,
     /// Format code using an external command.
     External {
+        /// The external program to run.
         command: Arc<str>,
+        /// The arguments to pass to the program.
         arguments: Arc<[String]>,
     },
 }
 
+/// The settings for inlay hints.
 #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
 pub struct InlayHintSettings {
     /// Global switch to toggle hints on and off.
@@ -238,10 +305,19 @@ pub struct InlayHintSettings {
     /// Default: false
     #[serde(default)]
     pub enabled: bool,
+    /// Whether type hints should be shown.
+    ///
+    /// Default: true
     #[serde(default = "default_true")]
     pub show_type_hints: bool,
+    /// Whether parameter hints should be shown.
+    ///
+    /// Default: true
     #[serde(default = "default_true")]
     pub show_parameter_hints: bool,
+    /// Whether other hints should be shown.
+    ///
+    /// Default: true
     #[serde(default = "default_true")]
     pub show_other_hints: bool,
 }
@@ -251,6 +327,7 @@ fn default_true() -> bool {
 }
 
 impl InlayHintSettings {
+    /// Returns the kinds of inlay hints that are enabled based on the settings.
     pub fn enabled_inlay_hint_kinds(&self) -> HashSet<Option<InlayHintKind>> {
         let mut kinds = HashSet::default();
         if self.show_type_hints {
@@ -267,6 +344,7 @@ impl InlayHintSettings {
 }
 
 impl AllLanguageSettings {
+    /// Returns the [`LanguageSettings`] for the language with the specified name.
     pub fn language<'a>(&'a self, language_name: Option<&str>) -> &'a LanguageSettings {
         if let Some(name) = language_name {
             if let Some(overrides) = self.languages.get(name) {
@@ -276,6 +354,7 @@ impl AllLanguageSettings {
         &self.defaults
     }
 
+    /// Returns whether GitHub Copilot is enabled for the given path.
     pub fn copilot_enabled_for_path(&self, path: &Path) -> bool {
         !self
             .copilot
@@ -284,6 +363,7 @@ impl AllLanguageSettings {
             .any(|glob| glob.is_match(path))
     }
 
+    /// Returns whether GitHub Copilot is enabled for the given language and path.
     pub fn copilot_enabled(&self, language: Option<&Arc<Language>>, path: Option<&Path>) -> bool {
         if !self.copilot.feature_enabled {
             return false;
@@ -300,13 +380,20 @@ impl AllLanguageSettings {
     }
 }
 
+/// The kind of an inlay hint.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub enum InlayHintKind {
+    /// An inlay hint for a type.
     Type,
+    /// An inlay hint for a parameter.
     Parameter,
 }
 
 impl InlayHintKind {
+    /// Returns the [`InlayHintKind`] from the given name.
+    ///
+    /// Returns `None` if `name` does not match any of the expected
+    /// string representations.
     pub fn from_name(name: &str) -> Option<Self> {
         match name {
             "type" => Some(InlayHintKind::Type),
@@ -315,6 +402,7 @@ impl InlayHintKind {
         }
     }
 
+    /// Returns the name of this [`InlayHintKind`].
     pub fn name(&self) -> &'static str {
         match self {
             InlayHintKind::Type => "type",
@@ -340,7 +428,7 @@ impl settings::Settings for AllLanguageSettings {
         let mut languages = HashMap::default();
         for (language_name, settings) in &default_value.languages {
             let mut language_settings = defaults.clone();
-            merge_settings(&mut language_settings, &settings);
+            merge_settings(&mut language_settings, settings);
             languages.insert(language_name.clone(), language_settings);
         }
 
@@ -380,7 +468,7 @@ impl settings::Settings for AllLanguageSettings {
                     languages
                         .entry(language_name.clone())
                         .or_insert_with(|| defaults.clone()),
-                    &user_language_settings,
+                    user_language_settings,
                 );
             }
         }
@@ -459,6 +547,7 @@ fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent
     merge(&mut settings.tab_size, src.tab_size);
     merge(&mut settings.hard_tabs, src.hard_tabs);
     merge(&mut settings.soft_wrap, src.soft_wrap);
+    merge(&mut settings.use_autoclose, src.use_autoclose);
     merge(&mut settings.show_wrap_guides, src.show_wrap_guides);
     merge(&mut settings.wrap_guides, src.wrap_guides.clone());
 

crates/language/src/markdown.rs ๐Ÿ”—

@@ -1,3 +1,5 @@
+//! Provides Markdown-related constructs.
+
 use std::sync::Arc;
 use std::{ops::Range, path::PathBuf};
 
@@ -5,21 +7,30 @@ use crate::{HighlightId, Language, LanguageRegistry};
 use gpui::{px, FontStyle, FontWeight, HighlightStyle, UnderlineStyle};
 use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag};
 
+/// Parsed Markdown content.
 #[derive(Debug, Clone)]
 pub struct ParsedMarkdown {
+    /// The Markdown text.
     pub text: String,
+    /// The list of highlights contained in the Markdown document.
     pub highlights: Vec<(Range<usize>, MarkdownHighlight)>,
+    /// The regions of the various ranges in the Markdown document.
     pub region_ranges: Vec<Range<usize>>,
+    /// The regions of the Markdown document.
     pub regions: Vec<ParsedRegion>,
 }
 
+/// A run of highlighted Markdown text.
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum MarkdownHighlight {
+    /// A styled Markdown highlight.
     Style(MarkdownHighlightStyle),
+    /// A highlighted code block.
     Code(HighlightId),
 }
 
 impl MarkdownHighlight {
+    /// Converts this [`MarkdownHighlight`] to a [`HighlightStyle`].
     pub fn to_highlight_style(&self, theme: &theme::SyntaxTheme) -> Option<HighlightStyle> {
         match self {
             MarkdownHighlight::Style(style) => {
@@ -48,23 +59,39 @@ impl MarkdownHighlight {
     }
 }
 
+/// The style for a Markdown highlight.
 #[derive(Debug, Clone, Default, PartialEq, Eq)]
 pub struct MarkdownHighlightStyle {
+    /// Whether the text should be italicized.
     pub italic: bool,
+    /// Whether the text should be underlined.
     pub underline: bool,
+    /// The weight of the text.
     pub weight: FontWeight,
 }
 
+/// A parsed region in a Markdown document.
 #[derive(Debug, Clone)]
 pub struct ParsedRegion {
+    /// Whether the region is a code block.
     pub code: bool,
+    /// The link contained in this region, if it has one.
     pub link: Option<Link>,
 }
 
+/// A Markdown link.
 #[derive(Debug, Clone)]
 pub enum Link {
-    Web { url: String },
-    Path { path: PathBuf },
+    /// A link to a webpage.
+    Web {
+        /// The URL of the webpage.
+        url: String,
+    },
+    /// A link to a path on the filesystem.
+    Path {
+        /// The path to the item.
+        path: PathBuf,
+    },
 }
 
 impl Link {
@@ -82,6 +109,7 @@ impl Link {
     }
 }
 
+/// Parses a string of Markdown.
 pub async fn parse_markdown(
     markdown: &str,
     language_registry: &Arc<LanguageRegistry>,
@@ -111,6 +139,7 @@ pub async fn parse_markdown(
     }
 }
 
+/// Parses a Markdown block.
 pub async fn parse_markdown_block(
     markdown: &str,
     language_registry: &Arc<LanguageRegistry>,
@@ -126,7 +155,7 @@ pub async fn parse_markdown_block(
     let mut current_language = None;
     let mut list_stack = Vec::new();
 
-    for event in Parser::new_ext(&markdown, Options::all()) {
+    for event in Parser::new_ext(markdown, Options::all()) {
         let prev_len = text.len();
         match event {
             Event::Text(t) => {
@@ -261,6 +290,7 @@ pub async fn parse_markdown_block(
     }
 }
 
+/// Appends a highlighted run of text to the provided `text` buffer.
 pub fn highlight_code(
     text: &mut String,
     highlights: &mut Vec<(Range<usize>, MarkdownHighlight)>,
@@ -275,6 +305,7 @@ pub fn highlight_code(
     }
 }
 
+/// Appends a new paragraph to the provided `text` buffer.
 pub fn new_paragraph(text: &mut String, list_stack: &mut Vec<(Option<u64>, bool)>) {
     let mut is_subsequent_paragraph_of_list = false;
     if let Some((_, has_content)) = list_stack.last_mut() {

crates/language/src/outline.rs ๐Ÿ”—

@@ -2,6 +2,7 @@ use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{BackgroundExecutor, HighlightStyle};
 use std::ops::Range;
 
+/// An outline of all the symbols contained in a buffer.
 #[derive(Debug)]
 pub struct Outline<T> {
     pub items: Vec<OutlineItem<T>>,

crates/language/src/proto.rs ๐Ÿ”—

@@ -1,3 +1,5 @@
+//! Handles conversions of `language` items to and from the [`rpc`] protocol.
+
 use crate::{
     diagnostic_set::DiagnosticEntry, CodeAction, CodeLabel, Completion, CursorShape, Diagnostic,
     Language,
@@ -11,15 +13,18 @@ use text::*;
 
 pub use proto::{BufferState, Operation};
 
+/// Serializes a [`RopeFingerprint`] to be sent over RPC.
 pub fn serialize_fingerprint(fingerprint: RopeFingerprint) -> String {
     fingerprint.to_hex()
 }
 
+/// Deserializes a [`RopeFingerprint`] from the RPC representation.
 pub fn deserialize_fingerprint(fingerprint: &str) -> Result<RopeFingerprint> {
     RopeFingerprint::from_hex(fingerprint)
         .map_err(|error| anyhow!("invalid fingerprint: {}", error))
 }
 
+/// Deserializes a `[text::LineEnding]` from the RPC representation.
 pub fn deserialize_line_ending(message: proto::LineEnding) -> text::LineEnding {
     match message {
         proto::LineEnding::Unix => text::LineEnding::Unix,
@@ -27,6 +32,7 @@ pub fn deserialize_line_ending(message: proto::LineEnding) -> text::LineEnding {
     }
 }
 
+/// Serializes a [`text::LineEnding`] to be sent over RPC.
 pub fn serialize_line_ending(message: text::LineEnding) -> proto::LineEnding {
     match message {
         text::LineEnding::Unix => proto::LineEnding::Unix,
@@ -34,6 +40,7 @@ pub fn serialize_line_ending(message: text::LineEnding) -> proto::LineEnding {
     }
 }
 
+/// Serializes a [`crate::Operation`] to be sent over RPC.
 pub fn serialize_operation(operation: &crate::Operation) -> proto::Operation {
     proto::Operation {
         variant: Some(match operation {
@@ -96,6 +103,7 @@ pub fn serialize_operation(operation: &crate::Operation) -> proto::Operation {
     }
 }
 
+/// Serializes an [`operation::EditOperation`] to be sent over RPC.
 pub fn serialize_edit_operation(operation: &EditOperation) -> proto::operation::Edit {
     proto::operation::Edit {
         replica_id: operation.timestamp.replica_id as u32,
@@ -110,6 +118,7 @@ pub fn serialize_edit_operation(operation: &EditOperation) -> proto::operation::
     }
 }
 
+/// Serializes an entry in the undo map to be sent over RPC.
 pub fn serialize_undo_map_entry(
     (edit_id, counts): (&clock::Lamport, &[(clock::Lamport, u32)]),
 ) -> proto::UndoMapEntry {
@@ -127,6 +136,7 @@ pub fn serialize_undo_map_entry(
     }
 }
 
+/// Splits the given list of operations into chunks.
 pub fn split_operations(
     mut operations: Vec<proto::Operation>,
 ) -> impl Iterator<Item = Vec<proto::Operation>> {
@@ -152,10 +162,12 @@ pub fn split_operations(
     })
 }
 
+/// Serializes selections to be sent over RPC.
 pub fn serialize_selections(selections: &Arc<[Selection<Anchor>]>) -> Vec<proto::Selection> {
     selections.iter().map(serialize_selection).collect()
 }
 
+/// Serializes a [`Selection`] to be sent over RPC.
 pub fn serialize_selection(selection: &Selection<Anchor>) -> proto::Selection {
     proto::Selection {
         id: selection.id as u64,
@@ -171,6 +183,7 @@ pub fn serialize_selection(selection: &Selection<Anchor>) -> proto::Selection {
     }
 }
 
+/// Serializes a [`CursorShape`] to be sent over RPC.
 pub fn serialize_cursor_shape(cursor_shape: &CursorShape) -> proto::CursorShape {
     match cursor_shape {
         CursorShape::Bar => proto::CursorShape::CursorBar,
@@ -180,6 +193,7 @@ pub fn serialize_cursor_shape(cursor_shape: &CursorShape) -> proto::CursorShape
     }
 }
 
+/// Deserializes a [`CursorShape`] from the RPC representation.
 pub fn deserialize_cursor_shape(cursor_shape: proto::CursorShape) -> CursorShape {
     match cursor_shape {
         proto::CursorShape::CursorBar => CursorShape::Bar,
@@ -189,6 +203,7 @@ pub fn deserialize_cursor_shape(cursor_shape: proto::CursorShape) -> CursorShape
     }
 }
 
+/// Serializes a list of diagnostics to be sent over RPC.
 pub fn serialize_diagnostics<'a>(
     diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<Anchor>>,
 ) -> Vec<proto::Diagnostic> {
@@ -208,7 +223,7 @@ pub fn serialize_diagnostics<'a>(
             } as i32,
             group_id: entry.diagnostic.group_id as u64,
             is_primary: entry.diagnostic.is_primary,
-            is_valid: entry.diagnostic.is_valid,
+            is_valid: true,
             code: entry.diagnostic.code.clone(),
             is_disk_based: entry.diagnostic.is_disk_based,
             is_unnecessary: entry.diagnostic.is_unnecessary,
@@ -216,6 +231,7 @@ pub fn serialize_diagnostics<'a>(
         .collect()
 }
 
+/// Serializes an [`Anchor`] to be sent over RPC.
 pub fn serialize_anchor(anchor: &Anchor) -> proto::Anchor {
     proto::Anchor {
         replica_id: anchor.timestamp.replica_id as u32,
@@ -230,6 +246,7 @@ pub fn serialize_anchor(anchor: &Anchor) -> proto::Anchor {
 }
 
 // This behavior is currently copied in the collab database, for snapshotting channel notes
+/// Deserializes an [`crate::Operation`] from the RPC representation.
 pub fn deserialize_operation(message: proto::Operation) -> Result<crate::Operation> {
     Ok(
         match message
@@ -312,6 +329,7 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<crate::Operati
     )
 }
 
+/// Deserializes an [`EditOperation`] from the RPC representation.
 pub fn deserialize_edit_operation(edit: proto::operation::Edit) -> EditOperation {
     EditOperation {
         timestamp: clock::Lamport {
@@ -324,6 +342,7 @@ pub fn deserialize_edit_operation(edit: proto::operation::Edit) -> EditOperation
     }
 }
 
+/// Deserializes an entry in the undo map from the RPC representation.
 pub fn deserialize_undo_map_entry(
     entry: proto::UndoMapEntry,
 ) -> (clock::Lamport, Vec<(clock::Lamport, u32)>) {
@@ -348,6 +367,7 @@ pub fn deserialize_undo_map_entry(
     )
 }
 
+/// Deserializes selections from the RPC representation.
 pub fn deserialize_selections(selections: Vec<proto::Selection>) -> Arc<[Selection<Anchor>]> {
     Arc::from(
         selections
@@ -357,6 +377,7 @@ pub fn deserialize_selections(selections: Vec<proto::Selection>) -> Arc<[Selecti
     )
 }
 
+/// Deserializes a [`Selection`] from the RPC representation.
 pub fn deserialize_selection(selection: proto::Selection) -> Option<Selection<Anchor>> {
     Some(Selection {
         id: selection.id as usize,
@@ -367,6 +388,7 @@ pub fn deserialize_selection(selection: proto::Selection) -> Option<Selection<An
     })
 }
 
+/// Deserializes a list of diagnostics from the RPC representation.
 pub fn deserialize_diagnostics(
     diagnostics: Vec<proto::Diagnostic>,
 ) -> Arc<[DiagnosticEntry<Anchor>]> {
@@ -387,7 +409,6 @@ pub fn deserialize_diagnostics(
                     message: diagnostic.message,
                     group_id: diagnostic.group_id as usize,
                     code: diagnostic.code,
-                    is_valid: diagnostic.is_valid,
                     is_primary: diagnostic.is_primary,
                     is_disk_based: diagnostic.is_disk_based,
                     is_unnecessary: diagnostic.is_unnecessary,
@@ -397,6 +418,7 @@ pub fn deserialize_diagnostics(
         .collect()
 }
 
+/// Deserializes an [`Anchor`] from the RPC representation.
 pub fn deserialize_anchor(anchor: proto::Anchor) -> Option<Anchor> {
     Some(Anchor {
         timestamp: clock::Lamport {
@@ -412,6 +434,7 @@ pub fn deserialize_anchor(anchor: proto::Anchor) -> Option<Anchor> {
     })
 }
 
+/// Returns a `[clock::Lamport`] timestamp for the given [`proto::Operation`].
 pub fn lamport_timestamp_for_operation(operation: &proto::Operation) -> Option<clock::Lamport> {
     let replica_id;
     let value;
@@ -444,6 +467,7 @@ pub fn lamport_timestamp_for_operation(operation: &proto::Operation) -> Option<c
     })
 }
 
+/// Serializes a [`Completion`] to be sent over RPC.
 pub fn serialize_completion(completion: &Completion) -> proto::Completion {
     proto::Completion {
         old_start: Some(serialize_anchor(&completion.old_range.start)),
@@ -454,6 +478,7 @@ pub fn serialize_completion(completion: &Completion) -> proto::Completion {
     }
 }
 
+/// Deserializes a [`Completion`] from the RPC representation.
 pub async fn deserialize_completion(
     completion: proto::Completion,
     language: Option<Arc<Language>>,
@@ -488,6 +513,7 @@ pub async fn deserialize_completion(
     })
 }
 
+/// Serializes a [`CodeAction`] to be sent over RPC.
 pub fn serialize_code_action(action: &CodeAction) -> proto::CodeAction {
     proto::CodeAction {
         server_id: action.server_id.0 as u64,
@@ -497,6 +523,7 @@ pub fn serialize_code_action(action: &CodeAction) -> proto::CodeAction {
     }
 }
 
+/// Deserializes a [`CodeAction`] from the RPC representation.
 pub fn deserialize_code_action(action: proto::CodeAction) -> Result<CodeAction> {
     let start = action
         .start
@@ -514,6 +541,7 @@ pub fn deserialize_code_action(action: proto::CodeAction) -> Result<CodeAction>
     })
 }
 
+/// Serializes a [`Transaction`] to be sent over RPC.
 pub fn serialize_transaction(transaction: &Transaction) -> proto::Transaction {
     proto::Transaction {
         id: Some(serialize_timestamp(transaction.id)),
@@ -527,6 +555,7 @@ pub fn serialize_transaction(transaction: &Transaction) -> proto::Transaction {
     }
 }
 
+/// Deserializes a [`Transaction`] from the RPC representation.
 pub fn deserialize_transaction(transaction: proto::Transaction) -> Result<Transaction> {
     Ok(Transaction {
         id: deserialize_timestamp(
@@ -543,6 +572,7 @@ pub fn deserialize_transaction(transaction: proto::Transaction) -> Result<Transa
     })
 }
 
+/// Serializes a [`clock::Lamport`] timestamp to be sent over RPC.
 pub fn serialize_timestamp(timestamp: clock::Lamport) -> proto::LamportTimestamp {
     proto::LamportTimestamp {
         replica_id: timestamp.replica_id as u32,
@@ -550,6 +580,7 @@ pub fn serialize_timestamp(timestamp: clock::Lamport) -> proto::LamportTimestamp
     }
 }
 
+/// Deserializes a [`clock::Lamport`] timestamp from the RPC representation.
 pub fn deserialize_timestamp(timestamp: proto::LamportTimestamp) -> clock::Lamport {
     clock::Lamport {
         replica_id: timestamp.replica_id as ReplicaId,
@@ -557,6 +588,7 @@ pub fn deserialize_timestamp(timestamp: proto::LamportTimestamp) -> clock::Lampo
     }
 }
 
+/// Serializes a range of [`FullOffset`]s to be sent over RPC.
 pub fn serialize_range(range: &Range<FullOffset>) -> proto::Range {
     proto::Range {
         start: range.start.0 as u64,
@@ -564,10 +596,12 @@ pub fn serialize_range(range: &Range<FullOffset>) -> proto::Range {
     }
 }
 
+/// Deserializes a range of [`FullOffset`]s from the RPC representation.
 pub fn deserialize_range(range: proto::Range) -> Range<FullOffset> {
     FullOffset(range.start as usize)..FullOffset(range.end as usize)
 }
 
+/// Deserializes a clock version from the RPC representation.
 pub fn deserialize_version(message: &[proto::VectorClockEntry]) -> clock::Global {
     let mut version = clock::Global::new();
     for entry in message {
@@ -579,6 +613,7 @@ pub fn deserialize_version(message: &[proto::VectorClockEntry]) -> clock::Global
     version
 }
 
+/// Serializes a clock version to be sent over RPC.
 pub fn serialize_version(version: &clock::Global) -> Vec<proto::VectorClockEntry> {
     version
         .iter()

crates/language/src/syntax_map.rs ๐Ÿ”—

@@ -29,7 +29,7 @@ pub struct SyntaxMap {
 
 #[derive(Clone, Default)]
 pub struct SyntaxSnapshot {
-    layers: SumTree<SyntaxLayer>,
+    layers: SumTree<SyntaxLayerEntry>,
     parsed_version: clock::Global,
     interpolated_version: clock::Global,
     language_registry_version: usize,
@@ -84,7 +84,7 @@ struct SyntaxMapMatchesLayer<'a> {
 }
 
 #[derive(Clone)]
-struct SyntaxLayer {
+struct SyntaxLayerEntry {
     depth: usize,
     range: Range<Anchor>,
     content: SyntaxLayerContent,
@@ -117,17 +117,22 @@ impl SyntaxLayerContent {
     }
 }
 
+/// A layer of syntax highlighting, corresponding to a single syntax
+/// tree in a particular language.
 #[derive(Debug)]
-pub struct SyntaxLayerInfo<'a> {
-    pub depth: usize,
+pub struct SyntaxLayer<'a> {
+    /// The language for this layer.
     pub language: &'a Arc<Language>,
+    depth: usize,
     tree: &'a Tree,
     offset: (usize, tree_sitter::Point),
 }
 
+/// A layer of syntax highlighting. Like [SyntaxLayer], but holding
+/// owned data instead of references.
 #[derive(Clone)]
-pub struct OwnedSyntaxLayerInfo {
-    pub depth: usize,
+pub struct OwnedSyntaxLayer {
+    /// The language for this layer.
     pub language: Arc<Language>,
     tree: tree_sitter::Tree,
     offset: (usize, tree_sitter::Point),
@@ -278,7 +283,7 @@ impl SyntaxSnapshot {
                     depth,
                     position: edit_range.start,
                 };
-                if target.cmp(&cursor.start(), text).is_gt() {
+                if target.cmp(cursor.start(), text).is_gt() {
                     let slice = cursor.slice(&target, Bias::Left, text);
                     layers.append(slice, text);
                 }
@@ -363,7 +368,7 @@ impl SyntaxSnapshot {
             cursor.next(text);
         }
 
-        layers.append(cursor.suffix(&text), &text);
+        layers.append(cursor.suffix(text), text);
         drop(cursor);
         self.layers = layers;
     }
@@ -428,7 +433,7 @@ impl SyntaxSnapshot {
 
         let max_depth = self.layers.summary().max_depth;
         let mut cursor = self.layers.cursor::<SyntaxLayerSummary>();
-        cursor.next(&text);
+        cursor.next(text);
         let mut layers = SumTree::new();
 
         let mut changed_regions = ChangeRegionSet::default();
@@ -466,17 +471,17 @@ impl SyntaxSnapshot {
             };
 
             let mut done = cursor.item().is_none();
-            while !done && position.cmp(&cursor.end(text), &text).is_gt() {
+            while !done && position.cmp(&cursor.end(text), text).is_gt() {
                 done = true;
 
                 let bounded_position = SyntaxLayerPositionBeforeChange {
                     position: position.clone(),
                     change: changed_regions.start_position(),
                 };
-                if bounded_position.cmp(&cursor.start(), &text).is_gt() {
+                if bounded_position.cmp(cursor.start(), text).is_gt() {
                     let slice = cursor.slice(&bounded_position, Bias::Left, text);
                     if !slice.is_empty() {
-                        layers.append(slice, &text);
+                        layers.append(slice, text);
                         if changed_regions.prune(cursor.end(text), text) {
                             done = false;
                         }
@@ -486,7 +491,7 @@ impl SyntaxSnapshot {
                 while position.cmp(&cursor.end(text), text).is_gt() {
                     let Some(layer) = cursor.item() else { break };
 
-                    if changed_regions.intersects(&layer, text) {
+                    if changed_regions.intersects(layer, text) {
                         if let SyntaxLayerContent::Parsed { language, .. } = &layer.content {
                             log::trace!(
                                 "discard layer. language:{}, range:{:?}. changed_regions:{:?}",
@@ -524,7 +529,7 @@ impl SyntaxSnapshot {
                 if layer.range.to_offset(text) == (step_start_byte..step_end_byte)
                     && layer.content.language_id() == step.language.id()
                 {
-                    cursor.next(&text);
+                    cursor.next(text);
                 } else {
                     old_layer = None;
                 }
@@ -556,7 +561,7 @@ impl SyntaxSnapshot {
                         log::trace!(
                             "existing layer. language:{}, start:{:?}, ranges:{:?}",
                             language.name(),
-                            LogPoint(layer_start.to_point(&text)),
+                            LogPoint(layer_start.to_point(text)),
                             LogIncludedRanges(&old_tree.included_ranges())
                         );
 
@@ -579,7 +584,7 @@ impl SyntaxSnapshot {
                             insert_newlines_between_ranges(
                                 changed_indices,
                                 &mut included_ranges,
-                                &text,
+                                text,
                                 step_start_byte,
                                 step_start_point,
                             );
@@ -691,12 +696,12 @@ impl SyntaxSnapshot {
             };
 
             layers.push(
-                SyntaxLayer {
+                SyntaxLayerEntry {
                     depth: step.depth,
                     range: step.range,
                     content,
                 },
-                &text,
+                text,
             );
         }
 
@@ -741,7 +746,7 @@ impl SyntaxSnapshot {
         SyntaxMapCaptures::new(
             range.clone(),
             text,
-            [SyntaxLayerInfo {
+            [SyntaxLayer {
                 language,
                 tree,
                 depth: 0,
@@ -781,7 +786,7 @@ impl SyntaxSnapshot {
     }
 
     #[cfg(test)]
-    pub fn layers<'a>(&'a self, buffer: &'a BufferSnapshot) -> Vec<SyntaxLayerInfo> {
+    pub fn layers<'a>(&'a self, buffer: &'a BufferSnapshot) -> Vec<SyntaxLayer> {
         self.layers_for_range(0..buffer.len(), buffer).collect()
     }
 
@@ -789,7 +794,7 @@ impl SyntaxSnapshot {
         &'a self,
         range: Range<T>,
         buffer: &'a BufferSnapshot,
-    ) -> impl 'a + Iterator<Item = SyntaxLayerInfo> {
+    ) -> impl 'a + Iterator<Item = SyntaxLayer> {
         let start_offset = range.start.to_offset(buffer);
         let end_offset = range.end.to_offset(buffer);
         let start = buffer.anchor_before(start_offset);
@@ -813,7 +818,7 @@ impl SyntaxSnapshot {
                     let layer_start_offset = layer.range.start.to_offset(buffer);
                     let layer_start_point = layer.range.start.to_point(buffer).to_ts_point();
 
-                    info = Some(SyntaxLayerInfo {
+                    info = Some(SyntaxLayer {
                         tree,
                         language,
                         depth: layer.depth,
@@ -842,7 +847,7 @@ impl<'a> SyntaxMapCaptures<'a> {
     fn new(
         range: Range<usize>,
         text: &'a Rope,
-        layers: impl Iterator<Item = SyntaxLayerInfo<'a>>,
+        layers: impl Iterator<Item = SyntaxLayer<'a>>,
         query: fn(&Grammar) -> Option<&Query>,
     ) -> Self {
         let mut result = Self {
@@ -855,7 +860,7 @@ impl<'a> SyntaxMapCaptures<'a> {
                 Some(grammar) => grammar,
                 None => continue,
             };
-            let query = match query(&grammar) {
+            let query = match query(grammar) {
                 Some(query) => query,
                 None => continue,
             };
@@ -964,7 +969,7 @@ impl<'a> SyntaxMapMatches<'a> {
     fn new(
         range: Range<usize>,
         text: &'a Rope,
-        layers: impl Iterator<Item = SyntaxLayerInfo<'a>>,
+        layers: impl Iterator<Item = SyntaxLayer<'a>>,
         query: fn(&Grammar) -> Option<&Query>,
     ) -> Self {
         let mut result = Self::default();
@@ -973,7 +978,7 @@ impl<'a> SyntaxMapMatches<'a> {
                 Some(grammar) => grammar,
                 None => continue,
             };
-            let query = match query(&grammar) {
+            let query = match query(grammar) {
                 Some(query) => query,
                 None => continue,
             };
@@ -1082,7 +1087,7 @@ impl<'a> SyntaxMapMatchesLayer<'a> {
     fn advance(&mut self) {
         if let Some(mat) = self.matches.next() {
             self.next_captures.clear();
-            self.next_captures.extend_from_slice(&mat.captures);
+            self.next_captures.extend_from_slice(mat.captures);
             self.next_pattern_index = mat.pattern_index;
             self.has_next = true;
         } else {
@@ -1296,7 +1301,7 @@ fn get_injections(
     }
 }
 
-/// Update the given list of included `ranges`, removing any ranges that intersect
+/// Updates the given list of included `ranges`, removing any ranges that intersect
 /// `removed_ranges`, and inserting the given `new_ranges`.
 ///
 /// Returns a new vector of ranges, and the range of the vector that was changed,
@@ -1436,23 +1441,25 @@ fn insert_newlines_between_ranges(
     }
 }
 
-impl OwnedSyntaxLayerInfo {
+impl OwnedSyntaxLayer {
+    /// Returns the root syntax node for this layer.
     pub fn node(&self) -> Node {
         self.tree
             .root_node_with_offset(self.offset.0, self.offset.1)
     }
 }
 
-impl<'a> SyntaxLayerInfo<'a> {
-    pub fn to_owned(&self) -> OwnedSyntaxLayerInfo {
-        OwnedSyntaxLayerInfo {
+impl<'a> SyntaxLayer<'a> {
+    /// Returns an owned version of this layer.
+    pub fn to_owned(&self) -> OwnedSyntaxLayer {
+        OwnedSyntaxLayer {
             tree: self.tree.clone(),
             offset: self.offset,
-            depth: self.depth,
             language: self.language.clone(),
         }
     }
 
+    /// Returns the root node for this layer.
     pub fn node(&self) -> Node<'a> {
         self.tree
             .root_node_with_offset(self.offset.0, self.offset.1)
@@ -1510,7 +1517,7 @@ impl Eq for ParseStep {}
 
 impl PartialOrd for ParseStep {
     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        Some(self.cmp(&other))
+        Some(self.cmp(other))
     }
 }
 
@@ -1564,7 +1571,7 @@ impl ChangeRegionSet {
         )
     }
 
-    fn intersects(&self, layer: &SyntaxLayer, text: &BufferSnapshot) -> bool {
+    fn intersects(&self, layer: &SyntaxLayerEntry, text: &BufferSnapshot) -> bool {
         for region in &self.0 {
             if region.depth < layer.depth {
                 continue;
@@ -1675,7 +1682,7 @@ impl<'a> SeekTarget<'a, SyntaxLayerSummary, SyntaxLayerSummary>
     }
 }
 
-impl sum_tree::Item for SyntaxLayer {
+impl sum_tree::Item for SyntaxLayerEntry {
     type Summary = SyntaxLayerSummary;
 
     fn summary(&self) -> Self::Summary {
@@ -1690,7 +1697,7 @@ impl sum_tree::Item for SyntaxLayer {
     }
 }
 
-impl std::fmt::Debug for SyntaxLayer {
+impl std::fmt::Debug for SyntaxLayerEntry {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         f.debug_struct("SyntaxLayer")
             .field("depth", &self.depth)

crates/language_selector/src/active_buffer_language.rs ๐Ÿ”—

@@ -1,7 +1,7 @@
 use editor::Editor;
 use gpui::{div, IntoElement, ParentElement, Render, Subscription, View, ViewContext, WeakView};
 use std::sync::Arc;
-use ui::{Button, ButtonCommon, Clickable, LabelSize, Tooltip};
+use ui::{Button, ButtonCommon, Clickable, FluentBuilder, LabelSize, Tooltip};
 use workspace::{item::ItemHandle, StatusItemView, Workspace};
 
 use crate::LanguageSelector;

crates/language_tools/src/lsp_log.rs ๐Ÿ”—

@@ -45,7 +45,7 @@ struct LanguageServerRpcState {
 
 pub struct LspLogView {
     pub(crate) editor: View<Editor>,
-    editor_subscription: Subscription,
+    editor_subscriptions: Vec<Subscription>,
     log_store: Model<LogStore>,
     current_server_id: Option<LanguageServerId>,
     is_showing_rpc_trace: bool,
@@ -419,7 +419,7 @@ impl LspLogView {
                 }
             }
         });
-        let (editor, editor_subscription) = Self::editor_for_logs(String::new(), cx);
+        let (editor, editor_subscriptions) = Self::editor_for_logs(String::new(), cx);
 
         let focus_handle = cx.focus_handle();
         let focus_subscription = cx.on_focus(&focus_handle, |log_view, cx| {
@@ -429,7 +429,7 @@ impl LspLogView {
         let mut this = Self {
             focus_handle,
             editor,
-            editor_subscription,
+            editor_subscriptions,
             project,
             log_store,
             current_server_id: None,
@@ -449,7 +449,7 @@ impl LspLogView {
     fn editor_for_logs(
         log_contents: String,
         cx: &mut ViewContext<Self>,
-    ) -> (View<Editor>, Subscription) {
+    ) -> (View<Editor>, Vec<Subscription>) {
         let editor = cx.new_view(|cx| {
             let mut editor = Editor::multi_line(cx);
             editor.set_text(log_contents, cx);
@@ -464,7 +464,13 @@ impl LspLogView {
                 cx.emit(event.clone())
             },
         );
-        (editor, editor_subscription)
+        let search_subscription = cx.subscribe(
+            &editor,
+            |_, _, event: &SearchEvent, cx: &mut ViewContext<'_, 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>> {
@@ -521,9 +527,9 @@ impl LspLogView {
         if let Some(log_contents) = log_contents {
             self.current_server_id = Some(server_id);
             self.is_showing_rpc_trace = false;
-            let (editor, editor_subscription) = Self::editor_for_logs(log_contents, cx);
+            let (editor, editor_subscriptions) = Self::editor_for_logs(log_contents, cx);
             self.editor = editor;
-            self.editor_subscription = editor_subscription;
+            self.editor_subscriptions = editor_subscriptions;
             cx.notify();
         }
         cx.focus(&self.focus_handle);
@@ -542,7 +548,7 @@ impl LspLogView {
         if let Some(rpc_log) = rpc_log {
             self.current_server_id = Some(server_id);
             self.is_showing_rpc_trace = true;
-            let (editor, editor_subscription) = Self::editor_for_logs(rpc_log, cx);
+            let (editor, editor_subscriptions) = Self::editor_for_logs(rpc_log, cx);
             let language = self.project.read(cx).languages().language_for_name("JSON");
             editor
                 .read(cx)
@@ -564,7 +570,7 @@ impl LspLogView {
                 });
 
             self.editor = editor;
-            self.editor_subscription = editor_subscription;
+            self.editor_subscriptions = editor_subscriptions;
             cx.notify();
         }
 

crates/language_tools/src/syntax_tree_view.rs ๐Ÿ”—

@@ -5,7 +5,7 @@ use gpui::{
     MouseButton, MouseDownEvent, MouseMoveEvent, ParentElement, Render, Styled,
     UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WindowContext,
 };
-use language::{Buffer, OwnedSyntaxLayerInfo};
+use language::{Buffer, OwnedSyntaxLayer};
 use std::{mem, ops::Range};
 use theme::ActiveTheme;
 use tree_sitter::{Node, TreeCursor};
@@ -54,7 +54,7 @@ struct EditorState {
 struct BufferState {
     buffer: Model<Buffer>,
     excerpt_id: ExcerptId,
-    active_layer: Option<OwnedSyntaxLayerInfo>,
+    active_layer: Option<OwnedSyntaxLayer>,
 }
 
 impl SyntaxTreeView {
@@ -477,7 +477,7 @@ impl SyntaxTreeToolbarItemView {
         })
     }
 
-    fn render_header(active_layer: &OwnedSyntaxLayerInfo) -> ButtonLike {
+    fn render_header(active_layer: &OwnedSyntaxLayer) -> ButtonLike {
         ButtonLike::new("syntax tree header")
             .child(Label::new(active_layer.language.name()))
             .child(Label::new(format_node_range(active_layer.node())))

crates/lsp/src/lsp.rs ๐Ÿ”—

@@ -345,7 +345,7 @@ impl LanguageServer {
                 if let Some(handler) = notification_handlers.lock().get_mut(msg.method) {
                     handler(
                         msg.id,
-                        &msg.params.map(|params| params.get()).unwrap_or("null"),
+                        msg.params.map(|params| params.get()).unwrap_or("null"),
                         cx.clone(),
                     );
                 } else {
@@ -863,17 +863,31 @@ impl LanguageServer {
             .try_send(message)
             .context("failed to write to language server's stdin");
 
+        let outbound_tx = outbound_tx.downgrade();
         let mut timeout = executor.timer(LSP_REQUEST_TIMEOUT).fuse();
         let started = Instant::now();
         async move {
             handle_response?;
             send?;
 
+            let cancel_on_drop = util::defer(move || {
+                if let Some(outbound_tx) = outbound_tx.upgrade() {
+                    Self::notify_internal::<notification::Cancel>(
+                        &outbound_tx,
+                        CancelParams {
+                            id: NumberOrString::Number(id as i32),
+                        },
+                    )
+                    .log_err();
+                }
+            });
+
             let method = T::METHOD;
             futures::select! {
                 response = rx.fuse() => {
                     let elapsed = started.elapsed();
                     log::trace!("Took {elapsed:?} to receive response to {method:?} id {id}");
+                    cancel_on_drop.abort();
                     response?
                 }
 

crates/multi_buffer/src/multi_buffer.rs ๐Ÿ”—

@@ -2316,7 +2316,7 @@ impl MultiBufferSnapshot {
         &self,
         point: T,
     ) -> Option<(&BufferSnapshot, usize)> {
-        let offset = point.to_offset(&self);
+        let offset = point.to_offset(self);
         let mut cursor = self.excerpts.cursor::<usize>();
         cursor.seek(&offset, Bias::Right, &());
         if cursor.item().is_none() {
@@ -3028,7 +3028,7 @@ impl MultiBufferSnapshot {
 
     pub fn has_git_diffs(&self) -> bool {
         for excerpt in self.excerpts.iter() {
-            if !excerpt.buffer.git_diff.is_empty() {
+            if excerpt.buffer.has_git_diff() {
                 return true;
             }
         }
@@ -3694,7 +3694,7 @@ impl ExcerptId {
     pub fn cmp(&self, other: &Self, snapshot: &MultiBufferSnapshot) -> cmp::Ordering {
         let a = snapshot.excerpt_locator_for_id(*self);
         let b = snapshot.excerpt_locator_for_id(*other);
-        a.cmp(&b).then_with(|| self.0.cmp(&other.0))
+        a.cmp(b).then_with(|| self.0.cmp(&other.0))
     }
 }
 

crates/plugin_runtime/src/plugin.rs ๐Ÿ”—

@@ -69,7 +69,7 @@ impl Default for Metering {
 }
 
 /// This struct is used to build a new [`Plugin`], using the builder pattern.
-/// Create a new default plugin with `PluginBuilder::new_with_default_ctx`,
+/// Creates a new default plugin with `PluginBuilder::new_with_default_ctx`,
 /// and add host-side exported functions using `host_function` and `host_function_async`.
 /// Finalize the plugin by calling [`init`].
 pub struct PluginBuilder {
@@ -90,7 +90,7 @@ fn create_default_engine() -> Result<Engine, Error> {
 }
 
 impl PluginBuilder {
-    /// Create a new [`PluginBuilder`] with the given WASI context.
+    /// Creates a new [`PluginBuilder`] with the given WASI context.
     /// Using the default context is a safe bet, see [`new_with_default_context`].
     /// This plugin will yield after a configurable amount of fuel is consumed.
     pub fn new(wasi_ctx: WasiCtx, metering: Metering) -> Result<Self, Error> {
@@ -105,7 +105,7 @@ impl PluginBuilder {
         })
     }
 
-    /// Create a new `PluginBuilder` with the default `WasiCtx` (see [`default_ctx`]).
+    /// Creates a new `PluginBuilder` with the default `WasiCtx` (see [`default_ctx`]).
     /// This plugin will yield after a configurable amount of fuel is consumed.
     pub fn new_default() -> Result<Self, Error> {
         let default_ctx = WasiCtxBuilder::new()

crates/project/src/lsp_command.rs ๐Ÿ”—

@@ -1532,7 +1532,7 @@ impl LspCommand for GetCompletions {
                 .iter()
                 .map(language::proto::serialize_completion)
                 .collect(),
-            version: serialize_version(&buffer_version),
+            version: serialize_version(buffer_version),
         }
     }
 
@@ -1672,7 +1672,7 @@ impl LspCommand for GetCodeActions {
                 .iter()
                 .map(language::proto::serialize_code_action)
                 .collect(),
-            version: serialize_version(&buffer_version),
+            version: serialize_version(buffer_version),
         }
     }
 

crates/project/src/project.rs ๐Ÿ”—

@@ -34,16 +34,16 @@ use gpui::{
 use itertools::Itertools;
 use language::{
     language_settings::{language_settings, FormatOnSave, Formatter, InlayHintKind},
-    point_to_lsp,
+    markdown, point_to_lsp,
     proto::{
         deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version,
         serialize_anchor, serialize_version, split_operations,
     },
     range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, Capability,
     CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff,
-    Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName, LocalFile,
-    LspAdapterDelegate, OffsetRangeExt, Operation, Patch, PendingLanguageServer, PointUtf16,
-    TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, Unclipped,
+    Documentation, Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName,
+    LocalFile, LspAdapterDelegate, OffsetRangeExt, Operation, Patch, PendingLanguageServer,
+    PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, Unclipped,
 };
 use log::error;
 use lsp::{
@@ -52,7 +52,7 @@ use lsp::{
 };
 use lsp_command::*;
 use node_runtime::NodeRuntime;
-use parking_lot::Mutex;
+use parking_lot::{Mutex, RwLock};
 use postage::watch;
 use prettier_support::{DefaultPrettier, PrettierInstance};
 use project_settings::{LspSettings, ProjectSettings};
@@ -2947,7 +2947,7 @@ impl Project {
             };
             task.await;
 
-            this.update(&mut cx, |this, mut cx| {
+            this.update(&mut cx, |this, cx| {
                 let worktrees = this.worktrees.clone();
                 for worktree in worktrees {
                     let worktree = match worktree.upgrade() {
@@ -2962,7 +2962,7 @@ impl Project {
                         root_path,
                         adapter.clone(),
                         language.clone(),
-                        &mut cx,
+                        cx,
                     );
                 }
             })
@@ -3544,8 +3544,8 @@ impl Project {
             if errored {
                 log::warn!("test binary check failed");
                 let task = this
-                    .update(&mut cx, move |this, mut cx| {
-                        this.reinstall_language_server(language, adapter, server_id, &mut cx)
+                    .update(&mut cx, move |this, cx| {
+                        this.reinstall_language_server(language, adapter, server_id, cx)
                     })
                     .ok()
                     .flatten();
@@ -3768,8 +3768,7 @@ impl Project {
                                     }
                                 };
                                 if let Some(relative_glob_pattern) = relative_glob_pattern {
-                                    let literal_prefix =
-                                        glob_literal_prefix(&relative_glob_pattern);
+                                    let literal_prefix = glob_literal_prefix(relative_glob_pattern);
                                     tree.as_local_mut()
                                         .unwrap()
                                         .add_path_prefix_to_scan(Path::new(literal_prefix).into());
@@ -3910,7 +3909,6 @@ impl Project {
                         message: diagnostic.message.clone(),
                         group_id,
                         is_primary: true,
-                        is_valid: true,
                         is_disk_based,
                         is_unnecessary,
                     },
@@ -3928,7 +3926,6 @@ impl Project {
                                     message: info.message.clone(),
                                     group_id,
                                     is_primary: false,
-                                    is_valid: true,
                                     is_disk_based,
                                     is_unnecessary: false,
                                 },
@@ -4233,9 +4230,9 @@ impl Project {
                                 format_operation = Some(FormatOperation::Lsp(
                                     Self::format_via_lsp(
                                         &project,
-                                        &buffer,
+                                        buffer,
                                         buffer_abs_path,
-                                        &language_server,
+                                        language_server,
                                         tab_size,
                                         &mut cx,
                                     )
@@ -4254,8 +4251,8 @@ impl Project {
                                 format_operation = Self::format_via_external_command(
                                     buffer,
                                     buffer_abs_path,
-                                    &command,
-                                    &arguments,
+                                    command,
+                                    arguments,
                                     &mut cx,
                                 )
                                 .await
@@ -4278,9 +4275,9 @@ impl Project {
                                 format_operation = Some(FormatOperation::Lsp(
                                     Self::format_via_lsp(
                                         &project,
-                                        &buffer,
+                                        buffer,
                                         buffer_abs_path,
-                                        &language_server,
+                                        language_server,
                                         tab_size,
                                         &mut cx,
                                     )
@@ -4830,6 +4827,170 @@ impl Project {
         }
     }
 
+    pub fn resolve_completions(
+        &self,
+        completion_indices: Vec<usize>,
+        completions: Arc<RwLock<Box<[Completion]>>>,
+        cx: &mut ModelContext<Self>,
+    ) -> Task<Result<bool>> {
+        let client = self.client();
+        let language_registry = self.languages().clone();
+
+        let is_remote = self.is_remote();
+        let project_id = self.remote_id();
+
+        cx.spawn(move |this, mut cx| async move {
+            let mut did_resolve = false;
+            if is_remote {
+                let project_id =
+                    project_id.ok_or_else(|| anyhow!("Remote project without remote_id"))?;
+
+                for completion_index in completion_indices {
+                    let completions_guard = completions.read();
+                    let completion = &completions_guard[completion_index];
+                    if completion.documentation.is_some() {
+                        continue;
+                    }
+
+                    did_resolve = true;
+                    let server_id = completion.server_id;
+                    let completion = completion.lsp_completion.clone();
+                    drop(completions_guard);
+
+                    Self::resolve_completion_documentation_remote(
+                        project_id,
+                        server_id,
+                        completions.clone(),
+                        completion_index,
+                        completion,
+                        client.clone(),
+                        language_registry.clone(),
+                    )
+                    .await;
+                }
+            } else {
+                for completion_index in completion_indices {
+                    let completions_guard = completions.read();
+                    let completion = &completions_guard[completion_index];
+                    if completion.documentation.is_some() {
+                        continue;
+                    }
+
+                    let server_id = completion.server_id;
+                    let completion = completion.lsp_completion.clone();
+                    drop(completions_guard);
+
+                    let server = this
+                        .read_with(&mut cx, |project, _| {
+                            project.language_server_for_id(server_id)
+                        })
+                        .ok()
+                        .flatten();
+                    let Some(server) = server else {
+                        continue;
+                    };
+
+                    did_resolve = true;
+                    Self::resolve_completion_documentation_local(
+                        server,
+                        completions.clone(),
+                        completion_index,
+                        completion,
+                        language_registry.clone(),
+                    )
+                    .await;
+                }
+            }
+
+            Ok(did_resolve)
+        })
+    }
+
+    async fn resolve_completion_documentation_local(
+        server: Arc<lsp::LanguageServer>,
+        completions: Arc<RwLock<Box<[Completion]>>>,
+        completion_index: usize,
+        completion: lsp::CompletionItem,
+        language_registry: Arc<LanguageRegistry>,
+    ) {
+        let can_resolve = server
+            .capabilities()
+            .completion_provider
+            .as_ref()
+            .and_then(|options| options.resolve_provider)
+            .unwrap_or(false);
+        if !can_resolve {
+            return;
+        }
+
+        let request = server.request::<lsp::request::ResolveCompletionItem>(completion);
+        let Some(completion_item) = request.await.log_err() else {
+            return;
+        };
+
+        if let Some(lsp_documentation) = completion_item.documentation {
+            let documentation = language::prepare_completion_documentation(
+                &lsp_documentation,
+                &language_registry,
+                None, // TODO: Try to reasonably work out which language the completion is for
+            )
+            .await;
+
+            let mut completions = completions.write();
+            let completion = &mut completions[completion_index];
+            completion.documentation = Some(documentation);
+        } else {
+            let mut completions = completions.write();
+            let completion = &mut completions[completion_index];
+            completion.documentation = Some(Documentation::Undocumented);
+        }
+    }
+
+    async fn resolve_completion_documentation_remote(
+        project_id: u64,
+        server_id: LanguageServerId,
+        completions: Arc<RwLock<Box<[Completion]>>>,
+        completion_index: usize,
+        completion: lsp::CompletionItem,
+        client: Arc<Client>,
+        language_registry: Arc<LanguageRegistry>,
+    ) {
+        let request = proto::ResolveCompletionDocumentation {
+            project_id,
+            language_server_id: server_id.0 as u64,
+            lsp_completion: serde_json::to_string(&completion).unwrap().into_bytes(),
+        };
+
+        let Some(response) = client
+            .request(request)
+            .await
+            .context("completion documentation resolve proto request")
+            .log_err()
+        else {
+            return;
+        };
+
+        if response.text.is_empty() {
+            let mut completions = completions.write();
+            let completion = &mut completions[completion_index];
+            completion.documentation = Some(Documentation::Undocumented);
+        }
+
+        let documentation = if response.is_markdown {
+            Documentation::MultiLineMarkdown(
+                markdown::parse_markdown(&response.text, &language_registry, None).await,
+            )
+        } else if response.text.lines().count() <= 1 {
+            Documentation::SingleLine(response.text)
+        } else {
+            Documentation::MultiLinePlainText(response.text)
+        };
+
+        let mut completions = completions.write();
+        let completion = &mut completions[completion_index];
+        completion.documentation = Some(documentation);
+    }
+
     pub fn apply_additional_edits_for_completion(
         &self,
         buffer_handle: Model<Buffer>,
@@ -5685,7 +5846,7 @@ impl Project {
                                             snapshot.file().map(|file| file.path().as_ref()),
                                         ) {
                                             query
-                                                .search(&snapshot, None)
+                                                .search(snapshot, None)
                                                 .await
                                                 .iter()
                                                 .map(|range| {
@@ -6528,7 +6689,7 @@ impl Project {
                                 snapshot.repository_and_work_directory_for_path(&path)?;
                             let repo = snapshot.get_local_repo(&repo)?;
                             let relative_path = path.strip_prefix(&work_directory).ok()?;
-                            let base_text = repo.repo_ptr.lock().load_index_text(&relative_path);
+                            let base_text = repo.repo_ptr.lock().load_index_text(relative_path);
                             Some((buffer, base_text))
                         })
                         .collect::<Vec<_>>()
@@ -6661,7 +6822,7 @@ impl Project {
         for (_, _, path_summary) in
             self.diagnostic_summaries(include_ignored, cx)
                 .filter(|(path, _, _)| {
-                    let worktree = self.entry_for_path(&path, cx).map(|entry| entry.is_ignored);
+                    let worktree = self.entry_for_path(path, cx).map(|entry| entry.is_ignored);
                     include_ignored || worktree == Some(false)
                 })
         {
@@ -6687,7 +6848,7 @@ impl Project {
                     })
             })
             .filter(move |(path, _, _)| {
-                let worktree = self.entry_for_path(&path, cx).map(|entry| entry.is_ignored);
+                let worktree = self.entry_for_path(path, cx).map(|entry| entry.is_ignored);
                 include_ignored || worktree == Some(false)
             })
     }

crates/project/src/terminals.rs ๐Ÿ”—

@@ -61,7 +61,7 @@ impl Project {
 
                 if let Some(python_settings) = &python_settings.as_option() {
                     let activate_script_path =
-                        self.find_activate_script_path(&python_settings, working_directory);
+                        self.find_activate_script_path(python_settings, working_directory);
                     self.activate_python_virtual_environment(
                         activate_script_path,
                         &terminal_handle,

crates/project/src/worktree.rs ๐Ÿ”—

@@ -1437,7 +1437,7 @@ impl LocalWorktree {
                 if let Err(e) = self.client.send(proto::UpdateDiagnosticSummary {
                     project_id,
                     worktree_id: cx.entity_id().as_u64(),
-                    summary: Some(summary.to_proto(server_id, &path)),
+                    summary: Some(summary.to_proto(server_id, path)),
                 }) {
                     return Task::ready(Err(e));
                 }
@@ -1866,7 +1866,7 @@ impl Snapshot {
         })
     }
 
-    /// Update the `git_status` of the given entries such that files'
+    /// Updates the `git_status` of the given entries such that files'
     /// statuses bubble up to their ancestor directories.
     pub fn propagate_git_statuses(&self, result: &mut [Entry]) {
         let mut cursor = self
@@ -2309,7 +2309,7 @@ impl LocalSnapshot {
 impl BackgroundScannerState {
     fn should_scan_directory(&self, entry: &Entry) -> bool {
         (!entry.is_external && !entry.is_ignored)
-            || entry.path.file_name() == Some(&*DOT_GIT)
+            || entry.path.file_name() == Some(*DOT_GIT)
             || self.scanned_dirs.contains(&entry.id) // If we've ever scanned it, keep scanning
             || self
                 .paths_to_scan
@@ -3374,7 +3374,7 @@ impl BackgroundScanner {
                 let mut is_git_related = false;
                 if let Some(dot_git_dir) = abs_path
                     .ancestors()
-                    .find(|ancestor| ancestor.file_name() == Some(&*DOT_GIT))
+                    .find(|ancestor| ancestor.file_name() == Some(*DOT_GIT))
                 {
                     let dot_git_path = dot_git_dir
                         .strip_prefix(&root_canonical_path)
@@ -3772,7 +3772,7 @@ impl BackgroundScanner {
         for entry in &mut new_entries {
             state.reuse_entry_id(entry);
             if entry.is_dir() {
-                if state.should_scan_directory(&entry) {
+                if state.should_scan_directory(entry) {
                     job_ix += 1;
                 } else {
                     log::debug!("defer scanning directory {:?}", entry.path);
@@ -3814,9 +3814,9 @@ impl BackgroundScanner {
             abs_paths
                 .iter()
                 .map(|abs_path| async move {
-                    let metadata = self.fs.metadata(&abs_path).await?;
+                    let metadata = self.fs.metadata(abs_path).await?;
                     if let Some(metadata) = metadata {
-                        let canonical_path = self.fs.canonicalize(&abs_path).await?;
+                        let canonical_path = self.fs.canonicalize(abs_path).await?;
                         anyhow::Ok(Some((metadata, canonical_path)))
                     } else {
                         Ok(None)
@@ -3864,7 +3864,7 @@ impl BackgroundScanner {
                     fs_entry.is_external = !canonical_path.starts_with(&root_canonical_path);
 
                     if !is_dir && !fs_entry.is_ignored {
-                        if let Some((work_dir, repo)) = state.snapshot.local_repo_for_path(&path) {
+                        if let Some((work_dir, repo)) = state.snapshot.local_repo_for_path(path) {
                             if let Ok(repo_path) = path.strip_prefix(work_dir.0) {
                                 let repo_path = RepoPath(repo_path.into());
                                 let repo = repo.repo_ptr.lock();
@@ -3884,7 +3884,7 @@ impl BackgroundScanner {
                     state.insert_entry(fs_entry, self.fs.as_ref());
                 }
                 Ok(None) => {
-                    self.remove_repo_path(&path, &mut state.snapshot);
+                    self.remove_repo_path(path, &mut state.snapshot);
                 }
                 Err(err) => {
                     // TODO - create a special 'error' entry in the entries tree to mark this

crates/project_panel/src/project_panel.rs ๐Ÿ”—

@@ -381,67 +381,57 @@ impl ProjectPanel {
             let is_local = project.is_local();
             let is_read_only = project.is_read_only();
 
-            let context_menu = ContextMenu::build(cx, |mut menu, cx| {
-                if is_read_only {
-                    menu = menu.action("Copy Relative Path", Box::new(CopyRelativePath));
-                    if is_dir {
-                        menu = menu.action("Search Inside", Box::new(NewSearchInDirectory))
-                    }
-
-                    return menu;
-                }
-
-                if is_local {
-                    menu = menu.action(
-                        "Add Folder to Project",
-                        Box::new(workspace::AddFolderToProject),
-                    );
-                    if is_root {
-                        menu = menu.entry(
-                            "Remove from Project",
-                            None,
-                            cx.handler_for(&this, move |this, cx| {
-                                this.project.update(cx, |project, cx| {
-                                    project.remove_worktree(worktree_id, cx)
-                                });
-                            }),
-                        );
-                    }
-                }
-
-                menu = menu
-                    .action("New File", Box::new(NewFile))
-                    .action("New Folder", Box::new(NewDirectory))
-                    .separator()
-                    .action("Cut", Box::new(Cut))
-                    .action("Copy", Box::new(Copy));
-
-                if let Some(clipboard_entry) = self.clipboard_entry {
-                    if clipboard_entry.worktree_id() == worktree_id {
-                        menu = menu.action("Paste", Box::new(Paste));
-                    }
-                }
-
-                menu = menu
-                    .separator()
-                    .action("Copy Path", Box::new(CopyPath))
-                    .action("Copy Relative Path", Box::new(CopyRelativePath))
-                    .separator()
-                    .action("Reveal in Finder", Box::new(RevealInFinder));
-
-                if is_dir {
-                    menu = menu
-                        .action("Open in Terminal", Box::new(OpenInTerminal))
-                        .action("Search Inside", Box::new(NewSearchInDirectory))
-                }
-
-                menu = menu.separator().action("Rename", Box::new(Rename));
-
-                if !is_root {
-                    menu = menu.action("Delete", Box::new(Delete));
-                }
-
-                menu
+            let context_menu = ContextMenu::build(cx, |menu, cx| {
+                menu.context(self.focus_handle.clone()).when_else(
+                    is_read_only,
+                    |menu| {
+                        menu.action("Copy Relative Path", Box::new(CopyRelativePath))
+                            .when(is_dir, |menu| {
+                                menu.action("Search Inside", Box::new(NewSearchInDirectory))
+                            })
+                    },
+                    |menu| {
+                        menu.when(is_local, |menu| {
+                            menu.action(
+                                "Add Folder to Project",
+                                Box::new(workspace::AddFolderToProject),
+                            )
+                            .when(is_root, |menu| {
+                                menu.entry(
+                                    "Remove from Project",
+                                    None,
+                                    cx.handler_for(&this, move |this, cx| {
+                                        this.project.update(cx, |project, cx| {
+                                            project.remove_worktree(worktree_id, cx)
+                                        });
+                                    }),
+                                )
+                            })
+                        })
+                        .action("New File", Box::new(NewFile))
+                        .action("New Folder", Box::new(NewDirectory))
+                        .separator()
+                        .action("Cut", Box::new(Cut))
+                        .action("Copy", Box::new(Copy))
+                        .when_some(self.clipboard_entry, |menu, entry| {
+                            menu.when(entry.worktree_id() == worktree_id, |menu| {
+                                menu.action("Paste", Box::new(Paste))
+                            })
+                        })
+                        .separator()
+                        .action("Copy Path", Box::new(CopyPath))
+                        .action("Copy Relative Path", Box::new(CopyRelativePath))
+                        .separator()
+                        .action("Reveal in Finder", Box::new(RevealInFinder))
+                        .when(is_dir, |menu| {
+                            menu.action("Open in Terminal", Box::new(OpenInTerminal))
+                                .action("Search Inside", Box::new(NewSearchInDirectory))
+                        })
+                        .separator()
+                        .action("Rename", Box::new(Rename))
+                        .when(!is_root, |menu| menu.action("Delete", Box::new(Delete)))
+                    },
+                )
             });
 
             cx.focus_view(&context_menu);

crates/rich_text/Cargo.toml ๐Ÿ”—

@@ -21,6 +21,7 @@ sum_tree = { path = "../sum_tree" }
 theme = { path = "../theme" }
 language = { path = "../language" }
 util = { path = "../util" }
+ui = { path = "../ui" }
 anyhow.workspace = true
 futures.workspace = true
 lazy_static.workspace = true

crates/rich_text/src/rich_text.rs ๐Ÿ”—

@@ -6,6 +6,7 @@ use gpui::{
 use language::{HighlightId, Language, LanguageRegistry};
 use std::{ops::Range, sync::Arc};
 use theme::ActiveTheme;
+use ui::LinkPreview;
 use util::RangeExt;
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -64,7 +65,7 @@ impl RichText {
                             },
                             Highlight::Id(id) => HighlightStyle {
                                 background_color: Some(code_background),
-                                ..id.style(&theme.syntax()).unwrap_or_default()
+                                ..id.style(theme.syntax()).unwrap_or_default()
                             },
                             Highlight::Highlight(highlight) => *highlight,
                             Highlight::Mention => HighlightStyle {
@@ -84,6 +85,18 @@ impl RichText {
             let link_urls = self.link_urls.clone();
             move |ix, cx| cx.open_url(&link_urls[ix])
         })
+        .tooltip({
+            let link_ranges = self.link_ranges.clone();
+            let link_urls = self.link_urls.clone();
+            move |idx, cx| {
+                for (ix, range) in link_ranges.iter().enumerate() {
+                    if range.contains(&idx) {
+                        return Some(LinkPreview::new(&link_urls[ix], cx));
+                    }
+                }
+                None
+            }
+        })
         .into_any_element()
     }
 }
@@ -107,7 +120,7 @@ pub fn render_markdown_mut(
     let mut list_stack = Vec::new();
 
     let options = Options::all();
-    for (event, source_range) in Parser::new_ext(&block, options).into_offset_iter() {
+    for (event, source_range) in Parser::new_ext(block, options).into_offset_iter() {
         let prev_len = text.len();
         match event {
             Event::Text(t) => {
@@ -237,7 +250,7 @@ pub fn render_markdown_mut(
                 _ => {}
             },
             Event::HardBreak => text.push('\n'),
-            Event::SoftBreak => text.push(' '),
+            Event::SoftBreak => text.push('\n'),
             _ => {}
         }
     }

crates/rpc/proto/zed.proto ๐Ÿ”—

@@ -1472,7 +1472,10 @@ message Diagnostic {
     optional string code = 6;
     uint64 group_id = 7;
     bool is_primary = 8;
+
+    // TODO: remove this field
     bool is_valid = 9;
+
     bool is_disk_based = 10;
     bool is_unnecessary = 11;
 

crates/settings/src/settings_store.rs ๐Ÿ”—

@@ -246,7 +246,7 @@ impl SettingsStore {
         this
     }
 
-    /// Update the value of a setting in the user's global configuration.
+    /// Updates the value of a setting in the user's global configuration.
     ///
     /// This is only for tests. Normally, settings are only loaded from
     /// JSON files.
@@ -261,7 +261,7 @@ impl SettingsStore {
         self.set_user_settings(&new_text, cx).unwrap();
     }
 
-    /// Update the value of a setting in a JSON file, returning the new text
+    /// Updates the value of a setting in a JSON file, returning the new text
     /// for that JSON file.
     pub fn new_text_for_update<T: Settings>(
         &self,
@@ -276,7 +276,7 @@ impl SettingsStore {
         new_text
     }
 
-    /// Update the value of a setting in a JSON file, returning a list
+    /// Updates the value of a setting in a JSON file, returning a list
     /// of edits to apply to the JSON file.
     pub fn edits_for_update<T: Settings>(
         &self,
@@ -344,7 +344,7 @@ impl SettingsStore {
         DEFAULT_JSON_TAB_SIZE
     }
 
-    /// Set the default settings via a JSON string.
+    /// Sets the default settings via a JSON string.
     ///
     /// The string should contain a JSON object with a default value for every setting.
     pub fn set_default_settings(
@@ -362,7 +362,7 @@ impl SettingsStore {
         }
     }
 
-    /// Set the user settings via a JSON string.
+    /// Sets the user settings via a JSON string.
     pub fn set_user_settings(
         &mut self,
         user_settings_content: &str,
@@ -533,7 +533,7 @@ impl SettingsStore {
                 }
 
                 if let Some(local_settings) =
-                    setting_value.deserialize_setting(&local_settings).log_err()
+                    setting_value.deserialize_setting(local_settings).log_err()
                 {
                     paths_stack.push(Some((*root_id, path.as_ref())));
                     user_settings_stack.push(local_settings);
@@ -697,8 +697,7 @@ fn update_value_in_json_text<'a>(
         if let Some(new_object) = new_value.as_object_mut() {
             new_object.retain(|_, v| !v.is_null());
         }
-        let (range, replacement) =
-            replace_value_in_json_text(text, &key_path, tab_size, &new_value);
+        let (range, replacement) = replace_value_in_json_text(text, key_path, tab_size, &new_value);
         text.replace_range(range.clone(), &replacement);
         edits.push((range, replacement));
     }

crates/story/src/story.rs ๐Ÿ”—

@@ -67,8 +67,8 @@ impl StoryContainer {
 }
 
 impl ParentElement for StoryContainer {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
-        &mut self.children
+    fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
+        self.children.extend(elements)
     }
 }
 
@@ -104,7 +104,7 @@ impl RenderOnce for StoryContainer {
                     .h_px()
                     .flex_1()
                     .id("story_body")
-                    .overflow_hidden_x()
+                    .overflow_x_hidden()
                     .overflow_y_scroll()
                     .flex()
                     .flex_col()
@@ -372,7 +372,7 @@ impl RenderOnce for StorySection {
 }
 
 impl ParentElement for StorySection {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
-        &mut self.children
+    fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
+        self.children.extend(elements)
     }
 }

crates/storybook/src/stories/text.rs ๐Ÿ”—

@@ -110,66 +110,3 @@ impl Render for TextStory {
             ).into_element()
     }
 }
-
-// TODO: Check all were updated to new style and remove
-
-// impl Render for TextStory {
-//     type Element = Div;
-
-//     fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
-//         v_flex()
-//             .bg(blue())
-//             .child(
-//                 div()
-//                     .flex()
-//                     .child(div().max_w_96().bg(white()).child(concat!(
-//         "max-width: 96. The quick brown fox jumps over the lazy dog. ",
-//         "Meanwhile, the lazy dog decided it was time for a change. ",
-//         "He started daily workout routines, ate healthier and became the fastest dog in town.",
-//     ))),
-//             )
-//             .child(div().h_5())
-//             .child(div().flex().flex_col().w_96().bg(white()).child(concat!(
-//         "flex-col. width: 96; The quick brown fox jumps over the lazy dog. ",
-//         "Meanwhile, the lazy dog decided it was time for a change. ",
-//         "He started daily workout routines, ate healthier and became the fastest dog in town.",
-//     )))
-//             .child(div().h_5())
-//             .child(
-//                 div()
-//                     .flex()
-//                     .child(div().min_w_96().bg(white()).child(concat!(
-//     "min-width: 96. The quick brown fox jumps over the lazy dog. ",
-//     "Meanwhile, the lazy dog decided it was time for a change. ",
-//     "He started daily workout routines, ate healthier and became the fastest dog in town.",
-// ))))
-//             .child(div().h_5())
-//             .child(div().flex().w_96().bg(white()).child(div().overflow_hidden().child(concat!(
-//         "flex-row. width 96. overflow-hidden. The quick brown fox jumps over the lazy dog. ",
-//         "Meanwhile, the lazy dog decided it was time for a change. ",
-//         "He started daily workout routines, ate healthier and became the fastest dog in town.",
-//     ))))
-//             // NOTE: When rendering text in a horizontal flex container,
-//             // Taffy will not pass width constraints down from the parent.
-//             // To fix this, render text in a parent with overflow: hidden
-//                     .child(div().h_5())
-//                     .child(div().flex().w_96().bg(red()).child(concat!(
-//                 "flex-row. width 96. The quick brown fox jumps over the lazy dog. ",
-//                 "Meanwhile, the lazy dog decided it was time for a change. ",
-//                 "He started daily workout routines, ate healthier and became the fastest dog in town.",
-//             ))).child(
-//                 InteractiveText::new(
-//                     "interactive",
-//                     StyledText::new("Hello world, how is it going?").with_highlights(&cx.text_style(), [
-//                         (6..11, HighlightStyle {
-//                             background_color: Some(green()),
-//                             ..Default::default()
-//                         }),
-//                     ]),
-//                 )
-//                 .on_click(vec![2..4, 1..3, 7..9], |range_ix, _cx| {
-//                     println!("Clicked range {range_ix}");
-//                 })
-//             )
-//     }
-// }

crates/storybook/src/stories/z_index.rs ๐Ÿ”—

@@ -76,7 +76,7 @@ impl Styles for Div {}
 
 #[derive(IntoElement)]
 struct ZIndexExample {
-    z_index: u8,
+    z_index: u16,
 }
 
 impl RenderOnce for ZIndexExample {
@@ -166,7 +166,7 @@ impl RenderOnce for ZIndexExample {
 }
 
 impl ZIndexExample {
-    pub fn new(z_index: u8) -> Self {
+    pub fn new(z_index: u16) -> Self {
         Self { z_index }
     }
 }

crates/terminal/src/terminal.rs ๐Ÿ”—

@@ -52,9 +52,9 @@ use std::{
 use thiserror::Error;
 
 use gpui::{
-    actions, black, px, red, AnyWindowHandle, AppContext, Bounds, ClipboardItem, EventEmitter,
-    Hsla, Keystroke, ModelContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent,
-    MouseUpEvent, Pixels, Point, Rgba, ScrollWheelEvent, Size, Task, TouchPhase,
+    actions, black, px, AnyWindowHandle, AppContext, Bounds, ClipboardItem, EventEmitter, Hsla,
+    Keystroke, ModelContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
+    Pixels, Point, Rgba, ScrollWheelEvent, Size, Task, TouchPhase,
 };
 
 use crate::mappings::{colors::to_alac_rgb, keys::to_esc_str};
@@ -599,7 +599,11 @@ impl Terminal {
         }
     }
 
-    /// Update the cached process info, returns whether the Zed-relevant info has changed
+    pub fn selection_started(&self) -> bool {
+        self.selection_phase == SelectionPhase::Selecting
+    }
+
+    /// Updates the cached process info, returns whether the Zed-relevant info has changed
     fn update_process_info(&mut self) -> bool {
         let mut pid = unsafe { libc::tcgetpgrp(self.shell_fd as i32) };
         if pid < 0 {
@@ -1206,7 +1210,7 @@ impl Terminal {
     pub fn scroll_wheel(&mut self, e: &ScrollWheelEvent, origin: Point<Pixels>) {
         let mouse_mode = self.mouse_mode(e.shift);
 
-        if let Some(scroll_lines) = self.determine_scroll_lines(&e, mouse_mode) {
+        if let Some(scroll_lines) = self.determine_scroll_lines(e, mouse_mode) {
             if mouse_mode {
                 let point = grid_point(
                     e.position - origin,
@@ -1215,7 +1219,7 @@ impl Terminal {
                 );
 
                 if let Some(scrolls) =
-                    scroll_report(point, scroll_lines as i32, &e, self.last_content.mode)
+                    scroll_report(point, scroll_lines as i32, e, self.last_content.mode)
                 {
                     for scroll in scrolls {
                         self.pty_tx.notify(scroll);
@@ -1295,7 +1299,7 @@ impl Terminal {
                     "{}{}",
                     fpi.name,
                     if fpi.argv.len() >= 1 {
-                        format!(" {}", (&fpi.argv[1..]).join(" "))
+                        format!(" {}", (fpi.argv[1..]).join(" "))
                     } else {
                         "".to_string()
                     }
@@ -1380,7 +1384,7 @@ pub fn get_color_at_index(index: usize, theme: &Theme) -> Hsla {
     let colors = theme.colors();
 
     match index {
-        //0-15 are the same as the named colors above
+        // 0-15 are the same as the named colors above
         0 => colors.terminal_ansi_black,
         1 => colors.terminal_ansi_red,
         2 => colors.terminal_ansi_green,
@@ -1397,34 +1401,32 @@ pub fn get_color_at_index(index: usize, theme: &Theme) -> Hsla {
         13 => colors.terminal_ansi_bright_magenta,
         14 => colors.terminal_ansi_bright_cyan,
         15 => colors.terminal_ansi_bright_white,
-        //16-231 are mapped to their RGB colors on a 0-5 range per channel
+        // 16-231 are mapped to their RGB colors on a 0-5 range per channel
         16..=231 => {
-            let (r, g, b) = rgb_for_index(&(index as u8)); //Split the index into it's ANSI-RGB components
-            let step = (u8::MAX as f32 / 5.).floor() as u8; //Split the RGB range into 5 chunks, with floor so no overflow
-            rgba_color(r * step, g * step, b * step) //Map the ANSI-RGB components to an RGB color
+            let (r, g, b) = rgb_for_index(&(index as u8)); // Split the index into it's ANSI-RGB components
+            let step = (u8::MAX as f32 / 5.).floor() as u8; // Split the RGB range into 5 chunks, with floor so no overflow
+            rgba_color(r * step, g * step, b * step) // Map the ANSI-RGB components to an RGB color
         }
-        //232-255 are a 24 step grayscale from black to white
+        // 232-255 are a 24 step grayscale from black to white
         232..=255 => {
-            let i = index as u8 - 232; //Align index to 0..24
-            let step = (u8::MAX as f32 / 24.).floor() as u8; //Split the RGB grayscale values into 24 chunks
-            rgba_color(i * step, i * step, i * step) //Map the ANSI-grayscale components to the RGB-grayscale
+            let i = index as u8 - 232; // Align index to 0..24
+            let step = (u8::MAX as f32 / 24.).floor() as u8; // Split the RGB grayscale values into 24 chunks
+            rgba_color(i * step, i * step, i * step) // Map the ANSI-grayscale components to the RGB-grayscale
         }
-        //For compatibility with the alacritty::Colors interface
+        // For compatibility with the alacritty::Colors interface
         256 => colors.text,
         257 => colors.background,
         258 => theme.players().local().cursor,
-
-        // todo!(more colors)
-        259 => red(),                      //style.dim_black,
-        260 => red(),                      //style.dim_red,
-        261 => red(),                      //style.dim_green,
-        262 => red(),                      //style.dim_yellow,
-        263 => red(),                      //style.dim_blue,
-        264 => red(),                      //style.dim_magenta,
-        265 => red(),                      //style.dim_cyan,
-        266 => red(),                      //style.dim_white,
-        267 => red(),                      //style.bright_foreground,
-        268 => colors.terminal_ansi_black, //'Dim Background', non-standard color
+        259 => colors.terminal_ansi_dim_black,
+        260 => colors.terminal_ansi_dim_red,
+        261 => colors.terminal_ansi_dim_green,
+        262 => colors.terminal_ansi_dim_yellow,
+        263 => colors.terminal_ansi_dim_blue,
+        264 => colors.terminal_ansi_dim_magenta,
+        265 => colors.terminal_ansi_dim_cyan,
+        266 => colors.terminal_ansi_dim_white,
+        267 => colors.terminal_bright_foreground,
+        268 => colors.terminal_ansi_black, // 'Dim Background', non-standard color
 
         _ => black(),
     }

crates/terminal/src/terminal_settings.rs ๐Ÿ”—

@@ -90,17 +90,17 @@ pub struct TerminalSettingsContent {
     ///
     /// Default: current_project_directory
     pub working_directory: Option<WorkingDirectory>,
-    /// Set the terminal's font size.
+    /// Sets the terminal's font size.
     ///
     /// If this option is not included,
     /// the terminal will default to matching the buffer's font size.
     pub font_size: Option<f32>,
-    /// Set the terminal's font family.
+    /// Sets the terminal's font family.
     ///
     /// If this option is not included,
     /// the terminal will default to matching the buffer's font family.
     pub font_family: Option<String>,
-    /// Set the terminal's line height.
+    /// Sets the terminal's line height.
     ///
     /// Default: comfortable
     pub line_height: Option<TerminalLineHeight>,
@@ -110,18 +110,18 @@ pub struct TerminalSettingsContent {
     ///
     /// Default: {}
     pub env: Option<HashMap<String, String>>,
-    /// Set the cursor blinking behavior in the terminal.
+    /// Sets the cursor blinking behavior in the terminal.
     ///
     /// Default: terminal_controlled
     pub blinking: Option<TerminalBlink>,
-    /// Set whether Alternate Scroll mode (code: ?1007) is active by default.
+    /// Sets whether Alternate Scroll mode (code: ?1007) is active by default.
     /// Alternate Scroll mode converts mouse scroll events into up / down key
     /// presses when in the alternate screen (e.g. when running applications
     /// like vim or  less). The terminal can still set and unset this mode.
     ///
     /// Default: off
     pub alternate_scroll: Option<AlternateScroll>,
-    /// Set whether the option key behaves as the meta key.
+    /// Sets whether the option key behaves as the meta key.
     ///
     /// Default: false
     pub option_as_meta: Option<bool>,
@@ -139,7 +139,7 @@ pub struct TerminalSettingsContent {
     ///
     /// Default: 320
     pub default_height: Option<f32>,
-    /// Activate the python virtual environment, if one is found, in the
+    /// Activates the python virtual environment, if one is found, in the
     /// terminal's working directory (as resolved by the working_directory
     /// setting). Set this to "off" to disable this behavior.
     ///

crates/terminal_view/src/terminal_element.rs ๐Ÿ”—

@@ -1,12 +1,11 @@
 use editor::{Cursor, HighlightedRange, HighlightedRangeLine};
 use gpui::{
-    div, fill, point, px, red, relative, AnyElement, AsyncWindowContext, AvailableSpace,
-    BorrowWindow, Bounds, DispatchPhase, Element, ElementId, FocusHandle, Font, FontStyle,
-    FontWeight, HighlightStyle, Hsla, InteractiveBounds, InteractiveElement,
-    InteractiveElementState, Interactivity, IntoElement, LayoutId, Model, ModelContext,
-    ModifiersChangedEvent, MouseButton, MouseMoveEvent, Pixels, PlatformInputHandler, Point,
-    ShapedLine, StatefulInteractiveElement, Styled, TextRun, TextStyle, TextSystem, UnderlineStyle,
-    WeakView, WhiteSpace, WindowContext,
+    div, fill, point, px, relative, AnyElement, AvailableSpace, Bounds, DispatchPhase, Element,
+    ElementContext, ElementId, FocusHandle, Font, FontStyle, FontWeight, HighlightStyle, Hsla,
+    InputHandler, InteractiveBounds, InteractiveElement, InteractiveElementState, Interactivity,
+    IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton, MouseMoveEvent,
+    Pixels, Point, ShapedLine, StatefulInteractiveElement, Styled, TextRun, TextStyle, TextSystem,
+    UnderlineStyle, WeakView, WhiteSpace, WindowContext,
 };
 use itertools::Itertools;
 use language::CursorShape;
@@ -29,7 +28,7 @@ use workspace::Workspace;
 use std::mem;
 use std::{fmt::Debug, ops::RangeInclusive};
 
-///The information generated during layout that is necessary for painting
+/// The information generated during layout that is necessary for painting.
 pub struct LayoutState {
     cells: Vec<LayoutCell>,
     rects: Vec<LayoutRect>,
@@ -43,7 +42,7 @@ pub struct LayoutState {
     gutter: Pixels,
 }
 
-///Helper struct for converting data between alacritty's cursor points, and displayed cursor points
+/// Helper struct for converting data between Alacritty's cursor points, and displayed cursor points.
 struct DisplayCursor {
     line: i32,
     col: usize,
@@ -82,7 +81,7 @@ impl LayoutCell {
         origin: Point<Pixels>,
         layout: &LayoutState,
         _visible_bounds: Bounds<Pixels>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let pos = {
             let point = self.point;
@@ -121,7 +120,7 @@ impl LayoutRect {
         }
     }
 
-    fn paint(&self, origin: Point<Pixels>, layout: &LayoutState, cx: &mut WindowContext) {
+    fn paint(&self, origin: Point<Pixels>, layout: &LayoutState, cx: &mut ElementContext) {
         let position = {
             let alac_point = self.point;
             point(
@@ -139,8 +138,8 @@ impl LayoutRect {
     }
 }
 
-///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?
+/// 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?
 pub struct TerminalElement {
     terminal: Model<Terminal>,
     workspace: WeakView<Workspace>,
@@ -278,8 +277,8 @@ impl TerminalElement {
         (cells, rects)
     }
 
-    // Compute the cursor position and expected block width, may return a zero width if x_for_index returns
-    // the same position for sequential indexes. Use em_width instead
+    /// Computes the cursor position and expected block width, may return a zero width if x_for_index returns
+    /// the same position for sequential indexes. Use em_width instead
     fn shape_cursor(
         cursor_point: DisplayCursor,
         size: TerminalSize,
@@ -306,7 +305,7 @@ impl TerminalElement {
         }
     }
 
-    /// Convert the Alacritty cell styles to GPUI text styles and background color
+    /// Converts the Alacritty cell styles to GPUI text styles and background color.
     fn cell_style(
         indexed: &IndexedCell,
         fg: terminal::alacritty_terminal::ansi::Color,
@@ -366,7 +365,7 @@ impl TerminalElement {
         result
     }
 
-    fn compute_layout(&self, bounds: Bounds<gpui::Pixels>, cx: &mut WindowContext) -> LayoutState {
+    fn compute_layout(&self, bounds: Bounds<gpui::Pixels>, cx: &mut ElementContext) -> LayoutState {
         let settings = ThemeSettings::get_global(cx).clone();
 
         let buffer_font_size = settings.buffer_font_size(cx);
@@ -506,8 +505,8 @@ impl TerminalElement {
             cx,
         );
 
-        //Layout cursor. Rectangle is used for IME, so we should lay it out even
-        //if we don't end up showing it.
+        // Layout cursor. Rectangle is used for IME, so we should lay it out even
+        // if we don't end up showing it.
         let cursor = if let AlacCursorShape::Hidden = cursor.shape {
             None
         } else {
@@ -550,12 +549,12 @@ impl TerminalElement {
                         theme.players().local().cursor,
                         shape,
                         text,
+                        None,
                     )
                 },
             )
         };
 
-        //Done!
         LayoutState {
             cells,
             cursor,
@@ -591,7 +590,7 @@ impl TerminalElement {
         origin: Point<Pixels>,
         mode: TermMode,
         bounds: Bounds<Pixels>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let focus = self.focus.clone();
         let terminal = self.terminal.clone();
@@ -622,9 +621,17 @@ impl TerminalElement {
                 }
 
                 if e.pressed_button.is_some() && !cx.has_active_drag() {
+                    let visibly_contains = interactive_bounds.visibly_contains(&e.position, cx);
                     terminal.update(cx, |terminal, cx| {
-                        terminal.mouse_drag(e, origin, bounds);
-                        cx.notify();
+                        if !terminal.selection_started() {
+                            if visibly_contains {
+                                terminal.mouse_drag(e, origin, bounds);
+                                cx.notify();
+                            }
+                        } else {
+                            terminal.mouse_drag(e, origin, bounds);
+                            cx.notify();
+                        }
                     })
                 }
 
@@ -715,7 +722,7 @@ impl Element for TerminalElement {
     fn request_layout(
         &mut self,
         element_state: Option<Self::State>,
-        cx: &mut WindowContext<'_>,
+        cx: &mut ElementContext<'_>,
     ) -> (LayoutId, Self::State) {
         let (layout_id, interactive_state) =
             self.interactivity
@@ -734,7 +741,7 @@ impl Element for TerminalElement {
         &mut self,
         bounds: Bounds<Pixels>,
         state: &mut Self::State,
-        cx: &mut WindowContext<'_>,
+        cx: &mut ElementContext<'_>,
     ) {
         let mut layout = self.compute_layout(bounds, cx);
 
@@ -742,7 +749,6 @@ impl Element for TerminalElement {
         let origin = bounds.origin + Point::new(layout.gutter, px(0.));
 
         let terminal_input_handler = TerminalInputHandler {
-            cx: cx.to_async(),
             terminal: self.terminal.clone(),
             cursor_bounds: layout
                 .cursor
@@ -831,37 +837,35 @@ impl IntoElement for TerminalElement {
 }
 
 struct TerminalInputHandler {
-    cx: AsyncWindowContext,
     terminal: Model<Terminal>,
     workspace: WeakView<Workspace>,
     cursor_bounds: Option<Bounds<Pixels>>,
 }
 
-impl PlatformInputHandler for TerminalInputHandler {
-    fn selected_text_range(&mut self) -> Option<std::ops::Range<usize>> {
-        self.cx
-            .update(|_, cx| {
-                if self
-                    .terminal
-                    .read(cx)
-                    .last_content
-                    .mode
-                    .contains(TermMode::ALT_SCREEN)
-                {
-                    None
-                } else {
-                    Some(0..0)
-                }
-            })
-            .ok()
-            .flatten()
+impl InputHandler for TerminalInputHandler {
+    fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option<std::ops::Range<usize>> {
+        if self
+            .terminal
+            .read(cx)
+            .last_content
+            .mode
+            .contains(TermMode::ALT_SCREEN)
+        {
+            None
+        } else {
+            Some(0..0)
+        }
     }
 
-    fn marked_text_range(&mut self) -> Option<std::ops::Range<usize>> {
+    fn marked_text_range(&mut self, _: &mut WindowContext) -> Option<std::ops::Range<usize>> {
         None
     }
 
-    fn text_for_range(&mut self, _: std::ops::Range<usize>) -> Option<String> {
+    fn text_for_range(
+        &mut self,
+        _: std::ops::Range<usize>,
+        _: &mut WindowContext,
+    ) -> Option<String> {
         None
     }
 
@@ -869,19 +873,16 @@ impl PlatformInputHandler for TerminalInputHandler {
         &mut self,
         _replacement_range: Option<std::ops::Range<usize>>,
         text: &str,
+        cx: &mut WindowContext,
     ) {
-        self.cx
-            .update(|_, cx| {
-                self.terminal.update(cx, |terminal, _| {
-                    terminal.input(text.into());
-                });
+        self.terminal.update(cx, |terminal, _| {
+            terminal.input(text.into());
+        });
 
-                self.workspace
-                    .update(cx, |this, cx| {
-                        let telemetry = this.project().read(cx).client().telemetry().clone();
-                        telemetry.log_edit_event("terminal");
-                    })
-                    .ok();
+        self.workspace
+            .update(cx, |this, cx| {
+                let telemetry = this.project().read(cx).client().telemetry().clone();
+                telemetry.log_edit_event("terminal");
             })
             .ok();
     }
@@ -891,12 +892,17 @@ impl PlatformInputHandler for TerminalInputHandler {
         _range_utf16: Option<std::ops::Range<usize>>,
         _new_text: &str,
         _new_selected_range: Option<std::ops::Range<usize>>,
+        _: &mut WindowContext,
     ) {
     }
 
-    fn unmark_text(&mut self) {}
+    fn unmark_text(&mut self, _: &mut WindowContext) {}
 
-    fn bounds_for_range(&mut self, _range_utf16: std::ops::Range<usize>) -> Option<Bounds<Pixels>> {
+    fn bounds_for_range(
+        &mut self,
+        _range_utf16: std::ops::Range<usize>,
+        _: &mut WindowContext,
+    ) -> Option<Bounds<Pixels>> {
         self.cursor_bounds
     }
 }
@@ -990,11 +996,11 @@ fn to_highlighted_range_lines(
     Some((start_y, highlighted_range_lines))
 }
 
-///Converts a 2, 8, or 24 bit color ANSI color to the GPUI equivalent
+/// Converts a 2, 8, or 24 bit color ANSI color to the GPUI equivalent.
 fn convert_color(fg: &terminal::alacritty_terminal::ansi::Color, theme: &Theme) -> Hsla {
     let colors = theme.colors();
     match fg {
-        //Named and theme defined colors
+        // Named and theme defined colors
         terminal::alacritty_terminal::ansi::Color::Named(n) => match n {
             NamedColor::Black => colors.terminal_ansi_black,
             NamedColor::Red => colors.terminal_ansi_red,
@@ -1015,24 +1021,22 @@ fn convert_color(fg: &terminal::alacritty_terminal::ansi::Color, theme: &Theme)
             NamedColor::Foreground => colors.text,
             NamedColor::Background => colors.background,
             NamedColor::Cursor => theme.players().local().cursor,
-
-            // todo!(more colors)
-            NamedColor::DimBlack => red(),
-            NamedColor::DimRed => red(),
-            NamedColor::DimGreen => red(),
-            NamedColor::DimYellow => red(),
-            NamedColor::DimBlue => red(),
-            NamedColor::DimMagenta => red(),
-            NamedColor::DimCyan => red(),
-            NamedColor::DimWhite => red(),
-            NamedColor::BrightForeground => red(),
-            NamedColor::DimForeground => red(),
+            NamedColor::DimBlack => colors.terminal_ansi_dim_black,
+            NamedColor::DimRed => colors.terminal_ansi_dim_red,
+            NamedColor::DimGreen => colors.terminal_ansi_dim_green,
+            NamedColor::DimYellow => colors.terminal_ansi_dim_yellow,
+            NamedColor::DimBlue => colors.terminal_ansi_dim_blue,
+            NamedColor::DimMagenta => colors.terminal_ansi_dim_magenta,
+            NamedColor::DimCyan => colors.terminal_ansi_dim_cyan,
+            NamedColor::DimWhite => colors.terminal_ansi_dim_white,
+            NamedColor::BrightForeground => colors.terminal_bright_foreground,
+            NamedColor::DimForeground => colors.terminal_dim_foreground,
         },
-        //'True' colors
+        // 'True' colors
         terminal::alacritty_terminal::ansi::Color::Spec(rgb) => {
             terminal::rgba_color(rgb.r, rgb.g, rgb.b)
         }
-        //8 bit, indexed colors
+        // 8 bit, indexed colors
         terminal::alacritty_terminal::ansi::Color::Indexed(i) => {
             terminal::get_color_at_index(*i as usize, theme)
         }

crates/terminal_view/src/terminal_view.rs ๐Ÿ”—

@@ -772,7 +772,7 @@ impl Item for TerminalView {
                 .log_err()
                 .flatten()
                 .or_else(|| {
-                    cx.update(|_, cx| {
+                    cx.update(|cx| {
                         let strategy = TerminalSettings::get_global(cx).working_directory.clone();
                         workspace
                             .upgrade()
@@ -832,7 +832,7 @@ impl SearchableItem for TerminalView {
         self.terminal().update(cx, |term, _| term.matches = matches)
     }
 
-    /// Return the selection content to pre-load into this search
+    /// Returns the selection content to pre-load into this search
     fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
         self.terminal()
             .read(cx)

crates/theme/src/default_colors.rs ๐Ÿ”—

@@ -77,6 +77,9 @@ impl ThemeColors {
             editor_document_highlight_read_background: neutral().light_alpha().step_3(),
             editor_document_highlight_write_background: neutral().light_alpha().step_4(),
             terminal_background: neutral().light().step_1(),
+            terminal_foreground: black().light().step_12(),
+            terminal_bright_foreground: black().light().step_11(),
+            terminal_dim_foreground: black().light().step_10(),
             terminal_ansi_bright_black: black().light().step_11(),
             terminal_ansi_bright_red: red().light().step_10(),
             terminal_ansi_bright_green: green().light().step_10(),
@@ -93,6 +96,14 @@ impl ThemeColors {
             terminal_ansi_magenta: violet().light().step_11(),
             terminal_ansi_cyan: cyan().light().step_11(),
             terminal_ansi_white: neutral().light().step_12(),
+            terminal_ansi_dim_black: black().light().step_11(),
+            terminal_ansi_dim_red: red().light().step_10(),
+            terminal_ansi_dim_green: green().light().step_10(),
+            terminal_ansi_dim_yellow: yellow().light().step_10(),
+            terminal_ansi_dim_blue: blue().light().step_10(),
+            terminal_ansi_dim_magenta: violet().light().step_10(),
+            terminal_ansi_dim_cyan: cyan().light().step_10(),
+            terminal_ansi_dim_white: neutral().light().step_11(),
             link_text_hover: orange().light().step_10(),
         }
     }
@@ -160,22 +171,33 @@ impl ThemeColors {
             editor_document_highlight_read_background: neutral().dark_alpha().step_4(),
             editor_document_highlight_write_background: neutral().dark_alpha().step_4(),
             terminal_background: neutral().dark().step_1(),
-            terminal_ansi_bright_black: black().dark().step_11(),
-            terminal_ansi_bright_red: red().dark().step_10(),
-            terminal_ansi_bright_green: green().dark().step_10(),
-            terminal_ansi_bright_yellow: yellow().dark().step_10(),
-            terminal_ansi_bright_blue: blue().dark().step_10(),
-            terminal_ansi_bright_magenta: violet().dark().step_10(),
-            terminal_ansi_bright_cyan: cyan().dark().step_10(),
-            terminal_ansi_bright_white: neutral().dark().step_11(),
+            terminal_foreground: white().dark().step_12(),
+            terminal_bright_foreground: white().dark().step_11(),
+            terminal_dim_foreground: white().dark().step_10(),
             terminal_ansi_black: black().dark().step_12(),
+            terminal_ansi_bright_black: black().dark().step_11(),
+            terminal_ansi_dim_black: black().dark().step_10(),
             terminal_ansi_red: red().dark().step_11(),
+            terminal_ansi_bright_red: red().dark().step_10(),
+            terminal_ansi_dim_red: red().dark().step_9(),
             terminal_ansi_green: green().dark().step_11(),
+            terminal_ansi_bright_green: green().dark().step_10(),
+            terminal_ansi_dim_green: green().dark().step_9(),
             terminal_ansi_yellow: yellow().dark().step_11(),
+            terminal_ansi_bright_yellow: yellow().dark().step_10(),
+            terminal_ansi_dim_yellow: yellow().dark().step_9(),
             terminal_ansi_blue: blue().dark().step_11(),
+            terminal_ansi_bright_blue: blue().dark().step_10(),
+            terminal_ansi_dim_blue: blue().dark().step_9(),
             terminal_ansi_magenta: violet().dark().step_11(),
+            terminal_ansi_bright_magenta: violet().dark().step_10(),
+            terminal_ansi_dim_magenta: violet().dark().step_9(),
             terminal_ansi_cyan: cyan().dark().step_11(),
+            terminal_ansi_bright_cyan: cyan().dark().step_10(),
+            terminal_ansi_dim_cyan: cyan().dark().step_9(),
             terminal_ansi_white: neutral().dark().step_12(),
+            terminal_ansi_bright_white: neutral().dark().step_11(),
+            terminal_ansi_dim_white: neutral().dark().step_10(),
             link_text_hover: orange().dark().step_10(),
         }
     }

crates/theme/src/one_themes.rs ๐Ÿ”—

@@ -101,6 +101,9 @@ pub(crate) fn one_dark() -> Theme {
 
                 terminal_background: bg,
                 // todo!("Use one colors for terminal")
+                terminal_foreground: crate::white().dark().step_12(),
+                terminal_bright_foreground: crate::white().dark().step_11(),
+                terminal_dim_foreground: crate::white().dark().step_10(),
                 terminal_ansi_black: crate::black().dark().step_12(),
                 terminal_ansi_red: crate::red().dark().step_11(),
                 terminal_ansi_green: crate::green().dark().step_11(),
@@ -117,6 +120,14 @@ pub(crate) fn one_dark() -> Theme {
                 terminal_ansi_bright_magenta: crate::violet().dark().step_10(),
                 terminal_ansi_bright_cyan: crate::cyan().dark().step_10(),
                 terminal_ansi_bright_white: crate::neutral().dark().step_11(),
+                terminal_ansi_dim_black: crate::black().dark().step_10(),
+                terminal_ansi_dim_red: crate::red().dark().step_9(),
+                terminal_ansi_dim_green: crate::green().dark().step_9(),
+                terminal_ansi_dim_yellow: crate::yellow().dark().step_9(),
+                terminal_ansi_dim_blue: crate::blue().dark().step_9(),
+                terminal_ansi_dim_magenta: crate::violet().dark().step_9(),
+                terminal_ansi_dim_cyan: crate::cyan().dark().step_9(),
+                terminal_ansi_dim_white: crate::neutral().dark().step_10(),
                 panel_background: bg,
                 panel_focused_border: blue,
                 pane_focused_border: blue,

crates/theme/src/styles/colors.rs ๐Ÿ”—

@@ -169,40 +169,63 @@ pub struct ThemeColors {
     // ===
     // Terminal
     // ===
-    /// Terminal Background Color
+    /// Terminal background color.
     pub terminal_background: Hsla,
-    /// Bright Black Color for ANSI Terminal
-    pub terminal_ansi_bright_black: Hsla,
-    /// Bright Red Color for ANSI Terminal
-    pub terminal_ansi_bright_red: Hsla,
-    /// Bright Green Color for ANSI Terminal
-    pub terminal_ansi_bright_green: Hsla,
-    /// Bright Yellow Color for ANSI Terminal
-    pub terminal_ansi_bright_yellow: Hsla,
-    /// Bright Blue Color for ANSI Terminal
-    pub terminal_ansi_bright_blue: Hsla,
-    /// Bright Magenta Color for ANSI Terminal
-    pub terminal_ansi_bright_magenta: Hsla,
-    /// Bright Cyan Color for ANSI Terminal
-    pub terminal_ansi_bright_cyan: Hsla,
-    /// Bright White Color for ANSI Terminal
-    pub terminal_ansi_bright_white: Hsla,
-    /// Black Color for ANSI Terminal
+    /// Terminal foreground color.
+    pub terminal_foreground: Hsla,
+    /// Bright terminal foreground color.
+    pub terminal_bright_foreground: Hsla,
+    /// Dim terminal foreground color.
+    pub terminal_dim_foreground: Hsla,
+
+    /// Black ANSI terminal color.
     pub terminal_ansi_black: Hsla,
-    /// Red Color for ANSI Terminal
+    /// Bright black ANSI terminal color.
+    pub terminal_ansi_bright_black: Hsla,
+    /// Dim black ANSI terminal color.
+    pub terminal_ansi_dim_black: Hsla,
+    /// Red ANSI terminal color.
     pub terminal_ansi_red: Hsla,
-    /// Green Color for ANSI Terminal
+    /// Bright red ANSI terminal color.
+    pub terminal_ansi_bright_red: Hsla,
+    /// Dim red ANSI terminal color.
+    pub terminal_ansi_dim_red: Hsla,
+    /// Green ANSI terminal color.
     pub terminal_ansi_green: Hsla,
-    /// Yellow Color for ANSI Terminal
+    /// Bright green ANSI terminal color.
+    pub terminal_ansi_bright_green: Hsla,
+    /// Dim green ANSI terminal color.
+    pub terminal_ansi_dim_green: Hsla,
+    /// Yellow ANSI terminal color.
     pub terminal_ansi_yellow: Hsla,
-    /// Blue Color for ANSI Terminal
+    /// Bright yellow ANSI terminal color.
+    pub terminal_ansi_bright_yellow: Hsla,
+    /// Dim yellow ANSI terminal color.
+    pub terminal_ansi_dim_yellow: Hsla,
+    /// Blue ANSI terminal color.
     pub terminal_ansi_blue: Hsla,
-    /// Magenta Color for ANSI Terminal
+    /// Bright blue ANSI terminal color.
+    pub terminal_ansi_bright_blue: Hsla,
+    /// Dim blue ANSI terminal color.
+    pub terminal_ansi_dim_blue: Hsla,
+    /// Magenta ANSI terminal color.
     pub terminal_ansi_magenta: Hsla,
-    /// Cyan Color for ANSI Terminal
+    /// Bright magenta ANSI terminal color.
+    pub terminal_ansi_bright_magenta: Hsla,
+    /// Dim magenta ANSI terminal color.
+    pub terminal_ansi_dim_magenta: Hsla,
+    /// Cyan ANSI terminal color.
     pub terminal_ansi_cyan: Hsla,
-    /// White Color for ANSI Terminal
+    /// Bright cyan ANSI terminal color.
+    pub terminal_ansi_bright_cyan: Hsla,
+    /// Dim cyan ANSI terminal color.
+    pub terminal_ansi_dim_cyan: Hsla,
+    /// White ANSI terminal color.
     pub terminal_ansi_white: Hsla,
+    /// Bright white ANSI terminal color.
+    pub terminal_ansi_bright_white: Hsla,
+    /// Dim white ANSI terminal color.
+    pub terminal_ansi_dim_white: Hsla,
 
     // ===
     // UI/Rich Text

crates/theme/src/themes/andromeda.rs ๐Ÿ”—

@@ -75,22 +75,33 @@ pub fn andromeda() -> UserThemeFamily {
                     editor_document_highlight_read_background: Some(rgba(0x11a7931a).into()),
                     editor_document_highlight_write_background: Some(rgba(0x64646d66).into()),
                     terminal_background: Some(rgba(0x1e2025ff).into()),
-                    terminal_ansi_bright_black: Some(rgba(0x40434cff).into()),
-                    terminal_ansi_bright_red: Some(rgba(0x8e103aff).into()),
-                    terminal_ansi_bright_green: Some(rgba(0x457c38ff).into()),
-                    terminal_ansi_bright_yellow: Some(rgba(0x958435ff).into()),
-                    terminal_ansi_bright_blue: Some(rgba(0x1b5148ff).into()),
-                    terminal_ansi_bright_magenta: Some(rgba(0x682781ff).into()),
-                    terminal_ansi_bright_cyan: Some(rgba(0x018169ff).into()),
-                    terminal_ansi_bright_white: Some(rgba(0xf7f7f8ff).into()),
+                    terminal_foreground: Some(rgba(0xf7f7f8ff).into()),
+                    terminal_bright_foreground: Some(rgba(0xf7f7f8ff).into()),
+                    terminal_dim_foreground: Some(rgba(0x1e2025ff).into()),
                     terminal_ansi_black: Some(rgba(0x1e2025ff).into()),
+                    terminal_ansi_bright_black: Some(rgba(0x40434cff).into()),
+                    terminal_ansi_dim_black: Some(rgba(0xf7f7f8ff).into()),
                     terminal_ansi_red: Some(rgba(0xf82872ff).into()),
+                    terminal_ansi_bright_red: Some(rgba(0x8e103aff).into()),
+                    terminal_ansi_dim_red: Some(rgba(0xffa3b6ff).into()),
                     terminal_ansi_green: Some(rgba(0x96df72ff).into()),
+                    terminal_ansi_bright_green: Some(rgba(0x457c38ff).into()),
+                    terminal_ansi_dim_green: Some(rgba(0xcef0b9ff).into()),
                     terminal_ansi_yellow: Some(rgba(0xfee56dff).into()),
+                    terminal_ansi_bright_yellow: Some(rgba(0x958435ff).into()),
+                    terminal_ansi_dim_yellow: Some(rgba(0xfff2b8ff).into()),
                     terminal_ansi_blue: Some(rgba(0x11a793ff).into()),
+                    terminal_ansi_bright_blue: Some(rgba(0x1b5148ff).into()),
+                    terminal_ansi_dim_blue: Some(rgba(0x9cd4c8ff).into()),
                     terminal_ansi_magenta: Some(rgba(0xc74decff).into()),
+                    terminal_ansi_bright_magenta: Some(rgba(0x682781ff).into()),
+                    terminal_ansi_dim_magenta: Some(rgba(0xe8abf7ff).into()),
                     terminal_ansi_cyan: Some(rgba(0x09e7c6ff).into()),
+                    terminal_ansi_bright_cyan: Some(rgba(0x018169ff).into()),
+                    terminal_ansi_dim_cyan: Some(rgba(0xaaf5e2ff).into()),
                     terminal_ansi_white: Some(rgba(0xf7f7f8ff).into()),
+                    terminal_ansi_bright_white: Some(rgba(0xf7f7f8ff).into()),
+                    terminal_ansi_dim_white: Some(rgba(0x88868dff).into()),
                     link_text_hover: Some(rgba(0x11a793ff).into()),
                     ..Default::default()
                 },

crates/theme/src/themes/atelier.rs ๐Ÿ”—

@@ -76,22 +76,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x576dda1a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x726c7a66).into()),
                         terminal_background: Some(rgba(0x19171cff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x635d6bff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x5c283cff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x1f4747ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x4e3821ff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x2d376fff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x60255bff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x26445eff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xefecf4ff).into()),
+                        terminal_foreground: Some(rgba(0xefecf4ff).into()),
+                        terminal_bright_foreground: Some(rgba(0xefecf4ff).into()),
+                        terminal_dim_foreground: Some(rgba(0x19171cff).into()),
                         terminal_ansi_black: Some(rgba(0x19171cff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x635d6bff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xefecf4ff).into()),
                         terminal_ansi_red: Some(rgba(0xbe4678ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x5c283cff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xe3a4b9ff).into()),
                         terminal_ansi_green: Some(rgba(0x2c9292ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x1f4747ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0x9dc8c8ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xa06e3bff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x4e3821ff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xd4b499ff).into()),
                         terminal_ansi_blue: Some(rgba(0x576ddaff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x2d376fff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0xb3b3eeff).into()),
                         terminal_ansi_magenta: Some(rgba(0xbf41bfff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x60255bff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xe3a4dfff).into()),
                         terminal_ansi_cyan: Some(rgba(0x3a8bc6ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x26445eff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0xa6c4e3ff).into()),
                         terminal_ansi_white: Some(rgba(0xefecf4ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xefecf4ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x807b89ff).into()),
                         link_text_hover: Some(rgba(0x576ddaff).into()),
                         ..Default::default()
                     },
@@ -541,22 +552,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x586dda1a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x726c7a66).into()),
                         terminal_background: Some(rgba(0xefecf4ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x807b89ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0xe3a4b9ff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x9dc8c8ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0xd4b499ff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0xb3b3eeff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0xe3a4dfff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0xa6c4e3ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0x19171cff).into()),
+                        terminal_foreground: Some(rgba(0x19171cff).into()),
+                        terminal_bright_foreground: Some(rgba(0x19171cff).into()),
+                        terminal_dim_foreground: Some(rgba(0xefecf4ff).into()),
                         terminal_ansi_black: Some(rgba(0xefecf4ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x807b89ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0x19171cff).into()),
                         terminal_ansi_red: Some(rgba(0xbe4778ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0xe3a4b9ff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0x5c283cff).into()),
                         terminal_ansi_green: Some(rgba(0x2c9292ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x9dc8c8ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0x1f4747ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xa06e3cff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0xd4b499ff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0x4e3821ff).into()),
                         terminal_ansi_blue: Some(rgba(0x586ddaff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0xb3b3eeff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0x2d376fff).into()),
                         terminal_ansi_magenta: Some(rgba(0xbf41bfff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0xe3a4dfff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0x60255bff).into()),
                         terminal_ansi_cyan: Some(rgba(0x3b8bc6ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0xa6c4e3ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x26445eff).into()),
                         terminal_ansi_white: Some(rgba(0x19171cff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0x19171cff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x635d6bff).into()),
                         link_text_hover: Some(rgba(0x586ddaff).into()),
                         ..Default::default()
                     },
@@ -1006,22 +1028,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x6684e01a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x8b887466).into()),
                         terminal_background: Some(rgba(0x20201dff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x7a7766ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x781c1fff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x335322ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x574815ff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x334173ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x721d2bff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x1e5341ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xfefbecff).into()),
+                        terminal_foreground: Some(rgba(0xfefbecff).into()),
+                        terminal_bright_foreground: Some(rgba(0xfefbecff).into()),
+                        terminal_dim_foreground: Some(rgba(0x20201dff).into()),
                         terminal_ansi_black: Some(rgba(0x20201dff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x7a7766ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xfefbecff).into()),
                         terminal_ansi_red: Some(rgba(0xd73837ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x781c1fff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xf7a195ff).into()),
                         terminal_ansi_green: Some(rgba(0x60ac3aff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x335322ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0xb3d69cff).into()),
                         terminal_ansi_yellow: Some(rgba(0xae9515ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x574815ff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xdcc98eff).into()),
                         terminal_ansi_blue: Some(rgba(0x6684e0ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x334173ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0xb8c0f1ff).into()),
                         terminal_ansi_magenta: Some(rgba(0xd43652ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x721d2bff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xf3a0a4ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x21ad83ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x1e5341ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x9ed7c0ff).into()),
                         terminal_ansi_white: Some(rgba(0xfefbecff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xfefbecff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x9b9782ff).into()),
                         link_text_hover: Some(rgba(0x6684e0ff).into()),
                         ..Default::default()
                     },
@@ -1471,22 +1504,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x6784e01a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x8b887466).into()),
                         terminal_background: Some(rgba(0xfefbecff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x9b9782ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0xf7a195ff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0xb3d69cff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0xdcc98eff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0xb8c0f1ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0xf3a0a4ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x9ed7c0ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0x20201dff).into()),
+                        terminal_foreground: Some(rgba(0x20201dff).into()),
+                        terminal_bright_foreground: Some(rgba(0x20201dff).into()),
+                        terminal_dim_foreground: Some(rgba(0xfefbecff).into()),
                         terminal_ansi_black: Some(rgba(0xfefbecff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x9b9782ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0x20201dff).into()),
                         terminal_ansi_red: Some(rgba(0xd73838ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0xf7a195ff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0x781c1fff).into()),
                         terminal_ansi_green: Some(rgba(0x61ac3aff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0xb3d69cff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0x335322ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xae9515ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0xdcc98eff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0x574815ff).into()),
                         terminal_ansi_blue: Some(rgba(0x6784e0ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0xb8c0f1ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0x334173ff).into()),
                         terminal_ansi_magenta: Some(rgba(0xd43753ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0xf3a0a4ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0x721d2bff).into()),
                         terminal_ansi_cyan: Some(rgba(0x22ad83ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x9ed7c0ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x1e5341ff).into()),
                         terminal_ansi_white: Some(rgba(0x20201dff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0x20201dff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x7a7766ff).into()),
                         link_text_hover: Some(rgba(0x6784e0ff).into()),
                         ..Default::default()
                     },
@@ -1936,22 +1980,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x37a1661a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x7a786766).into()),
                         terminal_background: Some(rgba(0x22221bff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x6a6958ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x5c331fff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x3f491aff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x514a14ff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x234e34ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x4c373eff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x314c27ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xf4f3ecff).into()),
+                        terminal_foreground: Some(rgba(0xf4f3ecff).into()),
+                        terminal_bright_foreground: Some(rgba(0xf4f3ecff).into()),
+                        terminal_dim_foreground: Some(rgba(0x22221bff).into()),
                         terminal_ansi_black: Some(rgba(0x22221bff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x6a6958ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xf4f3ecff).into()),
                         terminal_ansi_red: Some(rgba(0xba6237ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x5c331fff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xe4af96ff).into()),
                         terminal_ansi_green: Some(rgba(0x7d9727ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x3f491aff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0xc0ca93ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xa59810ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x514a14ff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xd7ca8dff).into()),
                         terminal_ansi_blue: Some(rgba(0x37a166ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x234e34ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0xa0d1b0ff).into()),
                         terminal_ansi_magenta: Some(rgba(0x9d6c7cff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x4c373eff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xcfb4bcff).into()),
                         terminal_ansi_cyan: Some(rgba(0x5b9d48ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x314c27ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0xaecea1ff).into()),
                         terminal_ansi_white: Some(rgba(0xf4f3ecff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xf4f3ecff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x898775ff).into()),
                         link_text_hover: Some(rgba(0x37a166ff).into()),
                         ..Default::default()
                     },
@@ -2401,22 +2456,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x38a1661a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x7a786766).into()),
                         terminal_background: Some(rgba(0xf4f3ecff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x898775ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0xe4af96ff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0xc0ca93ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0xd7ca8dff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0xa0d1b0ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0xcfb4bcff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0xaecea1ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0x22221bff).into()),
+                        terminal_foreground: Some(rgba(0x22221bff).into()),
+                        terminal_bright_foreground: Some(rgba(0x22221bff).into()),
+                        terminal_dim_foreground: Some(rgba(0xf4f3ecff).into()),
                         terminal_ansi_black: Some(rgba(0xf4f3ecff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x898775ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0x22221bff).into()),
                         terminal_ansi_red: Some(rgba(0xba6337ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0xe4af96ff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0x5c331fff).into()),
                         terminal_ansi_green: Some(rgba(0x7d9728ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0xc0ca93ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0x3f491aff).into()),
                         terminal_ansi_yellow: Some(rgba(0xa59810ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0xd7ca8dff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0x514a14ff).into()),
                         terminal_ansi_blue: Some(rgba(0x38a166ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0xa0d1b0ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0x234e34ff).into()),
                         terminal_ansi_magenta: Some(rgba(0x9d6c7cff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0xcfb4bcff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0x4c373eff).into()),
                         terminal_ansi_cyan: Some(rgba(0x5c9d49ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0xaecea1ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x314c27ff).into()),
                         terminal_ansi_white: Some(rgba(0x22221bff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0x22221bff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x6a6958ff).into()),
                         link_text_hover: Some(rgba(0x38a166ff).into()),
                         ..Default::default()
                     },
@@ -2866,22 +2932,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x417ee61a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x89817e66).into()),
                         terminal_background: Some(rgba(0x1b1918ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x746c69ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x8c1223ff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x3e491aff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x674115ff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x213f78ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x662186ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x264958ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xf1efeeff).into()),
+                        terminal_foreground: Some(rgba(0xf1efeeff).into()),
+                        terminal_bright_foreground: Some(rgba(0xf1efeeff).into()),
+                        terminal_dim_foreground: Some(rgba(0x1b1918ff).into()),
                         terminal_ansi_black: Some(rgba(0x1b1918ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x746c69ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xf1efeeff).into()),
                         terminal_ansi_red: Some(rgba(0xf22d40ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x8c1223ff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xffa29aff).into()),
                         terminal_ansi_green: Some(rgba(0x7b9727ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x3e491aff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0xbfca93ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xc38419ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x674115ff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xe9c08eff).into()),
                         terminal_ansi_blue: Some(rgba(0x417ee6ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x213f78ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0xaebcf4ff).into()),
                         terminal_ansi_magenta: Some(rgba(0xc340f2ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x662186ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xe7a6fbff).into()),
                         terminal_ansi_cyan: Some(rgba(0x3e97b8ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x264958ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0xa6cadbff).into()),
                         terminal_ansi_white: Some(rgba(0xf1efeeff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xf1efeeff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x9e9693ff).into()),
                         link_text_hover: Some(rgba(0x417ee6ff).into()),
                         ..Default::default()
                     },
@@ -3331,22 +3408,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x417ee61a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x89817e66).into()),
                         terminal_background: Some(rgba(0xf1efeeff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x9e9693ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0xffa29aff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0xbfca93ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0xe9c08eff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0xaebcf4ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0xe7a6fbff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0xa6cadbff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0x1b1918ff).into()),
+                        terminal_foreground: Some(rgba(0x1b1918ff).into()),
+                        terminal_bright_foreground: Some(rgba(0x1b1918ff).into()),
+                        terminal_dim_foreground: Some(rgba(0xf1efeeff).into()),
                         terminal_ansi_black: Some(rgba(0xf1efeeff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x9e9693ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0x1b1918ff).into()),
                         terminal_ansi_red: Some(rgba(0xf22e41ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0xffa29aff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0x8c1223ff).into()),
                         terminal_ansi_green: Some(rgba(0x7b9728ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0xbfca93ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0x3e491aff).into()),
                         terminal_ansi_yellow: Some(rgba(0xc3841aff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0xe9c08eff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0x674115ff).into()),
                         terminal_ansi_blue: Some(rgba(0x417ee6ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0xaebcf4ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0x213f78ff).into()),
                         terminal_ansi_magenta: Some(rgba(0xc340f2ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0xe7a6fbff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0x662186ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x3f97b8ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0xa6cadbff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x264958ff).into()),
                         terminal_ansi_white: Some(rgba(0x1b1918ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0x1b1918ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x746c69ff).into()),
                         link_text_hover: Some(rgba(0x417ee6ff).into()),
                         ..Default::default()
                     },
@@ -3796,22 +3884,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x526aeb1a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x8b7c8b66).into()),
                         terminal_background: Some(rgba(0x1b181bff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x756775ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x6d221aff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x474422ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x5e441fff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x26367eff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x6c1e67ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x1a4848ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xf7f3f7ff).into()),
+                        terminal_foreground: Some(rgba(0xf7f3f7ff).into()),
+                        terminal_bright_foreground: Some(rgba(0xf7f3f7ff).into()),
+                        terminal_dim_foreground: Some(rgba(0x1b181bff).into()),
                         terminal_ansi_black: Some(rgba(0x1b181bff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x756775ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xf7f3f7ff).into()),
                         terminal_ansi_red: Some(rgba(0xca402cff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x6d221aff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xf0a28fff).into()),
                         terminal_ansi_green: Some(rgba(0x918b3bff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x474422ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0xcac49aff).into()),
                         terminal_ansi_yellow: Some(rgba(0xbb8a36ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x5e441fff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xe2c398ff).into()),
                         terminal_ansi_blue: Some(rgba(0x526aebff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x26367eff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0xb4b2f7ff).into()),
                         terminal_ansi_magenta: Some(rgba(0xcc34ccff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x6c1e67ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xeba2e6ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x189393ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x1a4848ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x9ac9c8ff).into()),
                         terminal_ansi_white: Some(rgba(0xf7f3f7ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xf7f3f7ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0xa091a0ff).into()),
                         link_text_hover: Some(rgba(0x526aebff).into()),
                         ..Default::default()
                     },
@@ -4261,22 +4360,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x526aeb1a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x8b7c8b66).into()),
                         terminal_background: Some(rgba(0xf7f3f7ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0xa091a0ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0xf0a28fff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0xcac49aff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0xe2c398ff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0xb4b2f7ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0xeba2e6ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x9ac9c8ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0x1b181bff).into()),
+                        terminal_foreground: Some(rgba(0x1b181bff).into()),
+                        terminal_bright_foreground: Some(rgba(0x1b181bff).into()),
+                        terminal_dim_foreground: Some(rgba(0xf7f3f7ff).into()),
                         terminal_ansi_black: Some(rgba(0xf7f3f7ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0xa091a0ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0x1b181bff).into()),
                         terminal_ansi_red: Some(rgba(0xca412cff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0xf0a28fff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0x6d221aff).into()),
                         terminal_ansi_green: Some(rgba(0x918b3cff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0xcac49aff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0x474422ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xbb8a36ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0xe2c398ff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0x5e441fff).into()),
                         terminal_ansi_blue: Some(rgba(0x526aebff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0xb4b2f7ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0x26367eff).into()),
                         terminal_ansi_magenta: Some(rgba(0xcc35ccff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0xeba2e6ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0x6c1e67ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x199393ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x9ac9c8ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x1a4848ff).into()),
                         terminal_ansi_white: Some(rgba(0x1b181bff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0x1b181bff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x756775ff).into()),
                         link_text_hover: Some(rgba(0x526aebff).into()),
                         ..Default::default()
                     },
@@ -4726,22 +4836,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x277fad1a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x66889a66).into()),
                         terminal_background: Some(rgba(0x161b1dff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x587989ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x6f1c3aff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x2e4522ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x454413ff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x1e3f53ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x5c1e6bff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x1f4638ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xebf8ffff).into()),
+                        terminal_foreground: Some(rgba(0xebf8ffff).into()),
+                        terminal_bright_foreground: Some(rgba(0xebf8ffff).into()),
+                        terminal_dim_foreground: Some(rgba(0x161b1dff).into()),
                         terminal_ansi_black: Some(rgba(0x161b1dff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x587989ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xebf8ffff).into()),
                         terminal_ansi_red: Some(rgba(0xd22e72ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x6f1c3aff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xf09fb6ff).into()),
                         terminal_ansi_green: Some(rgba(0x568c3bff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x2e4522ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0xabc59aff).into()),
                         terminal_ansi_yellow: Some(rgba(0x8a8a11ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x454413ff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xc8c38bff).into()),
                         terminal_ansi_blue: Some(rgba(0x277fadff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x1e3f53ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0x9ebdd6ff).into()),
                         terminal_ansi_magenta: Some(rgba(0xb72ed2ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x5c1e6bff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xe09fe9ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x2e8f6fff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x1f4638ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x9bc7b5ff).into()),
                         terminal_ansi_white: Some(rgba(0xebf8ffff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xebf8ffff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x7397aaff).into()),
                         link_text_hover: Some(rgba(0x277fadff).into()),
                         ..Default::default()
                     },
@@ -5191,22 +5312,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x277fad1a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x66889a66).into()),
                         terminal_background: Some(rgba(0xebf8ffff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x7397aaff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0xf09fb6ff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0xabc59aff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0xc8c38bff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x9ebdd6ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0xe09fe9ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x9bc7b5ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0x161b1dff).into()),
+                        terminal_foreground: Some(rgba(0x161b1dff).into()),
+                        terminal_bright_foreground: Some(rgba(0x161b1dff).into()),
+                        terminal_dim_foreground: Some(rgba(0xebf8ffff).into()),
                         terminal_ansi_black: Some(rgba(0xebf8ffff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x7397aaff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0x161b1dff).into()),
                         terminal_ansi_red: Some(rgba(0xd22f72ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0xf09fb6ff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0x6f1c3aff).into()),
                         terminal_ansi_green: Some(rgba(0x578c3cff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0xabc59aff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0x2e4522ff).into()),
                         terminal_ansi_yellow: Some(rgba(0x8a8a11ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0xc8c38bff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0x454413ff).into()),
                         terminal_ansi_blue: Some(rgba(0x277fadff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x9ebdd6ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0x1e3f53ff).into()),
                         terminal_ansi_magenta: Some(rgba(0xb72fd2ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0xe09fe9ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0x5c1e6bff).into()),
                         terminal_ansi_cyan: Some(rgba(0x2f8f6fff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x9bc7b5ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x1f4638ff).into()),
                         terminal_ansi_white: Some(rgba(0x161b1dff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0x161b1dff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x587989ff).into()),
                         link_text_hover: Some(rgba(0x277fadff).into()),
                         ..Default::default()
                     },
@@ -5656,22 +5788,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x7272ca1a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x726a6a66).into()),
                         terminal_background: Some(rgba(0x1b1818ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x635b5bff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x692727ff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x2a4444ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x4e3821ff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x3b3960ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x5b2c42ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x2e4257ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xf4ececff).into()),
+                        terminal_foreground: Some(rgba(0xf4ececff).into()),
+                        terminal_bright_foreground: Some(rgba(0xf4ececff).into()),
+                        terminal_dim_foreground: Some(rgba(0x1b1818ff).into()),
                         terminal_ansi_black: Some(rgba(0x1b1818ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x635b5bff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xf4ececff).into()),
                         terminal_ansi_red: Some(rgba(0xca4949ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x692727ff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xeda69fff).into()),
                         terminal_ansi_green: Some(rgba(0x4b8b8bff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x2a4444ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0xa6c4c4ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xa06e3bff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x4e3821ff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xd4b499ff).into()),
                         terminal_ansi_blue: Some(rgba(0x7272caff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x3b3960ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0xbbb6e5ff).into()),
                         terminal_ansi_magenta: Some(rgba(0xbd5187ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x5b2c42ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xe2a9c2ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x5485b6ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x2e4257ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0xacc0daff).into()),
                         terminal_ansi_white: Some(rgba(0xf4ececff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xf4ececff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x807979ff).into()),
                         link_text_hover: Some(rgba(0x7272caff).into()),
                         ..Default::default()
                     },
@@ -6121,22 +6264,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x7372ca1a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x726a6a66).into()),
                         terminal_background: Some(rgba(0xf4ececff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x807979ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0xeda69fff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0xa6c4c4ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0xd4b499ff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0xbbb6e5ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0xe2a9c2ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0xacc0daff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0x1b1818ff).into()),
+                        terminal_foreground: Some(rgba(0x1b1818ff).into()),
+                        terminal_bright_foreground: Some(rgba(0x1b1818ff).into()),
+                        terminal_dim_foreground: Some(rgba(0xf4ececff).into()),
                         terminal_ansi_black: Some(rgba(0xf4ececff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x807979ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0x1b1818ff).into()),
                         terminal_ansi_red: Some(rgba(0xca4a4aff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0xeda69fff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0x692727ff).into()),
                         terminal_ansi_green: Some(rgba(0x4c8b8bff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0xa6c4c4ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0x2a4444ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xa06e3cff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0xd4b499ff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0x4e3821ff).into()),
                         terminal_ansi_blue: Some(rgba(0x7372caff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0xbbb6e5ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0x3b3960ff).into()),
                         terminal_ansi_magenta: Some(rgba(0xbd5287ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0xe2a9c2ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0x5b2c42ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x5585b6ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0xacc0daff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x2e4257ff).into()),
                         terminal_ansi_white: Some(rgba(0x1b1818ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0x1b1818ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x635b5bff).into()),
                         link_text_hover: Some(rgba(0x7372caff).into()),
                         ..Default::default()
                     },
@@ -6586,22 +6740,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x478c901a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x6c7a7166).into()),
                         terminal_background: Some(rgba(0x171c19ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x5d6b62ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x563220ff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x294a33ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x4e3f22ff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x284546ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x423a36ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x1d4b4dff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xecf4eeff).into()),
+                        terminal_foreground: Some(rgba(0xecf4eeff).into()),
+                        terminal_bright_foreground: Some(rgba(0xecf4eeff).into()),
+                        terminal_dim_foreground: Some(rgba(0x171c19ff).into()),
                         terminal_ansi_black: Some(rgba(0x171c19ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x5d6b62ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xecf4eeff).into()),
                         terminal_ansi_red: Some(rgba(0xb16139ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x563220ff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xdeae97ff).into()),
                         terminal_ansi_green: Some(rgba(0x489963ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x294a33ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0xa5ccafff).into()),
                         terminal_ansi_yellow: Some(rgba(0xa07e3bff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x4e3f22ff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xd3bd9aff).into()),
                         terminal_ansi_blue: Some(rgba(0x478c90ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x284546ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0xa5c5c6ff).into()),
                         terminal_ansi_magenta: Some(rgba(0x867469ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x423a36ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xc2b7b1ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x1e9aa0ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x1d4b4dff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x9dcdcfff).into()),
                         terminal_ansi_white: Some(rgba(0xecf4eeff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xecf4eeff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x7b897fff).into()),
                         link_text_hover: Some(rgba(0x478c90ff).into()),
                         ..Default::default()
                     },
@@ -7051,22 +7216,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x488c901a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x6c7a7166).into()),
                         terminal_background: Some(rgba(0xecf4eeff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x7b897fff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0xdeae97ff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0xa5ccafff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0xd3bd9aff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0xa5c5c6ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0xc2b7b1ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x9dcdcfff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0x171c19ff).into()),
+                        terminal_foreground: Some(rgba(0x171c19ff).into()),
+                        terminal_bright_foreground: Some(rgba(0x171c19ff).into()),
+                        terminal_dim_foreground: Some(rgba(0xecf4eeff).into()),
                         terminal_ansi_black: Some(rgba(0xecf4eeff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x7b897fff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0x171c19ff).into()),
                         terminal_ansi_red: Some(rgba(0xb1623aff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0xdeae97ff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0x563220ff).into()),
                         terminal_ansi_green: Some(rgba(0x499963ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0xa5ccafff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0x294a33ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xa07e3cff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0xd3bd9aff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0x4e3f22ff).into()),
                         terminal_ansi_blue: Some(rgba(0x488c90ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0xa5c5c6ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0x284546ff).into()),
                         terminal_ansi_magenta: Some(rgba(0x867469ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0xc2b7b1ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0x423a36ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x1f9aa0ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x9dcdcfff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x1d4b4dff).into()),
                         terminal_ansi_white: Some(rgba(0x171c19ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0x171c19ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x5d6b62ff).into()),
                         link_text_hover: Some(rgba(0x488c90ff).into()),
                         ..Default::default()
                     },
@@ -7516,22 +7692,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x3e62f41a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x748b7466).into()),
                         terminal_background: Some(rgba(0x131513ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x667a66ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x840b21ff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x204f1bff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x4b4a17ff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x193385ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x810e60ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x1d4a56ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xf4fbf4ff).into()),
+                        terminal_foreground: Some(rgba(0xf4fbf4ff).into()),
+                        terminal_bright_foreground: Some(rgba(0xf4fbf4ff).into()),
+                        terminal_dim_foreground: Some(rgba(0x131513ff).into()),
                         terminal_ansi_black: Some(rgba(0x131513ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x667a66ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xf4fbf4ff).into()),
                         terminal_ansi_red: Some(rgba(0xe61c3cff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x840b21ff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xff9d98ff).into()),
                         terminal_ansi_green: Some(rgba(0x2ba32aff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x204f1bff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0xa0d294ff).into()),
                         terminal_ansi_yellow: Some(rgba(0x98981cff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x4b4a17ff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xd0ca90ff).into()),
                         terminal_ansi_blue: Some(rgba(0x3e62f4ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x193385ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0xb1adfcff).into()),
                         terminal_ansi_magenta: Some(rgba(0xe61cc3ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x810e60ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xf9a1e1ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x1c99b3ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x1d4a56ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x9fccd9ff).into()),
                         terminal_ansi_white: Some(rgba(0xf4fbf4ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xf4fbf4ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x829b82ff).into()),
                         link_text_hover: Some(rgba(0x3e62f4ff).into()),
                         ..Default::default()
                     },
@@ -7981,22 +8168,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x3f62f41a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x748b7466).into()),
                         terminal_background: Some(rgba(0xf4fbf4ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x829b82ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0xff9d98ff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0xa0d294ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0xd0ca90ff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0xb1adfcff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0xf9a1e1ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x9fccd9ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0x131513ff).into()),
+                        terminal_foreground: Some(rgba(0x131513ff).into()),
+                        terminal_bright_foreground: Some(rgba(0x131513ff).into()),
+                        terminal_dim_foreground: Some(rgba(0xf4fbf4ff).into()),
                         terminal_ansi_black: Some(rgba(0xf4fbf4ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x829b82ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0x131513ff).into()),
                         terminal_ansi_red: Some(rgba(0xe61c3dff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0xff9d98ff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0x840b21ff).into()),
                         terminal_ansi_green: Some(rgba(0x2ba32bff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0xa0d294ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0x204f1bff).into()),
                         terminal_ansi_yellow: Some(rgba(0x98981dff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0xd0ca90ff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0x4b4a17ff).into()),
                         terminal_ansi_blue: Some(rgba(0x3f62f4ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0xb1adfcff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0x193385ff).into()),
                         terminal_ansi_magenta: Some(rgba(0xe61dc3ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0xf9a1e1ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0x810e60ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x1d99b3ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x9fccd9ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x1d4a56ff).into()),
                         terminal_ansi_white: Some(rgba(0x131513ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0x131513ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x667a66ff).into()),
                         link_text_hover: Some(rgba(0x3f62f4ff).into()),
                         ..Default::default()
                     },
@@ -8446,22 +8644,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x3e8fd01a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x7a819c66).into()),
                         terminal_background: Some(rgba(0x202746ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x697192ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x6d2616ff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x534921ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x63441eff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x274664ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x4c333dff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x214e5fff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xf5f7ffff).into()),
+                        terminal_foreground: Some(rgba(0xf5f7ffff).into()),
+                        terminal_bright_foreground: Some(rgba(0xf5f7ffff).into()),
+                        terminal_dim_foreground: Some(rgba(0x202746ff).into()),
                         terminal_ansi_black: Some(rgba(0x202746ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x697192ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xf5f7ffff).into()),
                         terminal_ansi_red: Some(rgba(0xc94923ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x6d2616ff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xefa58cff).into()),
                         terminal_ansi_green: Some(rgba(0xac973aff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x534921ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0xd9ca9bff).into()),
                         terminal_ansi_yellow: Some(rgba(0xc08b31ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x63441eff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xe5c497ff).into()),
                         terminal_ansi_blue: Some(rgba(0x3e8fd0ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x274664ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0xa9c6e8ff).into()),
                         terminal_ansi_magenta: Some(rgba(0x9c637aff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x4c333dff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xcfafbbff).into()),
                         terminal_ansi_cyan: Some(rgba(0x25a2c9ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x214e5fff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0xa4d0e4ff).into()),
                         terminal_ansi_white: Some(rgba(0xf5f7ffff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xf5f7ffff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x8b91a7ff).into()),
                         link_text_hover: Some(rgba(0x3e8fd0ff).into()),
                         ..Default::default()
                     },
@@ -8911,22 +9120,33 @@ pub fn atelier() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x3f8fd01a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x7a819c66).into()),
                         terminal_background: Some(rgba(0xf5f7ffff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x8b91a7ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0xefa58cff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0xd9ca9bff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0xe5c497ff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0xa9c6e8ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0xcfafbbff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0xa4d0e4ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0x202746ff).into()),
+                        terminal_foreground: Some(rgba(0x202746ff).into()),
+                        terminal_bright_foreground: Some(rgba(0x202746ff).into()),
+                        terminal_dim_foreground: Some(rgba(0xf5f7ffff).into()),
                         terminal_ansi_black: Some(rgba(0xf5f7ffff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x8b91a7ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0x202746ff).into()),
                         terminal_ansi_red: Some(rgba(0xc94a23ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0xefa58cff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0x6d2616ff).into()),
                         terminal_ansi_green: Some(rgba(0xac973aff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0xd9ca9bff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0x534921ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xc08b31ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0xe5c497ff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0x63441eff).into()),
                         terminal_ansi_blue: Some(rgba(0x3f8fd0ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0xa9c6e8ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0x274664ff).into()),
                         terminal_ansi_magenta: Some(rgba(0x9c637aff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0xcfafbbff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0x4c333dff).into()),
                         terminal_ansi_cyan: Some(rgba(0x25a2c9ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0xa4d0e4ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x214e5fff).into()),
                         terminal_ansi_white: Some(rgba(0x202746ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0x202746ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x697192ff).into()),
                         link_text_hover: Some(rgba(0x3f8fd0ff).into()),
                         ..Default::default()
                     },

crates/theme/src/themes/ayu.rs ๐Ÿ”—

@@ -76,22 +76,33 @@ pub fn ayu() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x5ac2fe1a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x66676766).into()),
                         terminal_background: Some(rgba(0x0d1017ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x545557ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x83363cff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x567627ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x92592cff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x28628cff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x205b78ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x4c806fff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xbfbdb6ff).into()),
+                        terminal_foreground: Some(rgba(0xbfbdb6ff).into()),
+                        terminal_bright_foreground: Some(rgba(0xbfbdb6ff).into()),
+                        terminal_dim_foreground: Some(rgba(0x0d1017ff).into()),
                         terminal_ansi_black: Some(rgba(0x0d1017ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x545557ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xbfbdb6ff).into()),
                         terminal_ansi_red: Some(rgba(0xef7178ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x83363cff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xfebab9ff).into()),
                         terminal_ansi_green: Some(rgba(0xaad84cff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x567627ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0xd8eca8ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xfeb454ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x92592cff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xffd9aaff).into()),
                         terminal_ansi_blue: Some(rgba(0x5ac2feff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x28628cff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0xb8e0ffff).into()),
                         terminal_ansi_magenta: Some(rgba(0x3abae5ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x205b78ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xaddcf3ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x95e5cbff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x4c806fff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0xccf3e5ff).into()),
                         terminal_ansi_white: Some(rgba(0xbfbdb6ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xbfbdb6ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x787876ff).into()),
                         link_text_hover: Some(rgba(0x5ac2feff).into()),
                         ..Default::default()
                     },
@@ -520,22 +531,33 @@ pub fn ayu() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x3b9ee51a).into()),
                         editor_document_highlight_write_background: Some(rgba(0xacafb166).into()),
                         terminal_background: Some(rgba(0xfcfcfcff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0xbcbec0ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0xfebab6ff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0xc7d98fff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0xffd6a4ff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0xaccef3ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0xb2d9e9ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0xace0cbff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0x5c6166ff).into()),
+                        terminal_foreground: Some(rgba(0x5c6166ff).into()),
+                        terminal_bright_foreground: Some(rgba(0x5c6166ff).into()),
+                        terminal_dim_foreground: Some(rgba(0xfcfcfcff).into()),
                         terminal_ansi_black: Some(rgba(0xfcfcfcff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0xbcbec0ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0x5c6166ff).into()),
                         terminal_ansi_red: Some(rgba(0xef7271ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0xfebab6ff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0x833639ff).into()),
                         terminal_ansi_green: Some(rgba(0x86b305ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0xc7d98fff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0x445614ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xf1ae4aff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0xffd6a4ff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0x8a5328ff).into()),
                         terminal_ansi_blue: Some(rgba(0x3b9ee5ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0xaccef3ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0x214d76ff).into()),
                         terminal_ansi_magenta: Some(rgba(0x56b4d3ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0xb2d9e9ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0x2f5669ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x4dbf99ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0xace0cbff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x2a5f4aff).into()),
                         terminal_ansi_white: Some(rgba(0x5c6166ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0x5c6166ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x9c9fa2ff).into()),
                         link_text_hover: Some(rgba(0x3b9ee5ff).into()),
                         ..Default::default()
                     },
@@ -964,22 +986,33 @@ pub fn ayu() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x73cffe1a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x787a7c66).into()),
                         terminal_background: Some(rgba(0x242936ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x67696eff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x83403dff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x76993dff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x937238ff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x346e8dff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x2b6c7bff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x4c806fff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xcccac2ff).into()),
+                        terminal_foreground: Some(rgba(0xcccac2ff).into()),
+                        terminal_bright_foreground: Some(rgba(0xcccac2ff).into()),
+                        terminal_dim_foreground: Some(rgba(0x242936ff).into()),
                         terminal_ansi_black: Some(rgba(0x242936ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x67696eff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xcccac2ff).into()),
                         terminal_ansi_red: Some(rgba(0xf18779ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x83403dff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xfec4baff).into()),
                         terminal_ansi_green: Some(rgba(0xd5fe80ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x76993dff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0xecffc1ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xfed073ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x937238ff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xffe7b9ff).into()),
                         terminal_ansi_blue: Some(rgba(0x73cffeff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x346e8dff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0xc1e7ffff).into()),
                         terminal_ansi_magenta: Some(rgba(0x5ccee5ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x2b6c7bff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xb7e7f2ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x95e5cbff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x4c806fff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0xccf3e5ff).into()),
                         terminal_ansi_white: Some(rgba(0xcccac2ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xcccac2ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x898a8aff).into()),
                         link_text_hover: Some(rgba(0x73cffeff).into()),
                         ..Default::default()
                     },

crates/theme/src/themes/gruvbox.rs ๐Ÿ”—

@@ -76,22 +76,33 @@ pub fn gruvbox() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x83a5981a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x92847466).into()),
                         terminal_background: Some(rgba(0x282828ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x73675eff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x93211eff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x615d1bff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x91611cff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x414f4aff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x514a41ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x45603eff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xfbf1c7ff).into()),
+                        terminal_foreground: Some(rgba(0xfbf1c7ff).into()),
+                        terminal_bright_foreground: Some(rgba(0xfbf1c7ff).into()),
+                        terminal_dim_foreground: Some(rgba(0x282828ff).into()),
                         terminal_ansi_black: Some(rgba(0x282828ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x73675eff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xfbf1c7ff).into()),
                         terminal_ansi_red: Some(rgba(0xfb4a35ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x93211eff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xffaa95ff).into()),
                         terminal_ansi_green: Some(rgba(0xb8bb27ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x615d1bff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0xe0dc98ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xf9bd30ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x91611cff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xffdd9cff).into()),
                         terminal_ansi_blue: Some(rgba(0x83a598ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x414f4aff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0xc0d2cbff).into()),
                         terminal_ansi_magenta: Some(rgba(0xa89984ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x514a41ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xd3cbc0ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x8ec07cff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x45603eff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0xc7dfbdff).into()),
                         terminal_ansi_white: Some(rgba(0xfbf1c7ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xfbf1c7ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0xb1a28aff).into()),
                         link_text_hover: Some(rgba(0x83a598ff).into()),
                         ..Default::default()
                     },
@@ -527,22 +538,33 @@ pub fn gruvbox() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x83a5981a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x92847466).into()),
                         terminal_background: Some(rgba(0x1d2021ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x73675eff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x93211eff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x615d1bff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x91611cff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x414f4aff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x514a41ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x45603eff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xfbf1c7ff).into()),
+                        terminal_foreground: Some(rgba(0xfbf1c7ff).into()),
+                        terminal_bright_foreground: Some(rgba(0xfbf1c7ff).into()),
+                        terminal_dim_foreground: Some(rgba(0x1d2021ff).into()),
                         terminal_ansi_black: Some(rgba(0x1d2021ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x73675eff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xfbf1c7ff).into()),
                         terminal_ansi_red: Some(rgba(0xfb4a35ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x93211eff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xffaa95ff).into()),
                         terminal_ansi_green: Some(rgba(0xb8bb27ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x615d1bff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0xe0dc98ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xf9bd30ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x91611cff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xffdd9cff).into()),
                         terminal_ansi_blue: Some(rgba(0x83a598ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x414f4aff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0xc0d2cbff).into()),
                         terminal_ansi_magenta: Some(rgba(0xa89984ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x514a41ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xd3cbc0ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x8ec07cff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x45603eff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0xc7dfbdff).into()),
                         terminal_ansi_white: Some(rgba(0xfbf1c7ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xfbf1c7ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0xb1a28aff).into()),
                         link_text_hover: Some(rgba(0x83a598ff).into()),
                         ..Default::default()
                     },
@@ -978,22 +1000,33 @@ pub fn gruvbox() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x83a5981a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x92847466).into()),
                         terminal_background: Some(rgba(0x32302fff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x73675eff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x93211eff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x615d1bff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x91611cff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x414f4aff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x514a41ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x45603eff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xfbf1c7ff).into()),
+                        terminal_foreground: Some(rgba(0xfbf1c7ff).into()),
+                        terminal_bright_foreground: Some(rgba(0xfbf1c7ff).into()),
+                        terminal_dim_foreground: Some(rgba(0x32302fff).into()),
                         terminal_ansi_black: Some(rgba(0x32302fff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x73675eff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xfbf1c7ff).into()),
                         terminal_ansi_red: Some(rgba(0xfb4a35ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x93211eff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xffaa95ff).into()),
                         terminal_ansi_green: Some(rgba(0xb8bb27ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x615d1bff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0xe0dc98ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xf9bd30ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x91611cff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xffdd9cff).into()),
                         terminal_ansi_blue: Some(rgba(0x83a598ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x414f4aff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0xc0d2cbff).into()),
                         terminal_ansi_magenta: Some(rgba(0xa89984ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x514a41ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xd3cbc0ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x8ec07cff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x45603eff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0xc7dfbdff).into()),
                         terminal_ansi_white: Some(rgba(0xfbf1c7ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xfbf1c7ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0xb1a28aff).into()),
                         link_text_hover: Some(rgba(0x83a598ff).into()),
                         ..Default::default()
                     },
@@ -1429,22 +1462,33 @@ pub fn gruvbox() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x0b66781a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x92847466).into()),
                         terminal_background: Some(rgba(0xfbf1c7ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0xb1a28aff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0xdc8c7bff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0xbfb787ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0xe2b88bff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x8fb0baff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0xbcb5afff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x9fbca8ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0x282828ff).into()),
+                        terminal_foreground: Some(rgba(0x282828ff).into()),
+                        terminal_bright_foreground: Some(rgba(0x282828ff).into()),
+                        terminal_dim_foreground: Some(rgba(0xfbf1c7ff).into()),
                         terminal_ansi_black: Some(rgba(0xfbf1c7ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0xb1a28aff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0x282828ff).into()),
                         terminal_ansi_red: Some(rgba(0x9d0408ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0xdc8c7bff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0x4f1207ff).into()),
                         terminal_ansi_green: Some(rgba(0x797410ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0xbfb787ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0x3e3a11ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xb57616ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0xe2b88bff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0x5c3b13ff).into()),
                         terminal_ansi_blue: Some(rgba(0x0b6678ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x8fb0baff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0x14343cff).into()),
                         terminal_ansi_magenta: Some(rgba(0x7c6f64ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0xbcb5afff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0x3e3833ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x437b59ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x9fbca8ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x253e2eff).into()),
                         terminal_ansi_white: Some(rgba(0x282828ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0x282828ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x73675eff).into()),
                         link_text_hover: Some(rgba(0x0b6678ff).into()),
                         ..Default::default()
                     },
@@ -1880,22 +1924,33 @@ pub fn gruvbox() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x0b66781a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x92847466).into()),
                         terminal_background: Some(rgba(0xf9f5d7ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0xb1a28aff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0xdc8c7bff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0xbfb787ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0xe2b88bff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x8fb0baff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0xbcb5afff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x9fbca8ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0x282828ff).into()),
+                        terminal_foreground: Some(rgba(0x282828ff).into()),
+                        terminal_bright_foreground: Some(rgba(0x282828ff).into()),
+                        terminal_dim_foreground: Some(rgba(0xf9f5d7ff).into()),
                         terminal_ansi_black: Some(rgba(0xf9f5d7ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0xb1a28aff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0x282828ff).into()),
                         terminal_ansi_red: Some(rgba(0x9d0408ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0xdc8c7bff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0x4f1207ff).into()),
                         terminal_ansi_green: Some(rgba(0x797410ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0xbfb787ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0x3e3a11ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xb57616ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0xe2b88bff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0x5c3b13ff).into()),
                         terminal_ansi_blue: Some(rgba(0x0b6678ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x8fb0baff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0x14343cff).into()),
                         terminal_ansi_magenta: Some(rgba(0x7c6f64ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0xbcb5afff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0x3e3833ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x437b59ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x9fbca8ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x253e2eff).into()),
                         terminal_ansi_white: Some(rgba(0x282828ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0x282828ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x73675eff).into()),
                         link_text_hover: Some(rgba(0x0b6678ff).into()),
                         ..Default::default()
                     },
@@ -2331,22 +2386,33 @@ pub fn gruvbox() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x0b66781a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x92847466).into()),
                         terminal_background: Some(rgba(0xf2e5bcff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0xb1a28aff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0xdc8c7bff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0xbfb787ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0xe2b88bff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x8fb0baff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0xbcb5afff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x9fbca8ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0x282828ff).into()),
+                        terminal_foreground: Some(rgba(0x282828ff).into()),
+                        terminal_bright_foreground: Some(rgba(0x282828ff).into()),
+                        terminal_dim_foreground: Some(rgba(0xf2e5bcff).into()),
                         terminal_ansi_black: Some(rgba(0xf2e5bcff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0xb1a28aff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0x282828ff).into()),
                         terminal_ansi_red: Some(rgba(0x9d0408ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0xdc8c7bff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0x4f1207ff).into()),
                         terminal_ansi_green: Some(rgba(0x797410ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0xbfb787ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0x3e3a11ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xb57616ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0xe2b88bff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0x5c3b13ff).into()),
                         terminal_ansi_blue: Some(rgba(0x0b6678ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x8fb0baff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0x14343cff).into()),
                         terminal_ansi_magenta: Some(rgba(0x7c6f64ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0xbcb5afff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0x3e3833ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x437b59ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x9fbca8ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x253e2eff).into()),
                         terminal_ansi_white: Some(rgba(0x282828ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0x282828ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x73675eff).into()),
                         link_text_hover: Some(rgba(0x0b6678ff).into()),
                         ..Default::default()
                     },

crates/theme/src/themes/one.rs ๐Ÿ”—

@@ -76,22 +76,33 @@ pub fn one() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x74ade81a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x555a6366).into()),
                         terminal_background: Some(rgba(0x282c34ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x525661ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x673a3cff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x4d6140ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x786441ff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x385378ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x5e2b26ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x3a565bff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xc8ccd4ff).into()),
+                        terminal_foreground: Some(rgba(0xc8ccd4ff).into()),
+                        terminal_bright_foreground: Some(rgba(0xc8ccd4ff).into()),
+                        terminal_dim_foreground: Some(rgba(0x282c34ff).into()),
                         terminal_ansi_black: Some(rgba(0x282c34ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x525661ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xc8ccd4ff).into()),
                         terminal_ansi_red: Some(rgba(0xd07277ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x673a3cff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xebb8b9ff).into()),
                         terminal_ansi_green: Some(rgba(0xa1c181ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x4d6140ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0xd1e0bfff).into()),
                         terminal_ansi_yellow: Some(rgba(0xdec184ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x786441ff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xf1dfc1ff).into()),
                         terminal_ansi_blue: Some(rgba(0x74ade8ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x385378ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0xbed5f4ff).into()),
                         terminal_ansi_magenta: Some(rgba(0xbe5046ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x5e2b26ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xe6a79eff).into()),
                         terminal_ansi_cyan: Some(rgba(0x6fb4c0ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x3a565bff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0xb9d9dfff).into()),
                         terminal_ansi_white: Some(rgba(0xc8ccd4ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xc8ccd4ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x575d65ff).into()),
                         link_text_hover: Some(rgba(0x74ade8ff).into()),
                         ..Default::default()
                     },
@@ -527,22 +538,33 @@ pub fn one() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x5c79e21a).into()),
                         editor_document_highlight_write_background: Some(rgba(0xa3a3a466).into()),
                         terminal_background: Some(rgba(0xfafafaff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0xaaaaaaff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0xf0b0a4ff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0xb2cfa9ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0xf1dfc1ff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0xb5baf2ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0xcea6d3ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0xa4bfdbff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0x383a41ff).into()),
+                        terminal_foreground: Some(rgba(0x383a41ff).into()),
+                        terminal_bright_foreground: Some(rgba(0x383a41ff).into()),
+                        terminal_dim_foreground: Some(rgba(0xfafafaff).into()),
                         terminal_ansi_black: Some(rgba(0xfafafaff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0xaaaaaaff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0x383a41ff).into()),
                         terminal_ansi_red: Some(rgba(0xd36151ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0xf0b0a4ff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0x6f312aff).into()),
                         terminal_ansi_green: Some(rgba(0x669f59ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0xb2cfa9ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0x354d2eff).into()),
                         terminal_ansi_yellow: Some(rgba(0xdec184ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0xf1dfc1ff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0x786441ff).into()),
                         terminal_ansi_blue: Some(rgba(0x5c79e2ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0xb5baf2ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0x2d3d75ff).into()),
                         terminal_ansi_magenta: Some(rgba(0x994fa6ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0xcea6d3ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0x4b2a50ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x3b82b7ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0xa4bfdbff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x254058ff).into()),
                         terminal_ansi_white: Some(rgba(0x383a41ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0x383a41ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x98989bff).into()),
                         link_text_hover: Some(rgba(0x5c79e2ff).into()),
                         ..Default::default()
                     },

crates/theme/src/themes/rose_pine.rs ๐Ÿ”—

@@ -76,22 +76,33 @@ pub fn rose_pine() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x9cced71a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x28253c66).into()),
                         terminal_background: Some(rgba(0x191724ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x403d55ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x7e3647ff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x31614fff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x8a653bff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x566c70ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x4c3b47ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x203a46ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xe0def4ff).into()),
+                        terminal_foreground: Some(rgba(0xe0def4ff).into()),
+                        terminal_bright_foreground: Some(rgba(0xe0def4ff).into()),
+                        terminal_dim_foreground: Some(rgba(0x191724ff).into()),
                         terminal_ansi_black: Some(rgba(0x191724ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x403d55ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xe0def4ff).into()),
                         terminal_ansi_red: Some(rgba(0xea6f92ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x7e3647ff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xfab9c7ff).into()),
                         terminal_ansi_green: Some(rgba(0x5dc2a3ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x31614fff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0xb3e1d1ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xf5c177ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x8a653bff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xfedfbbff).into()),
                         terminal_ansi_blue: Some(rgba(0x9cced7ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x566c70ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0xcfe7ebff).into()),
                         terminal_ansi_magenta: Some(rgba(0x9d7691ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x4c3b47ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xceb9c7ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x32748fff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x203a46ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x9cb7c6ff).into()),
                         terminal_ansi_white: Some(rgba(0xe0def4ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xe0def4ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x514e68ff).into()),
                         link_text_hover: Some(rgba(0x9cced7ff).into()),
                         ..Default::default()
                     },
@@ -534,22 +545,33 @@ pub fn rose_pine() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x57949f1a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x9691a466).into()),
                         terminal_background: Some(rgba(0xfaf4edff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0xb8b2baff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0xdcb0bbff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0xa5d5c5ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0xfccd9bff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0xacc9ceff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0xbcb1bdff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x97b1c0ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0x575279ff).into()),
+                        terminal_foreground: Some(rgba(0x575279ff).into()),
+                        terminal_bright_foreground: Some(rgba(0x575279ff).into()),
+                        terminal_dim_foreground: Some(rgba(0xfaf4edff).into()),
                         terminal_ansi_black: Some(rgba(0xfaf4edff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0xb8b2baff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0x575279ff).into()),
                         terminal_ansi_red: Some(rgba(0xb4647aff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0xdcb0bbff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0x57333dff).into()),
                         terminal_ansi_green: Some(rgba(0x3eaa8eff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0xa5d5c5ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0x265245ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xe99d35ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0xfccd9bff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0x854a1fff).into()),
                         terminal_ansi_blue: Some(rgba(0x57949fff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0xacc9ceff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0x2f484dff).into()),
                         terminal_ansi_magenta: Some(rgba(0x7c697fff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0xbcb1bdff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0x3e353fff).into()),
                         terminal_ansi_cyan: Some(rgba(0x2a6983ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x97b1c0ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x1c3641ff).into()),
                         terminal_ansi_white: Some(rgba(0x575279ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0x575279ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x827e98ff).into()),
                         link_text_hover: Some(rgba(0x57949fff).into()),
                         ..Default::default()
                     },
@@ -992,22 +1014,33 @@ pub fn rose_pine() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x9cced71a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x59557166).into()),
                         terminal_background: Some(rgba(0x232136ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x3f3b58ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x7e3647ff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x31614fff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x8a653bff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x566c70ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x51414eff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x264654ff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xe0def4ff).into()),
+                        terminal_foreground: Some(rgba(0xe0def4ff).into()),
+                        terminal_bright_foreground: Some(rgba(0xe0def4ff).into()),
+                        terminal_dim_foreground: Some(rgba(0x232136ff).into()),
                         terminal_ansi_black: Some(rgba(0x232136ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x3f3b58ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xe0def4ff).into()),
                         terminal_ansi_red: Some(rgba(0xea6f92ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x7e3647ff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xfab9c7ff).into()),
                         terminal_ansi_green: Some(rgba(0x5dc2a3ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x31614fff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0xb3e1d1ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xf5c177ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x8a653bff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xfedfbbff).into()),
                         terminal_ansi_blue: Some(rgba(0x9cced7ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x566c70ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0xcfe7ebff).into()),
                         terminal_ansi_magenta: Some(rgba(0xa784a1ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x51414eff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xd3c0cfff).into()),
                         terminal_ansi_cyan: Some(rgba(0x3f8fb0ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x264654ff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0xa5c6d7ff).into()),
                         terminal_ansi_white: Some(rgba(0xe0def4ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xe0def4ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x75718eff).into()),
                         link_text_hover: Some(rgba(0x9cced7ff).into()),
                         ..Default::default()
                     },

crates/theme/src/themes/sandcastle.rs ๐Ÿ”—

@@ -75,22 +75,33 @@ pub fn sandcastle() -> UserThemeFamily {
                     editor_document_highlight_read_background: Some(rgba(0x528b8b1a).into()),
                     editor_document_highlight_write_background: Some(rgba(0x7c6f6466).into()),
                     terminal_background: Some(rgba(0x282c34ff).into()),
-                    terminal_ansi_bright_black: Some(rgba(0x5e5753ff).into()),
-                    terminal_ansi_bright_red: Some(rgba(0x57333dff).into()),
-                    terminal_ansi_bright_green: Some(rgba(0x414f4aff).into()),
-                    terminal_ansi_bright_yellow: Some(rgba(0x4e3f22ff).into()),
-                    terminal_ansi_bright_blue: Some(rgba(0x2c4444ff).into()),
-                    terminal_ansi_bright_magenta: Some(rgba(0x523a18ff).into()),
-                    terminal_ansi_bright_cyan: Some(rgba(0x414f4aff).into()),
-                    terminal_ansi_bright_white: Some(rgba(0xfdf4c1ff).into()),
+                    terminal_foreground: Some(rgba(0xfdf4c1ff).into()),
+                    terminal_bright_foreground: Some(rgba(0xfdf4c1ff).into()),
+                    terminal_dim_foreground: Some(rgba(0x282c34ff).into()),
                     terminal_ansi_black: Some(rgba(0x282c34ff).into()),
+                    terminal_ansi_bright_black: Some(rgba(0x5e5753ff).into()),
+                    terminal_ansi_dim_black: Some(rgba(0xfdf4c1ff).into()),
                     terminal_ansi_red: Some(rgba(0xb4637aff).into()),
+                    terminal_ansi_bright_red: Some(rgba(0x57333dff).into()),
+                    terminal_ansi_dim_red: Some(rgba(0xdcb0bbff).into()),
                     terminal_ansi_green: Some(rgba(0x83a598ff).into()),
+                    terminal_ansi_bright_green: Some(rgba(0x414f4aff).into()),
+                    terminal_ansi_dim_green: Some(rgba(0xc0d2cbff).into()),
                     terminal_ansi_yellow: Some(rgba(0xa07e3bff).into()),
+                    terminal_ansi_bright_yellow: Some(rgba(0x4e3f22ff).into()),
+                    terminal_ansi_dim_yellow: Some(rgba(0xd3bd9aff).into()),
                     terminal_ansi_blue: Some(rgba(0x528b8bff).into()),
+                    terminal_ansi_bright_blue: Some(rgba(0x2c4444ff).into()),
+                    terminal_ansi_dim_blue: Some(rgba(0xa8c4c4ff).into()),
                     terminal_ansi_magenta: Some(rgba(0xa87323ff).into()),
+                    terminal_ansi_bright_magenta: Some(rgba(0x523a18ff).into()),
+                    terminal_ansi_dim_magenta: Some(rgba(0xdab78eff).into()),
                     terminal_ansi_cyan: Some(rgba(0x83a598ff).into()),
+                    terminal_ansi_bright_cyan: Some(rgba(0x414f4aff).into()),
+                    terminal_ansi_dim_cyan: Some(rgba(0xc0d2cbff).into()),
                     terminal_ansi_white: Some(rgba(0xfdf4c1ff).into()),
+                    terminal_ansi_bright_white: Some(rgba(0xfdf4c1ff).into()),
+                    terminal_ansi_dim_white: Some(rgba(0x968777ff).into()),
                     link_text_hover: Some(rgba(0x528b8bff).into()),
                     ..Default::default()
                 },

crates/theme/src/themes/solarized.rs ๐Ÿ”—

@@ -76,22 +76,33 @@ pub fn solarized() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x288bd11a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x6d828866).into()),
                         terminal_background: Some(rgba(0x002b36ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x5c7279ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0x7d181cff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0x434a11ff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0x5d4310ff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0x214465ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0x6f1f40ff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x204e4aff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0xfdf6e3ff).into()),
+                        terminal_foreground: Some(rgba(0xfdf6e3ff).into()),
+                        terminal_bright_foreground: Some(rgba(0xfdf6e3ff).into()),
+                        terminal_dim_foreground: Some(rgba(0x002b36ff).into()),
                         terminal_ansi_black: Some(rgba(0x002b36ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x5c7279ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0xfdf6e3ff).into()),
                         terminal_ansi_red: Some(rgba(0xdc3330ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0x7d181cff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0xfaa091ff).into()),
                         terminal_ansi_green: Some(rgba(0x859904ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0x434a11ff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0xc6cb8bff).into()),
                         terminal_ansi_yellow: Some(rgba(0xb58903ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0x5d4310ff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0xe1c28aff).into()),
                         terminal_ansi_blue: Some(rgba(0x288bd1ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0x214465ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0xa5c3e9ff).into()),
                         terminal_ansi_magenta: Some(rgba(0xd33782ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0x6f1f40ff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0xf0a2bfff).into()),
                         terminal_ansi_cyan: Some(rgba(0x2ca198ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x204e4aff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x9fd0cbff).into()),
                         terminal_ansi_white: Some(rgba(0xfdf6e3ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0xfdf6e3ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x7b8e91ff).into()),
                         link_text_hover: Some(rgba(0x288bd1ff).into()),
                         ..Default::default()
                     },
@@ -520,22 +531,33 @@ pub fn solarized() -> UserThemeFamily {
                         editor_document_highlight_read_background: Some(rgba(0x298bd11a).into()),
                         editor_document_highlight_write_background: Some(rgba(0x6d828866).into()),
                         terminal_background: Some(rgba(0xfdf6e3ff).into()),
-                        terminal_ansi_bright_black: Some(rgba(0x7b8e91ff).into()),
-                        terminal_ansi_bright_red: Some(rgba(0xfaa091ff).into()),
-                        terminal_ansi_bright_green: Some(rgba(0xc6cb8bff).into()),
-                        terminal_ansi_bright_yellow: Some(rgba(0xe1c28aff).into()),
-                        terminal_ansi_bright_blue: Some(rgba(0xa5c3e9ff).into()),
-                        terminal_ansi_bright_magenta: Some(rgba(0xf0a2bfff).into()),
-                        terminal_ansi_bright_cyan: Some(rgba(0x9fd0cbff).into()),
-                        terminal_ansi_bright_white: Some(rgba(0x002b36ff).into()),
+                        terminal_foreground: Some(rgba(0x002b36ff).into()),
+                        terminal_bright_foreground: Some(rgba(0x002b36ff).into()),
+                        terminal_dim_foreground: Some(rgba(0xfdf6e3ff).into()),
                         terminal_ansi_black: Some(rgba(0xfdf6e3ff).into()),
+                        terminal_ansi_bright_black: Some(rgba(0x7b8e91ff).into()),
+                        terminal_ansi_dim_black: Some(rgba(0x002b36ff).into()),
                         terminal_ansi_red: Some(rgba(0xdc3330ff).into()),
+                        terminal_ansi_bright_red: Some(rgba(0xfaa091ff).into()),
+                        terminal_ansi_dim_red: Some(rgba(0x7d181cff).into()),
                         terminal_ansi_green: Some(rgba(0x859904ff).into()),
+                        terminal_ansi_bright_green: Some(rgba(0xc6cb8bff).into()),
+                        terminal_ansi_dim_green: Some(rgba(0x434a11ff).into()),
                         terminal_ansi_yellow: Some(rgba(0xb58904ff).into()),
+                        terminal_ansi_bright_yellow: Some(rgba(0xe1c28aff).into()),
+                        terminal_ansi_dim_yellow: Some(rgba(0x5d4310ff).into()),
                         terminal_ansi_blue: Some(rgba(0x298bd1ff).into()),
+                        terminal_ansi_bright_blue: Some(rgba(0xa5c3e9ff).into()),
+                        terminal_ansi_dim_blue: Some(rgba(0x214465ff).into()),
                         terminal_ansi_magenta: Some(rgba(0xd33882ff).into()),
+                        terminal_ansi_bright_magenta: Some(rgba(0xf0a2bfff).into()),
+                        terminal_ansi_dim_magenta: Some(rgba(0x6f1f40ff).into()),
                         terminal_ansi_cyan: Some(rgba(0x2ca198ff).into()),
+                        terminal_ansi_bright_cyan: Some(rgba(0x9fd0cbff).into()),
+                        terminal_ansi_dim_cyan: Some(rgba(0x204e4aff).into()),
                         terminal_ansi_white: Some(rgba(0x002b36ff).into()),
+                        terminal_ansi_bright_white: Some(rgba(0x002b36ff).into()),
+                        terminal_ansi_dim_white: Some(rgba(0x5c7279ff).into()),
                         link_text_hover: Some(rgba(0x298bd1ff).into()),
                         ..Default::default()
                     },

crates/theme/src/themes/summercamp.rs ๐Ÿ”—

@@ -75,22 +75,33 @@ pub fn summercamp() -> UserThemeFamily {
                     editor_document_highlight_read_background: Some(rgba(0x499bef1a).into()),
                     editor_document_highlight_write_background: Some(rgba(0x49443366).into()),
                     terminal_background: Some(rgba(0x1c1810ff).into()),
-                    terminal_ansi_bright_black: Some(rgba(0x3b3627ff).into()),
-                    terminal_ansi_bright_red: Some(rgba(0x7f2724ff).into()),
-                    terminal_ansi_bright_green: Some(rgba(0x28842cff).into()),
-                    terminal_ansi_bright_yellow: Some(rgba(0x8c9a10ff).into()),
-                    terminal_ansi_bright_blue: Some(rgba(0x234b7fff).into()),
-                    terminal_ansi_bright_magenta: Some(rgba(0x88487eff).into()),
-                    terminal_ansi_bright_cyan: Some(rgba(0x298462ff).into()),
-                    terminal_ansi_bright_white: Some(rgba(0xf8f5deff).into()),
+                    terminal_foreground: Some(rgba(0xf8f5deff).into()),
+                    terminal_bright_foreground: Some(rgba(0xf8f5deff).into()),
+                    terminal_dim_foreground: Some(rgba(0x1c1810ff).into()),
                     terminal_ansi_black: Some(rgba(0x1c1810ff).into()),
+                    terminal_ansi_bright_black: Some(rgba(0x3b3627ff).into()),
+                    terminal_ansi_dim_black: Some(rgba(0xf8f5deff).into()),
                     terminal_ansi_red: Some(rgba(0xe35142ff).into()),
+                    terminal_ansi_bright_red: Some(rgba(0x7f2724ff).into()),
+                    terminal_ansi_dim_red: Some(rgba(0xfbab9cff).into()),
                     terminal_ansi_green: Some(rgba(0x5dea5aff).into()),
+                    terminal_ansi_bright_green: Some(rgba(0x28842cff).into()),
+                    terminal_ansi_dim_green: Some(rgba(0xb9f7aeff).into()),
                     terminal_ansi_yellow: Some(rgba(0xf1fe29ff).into()),
+                    terminal_ansi_bright_yellow: Some(rgba(0x8c9a10ff).into()),
+                    terminal_ansi_dim_yellow: Some(rgba(0xffffa2ff).into()),
                     terminal_ansi_blue: Some(rgba(0x499befff).into()),
+                    terminal_ansi_bright_blue: Some(rgba(0x234b7fff).into()),
+                    terminal_ansi_dim_blue: Some(rgba(0xb1ccf8ff).into()),
                     terminal_ansi_magenta: Some(rgba(0xf59be6ff).into()),
+                    terminal_ansi_bright_magenta: Some(rgba(0x88487eff).into()),
+                    terminal_ansi_dim_magenta: Some(rgba(0xfccef3ff).into()),
                     terminal_ansi_cyan: Some(rgba(0x5beabcff).into()),
+                    terminal_ansi_bright_cyan: Some(rgba(0x298462ff).into()),
+                    terminal_ansi_dim_cyan: Some(rgba(0xb7f6ddff).into()),
                     terminal_ansi_white: Some(rgba(0xf8f5deff).into()),
+                    terminal_ansi_bright_white: Some(rgba(0xf8f5deff).into()),
+                    terminal_ansi_dim_white: Some(rgba(0x57533fff).into()),
                     link_text_hover: Some(rgba(0x499befff).into()),
                     ..Default::default()
                 },

crates/theme_importer/src/theme_printer.rs ๐Ÿ”—

@@ -244,43 +244,60 @@ impl<'a> Debug for ThemeColorsRefinementPrinter<'a> {
                 self.0.editor_document_highlight_write_background,
             ),
             ("terminal_background", self.0.terminal_background),
+            ("terminal_foreground", self.0.terminal_foreground),
+            (
+                "terminal_bright_foreground",
+                self.0.terminal_bright_foreground,
+            ),
+            ("terminal_dim_foreground", self.0.terminal_dim_foreground),
+            ("terminal_ansi_black", self.0.terminal_ansi_black),
             (
                 "terminal_ansi_bright_black",
                 self.0.terminal_ansi_bright_black,
             ),
+            ("terminal_ansi_dim_black", self.0.terminal_ansi_dim_black),
+            ("terminal_ansi_red", self.0.terminal_ansi_red),
             ("terminal_ansi_bright_red", self.0.terminal_ansi_bright_red),
+            ("terminal_ansi_dim_red", self.0.terminal_ansi_dim_red),
+            ("terminal_ansi_green", self.0.terminal_ansi_green),
             (
                 "terminal_ansi_bright_green",
                 self.0.terminal_ansi_bright_green,
             ),
+            ("terminal_ansi_dim_green", self.0.terminal_ansi_dim_green),
+            ("terminal_ansi_yellow", self.0.terminal_ansi_yellow),
             (
                 "terminal_ansi_bright_yellow",
                 self.0.terminal_ansi_bright_yellow,
             ),
+            ("terminal_ansi_dim_yellow", self.0.terminal_ansi_dim_yellow),
+            ("terminal_ansi_blue", self.0.terminal_ansi_blue),
             (
                 "terminal_ansi_bright_blue",
                 self.0.terminal_ansi_bright_blue,
             ),
+            ("terminal_ansi_dim_blue", self.0.terminal_ansi_dim_blue),
+            ("terminal_ansi_magenta", self.0.terminal_ansi_magenta),
             (
                 "terminal_ansi_bright_magenta",
                 self.0.terminal_ansi_bright_magenta,
             ),
+            (
+                "terminal_ansi_dim_magenta",
+                self.0.terminal_ansi_dim_magenta,
+            ),
+            ("terminal_ansi_cyan", self.0.terminal_ansi_cyan),
             (
                 "terminal_ansi_bright_cyan",
                 self.0.terminal_ansi_bright_cyan,
             ),
+            ("terminal_ansi_dim_cyan", self.0.terminal_ansi_dim_cyan),
+            ("terminal_ansi_white", self.0.terminal_ansi_white),
             (
                 "terminal_ansi_bright_white",
                 self.0.terminal_ansi_bright_white,
             ),
-            ("terminal_ansi_black", self.0.terminal_ansi_black),
-            ("terminal_ansi_red", self.0.terminal_ansi_red),
-            ("terminal_ansi_green", self.0.terminal_ansi_green),
-            ("terminal_ansi_yellow", self.0.terminal_ansi_yellow),
-            ("terminal_ansi_blue", self.0.terminal_ansi_blue),
-            ("terminal_ansi_magenta", self.0.terminal_ansi_magenta),
-            ("terminal_ansi_cyan", self.0.terminal_ansi_cyan),
-            ("terminal_ansi_white", self.0.terminal_ansi_white),
+            ("terminal_ansi_dim_white", self.0.terminal_ansi_dim_white),
             ("link_text_hover", self.0.link_text_hover),
         ];
 

crates/theme_importer/src/zed1/converter.rs ๐Ÿ”—

@@ -250,22 +250,33 @@ impl Zed1ThemeConverter {
                 editor.document_highlight_write_background,
             ),
             terminal_background: convert(terminal.background),
-            terminal_ansi_bright_black: convert(terminal.bright_black),
-            terminal_ansi_bright_red: convert(terminal.bright_red),
-            terminal_ansi_bright_green: convert(terminal.bright_green),
-            terminal_ansi_bright_yellow: convert(terminal.bright_yellow),
-            terminal_ansi_bright_blue: convert(terminal.bright_blue),
-            terminal_ansi_bright_magenta: convert(terminal.bright_magenta),
-            terminal_ansi_bright_cyan: convert(terminal.bright_cyan),
-            terminal_ansi_bright_white: convert(terminal.bright_white),
+            terminal_foreground: convert(terminal.foreground),
+            terminal_bright_foreground: convert(terminal.bright_foreground),
+            terminal_dim_foreground: convert(terminal.dim_foreground),
             terminal_ansi_black: convert(terminal.black),
+            terminal_ansi_bright_black: convert(terminal.bright_black),
+            terminal_ansi_dim_black: convert(terminal.dim_black),
             terminal_ansi_red: convert(terminal.red),
+            terminal_ansi_bright_red: convert(terminal.bright_red),
+            terminal_ansi_dim_red: convert(terminal.dim_red),
             terminal_ansi_green: convert(terminal.green),
+            terminal_ansi_bright_green: convert(terminal.bright_green),
+            terminal_ansi_dim_green: convert(terminal.dim_green),
             terminal_ansi_yellow: convert(terminal.yellow),
+            terminal_ansi_bright_yellow: convert(terminal.bright_yellow),
+            terminal_ansi_dim_yellow: convert(terminal.dim_yellow),
             terminal_ansi_blue: convert(terminal.blue),
+            terminal_ansi_bright_blue: convert(terminal.bright_blue),
+            terminal_ansi_dim_blue: convert(terminal.dim_blue),
             terminal_ansi_magenta: convert(terminal.magenta),
+            terminal_ansi_bright_magenta: convert(terminal.bright_magenta),
+            terminal_ansi_dim_magenta: convert(terminal.dim_magenta),
             terminal_ansi_cyan: convert(terminal.cyan),
+            terminal_ansi_bright_cyan: convert(terminal.bright_cyan),
+            terminal_ansi_dim_cyan: convert(terminal.dim_cyan),
             terminal_ansi_white: convert(terminal.white),
+            terminal_ansi_bright_white: convert(terminal.bright_white),
+            terminal_ansi_dim_white: convert(terminal.dim_white),
             link_text_hover: convert(highest.accent.default.foreground),
         })
     }

crates/ui/src/components/avatar/avatar.rs ๐Ÿ”—

@@ -44,7 +44,7 @@ impl Avatar {
 
     /// Sets the shape of the avatar image.
     ///
-    /// This method allows the shape of the avatar to be specified using a [`Shape`].
+    /// This method allows the shape of the avatar to be specified using an [`AvatarShape`].
     /// It modifies the corner radius of the image to match the specified shape.
     ///
     /// # Examples

crates/ui/src/components/avatar/avatar_availability_indicator.rs ๐Ÿ”—

@@ -20,7 +20,7 @@ impl AvatarAvailabilityIndicator {
         }
     }
 
-    /// Sets the size of the [`Avatar`] this indicator appears on.
+    /// Sets the size of the [`Avatar`](crate::Avatar) this indicator appears on.
     pub fn avatar_size(mut self, size: impl Into<Option<Pixels>>) -> Self {
         self.avatar_size = size.into();
         self

crates/ui/src/components/button/button_like.rs ๐Ÿ”—

@@ -407,8 +407,8 @@ impl VisibleOnHover for ButtonLike {
 }
 
 impl ParentElement for ButtonLike {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
-        &mut self.children
+    fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
+        self.children.extend(elements)
     }
 }
 

crates/ui/src/components/context_menu.rs ๐Ÿ”—

@@ -27,6 +27,7 @@ enum ContextMenuItem {
 pub struct ContextMenu {
     items: Vec<ContextMenuItem>,
     focus_handle: FocusHandle,
+    action_context: Option<FocusHandle>,
     selected_index: Option<usize>,
     delayed: bool,
     clicked: bool,
@@ -41,6 +42,8 @@ impl FocusableView for ContextMenu {
 
 impl EventEmitter<DismissEvent> for ContextMenu {}
 
+impl FluentBuilder for ContextMenu {}
+
 impl ContextMenu {
     pub fn build(
         cx: &mut WindowContext,
@@ -56,6 +59,7 @@ impl ContextMenu {
                 Self {
                     items: Default::default(),
                     focus_handle,
+                    action_context: None,
                     selected_index: None,
                     delayed: false,
                     clicked: false,
@@ -66,6 +70,11 @@ impl ContextMenu {
         })
     }
 
+    pub fn context(mut self, focus: FocusHandle) -> Self {
+        self.action_context = Some(focus);
+        self
+    }
+
     pub fn header(mut self, title: impl Into<SharedString>) -> Self {
         self.items.push(ContextMenuItem::Header(title.into()));
         self
@@ -305,7 +314,14 @@ impl Render for ContextMenu {
                                         .child(label_element)
                                         .debug_selector(|| format!("MENU_ITEM-{}", label))
                                         .children(action.as_ref().and_then(|action| {
-                                            KeyBinding::for_action(&**action, cx)
+                                            self.action_context
+                                                .as_ref()
+                                                .map(|focus| {
+                                                    KeyBinding::for_action_in(&**action, focus, cx)
+                                                })
+                                                .unwrap_or_else(|| {
+                                                    KeyBinding::for_action(&**action, cx)
+                                                })
                                                 .map(|binding| div().ml_1().child(binding))
                                         })),
                                 )

crates/ui/src/components/keybinding.rs ๐Ÿ”—

@@ -16,7 +16,7 @@ impl RenderOnce for KeyBinding {
             .flex_none()
             .gap_2()
             .children(self.key_binding.keystrokes().iter().map(|keystroke| {
-                let key_icon = Self::icon_for_key(&keystroke);
+                let key_icon = Self::icon_for_key(keystroke);
 
                 h_flex()
                     .flex_none()

crates/ui/src/components/label/label_like.rs ๐Ÿ”—

@@ -78,8 +78,8 @@ impl LabelCommon for LabelLike {
 }
 
 impl ParentElement for LabelLike {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
-        &mut self.children
+    fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
+        self.children.extend(elements)
     }
 }
 

crates/ui/src/components/list/list.rs ๐Ÿ”—

@@ -40,8 +40,8 @@ impl List {
 }
 
 impl ParentElement for List {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
-        &mut self.children
+    fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
+        self.children.extend(elements)
     }
 }
 

crates/ui/src/components/list/list_item.rs ๐Ÿ”—

@@ -141,8 +141,8 @@ impl Selectable for ListItem {
 }
 
 impl ParentElement for ListItem {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
-        &mut self.children
+    fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
+        self.children.extend(elements)
     }
 }
 
@@ -222,17 +222,19 @@ impl RenderOnce for ListItem {
                     }))
                     .child(
                         h_flex()
-                            // HACK: We need to set *any* width value here in order for this container to size correctly.
-                            // Without this the `h_flex` will overflow the parent `inner_list_item`.
-                            .w_px()
-                            .flex_1()
+                            .flex_grow()
+                            .flex_shrink_0()
+                            .flex_basis(relative(0.25))
                             .gap_1()
+                            .overflow_hidden()
                             .children(self.start_slot)
                             .children(self.children),
                     )
                     .when_some(self.end_slot, |this, end_slot| {
                         this.justify_between().child(
                             h_flex()
+                                .flex_shrink()
+                                .overflow_hidden()
                                 .when(self.end_hover_slot.is_some(), |this| {
                                     this.visible()
                                         .group_hover("list_item", |this| this.invisible())

crates/ui/src/components/popover.rs ๐Ÿ”—

@@ -16,7 +16,7 @@ use smallvec::SmallVec;
 ///
 /// Related elements:
 ///
-/// `ContextMenu`:
+/// [`ContextMenu`](crate::ContextMenu):
 ///
 /// Used to display a popover menu that only contains a list of items. Context menus are always
 /// launched by secondary clicking on an element. The menu is positioned relative to the user's cursor.
@@ -74,7 +74,7 @@ impl Popover {
 }
 
 impl ParentElement for Popover {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
-        &mut self.children
+    fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
+        self.children.extend(elements)
     }
 }

crates/ui/src/components/popover_menu.rs ๐Ÿ”—

@@ -1,9 +1,10 @@
 use std::{cell::RefCell, rc::Rc};
 
 use gpui::{
-    overlay, point, px, rems, AnchorCorner, AnyElement, Bounds, DismissEvent, DispatchPhase,
-    Element, ElementId, InteractiveBounds, IntoElement, LayoutId, ManagedView, MouseDownEvent,
-    ParentElement, Pixels, Point, View, VisualContext, WindowContext,
+    overlay, point, prelude::FluentBuilder, px, rems, AnchorCorner, AnyElement, Bounds,
+    DismissEvent, DispatchPhase, Element, ElementContext, ElementId, InteractiveBounds,
+    IntoElement, LayoutId, ManagedView, MouseDownEvent, ParentElement, Pixels, Point, View,
+    VisualContext, WindowContext,
 };
 
 use crate::{Clickable, Selectable};
@@ -51,7 +52,7 @@ impl<M: ManagedView> PopoverMenu<M> {
                             cx.subscribe(&new_menu, move |modal, _: &DismissEvent, cx| {
                                 if modal.focus_handle(cx).contains_focused(cx) {
                                     if previous_focus_handle.is_some() {
-                                        cx.focus(&previous_focus_handle.as_ref().unwrap())
+                                        cx.focus(previous_focus_handle.as_ref().unwrap())
                                     }
                                 }
                                 *menu2.borrow_mut() = None;
@@ -134,7 +135,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
     fn request_layout(
         &mut self,
         element_state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (gpui::LayoutId, Self::State) {
         let mut menu_layout_id = None;
 
@@ -188,7 +189,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
         &mut self,
         _: Bounds<gpui::Pixels>,
         element_state: &mut Self::State,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         if let Some(mut child) = element_state.child_element.take() {
             child.paint(cx);

crates/ui/src/components/right_click_menu.rs ๐Ÿ”—

@@ -1,9 +1,9 @@
 use std::{cell::RefCell, rc::Rc};
 
 use gpui::{
-    overlay, AnchorCorner, AnyElement, BorrowWindow, Bounds, DismissEvent, DispatchPhase, Element,
-    ElementId, InteractiveBounds, IntoElement, LayoutId, ManagedView, MouseButton, MouseDownEvent,
-    ParentElement, Pixels, Point, View, VisualContext, WindowContext,
+    overlay, AnchorCorner, AnyElement, Bounds, DismissEvent, DispatchPhase, Element,
+    ElementContext, ElementId, InteractiveBounds, IntoElement, LayoutId, ManagedView, MouseButton,
+    MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext, WindowContext,
 };
 
 pub struct RightClickMenu<M: ManagedView> {
@@ -64,7 +64,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
     fn request_layout(
         &mut self,
         element_state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (gpui::LayoutId, Self::State) {
         let (menu, position) = if let Some(element_state) = element_state {
             (element_state.menu, element_state.position)
@@ -116,7 +116,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
         &mut self,
         bounds: Bounds<gpui::Pixels>,
         element_state: &mut Self::State,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         if let Some(mut child) = element_state.child_element.take() {
             child.paint(cx);
@@ -155,7 +155,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
                 cx.subscribe(&new_menu, move |modal, _: &DismissEvent, cx| {
                     if modal.focus_handle(cx).contains_focused(cx) {
                         if previous_focus_handle.is_some() {
-                            cx.focus(&previous_focus_handle.as_ref().unwrap())
+                            cx.focus(previous_focus_handle.as_ref().unwrap())
                         }
                     }
                     *menu2.borrow_mut() = None;

crates/ui/src/components/stories/list_item.rs ๐Ÿ”—

@@ -4,6 +4,8 @@ use story::Story;
 use crate::{prelude::*, Avatar};
 use crate::{IconName, ListItem};
 
+const OVERFLOWING_TEXT: &'static str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean mauris ligula, luctus vel dignissim eu, vestibulum sed libero. Sed at convallis velit.";
+
 pub struct ListItemStory;
 
 impl Render for ListItemStory {
@@ -98,5 +100,29 @@ impl Render for ListItemStory {
                         println!("Right mouse down!");
                     }),
             )
+            .child(Story::label("With overflowing content in the `end_slot`"))
+            .child(
+                ListItem::new("with_overflowing_content_in_end_slot")
+                    .child("An excerpt")
+                    .end_slot(Label::new(OVERFLOWING_TEXT).color(Color::Muted)),
+            )
+            .child(Story::label(
+                "`inset` with overflowing content in the `end_slot`",
+            ))
+            .child(
+                ListItem::new("inset_with_overflowing_content_in_end_slot")
+                    .inset(true)
+                    .child("An excerpt")
+                    .end_slot(Label::new(OVERFLOWING_TEXT).color(Color::Muted)),
+            )
+            .child(Story::label(
+                "`inset` with overflowing content in `children` and `end_slot`",
+            ))
+            .child(
+                ListItem::new("inset_with_overflowing_content_in_children_and_end_slot")
+                    .inset(true)
+                    .child(Label::new(OVERFLOWING_TEXT))
+                    .end_slot(Label::new(OVERFLOWING_TEXT).color(Color::Muted)),
+            )
     }
 }

crates/ui/src/components/tab.rs ๐Ÿ”—

@@ -92,8 +92,8 @@ impl Selectable for Tab {
 }
 
 impl ParentElement for Tab {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
-        &mut self.children
+    fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
+        self.children.extend(elements)
     }
 }
 

crates/ui/src/components/tab_bar.rs ๐Ÿ”—

@@ -83,8 +83,8 @@ impl TabBar {
 }
 
 impl ParentElement for TabBar {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
-        &mut self.children
+    fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
+        self.children.extend(elements)
     }
 }
 
@@ -117,7 +117,7 @@ impl RenderOnce for TabBar {
                     .relative()
                     .flex_1()
                     .h_full()
-                    .overflow_hidden_x()
+                    .overflow_x_hidden()
                     .child(
                         div()
                             .absolute()

crates/ui/src/components/tooltip.rs ๐Ÿ”—

@@ -69,29 +69,74 @@ impl Tooltip {
 
 impl Render for Tooltip {
     fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
-        overlay().child(
-            // padding to avoid mouse cursor
-            div().pl_2().pt_2p5().child(
-                v_flex()
-                    .elevation_2(cx)
-                    .font(ui_font)
-                    .text_ui()
-                    .text_color(cx.theme().colors().text)
-                    .py_1()
-                    .px_2()
-                    .child(
-                        h_flex()
-                            .gap_4()
-                            .child(self.title.clone())
-                            .when_some(self.key_binding.clone(), |this, key_binding| {
-                                this.justify_between().child(key_binding)
-                            }),
-                    )
-                    .when_some(self.meta.clone(), |this, meta| {
-                        this.child(Label::new(meta).size(LabelSize::Small).color(Color::Muted))
+        tooltip_container(cx, |el, _| {
+            el.child(
+                h_flex()
+                    .gap_4()
+                    .child(self.title.clone())
+                    .when_some(self.key_binding.clone(), |this, key_binding| {
+                        this.justify_between().child(key_binding)
                     }),
-            ),
-        )
+            )
+            .when_some(self.meta.clone(), |this, meta| {
+                this.child(Label::new(meta).size(LabelSize::Small).color(Color::Muted))
+            })
+        })
+    }
+}
+
+fn tooltip_container<V>(
+    cx: &mut ViewContext<V>,
+    f: impl FnOnce(Div, &mut ViewContext<V>) -> Div,
+) -> impl IntoElement {
+    let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
+    overlay().child(
+        // padding to avoid mouse cursor
+        div().pl_2().pt_2p5().child(
+            v_flex()
+                .elevation_2(cx)
+                .font(ui_font)
+                .text_ui()
+                .text_color(cx.theme().colors().text)
+                .py_1()
+                .px_2()
+                .map(|el| f(el, cx)),
+        ),
+    )
+}
+
+pub struct LinkPreview {
+    link: SharedString,
+}
+
+impl LinkPreview {
+    pub fn new(url: &str, cx: &mut WindowContext) -> AnyView {
+        let mut wrapped_url = String::new();
+        for (i, ch) in url.chars().enumerate() {
+            if i == 500 {
+                wrapped_url.push('โ€ฆ');
+                break;
+            }
+            if i % 100 == 0 && i != 0 {
+                wrapped_url.push('\n');
+            }
+            wrapped_url.push(ch);
+        }
+        cx.new_view(|_cx| LinkPreview {
+            link: wrapped_url.into(),
+        })
+        .into()
+    }
+}
+
+impl Render for LinkPreview {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+        tooltip_container(cx, |el, _| {
+            el.child(
+                Label::new(self.link.clone())
+                    .size(LabelSize::XSmall)
+                    .color(Color::Muted),
+            )
+        })
     }
 }

crates/ui/src/prelude.rs ๐Ÿ”—

@@ -2,9 +2,9 @@
 
 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, DefiniteLength, Div, Element, ElementContext,
+    ElementId, InteractiveElement, ParentElement, Pixels, Rems, RenderOnce, SharedString, Styled,
+    ViewContext, WindowContext,
 };
 
 pub use crate::clickable::*;

crates/ui/src/styles/elevation.rs ๐Ÿ”—

@@ -20,7 +20,7 @@ pub enum ElevationIndex {
 }
 
 impl ElevationIndex {
-    pub fn z_index(self) -> u8 {
+    pub fn z_index(self) -> u16 {
         match self {
             ElevationIndex::Background => 0,
             ElevationIndex::Surface => 42,

crates/util/src/http.rs ๐Ÿ”—

@@ -99,6 +99,15 @@ impl FakeHttpClient {
                 .unwrap())
         })
     }
+
+    pub fn with_200_response() -> Arc<dyn HttpClient> {
+        Self::create(|_| async move {
+            Ok(Response::builder()
+                .status(200)
+                .body(Default::default())
+                .unwrap())
+        })
+    }
 }
 
 #[cfg(feature = "test-support")]

crates/util/src/util.rs ๐Ÿ”—

@@ -7,19 +7,21 @@ pub mod paths;
 #[cfg(any(test, feature = "test-support"))]
 pub mod test;
 
+pub use backtrace::Backtrace;
+use futures::Future;
+use lazy_static::lazy_static;
+use rand::{seq::SliceRandom, Rng};
 use std::{
     borrow::Cow,
     cmp::{self, Ordering},
+    env,
     ops::{AddAssign, Range, RangeInclusive},
     panic::Location,
     pin::Pin,
     task::{Context, Poll},
+    time::Instant,
 };
 
-pub use backtrace::Backtrace;
-use futures::Future;
-use rand::{seq::SliceRandom, Rng};
-
 pub use take_until::*;
 
 #[macro_export]
@@ -133,6 +135,24 @@ pub fn merge_non_null_json_value_into(source: serde_json::Value, target: &mut se
     }
 }
 
+pub fn measure<R>(label: &str, f: impl FnOnce() -> R) -> R {
+    lazy_static! {
+        pub static ref ZED_MEASUREMENTS: bool = env::var("ZED_MEASUREMENTS")
+            .map(|measurements| measurements == "1" || measurements == "true")
+            .unwrap_or(false);
+    }
+
+    if *ZED_MEASUREMENTS {
+        let start = Instant::now();
+        let result = f();
+        let elapsed = start.elapsed();
+        eprintln!("{}: {:?}", label, elapsed);
+        result
+    } else {
+        f()
+    }
+}
+
 pub trait ResultExt<E> {
     type Ok;
 
@@ -299,7 +319,7 @@ pub struct Deferred<F: FnOnce()>(Option<F>);
 
 impl<F: FnOnce()> Deferred<F> {
     /// Drop without running the deferred function.
-    pub fn cancel(mut self) {
+    pub fn abort(mut self) {
         self.0.take();
     }
 }

crates/vim/Cargo.toml ๐Ÿ”—

@@ -23,6 +23,7 @@ async-trait = { workspace = true, "optional" = true }
 nvim-rs = { git = "https://github.com/KillTheMule/nvim-rs", branch = "master", features = ["use_tokio"], optional = true }
 tokio = { version = "1.15", "optional" = true }
 serde_json.workspace = true
+regex.workspace = true
 
 collections = { path = "../collections" }
 command_palette = { path = "../command_palette" }

crates/vim/README.md ๐Ÿ”—

@@ -0,0 +1,36 @@
+This contains the code for Zed's Vim emulation mode.
+
+Vim mode in Zed is supposed to primarily "do what you expect": it mostly tries to copy vim exactly, but will use Zed-specific functionality when available to make things smoother. This means Zed will never be 100% vim compatible, but should be 100% vim familiar!
+
+The backlog is maintained in the `#vim` channel notes.
+
+## Testing against Neovim
+
+If you are making a change to make Zed's behaviour more closely match vim/nvim, you can create a test using the `NeovimBackedTestContext`.
+
+For example, the following test checks that Zed and Neovim have the same behaviour when running `*` in visual mode:
+
+```rust
+#[gpui::test]
+async fn test_visual_star_hash(cx: &mut gpui::TestAppContext) {
+    let mut cx = NeovimBackedTestContext::new(cx).await;
+
+    cx.set_shared_state("ห‡a.c. abcd a.c. abcd").await;
+    cx.simulate_shared_keystrokes(["v", "3", "l", "*"]).await;
+    cx.assert_shared_state("a.c. abcd ห‡a.c. abcd").await;
+}
+```
+
+To keep CI runs fast, by default the neovim tests use a cached JSON file that records what neovim did (see crates/vim/test_data),
+but while developing this test you'll need to run it with the neovim flag enabled:
+
+```
+cargo test -p vim --features neovim test_visual_star_hash
+```
+
+This will run your keystrokes against a headless neovim and cache the results in the test_data directory.
+
+
+## Testing zed-only behaviour
+
+Zed does more than vim/neovim in their default modes. The `VimTestContext` can be used instead. This lets you test integration with the language server and other parts of zed's UI that don't have a NeoVim equivalent.

crates/vim/src/motion.rs ๐Ÿ”—

@@ -11,6 +11,7 @@ use workspace::Workspace;
 use crate::{
     normal::normal_motion,
     state::{Mode, Operator},
+    utils::coerce_punctuation,
     visual::visual_motion,
     Vim,
 };
@@ -72,9 +73,9 @@ pub(crate) struct Up {
 
 #[derive(Clone, Deserialize, PartialEq)]
 #[serde(rename_all = "camelCase")]
-struct Down {
+pub(crate) struct Down {
     #[serde(default)]
-    display_lines: bool,
+    pub(crate) display_lines: bool,
 }
 
 #[derive(Clone, Deserialize, PartialEq)]
@@ -680,8 +681,8 @@ pub(crate) fn next_word_start(
     for _ in 0..times {
         let mut crossed_newline = false;
         point = movement::find_boundary(map, point, FindRange::MultiLine, |left, right| {
-            let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation);
-            let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation);
+            let left_kind = coerce_punctuation(char_kind(&scope, left), ignore_punctuation);
+            let right_kind = coerce_punctuation(char_kind(&scope, right), ignore_punctuation);
             let at_newline = right == '\n';
 
             let found = (left_kind != right_kind && right_kind != CharKind::Whitespace)
@@ -710,8 +711,8 @@ fn next_word_end(
             *point.column_mut() = 0;
         }
         point = movement::find_boundary(map, point, FindRange::MultiLine, |left, right| {
-            let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation);
-            let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation);
+            let left_kind = coerce_punctuation(char_kind(&scope, left), ignore_punctuation);
+            let right_kind = coerce_punctuation(char_kind(&scope, right), ignore_punctuation);
 
             left_kind != right_kind && left_kind != CharKind::Whitespace
         });
@@ -743,8 +744,8 @@ fn previous_word_start(
         // cursor because the newline is checked only once.
         point =
             movement::find_preceding_boundary(map, point, FindRange::MultiLine, |left, right| {
-                let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation);
-                let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation);
+                let left_kind = coerce_punctuation(char_kind(&scope, left), ignore_punctuation);
+                let right_kind = coerce_punctuation(char_kind(&scope, right), ignore_punctuation);
 
                 (left_kind != right_kind && !right.is_whitespace()) || left == '\n'
             });

crates/vim/src/normal/change.rs ๐Ÿ”—

@@ -1,4 +1,10 @@
-use crate::{motion::Motion, object::Object, state::Mode, utils::copy_selections_content, Vim};
+use crate::{
+    motion::Motion,
+    object::Object,
+    state::Mode,
+    utils::{coerce_punctuation, copy_selections_content},
+    Vim,
+};
 use editor::{
     display_map::DisplaySnapshot,
     movement::{self, FindRange, TextLayoutDetails},
@@ -102,9 +108,9 @@ fn expand_changed_word_selection(
         if in_word {
             selection.end =
                 movement::find_boundary(map, selection.end, FindRange::MultiLine, |left, right| {
-                    let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation);
+                    let left_kind = coerce_punctuation(char_kind(&scope, left), ignore_punctuation);
                     let right_kind =
-                        char_kind(&scope, right).coerce_punctuation(ignore_punctuation);
+                        coerce_punctuation(char_kind(&scope, right), ignore_punctuation);
 
                     left_kind != right_kind && left_kind != CharKind::Whitespace
                 });

crates/vim/src/normal/search.rs ๐Ÿ”—

@@ -3,7 +3,12 @@ use search::{buffer_search, BufferSearchBar, SearchMode, SearchOptions};
 use serde_derive::Deserialize;
 use workspace::{searchable::Direction, Workspace};
 
-use crate::{motion::Motion, normal::move_cursor, state::SearchState, Vim};
+use crate::{
+    motion::Motion,
+    normal::move_cursor,
+    state::{Mode, SearchState},
+    Vim,
+};
 
 #[derive(Clone, Deserialize, PartialEq)]
 #[serde(rename_all = "camelCase")]
@@ -91,7 +96,6 @@ fn search(workspace: &mut Workspace, action: &Search, cx: &mut ViewContext<Works
 
                     if query.is_empty() {
                         search_bar.set_replacement(None, cx);
-                        search_bar.set_search_options(SearchOptions::CASE_SENSITIVE, cx);
                         search_bar.activate_search_mode(SearchMode::Regex, cx);
                     }
                     vim.workspace_state.search = SearchState {
@@ -146,18 +150,23 @@ pub fn move_to_internal(
     Vim::update(cx, |vim, cx| {
         let pane = workspace.active_pane().clone();
         let count = vim.take_count(cx).unwrap_or(1);
+
         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| {
-                    let mut options = SearchOptions::CASE_SENSITIVE;
-                    options.set(SearchOptions::WHOLE_WORD, whole_word);
-                    if search_bar.show(cx) {
-                        search_bar
-                            .query_suggestion(cx)
-                            .map(|query| search_bar.search(&query, Some(options), cx))
-                    } else {
-                        None
+                    let options = SearchOptions::CASE_SENSITIVE;
+                    if !search_bar.show(cx) {
+                        return None;
+                    }
+                    let Some(query) = search_bar.query_suggestion(cx) else {
+                        return None;
+                    };
+                    let mut query = regex::escape(&query);
+                    if whole_word {
+                        query = format!(r"\b{}\b", query);
                     }
+                    search_bar.activate_search_mode(SearchMode::Regex, cx);
+                    Some(search_bar.search(&query, Some(options), cx))
                 });
 
                 if let Some(search) = search {
@@ -173,6 +182,11 @@ pub fn move_to_internal(
                 }
             }
         });
+
+        if vim.state().mode.is_visual() {
+            vim.switch_mode(Mode::Normal, false, cx)
+        }
+
         vim.clear_operator(cx);
     });
 }
@@ -350,7 +364,10 @@ mod test {
     use editor::DisplayPoint;
     use search::BufferSearchBar;
 
-    use crate::{state::Mode, test::VimTestContext};
+    use crate::{
+        state::Mode,
+        test::{NeovimBackedTestContext, VimTestContext},
+    };
 
     #[gpui::test]
     async fn test_move_to_next(cx: &mut gpui::TestAppContext) {
@@ -459,6 +476,13 @@ mod test {
         cx.simulate_keystrokes(["/", "b"]);
         cx.simulate_keystrokes(["enter"]);
         cx.assert_state("aa\nห‡bb\ndd\ncc\nbb\n", Mode::Normal);
+
+        // check that searching switches to normal mode if in visual mode
+        cx.set_state("ห‡one two one", Mode::Normal);
+        cx.simulate_keystrokes(["v", "l", "l"]);
+        cx.assert_editor_state("ยซoneห‡ยป two one");
+        cx.simulate_keystrokes(["*"]);
+        cx.assert_state("one two ห‡one", Mode::Normal);
     }
 
     #[gpui::test]
@@ -474,4 +498,14 @@ mod test {
         cx.simulate_keystrokes(["shift-enter"]);
         cx.assert_editor_state("ยซoneห‡ยป one one one");
     }
+
+    #[gpui::test]
+    async fn test_visual_star_hash(cx: &mut gpui::TestAppContext) {
+        let mut cx = NeovimBackedTestContext::new(cx).await;
+
+        cx.set_shared_state("ห‡a.c. abcd a.c. abcd").await;
+        cx.simulate_shared_keystrokes(["v", "3", "l", "*"]).await;
+        cx.assert_shared_state("a.c. abcd ห‡a.c. abcd").await;
+        cx.assert_shared_mode(Mode::Normal).await;
+    }
 }

crates/vim/src/object.rs ๐Ÿ”—

@@ -10,7 +10,10 @@ use language::{char_kind, CharKind, Selection};
 use serde::Deserialize;
 use workspace::Workspace;
 
-use crate::{motion::right, normal::normal_object, state::Mode, visual::visual_object, Vim};
+use crate::{
+    motion::right, normal::normal_object, state::Mode, utils::coerce_punctuation,
+    visual::visual_object, Vim,
+};
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum Object {
@@ -195,9 +198,10 @@ impl Object {
     }
 }
 
-/// Return a range that surrounds the word relative_to is in
-/// If relative_to is at the start of a word, return the word.
-/// If relative_to is between words, return the space between
+/// Returns a range that surrounds the word `relative_to` is in.
+///
+/// If `relative_to` is at the start of a word, return the word.
+/// If `relative_to` is between words, return the space between.
 fn in_word(
     map: &DisplaySnapshot,
     relative_to: DisplayPoint,
@@ -212,24 +216,25 @@ fn in_word(
         right(map, relative_to, 1),
         movement::FindRange::SingleLine,
         |left, right| {
-            char_kind(&scope, left).coerce_punctuation(ignore_punctuation)
-                != char_kind(&scope, right).coerce_punctuation(ignore_punctuation)
+            coerce_punctuation(char_kind(&scope, left), ignore_punctuation)
+                != coerce_punctuation(char_kind(&scope, right), ignore_punctuation)
         },
     );
 
     let end = movement::find_boundary(map, relative_to, FindRange::SingleLine, |left, right| {
-        char_kind(&scope, left).coerce_punctuation(ignore_punctuation)
-            != char_kind(&scope, right).coerce_punctuation(ignore_punctuation)
+        coerce_punctuation(char_kind(&scope, left), ignore_punctuation)
+            != coerce_punctuation(char_kind(&scope, right), ignore_punctuation)
     });
 
     Some(start..end)
 }
 
-/// Return a range that surrounds the word and following whitespace
+/// Returns a range that surrounds the word and following whitespace
 /// relative_to is in.
-/// If relative_to is at the start of a word, return the word and following whitespace.
-/// If relative_to is between words, return the whitespace back and the following word
-
+///
+/// If `relative_to` is at the start of a word, return the word and following whitespace.
+/// If `relative_to` is between words, return the whitespace back and the following word.
+///
 /// if in word
 ///   delete that word
 ///   if there is whitespace following the word, delete that as well
@@ -281,15 +286,15 @@ fn around_next_word(
         right(map, relative_to, 1),
         FindRange::SingleLine,
         |left, right| {
-            char_kind(&scope, left).coerce_punctuation(ignore_punctuation)
-                != char_kind(&scope, right).coerce_punctuation(ignore_punctuation)
+            coerce_punctuation(char_kind(&scope, left), ignore_punctuation)
+                != coerce_punctuation(char_kind(&scope, right), ignore_punctuation)
         },
     );
 
     let mut word_found = false;
     let end = movement::find_boundary(map, relative_to, FindRange::MultiLine, |left, right| {
-        let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation);
-        let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation);
+        let left_kind = coerce_punctuation(char_kind(&scope, left), ignore_punctuation);
+        let right_kind = coerce_punctuation(char_kind(&scope, right), ignore_punctuation);
 
         let found = (word_found && left_kind != right_kind) || right == '\n' && left == '\n';
 

crates/vim/src/test.rs ๐Ÿ”—

@@ -3,8 +3,11 @@ mod neovim_backed_test_context;
 mod neovim_connection;
 mod vim_test_context;
 
+use std::time::Duration;
+
 use command_palette::CommandPalette;
 use editor::DisplayPoint;
+use gpui::KeyBinding;
 pub use neovim_backed_binding_test_context::*;
 pub use neovim_backed_test_context::*;
 pub use vim_test_context::*;
@@ -12,7 +15,7 @@ pub use vim_test_context::*;
 use indoc::indoc;
 use search::BufferSearchBar;
 
-use crate::{state::Mode, ModeIndicator};
+use crate::{insert::NormalBefore, motion, state::Mode, ModeIndicator};
 
 #[gpui::test]
 async fn test_initially_disabled(cx: &mut gpui::TestAppContext) {
@@ -774,3 +777,73 @@ async fn test_select_all_issue_2170(cx: &mut gpui::TestAppContext) {
         Mode::Visual,
     );
 }
+
+#[gpui::test]
+async fn test_jk(cx: &mut gpui::TestAppContext) {
+    let mut cx = NeovimBackedTestContext::new(cx).await;
+
+    cx.update(|cx| {
+        cx.bind_keys([KeyBinding::new(
+            "j k",
+            NormalBefore,
+            Some("vim_mode == insert"),
+        )])
+    });
+    cx.neovim.exec("imap jk <esc>").await;
+
+    cx.set_shared_state("ห‡hello").await;
+    cx.simulate_shared_keystrokes(["i", "j", "o", "j", "k"])
+        .await;
+    cx.assert_shared_state("jห‡ohello").await;
+}
+
+#[gpui::test]
+async fn test_jk_delay(cx: &mut gpui::TestAppContext) {
+    let mut cx = VimTestContext::new(cx, true).await;
+
+    cx.update(|cx| {
+        cx.bind_keys([KeyBinding::new(
+            "j k",
+            NormalBefore,
+            Some("vim_mode == insert"),
+        )])
+    });
+
+    cx.set_state("ห‡hello", Mode::Normal);
+    cx.simulate_keystrokes(["i", "j"]);
+    cx.executor().advance_clock(Duration::from_millis(500));
+    cx.run_until_parked();
+    cx.assert_state("ห‡hello", Mode::Insert);
+    cx.executor().advance_clock(Duration::from_millis(500));
+    cx.run_until_parked();
+    cx.assert_state("jห‡hello", Mode::Insert);
+    cx.simulate_keystrokes(["k", "j", "k"]);
+    cx.assert_state("jห‡khello", Mode::Normal);
+}
+
+#[gpui::test]
+async fn test_comma_w(cx: &mut gpui::TestAppContext) {
+    let mut cx = NeovimBackedTestContext::new(cx).await;
+
+    cx.update(|cx| {
+        cx.bind_keys([KeyBinding::new(
+            ", w",
+            motion::Down {
+                display_lines: false,
+            },
+            Some("vim_mode == normal"),
+        )])
+    });
+    cx.neovim.exec("map ,w j").await;
+
+    cx.set_shared_state("ห‡hello hello\nhello hello").await;
+    cx.simulate_shared_keystrokes(["f", "o", ";", ",", "w"])
+        .await;
+    cx.assert_shared_state("hello hello\nhello hellห‡o").await;
+
+    cx.set_shared_state("ห‡hello hello\nhello hello").await;
+    cx.simulate_shared_keystrokes(["f", "o", ";", ",", "i"])
+        .await;
+    cx.assert_shared_state("hellห‡o hello\nhello hello").await;
+    cx.assert_shared_mode(Mode::Insert).await;
+}

crates/vim/src/test/neovim_backed_test_context.rs ๐Ÿ”—

@@ -52,7 +52,7 @@ pub struct NeovimBackedTestContext {
     // Lookup for exempted assertions. Keyed by the insertion text, and with a value indicating which
     // bindings are exempted. If None, all bindings are ignored for that insertion text.
     exemptions: HashMap<String, Option<HashSet<String>>>,
-    neovim: NeovimConnection,
+    pub(crate) neovim: NeovimConnection,
 
     last_set_state: Option<String>,
     recent_keystrokes: Vec<String>,
@@ -277,6 +277,24 @@ impl NeovimBackedTestContext {
         self.neovim.mode().await.unwrap()
     }
 
+    pub async fn assert_shared_mode(&mut self, mode: Mode) {
+        let neovim = self.neovim_mode().await;
+        let editor = self.cx.mode();
+
+        if neovim != mode || editor != mode {
+            panic!(
+                indoc! {"Test failed (zed does not match nvim behaviour)
+                    # desired mode:
+                    {:?}
+                    # neovim mode:
+                    {:?}
+                    # zed mode:
+                    {:?}"},
+                mode, neovim, editor,
+            )
+        }
+    }
+
     pub async fn assert_state_matches(&mut self) {
         self.is_dirty = false;
         let neovim = self.neovim_state().await;

crates/vim/src/test/neovim_connection.rs ๐Ÿ”—

@@ -42,6 +42,7 @@ pub enum NeovimData {
     Key(String),
     Get { state: String, mode: Option<Mode> },
     ReadRegister { name: char, value: String },
+    Exec { command: String },
     SetOption { value: String },
 }
 
@@ -269,6 +270,32 @@ impl NeovimConnection {
         );
     }
 
+    #[cfg(feature = "neovim")]
+    pub async fn exec(&mut self, value: &str) {
+        self.nvim
+            .command_output(format!("{}", value).as_str())
+            .await
+            .unwrap();
+
+        self.data.push_back(NeovimData::Exec {
+            command: value.to_string(),
+        })
+    }
+
+    #[cfg(not(feature = "neovim"))]
+    pub async fn exec(&mut self, value: &str) {
+        if let Some(NeovimData::Get { .. }) = self.data.front() {
+            self.data.pop_front();
+        };
+        assert_eq!(
+            self.data.pop_front(),
+            Some(NeovimData::Exec {
+                command: value.to_string(),
+            }),
+            "operation does not match recorded script. re-record with --features=neovim"
+        );
+    }
+
     #[cfg(not(feature = "neovim"))]
     pub async fn read_register(&mut self, register: char) -> String {
         if let Some(NeovimData::Get { .. }) = self.data.front() {

crates/vim/src/utils.rs ๐Ÿ”—

@@ -1,6 +1,6 @@
 use editor::{ClipboardSelection, Editor};
 use gpui::{AppContext, ClipboardItem};
-use language::Point;
+use language::{CharKind, Point};
 
 pub fn copy_selections_content(editor: &mut Editor, linewise: bool, cx: &mut AppContext) {
     let selections = editor.selections.all_adjusted(cx);
@@ -48,3 +48,11 @@ pub fn copy_selections_content(editor: &mut Editor, linewise: bool, cx: &mut App
 
     cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
 }
+
+pub fn coerce_punctuation(kind: CharKind, treat_punctuation_as_word: bool) -> CharKind {
+    if treat_punctuation_as_word && kind == CharKind::Punctuation {
+        CharKind::Word
+    } else {
+        kind
+    }
+}

crates/vim/test_data/test_comma_w.json ๐Ÿ”—

@@ -0,0 +1,15 @@
+{"Exec":{"command":"map ,w j"}}
+{"Put":{"state":"ห‡hello hello\nhello hello"}}
+{"Key":"f"}
+{"Key":"o"}
+{"Key":";"}
+{"Key":","}
+{"Key":"w"}
+{"Get":{"state":"hello hello\nhello hellห‡o","mode":"Normal"}}
+{"Put":{"state":"ห‡hello hello\nhello hello"}}
+{"Key":"f"}
+{"Key":"o"}
+{"Key":";"}
+{"Key":","}
+{"Key":"i"}
+{"Get":{"state":"hellห‡o hello\nhello hello","mode":"Insert"}}

crates/vim/test_data/test_jk.json ๐Ÿ”—

@@ -0,0 +1,8 @@
+{"Exec":{"command":"imap jk <esc>"}}
+{"Put":{"state":"ห‡hello"}}
+{"Key":"i"}
+{"Key":"j"}
+{"Key":"o"}
+{"Key":"j"}
+{"Key":"k"}
+{"Get":{"state":"jห‡ohello","mode":"Normal"}}

crates/workspace/src/dock.rs ๐Ÿ”—

@@ -1,3 +1,4 @@
+use crate::persistence::model::DockData;
 use crate::DraggedDock;
 use crate::{status_bar::StatusItemView, Workspace};
 use gpui::{
@@ -141,6 +142,7 @@ pub struct Dock {
     is_open: bool,
     active_panel_index: usize,
     focus_handle: FocusHandle,
+    pub(crate) serialized_dock: Option<DockData>,
     _focus_subscription: Subscription,
 }
 
@@ -201,6 +203,7 @@ impl Dock {
                 is_open: false,
                 focus_handle: focus_handle.clone(),
                 _focus_subscription: focus_subscription,
+                serialized_dock: None,
             }
         });
 
@@ -408,10 +411,26 @@ impl Dock {
             }),
         ];
 
+        let name = panel.persistent_name().to_string();
+
         self.panel_entries.push(PanelEntry {
             panel: Arc::new(panel),
             _subscriptions: subscriptions,
         });
+        if let Some(serialized) = self.serialized_dock.clone() {
+            if serialized.active_panel == Some(name) {
+                self.activate_panel(self.panel_entries.len() - 1, cx);
+                if serialized.visible {
+                    self.set_open(true, cx);
+                }
+                if serialized.zoom {
+                    if let Some(panel) = self.active_panel() {
+                        panel.set_zoomed(true, cx)
+                    };
+                }
+            }
+        }
+
         cx.notify()
     }
 

crates/workspace/src/notifications.rs ๐Ÿ”—

@@ -281,7 +281,7 @@ where
             Ok(value) => Some(value),
             Err(err) => {
                 log::error!("TODO {err:?}");
-                cx.update(|view, cx| {
+                cx.update_root(|view, cx| {
                     if let Ok(workspace) = view.downcast::<Workspace>() {
                         workspace.update(cx, |workspace, cx| workspace.show_error(&err, cx))
                     }

crates/workspace/src/pane.rs ๐Ÿ”—

@@ -1092,7 +1092,7 @@ impl Pane {
             return Ok(true);
         }
 
-        let (mut has_conflict, mut is_dirty, mut can_save, can_save_as) = cx.update(|_, cx| {
+        let (mut has_conflict, mut is_dirty, mut can_save, can_save_as) = cx.update(|cx| {
             (
                 item.has_conflict(cx),
                 item.is_dirty(cx),
@@ -1132,7 +1132,7 @@ impl Pane {
             }
         } else if is_dirty && (can_save || can_save_as) {
             if save_intent == SaveIntent::Close {
-                let will_autosave = cx.update(|_, cx| {
+                let will_autosave = cx.update(|cx| {
                     matches!(
                         WorkspaceSettings::get_global(cx).autosave,
                         AutosaveSetting::OnFocusChange | AutosaveSetting::OnWindowChange
@@ -1166,7 +1166,7 @@ impl Pane {
                     })?
                     .unwrap_or_else(|| Path::new("").into());
 
-                let abs_path = cx.update(|_, cx| cx.prompt_for_new_path(&start_abs_path))?;
+                let abs_path = cx.update(|cx| cx.prompt_for_new_path(&start_abs_path))?;
                 if let Some(abs_path) = abs_path.await.ok().flatten() {
                     pane.update(cx, |_, cx| item.save_as(project, abs_path, cx))?
                         .await?;
@@ -2054,7 +2054,7 @@ fn dirty_message_for(buffer_path: Option<ProjectPath>) -> String {
     let path = buffer_path
         .as_ref()
         .and_then(|p| p.path.to_str())
-        .unwrap_or(&"This buffer");
+        .unwrap_or("This buffer");
     let path = truncate_and_remove_front(path, 80);
     format!("{path} contains unsaved edits. Do you want to save it?")
 }

crates/workspace/src/pane_group.rs ๐Ÿ”—

@@ -710,7 +710,7 @@ mod element {
             pane_bounds: Bounds<Pixels>,
             axis_bounds: Bounds<Pixels>,
             workspace: WeakView<Workspace>,
-            cx: &mut WindowContext,
+            cx: &mut ElementContext,
         ) {
             let handle_bounds = Bounds {
                 origin: pane_bounds.origin.apply_along(axis, |origin| {
@@ -803,7 +803,7 @@ mod element {
         fn request_layout(
             &mut self,
             state: Option<Self::State>,
-            cx: &mut ui::prelude::WindowContext,
+            cx: &mut ui::prelude::ElementContext,
         ) -> (gpui::LayoutId, Self::State) {
             let mut style = Style::default();
             style.flex_grow = 1.;
@@ -820,7 +820,7 @@ mod element {
             &mut self,
             bounds: gpui::Bounds<ui::prelude::Pixels>,
             state: &mut Self::State,
-            cx: &mut ui::prelude::WindowContext,
+            cx: &mut ui::prelude::ElementContext,
         ) {
             let flexes = self.flexes.lock().clone();
             let len = self.children.len();
@@ -904,8 +904,8 @@ mod element {
     }
 
     impl ParentElement for PaneAxisElement {
-        fn children_mut(&mut self) -> &mut smallvec::SmallVec<[AnyElement; 2]> {
-            &mut self.children
+        fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
+            self.children.extend(elements)
         }
     }
 

crates/workspace/src/persistence.rs ๐Ÿ”—

@@ -591,7 +591,7 @@ impl WorkspaceDb {
                 Ok(())
             }
             SerializedPaneGroup::Pane(pane) => {
-                Self::save_pane(conn, workspace_id, &pane, parent)?;
+                Self::save_pane(conn, workspace_id, pane, parent)?;
                 Ok(())
             }
         }

crates/workspace/src/status_bar.rs ๐Ÿ”—

@@ -1,6 +1,6 @@
 use crate::{ItemHandle, Pane};
 use gpui::{
-    div, AnyView, IntoElement, ParentElement, Render, Styled, Subscription, View, ViewContext,
+    AnyView, IntoElement, ParentElement, Render, Styled, Subscription, View, ViewContext,
     WindowContext,
 };
 use std::any::TypeId;
@@ -34,13 +34,12 @@ pub struct StatusBar {
 
 impl Render for StatusBar {
     fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        div()
+        h_flex()
+            .w_full()
+            .justify_between()
+            .gap_2()
             .py_0p5()
             .px_1()
-            .flex()
-            .items_center()
-            .justify_between()
-            .w_full()
             .h_8()
             .bg(cx.theme().colors().status_bar_background)
             .child(self.render_left_tools(cx))
@@ -51,14 +50,13 @@ impl Render for StatusBar {
 impl StatusBar {
     fn render_left_tools(&self, _: &mut ViewContext<Self>) -> impl IntoElement {
         h_flex()
-            .items_center()
             .gap_2()
+            .overflow_x_hidden()
             .children(self.left_items.iter().map(|item| item.to_any()))
     }
 
     fn render_right_tools(&self, _: &mut ViewContext<Self>) -> impl IntoElement {
         h_flex()
-            .items_center()
             .gap_2()
             .children(self.right_items.iter().rev().map(|item| item.to_any()))
     }

crates/workspace/src/toolbar.rs ๐Ÿ”—

@@ -112,18 +112,22 @@ impl Render for Toolbar {
             .child(
                 h_flex()
                     .justify_between()
+                    .gap_2()
                     .when(has_left_items, |this| {
                         this.child(
                             h_flex()
-                                .flex_1()
+                                .flex_auto()
                                 .justify_start()
+                                .overflow_x_hidden()
                                 .children(self.left_items().map(|item| item.to_any())),
                         )
                     })
                     .when(has_right_items, |this| {
                         this.child(
                             h_flex()
-                                .flex_1()
+                                // We're using `flex_none` here to prevent some flickering that can occur when the
+                                // size of the left items container changes.
+                                .flex_none()
                                 .justify_end()
                                 .children(self.right_items().map(|item| item.to_any())),
                         )

crates/workspace/src/workspace.rs ๐Ÿ”—

@@ -26,12 +26,12 @@ use futures::{
 };
 use gpui::{
     actions, canvas, div, impl_actions, point, px, size, Action, AnyElement, AnyModel, AnyView,
-    AnyWeakView, AppContext, AsyncAppContext, AsyncWindowContext, BorrowWindow, Bounds, Context,
-    Div, DragMoveEvent, Element, Entity, EntityId, EventEmitter, FocusHandle, FocusableView,
-    GlobalPixels, InteractiveElement, IntoElement, KeyContext, LayoutId, ManagedView, Model,
-    ModelContext, ParentElement, PathPromptOptions, Pixels, Point, PromptLevel, Render, Size,
-    Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowBounds,
-    WindowContext, WindowHandle, WindowOptions,
+    AnyWeakView, AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Context, Div,
+    DragMoveEvent, Element, ElementContext, Entity, EntityId, EventEmitter, FocusHandle,
+    FocusableView, GlobalPixels, InteractiveElement, IntoElement, KeyContext, LayoutId,
+    ManagedView, Model, ModelContext, ParentElement, PathPromptOptions, Pixels, Point, PromptLevel,
+    Render, Size, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView,
+    WindowBounds, WindowContext, WindowHandle, WindowOptions,
 };
 use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
 use itertools::Itertools;
@@ -669,18 +669,6 @@ impl Workspace {
 
         cx.defer(|this, cx| {
             this.update_window_title(cx);
-            // todo! @nate - these are useful for testing notifications
-            // this.show_error(
-            //     &anyhow::anyhow!("what happens if this message is very very very very very long"),
-            //     cx,
-            // );
-
-            // this.show_notification(1, cx, |cx| {
-            //     cx.new_view(|_cx| {
-            //         simple_message_notification::MessageNotification::new(format!("Error:"))
-            //             .with_click_message("click here because!")
-            //     })
-            // });
         });
         Workspace {
             weak_self: weak_handle.clone(),
@@ -740,7 +728,7 @@ impl Workspace {
 
         cx.spawn(|mut cx| async move {
             let serialized_workspace: Option<SerializedWorkspace> =
-                persistence::DB.workspace_for_roots(&abs_paths.as_slice());
+                persistence::DB.workspace_for_roots(abs_paths.as_slice());
 
             let paths_to_open = Arc::new(abs_paths);
 
@@ -860,7 +848,7 @@ impl Workspace {
         self.window_edited
     }
 
-    pub fn add_panel<T: Panel>(&mut self, panel: View<T>, cx: &mut ViewContext<Self>) {
+    pub fn add_panel<T: Panel>(&mut self, panel: View<T>, cx: &mut WindowContext) {
         let dock = match panel.position(cx) {
             DockPosition::Left => &self.left_dock,
             DockPosition::Bottom => &self.bottom_dock,
@@ -1245,7 +1233,7 @@ impl Workspace {
             }
             for (pane, item) in dirty_items {
                 let (singleton, project_entry_ids) =
-                    cx.update(|_, cx| (item.is_singleton(cx), item.project_entry_ids(cx)))?;
+                    cx.update(|cx| (item.is_singleton(cx), item.project_entry_ids(cx)))?;
                 if singleton || !project_entry_ids.is_empty() {
                     if let Some(ix) =
                         pane.update(&mut cx, |pane, _| pane.index_for_item(item.as_ref()))?
@@ -1319,7 +1307,7 @@ impl Workspace {
             } else {
                 None
             };
-            cx.update(|_, cx| open_paths(&paths, &app_state, window_to_replace, cx))?
+            cx.update(|cx| open_paths(&paths, &app_state, window_to_replace, cx))?
                 .await?;
             Ok(())
         })
@@ -1500,14 +1488,14 @@ impl Workspace {
         item.to_any().downcast::<I>().ok()
     }
 
-    fn active_project_path(&self, cx: &ViewContext<Self>) -> Option<ProjectPath> {
+    fn active_project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
         self.active_item(cx).and_then(|item| item.project_path(cx))
     }
 
     pub fn save_active_item(
         &mut self,
         save_intent: SaveIntent,
-        cx: &mut ViewContext<Self>,
+        cx: &mut WindowContext,
     ) -> Task<Result<()>> {
         let project = self.project.clone();
         let pane = self.active_pane();
@@ -1515,7 +1503,7 @@ impl Workspace {
         let item = pane.read(cx).active_item();
         let pane = pane.downgrade();
 
-        cx.spawn(|_, mut cx| async move {
+        cx.spawn(|mut cx| async move {
             if let Some(item) = item {
                 Pane::save_item(project, &pane, item_ix, item.as_ref(), save_intent, &mut cx)
                     .await
@@ -1693,6 +1681,18 @@ impl Workspace {
         None
     }
 
+    /// Open the panel of the given type
+    pub fn open_panel<T: Panel>(&mut self, cx: &mut ViewContext<Self>) {
+        for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] {
+            if let Some(panel_index) = dock.read(cx).panel_index_for_type::<T>() {
+                dock.update(cx, |dock, cx| {
+                    dock.activate_panel(panel_index, cx);
+                    dock.set_open(true, cx);
+                });
+            }
+        }
+    }
+
     pub fn panel<T: Panel>(&self, cx: &WindowContext) -> Option<View<T>> {
         for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] {
             let dock = dock.read(cx);
@@ -1776,7 +1776,7 @@ impl Workspace {
         }
     }
 
-    pub fn add_item(&mut self, item: Box<dyn ItemHandle>, cx: &mut ViewContext<Self>) {
+    pub fn add_item(&mut self, item: Box<dyn ItemHandle>, cx: &mut WindowContext) {
         if let Some(text) = item.telemetry_event_text(cx) {
             self.client()
                 .telemetry()
@@ -1858,7 +1858,7 @@ impl Workspace {
         path: impl Into<ProjectPath>,
         pane: Option<WeakView<Pane>>,
         focus_item: bool,
-        cx: &mut ViewContext<Self>,
+        cx: &mut WindowContext,
     ) -> Task<Result<Box<dyn ItemHandle>, anyhow::Error>> {
         let pane = pane.unwrap_or_else(|| {
             self.last_active_center_pane.clone().unwrap_or_else(|| {
@@ -1870,7 +1870,7 @@ impl Workspace {
         });
 
         let task = self.load_path(path.into(), cx);
-        cx.spawn(move |_, mut cx| async move {
+        cx.spawn(move |mut cx| async move {
             let (project_entry_id, build_item) = task.await?;
             pane.update(&mut cx, |pane, cx| {
                 pane.open_item(project_entry_id, focus_item, cx, build_item)
@@ -1913,7 +1913,7 @@ impl Workspace {
     fn load_path(
         &mut self,
         path: ProjectPath,
-        cx: &mut ViewContext<Self>,
+        cx: &mut WindowContext,
     ) -> Task<
         Result<(
             Option<ProjectEntryId>,
@@ -1922,9 +1922,9 @@ impl Workspace {
     > {
         let project = self.project().clone();
         let project_item = project.update(cx, |project, cx| project.open_path(path, cx));
-        cx.spawn(|_, mut cx| async move {
+        cx.spawn(|mut cx| async move {
             let (project_entry_id, project_item) = project_item.await?;
-            let build_item = cx.update(|_, cx| {
+            let build_item = cx.update(|cx| {
                 cx.default_global::<ProjectItemBuilders>()
                     .get(&project_item.entity_type())
                     .ok_or_else(|| anyhow!("no item builder for project item"))
@@ -1992,7 +1992,7 @@ impl Workspace {
         }
     }
 
-    pub fn activate_item(&mut self, item: &dyn ItemHandle, cx: &mut ViewContext<Self>) -> bool {
+    pub fn activate_item(&mut self, item: &dyn ItemHandle, cx: &mut WindowContext) -> bool {
         let result = self.panes.iter().find_map(|pane| {
             pane.read(cx)
                 .index_for_item(item)
@@ -2015,7 +2015,7 @@ impl Workspace {
         }
     }
 
-    pub fn activate_next_pane(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn activate_next_pane(&mut self, cx: &mut WindowContext) {
         let panes = self.center.panes();
         if let Some(ix) = panes.iter().position(|pane| **pane == self.active_pane) {
             let next_ix = (ix + 1) % panes.len();
@@ -2024,7 +2024,7 @@ impl Workspace {
         }
     }
 
-    pub fn activate_previous_pane(&mut self, cx: &mut ViewContext<Self>) {
+    pub fn activate_previous_pane(&mut self, cx: &mut WindowContext) {
         let panes = self.center.panes();
         if let Some(ix) = panes.iter().position(|pane| **pane == self.active_pane) {
             let prev_ix = cmp::min(ix.wrapping_sub(1), panes.len() - 1);
@@ -2036,7 +2036,7 @@ impl Workspace {
     pub fn activate_pane_in_direction(
         &mut self,
         direction: SplitDirection,
-        cx: &mut ViewContext<Self>,
+        cx: &mut WindowContext,
     ) {
         if let Some(pane) = self.find_pane_in_direction(direction, cx) {
             cx.focus_view(pane);
@@ -2060,7 +2060,7 @@ impl Workspace {
     fn find_pane_in_direction(
         &mut self,
         direction: SplitDirection,
-        cx: &mut ViewContext<Self>,
+        cx: &AppContext,
     ) -> Option<&View<Pane>> {
         let Some(bounding_box) = self.center.bounding_box_for_pane(&self.active_pane) else {
             return None;
@@ -2495,14 +2495,14 @@ impl Workspace {
             .any(|state| state.leader_id == peer_id)
     }
 
-    fn active_item_path_changed(&mut self, cx: &mut ViewContext<Self>) {
+    fn active_item_path_changed(&mut self, cx: &mut WindowContext) {
         let active_entry = self.active_project_path(cx);
         self.project
             .update(cx, |project, cx| project.set_active_path(active_entry, cx));
         self.update_window_title(cx);
     }
 
-    fn update_window_title(&mut self, cx: &mut ViewContext<Self>) {
+    fn update_window_title(&mut self, cx: &mut WindowContext) {
         let project = self.project().read(cx);
         let mut title = String::new();
 
@@ -2546,7 +2546,7 @@ impl Workspace {
         cx.set_window_title(&title);
     }
 
-    fn update_window_edited(&mut self, cx: &mut ViewContext<Self>) {
+    fn update_window_edited(&mut self, cx: &mut WindowContext) {
         let is_edited = !self.project.read(cx).is_disconnected()
             && self
                 .items(cx)
@@ -2721,7 +2721,7 @@ impl Workspace {
     ) -> Result<()> {
         let this = this.upgrade().context("workspace dropped")?;
 
-        let item_builders = cx.update(|_, cx| {
+        let item_builders = cx.update(|cx| {
             cx.default_global::<FollowableItemBuilders>()
                 .values()
                 .map(|b| b.0)
@@ -2740,7 +2740,7 @@ impl Workspace {
                     Err(anyhow!("missing view variant"))?;
                 }
                 for build_item in &item_builders {
-                    let task = cx.update(|_, cx| {
+                    let task = cx.update(|cx| {
                         build_item(pane.clone(), this.clone(), id, &mut variant, cx)
                     })?;
                     if let Some(task) = task {
@@ -2771,7 +2771,7 @@ impl Workspace {
         Ok(())
     }
 
-    fn update_active_view_for_followers(&mut self, cx: &mut ViewContext<Self>) {
+    fn update_active_view_for_followers(&mut self, cx: &mut WindowContext) {
         let mut is_project_item = true;
         let mut update = proto::UpdateActiveView::default();
 
@@ -2888,7 +2888,7 @@ impl Workspace {
         &self,
         peer_id: PeerId,
         pane: &View<Pane>,
-        cx: &mut ViewContext<Self>,
+        cx: &mut WindowContext,
     ) -> Option<View<SharedScreen>> {
         let call = self.active_call()?;
         let room = call.read(cx).room()?.read(cx);
@@ -3005,7 +3005,7 @@ impl Workspace {
         }));
     }
 
-    fn serialize_workspace(&self, cx: &mut ViewContext<Self>) {
+    fn serialize_workspace(&self, cx: &mut WindowContext) {
         fn serialize_pane_handle(pane_handle: &View<Pane>, cx: &WindowContext) -> SerializedPane {
             let (items, active) = {
                 let pane = pane_handle.read(cx);
@@ -3046,15 +3046,12 @@ impl Workspace {
                     flexes: Some(flexes.lock().clone()),
                 },
                 Member::Pane(pane_handle) => {
-                    SerializedPaneGroup::Pane(serialize_pane_handle(&pane_handle, cx))
+                    SerializedPaneGroup::Pane(serialize_pane_handle(pane_handle, cx))
                 }
             }
         }
 
-        fn build_serialized_docks(
-            this: &Workspace,
-            cx: &mut ViewContext<Workspace>,
-        ) -> DockStructure {
+        fn build_serialized_docks(this: &Workspace, cx: &mut WindowContext) -> DockStructure {
             let left_dock = this.left_dock.read(cx);
             let left_visible = left_dock.is_open();
             let left_active_panel = left_dock
@@ -3121,7 +3118,7 @@ impl Workspace {
                     docks,
                 };
 
-                cx.spawn(|_, _| persistence::DB.save_workspace(serialized_workspace))
+                cx.spawn(|_| persistence::DB.save_workspace(serialized_workspace))
                     .detach();
             }
         }
@@ -3153,7 +3150,7 @@ impl Workspace {
                 center_group = Some((group, active_pane))
             }
 
-            let mut items_by_project_path = cx.update(|_, cx| {
+            let mut items_by_project_path = cx.update(|cx| {
                 center_items
                     .unwrap_or_default()
                     .into_iter()
@@ -3190,49 +3187,19 @@ impl Workspace {
                 }
 
                 let docks = serialized_workspace.docks;
-                workspace.left_dock.update(cx, |dock, cx| {
-                    dock.set_open(docks.left.visible, cx);
-                    if let Some(active_panel) = docks.left.active_panel {
-                        if let Some(ix) = dock.panel_index_for_persistent_name(&active_panel, cx) {
-                            dock.activate_panel(ix, cx);
-                        }
-                    }
-                    dock.active_panel()
-                        .map(|panel| panel.set_zoomed(docks.left.zoom, cx));
-                    if docks.left.visible && docks.left.zoom {
-                        cx.focus_self()
-                    }
-                });
-                // TODO: I think the bug is that setting zoom or active undoes the bottom zoom or something
-                workspace.right_dock.update(cx, |dock, cx| {
-                    dock.set_open(docks.right.visible, cx);
-                    if let Some(active_panel) = docks.right.active_panel {
-                        if let Some(ix) = dock.panel_index_for_persistent_name(&active_panel, cx) {
-                            dock.activate_panel(ix, cx);
-                        }
-                    }
-                    dock.active_panel()
-                        .map(|panel| panel.set_zoomed(docks.right.zoom, cx));
-
-                    if docks.right.visible && docks.right.zoom {
-                        cx.focus_self()
-                    }
-                });
-                workspace.bottom_dock.update(cx, |dock, cx| {
-                    dock.set_open(docks.bottom.visible, cx);
-                    if let Some(active_panel) = docks.bottom.active_panel {
-                        if let Some(ix) = dock.panel_index_for_persistent_name(&active_panel, cx) {
-                            dock.activate_panel(ix, cx);
-                        }
-                    }
-
-                    dock.active_panel()
-                        .map(|panel| panel.set_zoomed(docks.bottom.zoom, cx));
 
-                    if docks.bottom.visible && docks.bottom.zoom {
-                        cx.focus_self()
-                    }
-                });
+                let right = docks.right.clone();
+                workspace
+                    .right_dock
+                    .update(cx, |dock, _| dock.serialized_dock = Some(right));
+                let left = docks.left.clone();
+                workspace
+                    .left_dock
+                    .update(cx, |dock, _| dock.serialized_dock = Some(left));
+                let bottom = docks.bottom.clone();
+                workspace
+                    .bottom_dock
+                    .update(cx, |dock, _| dock.serialized_dock = Some(bottom));
 
                 cx.notify();
             })?;
@@ -3362,14 +3329,11 @@ impl Workspace {
         self.modal_layer.read(cx).has_active_modal()
     }
 
-    pub fn active_modal<V: ManagedView + 'static>(
-        &mut self,
-        cx: &ViewContext<Self>,
-    ) -> Option<View<V>> {
+    pub fn active_modal<V: ManagedView + 'static>(&mut self, cx: &AppContext) -> Option<View<V>> {
         self.modal_layer.read(cx).active_modal()
     }
 
-    pub fn toggle_modal<V: ModalView, B>(&mut self, cx: &mut ViewContext<Self>, build: B)
+    pub fn toggle_modal<V: ModalView, B>(&mut self, cx: &mut WindowContext, build: B)
     where
         B: FnOnce(&mut ViewContext<V>) -> V,
     {
@@ -3419,7 +3383,7 @@ fn open_items(
             let restored_project_paths = restored_items
                 .iter()
                 .filter_map(|item| {
-                    cx.update(|_, cx| item.as_ref()?.project_path(cx))
+                    cx.update(|cx| item.as_ref()?.project_path(cx))
                         .ok()
                         .flatten()
                 })
@@ -3557,9 +3521,14 @@ impl Render for Workspace {
                     .border_b()
                     .border_color(colors.border)
                     .child(
-                        canvas(cx.listener(|workspace, bounds, _| {
-                            workspace.bounds = *bounds;
-                        }))
+                        canvas({
+                            let this = cx.view().clone();
+                            move |bounds, cx| {
+                                this.update(cx, |this, _cx| {
+                                    this.bounds = *bounds;
+                                })
+                            }
+                        })
                         .absolute()
                         .size_full(),
                     )
@@ -4246,7 +4215,7 @@ pub fn restart(_: &Restart, cx: &mut AppContext) {
 
     // If multiple windows have unsaved changes, and need a save prompt,
     // prompt in the active window before switching to a different window.
-    workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false));
+    workspace_windows.sort_by_key(|window| window.is_active(cx) == Some(false));
 
     let mut prompt = None;
     if let (true, Some(window)) = (should_confirm, workspace_windows.first()) {
@@ -4311,7 +4280,7 @@ impl Element for DisconnectedOverlay {
     fn request_layout(
         &mut self,
         _: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         let mut background = cx.theme().colors().elevated_surface_background;
         background.fade_out(0.2);
@@ -4333,8 +4302,13 @@ impl Element for DisconnectedOverlay {
         (overlay.request_layout(cx), overlay)
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, overlay: &mut Self::State, cx: &mut WindowContext) {
-        cx.with_z_index(u8::MAX, |cx| {
+    fn paint(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        overlay: &mut Self::State,
+        cx: &mut ElementContext,
+    ) {
+        cx.with_z_index(u16::MAX, |cx| {
             cx.add_opaque_layer(bounds);
             overlay.paint(cx);
         })

crates/zed/src/main.rs ๐Ÿ”—

@@ -544,22 +544,7 @@ fn init_panic_hook(app: &App, installation_id: Option<String>, session_id: Strin
         let mut backtrace = backtrace
             .frames()
             .iter()
-            .flat_map(|frame| {
-                frame.symbols().iter().filter_map(|symbol| {
-                    let name = symbol.name()?;
-                    let addr = symbol.addr()? as usize;
-                    let position = if let (Some(path), Some(lineno)) = (
-                        symbol.filename().and_then(|path| path.file_name()),
-                        symbol.lineno(),
-                    ) {
-                        format!("{}:{}", path.to_string_lossy(), lineno)
-                    } else {
-                        "?".to_string()
-                    };
-
-                    Some(format!("{:} ({:#x}) at {}", name, addr, position))
-                })
-            })
+            .filter_map(|frame| Some(format!("{:#}", frame.symbols().first()?.name()?)))
             .collect::<Vec<_>>();
 
         // Strip out leading stack frames for rust panic-handling.

crates/zed/src/zed.rs ๐Ÿ”—

@@ -32,11 +32,11 @@ use util::{
 };
 use uuid::Uuid;
 use welcome::BaseKeymap;
+use workspace::Pane;
 use workspace::{
     create_and_open_local_file, notifications::simple_message_notification::MessageNotification,
     open_new, AppState, NewFile, NewWindow, Workspace, WorkspaceSettings,
 };
-use workspace::{dock::Panel, Pane};
 use zed_actions::{OpenBrowser, OpenSettings, OpenZedURL, Quit};
 
 actions!(
@@ -178,10 +178,7 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
             )?;
 
             workspace_handle.update(&mut cx, |workspace, cx| {
-                let (position, was_deserialized) = {
-                    let project_panel = project_panel.read(cx);
-                    (project_panel.position(cx), project_panel.was_deserialized())
-                };
+                let was_deserialized = project_panel.read(cx).was_deserialized();
                 workspace.add_panel(project_panel, cx);
                 workspace.add_panel(terminal_panel, cx);
                 workspace.add_panel(assistant_panel, cx);
@@ -200,7 +197,7 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
                                 .map_or(false, |entry| entry.is_dir())
                         })
                 {
-                    workspace.toggle_dock(position, cx);
+                    workspace.open_panel::<ProjectPanel>(cx);
                 }
                 cx.focus_self();
             })
@@ -875,7 +872,7 @@ mod tests {
         let window = cx.update(|cx| cx.windows()[0].downcast::<Workspace>().unwrap());
 
         let window_is_edited = |window: WindowHandle<Workspace>, cx: &mut TestAppContext| {
-            cx.test_window(window.into()).edited()
+            cx.update(|cx| window.read(cx).unwrap().is_edited())
         };
         let pane = window
             .read_with(cx, |workspace, _| workspace.active_pane().clone())

script/deploy-collab ๐Ÿ”—

@@ -19,5 +19,6 @@ export ZED_IMAGE_ID=${image_id}
 
 target_zed_kube_cluster
 envsubst < crates/collab/k8s/collab.template.yml | kubectl apply -f -
+kubectl -n "$environment" rollout status deployment/collab --watch
 
 echo "deployed collab v${version} to ${environment}"

script/histogram ๐Ÿ”—

@@ -0,0 +1,73 @@
+#!/usr/bin/env python3
+
+# Required dependencies for this script:
+#
+# pandas: For data manipulation and analysis.
+# matplotlib: For creating static, interactive, and animated visualizations in Python.
+# seaborn: For making statistical graphics in Python, based on matplotlib.
+
+# To install these dependencies, use the following pip command:
+# pip install pandas matplotlib seaborn
+
+# This script is designed to parse log files for performance measurements and create histograms of these measurements.
+# It expects log files to contain lines with measurements in the format "measurement: timeunit" where timeunit can be in milliseconds (ms) or microseconds (ยตs).
+# Lines that do not contain a colon ':' are skipped.
+# The script takes one or more file paths as command-line arguments, parses each log file, and then combines the data into a single DataFrame.
+# It then converts all time measurements into milliseconds, discards the original time and unit columns, and creates histograms for each unique measurement type.
+# The histograms display the distribution of times for each measurement, separated by log file, and normalized to show density rather than count.
+# To use this script, run it from the command line with the log file paths as arguments, like so:
+# python this_script.py log1.txt log2.txt ...
+# The script will then parse the provided log files and display the histograms for each type of measurement found.
+
+import pandas as pd
+import matplotlib.pyplot as plt
+import seaborn as sns
+import sys
+
+def parse_log_file(file_path):
+    data = {'measurement': [], 'time': [], 'unit': [], 'log_file': []}
+    with open(file_path, 'r') as file:
+        for line in file:
+            if ':' not in line:
+                continue
+
+            parts = line.strip().split(': ')
+            if len(parts) != 2:
+                continue
+
+            measurement, time_with_unit = parts[0], parts[1]
+            if 'ms' in time_with_unit:
+                time, unit = time_with_unit[:-2], 'ms'
+            elif 'ยตs' in time_with_unit:
+                time, unit = time_with_unit[:-2], 'ยตs'
+            else:
+                raise ValueError(f"Invalid time unit in line: {line.strip()}")
+                continue
+
+            data['measurement'].append(measurement)
+            data['time'].append(float(time))
+            data['unit'].append(unit)
+            data['log_file'].append(file_path.split('/')[-1])
+    return pd.DataFrame(data)
+
+def create_histograms(df, measurement):
+    filtered_df = df[df['measurement'] == measurement]
+    plt.figure(figsize=(12, 6))
+    sns.histplot(data=filtered_df, x='time_ms', hue='log_file', element='step', stat='density', common_norm=False, palette='bright')
+    plt.title(f'Histogram of {measurement}')
+    plt.xlabel('Time (ms)')
+    plt.ylabel('Density')
+    plt.grid(True)
+    plt.xlim(filtered_df['time_ms'].quantile(0.01), filtered_df['time_ms'].quantile(0.99))
+    plt.show()
+
+
+file_paths = sys.argv[1:]
+dfs = [parse_log_file(path) for path in file_paths]
+combined_df = pd.concat(dfs, ignore_index=True)
+combined_df['time_ms'] = combined_df.apply(lambda row: row['time'] if row['unit'] == 'ms' else row['time'] / 1000, axis=1)
+combined_df.drop(['time', 'unit'], axis=1, inplace=True)
+
+measurement_types = combined_df['measurement'].unique()
+for measurement in measurement_types:
+    create_histograms(combined_df, measurement)