Remove 2 suffix for editor

Max Brunsfeld and Mikayla created

Co-authored-by: Mikayla <mikayla@zed.dev>

Change summary

Cargo.lock                                         |  108 
crates/activity_indicator/Cargo.toml               |    4 
crates/assistant2/Cargo.toml                       |    4 
crates/breadcrumbs/Cargo.toml                      |    4 
crates/collab2/Cargo.toml                          |    2 
crates/collab_ui/Cargo.toml                        |    4 
crates/command_palette/Cargo.toml                  |    4 
crates/command_palette2/Cargo.toml                 |    4 
crates/copilot_button/Cargo.toml                   |    4 
crates/diagnostics/Cargo.toml                      |    4 
crates/editor/Cargo.toml                           |   55 
crates/editor/src/blink_manager.rs                 |   38 
crates/editor/src/display_map.rs                   |  410 
crates/editor/src/display_map/block_map.rs         |  124 
crates/editor/src/display_map/fold_map.rs          |  116 
crates/editor/src/display_map/inlay_map.rs         |    7 
crates/editor/src/display_map/wrap_map.rs          |  150 
crates/editor/src/editor.rs                        |  693 
crates/editor/src/editor_settings.rs               |    8 
crates/editor/src/editor_tests.rs                  |  639 
crates/editor/src/element.rs                       |  819 
crates/editor/src/git.rs                           |   16 
crates/editor/src/highlight_matching_bracket.rs    |    2 
crates/editor/src/hover_popover.rs                 |  436 
crates/editor/src/inlay_hint_cache.rs              |  544 
crates/editor/src/items.rs                         |  404 
crates/editor/src/link_go_to_definition.rs         |  370 
crates/editor/src/mouse_context_menu.rs            |   66 
crates/editor/src/movement.rs                      |  115 
crates/editor/src/rust_analyzer_ext.rs             |   59 
crates/editor/src/scroll.rs                        |  124 
crates/editor/src/scroll/actions.rs                |  111 
crates/editor/src/scroll/autoscroll.rs             |   53 
crates/editor/src/selections_collection.rs         |   28 
crates/editor/src/test.rs                          |   31 
crates/editor/src/test/editor_lsp_test_context.rs  |   27 
crates/editor/src/test/editor_test_context.rs      |  150 
crates/editor2/Cargo.toml                          |   93 
crates/editor2/src/blink_manager.rs                |  107 
crates/editor2/src/display_map.rs                  | 1854 ---
crates/editor2/src/display_map/block_map.rs        | 1647 --
crates/editor2/src/display_map/fold_map.rs         | 1746 --
crates/editor2/src/display_map/inlay_map.rs        | 1896 ---
crates/editor2/src/display_map/tab_map.rs          |  765 -
crates/editor2/src/display_map/wrap_map.rs         | 1359 --
crates/editor2/src/editor.rs                       | 9884 ----------------
crates/editor2/src/editor_settings.rs              |   72 
crates/editor2/src/element.rs                      | 3815 ------
crates/editor2/src/git.rs                          |  282 
crates/editor2/src/highlight_matching_bracket.rs   |  138 
crates/editor2/src/hover_popover.rs                | 1345 --
crates/editor2/src/inlay_hint_cache.rs             | 3268 -----
crates/editor2/src/items.rs                        | 1339 --
crates/editor2/src/link_go_to_definition.rs        | 1279 --
crates/editor2/src/mouse_context_menu.rs           |  110 
crates/editor2/src/movement.rs                     |  926 -
crates/editor2/src/persistence.rs                  |   83 
crates/editor2/src/rust_analyzer_ext.rs            |  119 
crates/editor2/src/scroll.rs                       |  460 
crates/editor2/src/scroll/actions.rs               |  103 
crates/editor2/src/scroll/autoscroll.rs            |  253 
crates/editor2/src/scroll/scroll_amount.rs         |   28 
crates/editor2/src/selections_collection.rs        |  888 -
crates/editor2/src/test.rs                         |   74 
crates/editor2/src/test/editor_lsp_test_context.rs |  298 
crates/editor2/src/test/editor_test_context.rs     |  404 
crates/feedback/Cargo.toml                         |    4 
crates/file_finder/Cargo.toml                      |    4 
crates/go_to_line/Cargo.toml                       |    4 
crates/journal2/Cargo.toml                         |    4 
crates/language_selector/Cargo.toml                |    4 
crates/language_tools/Cargo.toml                   |    4 
crates/outline2/Cargo.toml                         |    4 
crates/picker/Cargo.toml                           |    4 
crates/project_panel/Cargo.toml                    |    4 
crates/project_symbols/Cargo.toml                  |    4 
crates/quick_action_bar/Cargo.toml                 |    4 
crates/recent_projects/Cargo.toml                  |    4 
crates/search/Cargo.toml                           |    4 
crates/storybook2/Cargo.toml                       |    2 
crates/terminal_view/Cargo.toml                    |    4 
crates/theme_selector/Cargo.toml                   |    4 
crates/vim/Cargo.toml                              |    4 
crates/welcome/Cargo.toml                          |    4 
crates/zed/Cargo.toml                              |    2 
85 files changed, 2,884 insertions(+), 37,560 deletions(-)

Detailed changes

Cargo.lock ๐Ÿ”—

@@ -8,7 +8,7 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "auto_update",
- "editor2",
+ "editor",
  "futures 0.3.28",
  "gpui2",
  "language2",
@@ -375,7 +375,7 @@ dependencies = [
  "client2",
  "collections",
  "ctor",
- "editor2",
+ "editor",
  "env_logger",
  "fs2",
  "futures 0.3.28",
@@ -1089,7 +1089,7 @@ name = "breadcrumbs"
 version = "0.1.0"
 dependencies = [
  "collections",
- "editor2",
+ "editor",
  "gpui2",
  "itertools 0.10.5",
  "language2",
@@ -1717,7 +1717,7 @@ dependencies = [
  "collections",
  "ctor",
  "dashmap",
- "editor2",
+ "editor",
  "env_logger",
  "envy",
  "fs2",
@@ -1782,7 +1782,7 @@ dependencies = [
  "clock",
  "collections",
  "db2",
- "editor2",
+ "editor",
  "feature_flags2",
  "feedback",
  "futures 0.3.28",
@@ -1852,7 +1852,7 @@ dependencies = [
  "anyhow",
  "collections",
  "ctor",
- "editor2",
+ "editor",
  "env_logger",
  "fuzzy2",
  "go_to_line",
@@ -2004,7 +2004,7 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "copilot2",
- "editor2",
+ "editor",
  "fs2",
  "futures 0.3.28",
  "gpui2",
@@ -2529,7 +2529,7 @@ dependencies = [
  "anyhow",
  "client2",
  "collections",
- "editor2",
+ "editor",
  "futures 0.3.28",
  "gpui2",
  "language2",
@@ -2685,60 +2685,6 @@ checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd"
 [[package]]
 name = "editor"
 version = "0.1.0"
-dependencies = [
- "aho-corasick",
- "anyhow",
- "client",
- "clock",
- "collections",
- "context_menu",
- "convert_case 0.6.0",
- "copilot",
- "ctor",
- "db",
- "drag_and_drop",
- "env_logger",
- "futures 0.3.28",
- "fuzzy",
- "git",
- "gpui",
- "indoc",
- "itertools 0.10.5",
- "language",
- "lazy_static",
- "log",
- "lsp",
- "multi_buffer",
- "ordered-float 2.10.0",
- "parking_lot 0.11.2",
- "postage",
- "project",
- "rand 0.8.5",
- "rich_text",
- "rpc",
- "schemars",
- "serde",
- "serde_derive",
- "settings",
- "smallvec",
- "smol",
- "snippet",
- "sqlez",
- "sum_tree",
- "text",
- "theme",
- "tree-sitter",
- "tree-sitter-html",
- "tree-sitter-rust",
- "tree-sitter-typescript",
- "unindent",
- "util",
- "workspace",
-]
-
-[[package]]
-name = "editor2"
-version = "0.1.0"
 dependencies = [
  "aho-corasick",
  "anyhow",
@@ -2981,7 +2927,7 @@ dependencies = [
  "bitflags 2.4.1",
  "client2",
  "db2",
- "editor2",
+ "editor",
  "futures 0.3.28",
  "gpui2",
  "human_bytes",
@@ -3014,7 +2960,7 @@ version = "0.1.0"
 dependencies = [
  "collections",
  "ctor",
- "editor2",
+ "editor",
  "env_logger",
  "fuzzy2",
  "gpui2",
@@ -3568,7 +3514,7 @@ dependencies = [
 name = "go_to_line"
 version = "0.1.0"
 dependencies = [
- "editor2",
+ "editor",
  "gpui2",
  "menu2",
  "postage",
@@ -4296,7 +4242,7 @@ dependencies = [
  "anyhow",
  "chrono",
  "dirs 4.0.0",
- "editor2",
+ "editor",
  "gpui2",
  "log",
  "schemars",
@@ -4488,7 +4434,7 @@ name = "language_selector"
 version = "0.1.0"
 dependencies = [
  "anyhow",
- "editor2",
+ "editor",
  "fuzzy2",
  "gpui2",
  "language2",
@@ -4508,7 +4454,7 @@ dependencies = [
  "anyhow",
  "client2",
  "collections",
- "editor2",
+ "editor",
  "env_logger",
  "futures 0.3.28",
  "gpui2",
@@ -5815,7 +5761,7 @@ dependencies = [
 name = "outline2"
 version = "0.1.0"
 dependencies = [
- "editor2",
+ "editor",
  "fuzzy2",
  "gpui2",
  "language2",
@@ -6039,7 +5985,7 @@ name = "picker"
 version = "0.1.0"
 dependencies = [
  "ctor",
- "editor2",
+ "editor",
  "env_logger",
  "gpui2",
  "menu2",
@@ -6459,7 +6405,7 @@ dependencies = [
  "client2",
  "collections",
  "db2",
- "editor2",
+ "editor",
  "futures 0.3.28",
  "gpui2",
  "language2",
@@ -6486,7 +6432,7 @@ name = "project_symbols"
 version = "0.1.0"
 dependencies = [
  "anyhow",
- "editor2",
+ "editor",
  "futures 0.3.28",
  "fuzzy2",
  "gpui2",
@@ -6665,7 +6611,7 @@ name = "quick_action_bar"
 version = "0.1.0"
 dependencies = [
  "assistant2",
- "editor2",
+ "editor",
  "gpui2",
  "search",
  "ui2",
@@ -6837,7 +6783,7 @@ dependencies = [
 name = "recent_projects"
 version = "0.1.0"
 dependencies = [
- "editor2",
+ "editor",
  "futures 0.3.28",
  "fuzzy2",
  "gpui2",
@@ -7679,7 +7625,7 @@ dependencies = [
  "bitflags 1.3.2",
  "client2",
  "collections",
- "editor2",
+ "editor",
  "futures 0.3.28",
  "gpui2",
  "language2",
@@ -8605,7 +8551,7 @@ dependencies = [
  "chrono",
  "clap 4.4.4",
  "dialoguer",
- "editor2",
+ "editor",
  "fuzzy2",
  "gpui2",
  "indoc",
@@ -8968,7 +8914,7 @@ dependencies = [
  "client2",
  "db2",
  "dirs 4.0.0",
- "editor2",
+ "editor",
  "futures 0.3.28",
  "gpui2",
  "itertools 0.10.5",
@@ -9114,7 +9060,7 @@ name = "theme_selector"
 version = "0.1.0"
 dependencies = [
  "client2",
- "editor2",
+ "editor",
  "feature_flags2",
  "fs2",
  "fuzzy2",
@@ -10211,7 +10157,7 @@ dependencies = [
  "collections",
  "command_palette",
  "diagnostics",
- "editor2",
+ "editor",
  "futures 0.3.28",
  "gpui2",
  "indoc",
@@ -10629,7 +10575,7 @@ dependencies = [
  "anyhow",
  "client2",
  "db2",
- "editor2",
+ "editor",
  "fs2",
  "fuzzy2",
  "gpui2",
@@ -11072,7 +11018,7 @@ dependencies = [
  "ctor",
  "db2",
  "diagnostics",
- "editor2",
+ "editor",
  "env_logger",
  "feature_flags2",
  "feedback",

crates/activity_indicator/Cargo.toml ๐Ÿ”—

@@ -10,7 +10,7 @@ doctest = false
 
 [dependencies]
 auto_update = { path = "../auto_update" }
-editor = { path = "../editor2", package = "editor2" }
+editor = { path = "../editor" }
 language = { path = "../language2", package = "language2" }
 gpui = { path = "../gpui2", package = "gpui2" }
 project = { path = "../project2", package = "project2" }
@@ -25,4 +25,4 @@ futures.workspace = true
 smallvec.workspace = true
 
 [dev-dependencies]
-editor = { path = "../editor2", package = "editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }

crates/assistant2/Cargo.toml ๐Ÿ”—

@@ -12,7 +12,7 @@ doctest = false
 ai = { package = "ai2", path = "../ai2" }
 client = { package = "client2", path = "../client2" }
 collections = { path = "../collections"}
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 fs = { package = "fs2", path = "../fs2" }
 gpui = { package = "gpui2", path = "../gpui2" }
 language = { package = "language2", path = "../language2" }
@@ -45,7 +45,7 @@ tiktoken-rs.workspace = true
 
 [dev-dependencies]
 ai = { package = "ai2", path = "../ai2", features = ["test-support"]}
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }
 project = { package = "project2", path = "../project2", features = ["test-support"] }
 
 ctor.workspace = true

crates/breadcrumbs/Cargo.toml ๐Ÿ”—

@@ -10,7 +10,7 @@ doctest = false
 
 [dependencies]
 collections = { path = "../collections" }
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 gpui = { package = "gpui2", path = "../gpui2" }
 ui = { package = "ui2", path = "../ui2" }
 language = { package = "language2", path = "../language2" }
@@ -23,6 +23,6 @@ outline = { package = "outline2", path = "../outline2" }
 itertools = "0.10"
 
 [dev-dependencies]
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }
 gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }

crates/collab2/Cargo.toml ๐Ÿ”—

@@ -66,7 +66,7 @@ gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 call = { package = "call2", path = "../call2", features = ["test-support"] }
 client = { package = "client2", path = "../client2", features = ["test-support"] }
 channel = { package = "channel2", path = "../channel2" }
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }
 language = { package = "language2", path = "../language2", features = ["test-support"] }
 fs = { package = "fs2", path = "../fs2", features = ["test-support"] }
 git = { package = "git3", path = "../git3", features = ["test-support"] }

crates/collab_ui/Cargo.toml ๐Ÿ”—

@@ -31,7 +31,7 @@ clock = { path = "../clock" }
 collections = { path = "../collections" }
 # context_menu = { path = "../context_menu" }
 # drag_and_drop = { path = "../drag_and_drop" }
-editor = { package="editor2", path = "../editor2" }
+editor = { path = "../editor" }
 feedback = { path = "../feedback" }
 fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
 gpui = { package = "gpui2", path = "../gpui2" }
@@ -68,7 +68,7 @@ smallvec.workspace = true
 call = { package = "call2", path = "../call2", features = ["test-support"] }
 client = { package = "client2", path = "../client2", features = ["test-support"] }
 collections = { path = "../collections", features = ["test-support"] }
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }
 gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 notifications = { package = "notifications2", path = "../notifications2", features = ["test-support"] }
 project = { package = "project2", path = "../project2", features = ["test-support"] }

crates/command_palette/Cargo.toml ๐Ÿ”—

@@ -10,7 +10,7 @@ doctest = false
 
 [dependencies]
 collections = { path = "../collections" }
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
 gpui = { package = "gpui2", path = "../gpui2" }
 picker = { path = "../picker" }
@@ -26,7 +26,7 @@ serde.workspace = true
 
 [dev-dependencies]
 gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }
 language = { package="language2", path = "../language2", features = ["test-support"] }
 project = { package="project2", path = "../project2", features = ["test-support"] }
 menu = { package = "menu2", path = "../menu2" }

crates/command_palette2/Cargo.toml ๐Ÿ”—

@@ -10,7 +10,7 @@ doctest = false
 
 [dependencies]
 collections = { path = "../collections" }
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
 gpui = { package = "gpui2", path = "../gpui2" }
 picker = { path = "../picker" }
@@ -25,7 +25,7 @@ anyhow.workspace = true
 serde.workspace = true
 [dev-dependencies]
 gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }
 language = { package="language2", path = "../language2", features = ["test-support"] }
 project = { package="project2", path = "../project2", features = ["test-support"] }
 menu = { package = "menu2", path = "../menu2" }

crates/copilot_button/Cargo.toml ๐Ÿ”—

@@ -10,7 +10,7 @@ doctest = false
 
 [dependencies]
 copilot = { package = "copilot2", path = "../copilot2" }
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 fs = { package = "fs2", path = "../fs2" }
 zed-actions = { package="zed_actions2", path = "../zed_actions2"}
 gpui = { package = "gpui2", path = "../gpui2" }
@@ -24,4 +24,4 @@ smol.workspace = true
 futures.workspace = true
 
 [dev-dependencies]
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }

crates/diagnostics/Cargo.toml ๐Ÿ”—

@@ -10,7 +10,7 @@ doctest = false
 
 [dependencies]
 collections = { path = "../collections" }
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 gpui = { package = "gpui2", path = "../gpui2" }
 ui = { package = "ui2", path = "../ui2" }
 language = { package = "language2", path = "../language2" }
@@ -32,7 +32,7 @@ postage.workspace = true
 
 [dev-dependencies]
 client = { package = "client2", path = "../client2", features = ["test-support"] }
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }
 language = { package = "language2", path = "../language2", features = ["test-support"] }
 lsp = { package = "lsp2", path = "../lsp2", features = ["test-support"] }
 gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }

crates/editor/Cargo.toml ๐Ÿ”—

@@ -23,30 +23,30 @@ test-support = [
 ]
 
 [dependencies]
-client = { path = "../client" }
+client = { package = "client2", path = "../client2" }
 clock = { path = "../clock" }
-copilot = { path = "../copilot" }
-db = { path = "../db" }
-drag_and_drop = { path = "../drag_and_drop" }
+copilot = { package="copilot2", path = "../copilot2" }
+db = { package="db2", path = "../db2" }
 collections = { path = "../collections" }
-context_menu = { path = "../context_menu" }
-fuzzy = { path = "../fuzzy" }
-git = { path = "../git" }
-gpui = { path = "../gpui" }
-language = { path = "../language" }
-lsp = { path = "../lsp" }
-multi_buffer = { path = "../multi_buffer" }
-project = { path = "../project" }
-rpc = { path = "../rpc" }
-rich_text = { path = "../rich_text" }
-settings = { path = "../settings" }
+# context_menu = { path = "../context_menu" }
+fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
+git = { package = "git3", path = "../git3" }
+gpui = { package = "gpui2", path = "../gpui2" }
+language = { package = "language2", path = "../language2" }
+lsp = { package = "lsp2", path = "../lsp2" }
+multi_buffer = { package = "multi_buffer2", path = "../multi_buffer2" }
+project = { package = "project2", path = "../project2" }
+rpc = { package = "rpc2", path = "../rpc2" }
+rich_text = { package = "rich_text2", path = "../rich_text2" }
+settings = { package="settings2", path = "../settings2" }
 snippet = { path = "../snippet" }
 sum_tree = { path = "../sum_tree" }
-text = { path = "../text" }
-theme = { path = "../theme" }
+text = { package="text2", path = "../text2" }
+theme = { package="theme2", path = "../theme2" }
+ui = { package = "ui2", path = "../ui2" }
 util = { path = "../util" }
 sqlez = { path = "../sqlez" }
-workspace = { path = "../workspace" }
+workspace = { package = "workspace2", path = "../workspace2" }
 
 aho-corasick = "1.1"
 anyhow.workspace = true
@@ -62,6 +62,7 @@ postage.workspace = true
 rand.workspace = true
 schemars.workspace = true
 serde.workspace = true
+serde_json.workspace = true
 serde_derive.workspace = true
 smallvec.workspace = true
 smol.workspace = true
@@ -71,16 +72,16 @@ tree-sitter-html = { workspace = true, optional = true }
 tree-sitter-typescript = { workspace = true, optional = true }
 
 [dev-dependencies]
-copilot = { path = "../copilot", features = ["test-support"] }
-text = { path = "../text", features = ["test-support"] }
-language = { path = "../language", features = ["test-support"] }
-lsp = { path = "../lsp", features = ["test-support"] }
-gpui = { path = "../gpui", features = ["test-support"] }
+copilot = { package="copilot2", path = "../copilot2", features = ["test-support"] }
+text = { package="text2", path = "../text2", features = ["test-support"] }
+language = { package="language2", path = "../language2", features = ["test-support"] }
+lsp = { package = "lsp2", path = "../lsp2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }
-project = { path = "../project", features = ["test-support"] }
-settings = { path = "../settings", features = ["test-support"] }
-workspace = { path = "../workspace", features = ["test-support"] }
-multi_buffer = { path = "../multi_buffer", features = ["test-support"] }
+project = { package = "project2", path = "../project2", features = ["test-support"] }
+settings = { package = "settings2", path = "../settings2", features = ["test-support"] }
+workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
+multi_buffer = { package = "multi_buffer2", path = "../multi_buffer2", features = ["test-support"] }
 
 ctor.workspace = true
 env_logger.workspace = true
@@ -1,5 +1,6 @@
 use crate::EditorSettings;
-use gpui::{Entity, ModelContext};
+use gpui::ModelContext;
+use settings::Settings;
 use settings::SettingsStore;
 use smol::Timer;
 use std::time::Duration;
@@ -16,7 +17,7 @@ pub struct BlinkManager {
 impl BlinkManager {
     pub fn new(blink_interval: Duration, cx: &mut ModelContext<Self>) -> Self {
         // Make sure we blink the cursors if the setting is re-enabled
-        cx.observe_global::<SettingsStore, _>(move |this, cx| {
+        cx.observe_global::<SettingsStore>(move |this, cx| {
             this.blink_cursors(this.blink_epoch, cx)
         })
         .detach();
@@ -41,14 +42,9 @@ impl BlinkManager {
 
         let epoch = self.next_blink_epoch();
         let interval = self.blink_interval;
-        cx.spawn(|this, mut cx| {
-            let this = this.downgrade();
-            async move {
-                Timer::after(interval).await;
-                if let Some(this) = this.upgrade(&cx) {
-                    this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
-                }
-            }
+        cx.spawn(|this, mut cx| async move {
+            Timer::after(interval).await;
+            this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
         })
         .detach();
     }
@@ -61,20 +57,18 @@ impl BlinkManager {
     }
 
     fn blink_cursors(&mut self, epoch: usize, cx: &mut ModelContext<Self>) {
-        if settings::get::<EditorSettings>(cx).cursor_blink {
+        if EditorSettings::get_global(cx).cursor_blink {
             if epoch == self.blink_epoch && self.enabled && !self.blinking_paused {
                 self.visible = !self.visible;
                 cx.notify();
 
                 let epoch = self.next_blink_epoch();
                 let interval = self.blink_interval;
-                cx.spawn(|this, mut cx| {
-                    let this = this.downgrade();
-                    async move {
-                        Timer::after(interval).await;
-                        if let Some(this) = this.upgrade(&cx) {
-                            this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
-                        }
+                cx.spawn(|this, mut cx| async move {
+                    Timer::after(interval).await;
+                    if let Some(this) = this.upgrade() {
+                        this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx))
+                            .ok();
                     }
                 })
                 .detach();
@@ -92,6 +86,10 @@ impl BlinkManager {
     }
 
     pub fn enable(&mut self, cx: &mut ModelContext<Self>) {
+        if self.enabled {
+            return;
+        }
+
         self.enabled = true;
         // Set cursors as invisible and start blinking: this causes cursors
         // to be visible during the next render.
@@ -107,7 +105,3 @@ impl BlinkManager {
         self.visible
     }
 }
-
-impl Entity for BlinkManager {
-    type Event = ();
-}

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

@@ -4,19 +4,15 @@ mod inlay_map;
 mod tab_map;
 mod wrap_map;
 
+use crate::EditorStyle;
 use crate::{
     link_go_to_definition::InlayHighlight, movement::TextLayoutDetails, Anchor, AnchorRangeExt,
-    EditorStyle, InlayId, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint,
+    InlayId, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint,
 };
 pub use block_map::{BlockMap, BlockPoint};
 use collections::{BTreeMap, HashMap, HashSet};
 use fold_map::FoldMap;
-use gpui::{
-    color::Color,
-    fonts::{FontId, HighlightStyle, Underline},
-    text_layout::{Line, RunStyle},
-    Entity, ModelContext, ModelHandle,
-};
+use gpui::{Font, HighlightStyle, Hsla, LineLayout, Model, ModelContext, Pixels, UnderlineStyle};
 use inlay_map::InlayMap;
 use language::{
     language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription,
@@ -25,6 +21,7 @@ use lsp::DiagnosticSeverity;
 use std::{any::TypeId, borrow::Cow, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc};
 use sum_tree::{Bias, TreeMap};
 use tab_map::TabMap;
+
 use wrap_map::WrapMap;
 
 pub use block_map::{
@@ -32,7 +29,7 @@ pub use block_map::{
     BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock, TransformBlock,
 };
 
-pub use self::fold_map::FoldPoint;
+pub use self::fold_map::{Fold, FoldPoint};
 pub use self::inlay_map::{Inlay, InlayOffset, InlayPoint};
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -41,6 +38,8 @@ pub enum FoldStatus {
     Foldable,
 }
 
+const UNNECESSARY_CODE_FADE: f32 = 0.3;
+
 pub trait ToDisplayPoint {
     fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint;
 }
@@ -49,28 +48,24 @@ type TextHighlights = TreeMap<Option<TypeId>, Arc<(HighlightStyle, Vec<Range<Anc
 type InlayHighlights = BTreeMap<TypeId, HashMap<InlayId, (HighlightStyle, InlayHighlight)>>;
 
 pub struct DisplayMap {
-    buffer: ModelHandle<MultiBuffer>,
+    buffer: Model<MultiBuffer>,
     buffer_subscription: BufferSubscription,
     fold_map: FoldMap,
     inlay_map: InlayMap,
     tab_map: TabMap,
-    wrap_map: ModelHandle<WrapMap>,
+    wrap_map: Model<WrapMap>,
     block_map: BlockMap,
     text_highlights: TextHighlights,
     inlay_highlights: InlayHighlights,
     pub clip_at_line_ends: bool,
 }
 
-impl Entity for DisplayMap {
-    type Event = ();
-}
-
 impl DisplayMap {
     pub fn new(
-        buffer: ModelHandle<MultiBuffer>,
-        font_id: FontId,
-        font_size: f32,
-        wrap_width: Option<f32>,
+        buffer: Model<MultiBuffer>,
+        font: Font,
+        font_size: Pixels,
+        wrap_width: Option<Pixels>,
         buffer_header_height: u8,
         excerpt_header_height: u8,
         cx: &mut ModelContext<Self>,
@@ -81,7 +76,7 @@ impl DisplayMap {
         let (inlay_map, snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));
         let (fold_map, snapshot) = FoldMap::new(snapshot);
         let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
-        let (wrap_map, snapshot) = WrapMap::new(snapshot, font_id, font_size, wrap_width, cx);
+        let (wrap_map, snapshot) = WrapMap::new(snapshot, font, font_size, wrap_width, cx);
         let block_map = BlockMap::new(snapshot, buffer_header_height, excerpt_header_height);
         cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach();
         DisplayMap {
@@ -127,7 +122,7 @@ impl DisplayMap {
         self.fold(
             other
                 .folds_in_range(0..other.buffer_snapshot.len())
-                .map(|fold| fold.to_offset(&other.buffer_snapshot)),
+                .map(|fold| fold.range.to_offset(&other.buffer_snapshot)),
             cx,
         );
     }
@@ -249,16 +244,16 @@ impl DisplayMap {
         cleared
     }
 
-    pub fn set_font(&self, font_id: FontId, font_size: f32, cx: &mut ModelContext<Self>) -> bool {
+    pub fn set_font(&self, font: Font, font_size: Pixels, cx: &mut ModelContext<Self>) -> bool {
         self.wrap_map
-            .update(cx, |map, cx| map.set_font(font_id, font_size, cx))
+            .update(cx, |map, cx| map.set_font_with_size(font, font_size, cx))
     }
 
-    pub fn set_fold_ellipses_color(&mut self, color: Color) -> bool {
+    pub fn set_fold_ellipses_color(&mut self, color: Hsla) -> bool {
         self.fold_map.set_ellipses_color(color)
     }
 
-    pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut ModelContext<Self>) -> bool {
+    pub fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut ModelContext<Self>) -> bool {
         self.wrap_map
             .update(cx, |map, cx| map.set_wrap_width(width, cx))
     }
@@ -296,7 +291,7 @@ impl DisplayMap {
         self.block_map.read(snapshot, edits);
     }
 
-    fn tab_size(buffer: &ModelHandle<MultiBuffer>, cx: &mut ModelContext<Self>) -> NonZeroU32 {
+    fn tab_size(buffer: &Model<MultiBuffer>, cx: &mut ModelContext<Self>) -> NonZeroU32 {
         let language = buffer
             .read(cx)
             .as_singleton()
@@ -510,18 +505,18 @@ impl DisplaySnapshot {
         &'a self,
         display_rows: Range<u32>,
         language_aware: bool,
-        style: &'a EditorStyle,
+        editor_style: &'a EditorStyle,
     ) -> impl Iterator<Item = HighlightedChunk<'a>> {
         self.chunks(
             display_rows,
             language_aware,
-            Some(style.theme.hint),
-            Some(style.theme.suggestion),
+            Some(editor_style.inlays_style),
+            Some(editor_style.suggestions_style),
         )
         .map(|chunk| {
             let mut highlight_style = chunk
                 .syntax_highlight_id
-                .and_then(|id| id.style(&style.syntax));
+                .and_then(|id| id.style(&editor_style.syntax));
 
             if let Some(chunk_highlight) = chunk.highlight_style {
                 if let Some(highlight_style) = highlight_style.as_mut() {
@@ -534,17 +529,18 @@ impl DisplaySnapshot {
             let mut diagnostic_highlight = HighlightStyle::default();
 
             if chunk.is_unnecessary {
-                diagnostic_highlight.fade_out = Some(style.unnecessary_code_fade);
+                diagnostic_highlight.fade_out = Some(UNNECESSARY_CODE_FADE);
             }
 
             if let Some(severity) = chunk.diagnostic_severity {
                 // Omit underlines for HINT/INFO diagnostics on 'unnecessary' code.
                 if severity <= DiagnosticSeverity::WARNING || !chunk.is_unnecessary {
-                    let diagnostic_style = super::diagnostic_style(severity, true, style);
-                    diagnostic_highlight.underline = Some(Underline {
-                        color: Some(diagnostic_style.message.text.color),
+                    let diagnostic_color =
+                        super::diagnostic_style(severity, true, &editor_style.status);
+                    diagnostic_highlight.underline = Some(UnderlineStyle {
+                        color: Some(diagnostic_color),
                         thickness: 1.0.into(),
-                        squiggly: true,
+                        wavy: true,
                     });
                 }
             }
@@ -563,81 +559,64 @@ impl DisplaySnapshot {
         })
     }
 
-    pub fn lay_out_line_for_row(
+    pub fn layout_row(
         &self,
         display_row: u32,
         TextLayoutDetails {
-            font_cache,
-            text_layout_cache,
+            text_system,
             editor_style,
+            rem_size,
         }: &TextLayoutDetails,
-    ) -> Line {
-        let mut styles = Vec::new();
+    ) -> Arc<LineLayout> {
+        let mut runs = Vec::new();
         let mut line = String::new();
-        let mut ended_in_newline = false;
 
         let range = display_row..display_row + 1;
-        for chunk in self.highlighted_chunks(range, false, editor_style) {
+        for chunk in self.highlighted_chunks(range, false, &editor_style) {
             line.push_str(chunk.chunk);
 
             let text_style = if let Some(style) = chunk.style {
-                editor_style
-                    .text
-                    .clone()
-                    .highlight(style, font_cache)
-                    .map(Cow::Owned)
-                    .unwrap_or_else(|_| Cow::Borrowed(&editor_style.text))
+                Cow::Owned(editor_style.text.clone().highlight(style))
             } else {
                 Cow::Borrowed(&editor_style.text)
             };
-            ended_in_newline = chunk.chunk.ends_with("\n");
-
-            styles.push((
-                chunk.chunk.len(),
-                RunStyle {
-                    font_id: text_style.font_id,
-                    color: text_style.color,
-                    underline: text_style.underline,
-                },
-            ));
+
+            runs.push(text_style.to_run(chunk.chunk.len()))
         }
 
-        // our pixel positioning logic assumes each line ends in \n,
-        // this is almost always true except for the last line which
-        // may have no trailing newline.
-        if !ended_in_newline && display_row == self.max_point().row() {
-            line.push_str("\n");
-
-            styles.push((
-                "\n".len(),
-                RunStyle {
-                    font_id: editor_style.text.font_id,
-                    color: editor_style.text_color,
-                    underline: editor_style.text.underline,
-                },
-            ));
+        if line.ends_with('\n') {
+            line.pop();
+            if let Some(last_run) = runs.last_mut() {
+                last_run.len -= 1;
+                if last_run.len == 0 {
+                    runs.pop();
+                }
+            }
         }
 
-        text_layout_cache.layout_str(&line, editor_style.text.font_size, &styles)
+        let font_size = editor_style.text.font_size.to_pixels(*rem_size);
+        text_system
+            .layout_line(&line, font_size, &runs)
+            .expect("we expect the font to be loaded because it's rendered by the editor")
     }
 
-    pub fn x_for_point(
+    pub fn x_for_display_point(
         &self,
         display_point: DisplayPoint,
         text_layout_details: &TextLayoutDetails,
-    ) -> f32 {
-        let layout_line = self.lay_out_line_for_row(display_point.row(), text_layout_details);
-        layout_line.x_for_index(display_point.column() as usize)
+    ) -> Pixels {
+        let line = self.layout_row(display_point.row(), text_layout_details);
+        line.x_for_index(display_point.column() as usize)
     }
 
-    pub fn column_for_x(
+    pub fn display_column_for_x(
         &self,
         display_row: u32,
-        x_coordinate: f32,
-        text_layout_details: &TextLayoutDetails,
+        x: Pixels,
+        details: &TextLayoutDetails,
     ) -> u32 {
-        let layout_line = self.lay_out_line_for_row(display_row, text_layout_details);
-        layout_line.closest_index_for_x(x_coordinate) as u32
+        let layout_line = self.layout_row(display_row, details);
+        layout_line.closest_index_for_x(x) as u32
     }
 
     pub fn chars_at(
@@ -740,7 +719,7 @@ impl DisplaySnapshot {
         DisplayPoint(point)
     }
 
-    pub fn folds_in_range<T>(&self, range: Range<T>) -> impl Iterator<Item = &Range<Anchor>>
+    pub fn folds_in_range<T>(&self, range: Range<T>) -> impl Iterator<Item = &Fold>
     where
         T: ToOffset,
     {
@@ -1015,7 +994,7 @@ pub mod tests {
         movement,
         test::{editor_test_context::EditorTestContext, marked_display_snapshot},
     };
-    use gpui::{color::Color, elements::*, test::observe, AppContext};
+    use gpui::{div, font, observe, px, AppContext, Context, Element, Hsla};
     use language::{
         language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
         Buffer, Language, LanguageConfig, SelectionGoal,
@@ -1025,34 +1004,27 @@ pub mod tests {
     use settings::SettingsStore;
     use smol::stream::StreamExt;
     use std::{env, sync::Arc};
-    use theme::SyntaxTheme;
+    use theme::{LoadThemes, SyntaxTheme};
     use util::test::{marked_text_ranges, sample_text};
     use Bias::*;
 
     #[gpui::test(iterations = 100)]
     async fn test_random_display_map(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
-        cx.foreground().set_block_on_ticks(0..=50);
-        cx.foreground().forbid_parking();
+        cx.background_executor.set_block_on_ticks(0..=50);
         let operations = env::var("OPERATIONS")
             .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
             .unwrap_or(10);
 
-        let font_cache = cx.font_cache().clone();
+        let _test_platform = &cx.test_platform;
         let mut tab_size = rng.gen_range(1..=4);
         let buffer_start_excerpt_header_height = rng.gen_range(1..=5);
         let excerpt_header_height = rng.gen_range(1..=5);
-        let family_id = font_cache
-            .load_family(&["Helvetica"], &Default::default())
-            .unwrap();
-        let font_id = font_cache
-            .select_font(family_id, &Default::default())
-            .unwrap();
-        let font_size = 14.0;
+        let font_size = px(14.0);
         let max_wrap_width = 300.0;
         let mut wrap_width = if rng.gen_bool(0.1) {
             None
         } else {
-            Some(rng.gen_range(0.0..=max_wrap_width))
+            Some(px(rng.gen_range(0.0..=max_wrap_width)))
         };
 
         log::info!("tab size: {}", tab_size);
@@ -1074,10 +1046,10 @@ pub mod tests {
             }
         });
 
-        let map = cx.add_model(|cx| {
+        let map = cx.new_model(|cx| {
             DisplayMap::new(
                 buffer.clone(),
-                font_id,
+                font("Helvetica"),
                 font_size,
                 wrap_width,
                 buffer_start_excerpt_header_height,
@@ -1103,7 +1075,7 @@ pub mod tests {
                     wrap_width = if rng.gen_bool(0.2) {
                         None
                     } else {
-                        Some(rng.gen_range(0.0..=max_wrap_width))
+                        Some(px(rng.gen_range(0.0..=max_wrap_width)))
                     };
                     log::info!("setting wrap width to {:?}", wrap_width);
                     map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
@@ -1114,7 +1086,7 @@ pub mod tests {
                     tab_size = *tab_sizes.choose(&mut rng).unwrap();
                     log::info!("setting tab size to {:?}", tab_size);
                     cx.update(|cx| {
-                        cx.update_global::<SettingsStore, _, _>(|store, cx| {
+                        cx.update_global::<SettingsStore, _>(|store, cx| {
                             store.update_user_settings::<AllLanguageSettings>(cx, |s| {
                                 s.defaults.tab_size = NonZeroU32::new(tab_size);
                             });
@@ -1150,7 +1122,7 @@ pub mod tests {
                                         position,
                                         height,
                                         disposition,
-                                        render: Arc::new(|_| Empty::new().into_any()),
+                                        render: Arc::new(|_| div().into_any()),
                                     }
                                 })
                                 .collect::<Vec<_>>();
@@ -1295,7 +1267,8 @@ pub mod tests {
 
     #[gpui::test(retries = 5)]
     async fn test_soft_wraps(cx: &mut gpui::TestAppContext) {
-        cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
+        cx.background_executor
+            .set_block_on_ticks(usize::MAX..=usize::MAX);
         cx.update(|cx| {
             init_test(cx, |_| {});
         });
@@ -1304,25 +1277,25 @@ pub mod tests {
         let editor = cx.editor.clone();
         let window = cx.window.clone();
 
-        cx.update_window(window, |cx| {
+        _ = cx.update_window(window, |_, cx| {
             let text_layout_details =
-                editor.read_with(cx, |editor, cx| editor.text_layout_details(cx));
-
-            let font_cache = cx.font_cache().clone();
+                editor.update(cx, |editor, cx| editor.text_layout_details(cx));
 
-            let family_id = font_cache
-                .load_family(&["Helvetica"], &Default::default())
-                .unwrap();
-            let font_id = font_cache
-                .select_font(family_id, &Default::default())
-                .unwrap();
-            let font_size = 12.0;
-            let wrap_width = Some(64.);
+            let font_size = px(12.0);
+            let wrap_width = Some(px(64.));
 
             let text = "one two three four five\nsix seven eight";
             let buffer = MultiBuffer::build_simple(text, cx);
-            let map = cx.add_model(|cx| {
-                DisplayMap::new(buffer.clone(), font_id, font_size, wrap_width, 1, 1, cx)
+            let map = cx.new_model(|cx| {
+                DisplayMap::new(
+                    buffer.clone(),
+                    font("Helvetica"),
+                    font_size,
+                    wrap_width,
+                    1,
+                    1,
+                    cx,
+                )
             });
 
             let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
@@ -1347,7 +1320,7 @@ pub mod tests {
                 DisplayPoint::new(0, 7)
             );
 
-            let x = snapshot.x_for_point(DisplayPoint::new(1, 10), &text_layout_details);
+            let x = snapshot.x_for_display_point(DisplayPoint::new(1, 10), &text_layout_details);
             assert_eq!(
                 movement::up(
                     &snapshot,
@@ -1358,33 +1331,33 @@ pub mod tests {
                 ),
                 (
                     DisplayPoint::new(0, 7),
-                    SelectionGoal::HorizontalPosition(x)
+                    SelectionGoal::HorizontalPosition(x.0)
                 )
             );
             assert_eq!(
                 movement::down(
                     &snapshot,
                     DisplayPoint::new(0, 7),
-                    SelectionGoal::HorizontalPosition(x),
+                    SelectionGoal::HorizontalPosition(x.0),
                     false,
                     &text_layout_details
                 ),
                 (
                     DisplayPoint::new(1, 10),
-                    SelectionGoal::HorizontalPosition(x)
+                    SelectionGoal::HorizontalPosition(x.0)
                 )
             );
             assert_eq!(
                 movement::down(
                     &snapshot,
                     DisplayPoint::new(1, 10),
-                    SelectionGoal::HorizontalPosition(x),
+                    SelectionGoal::HorizontalPosition(x.0),
                     false,
                     &text_layout_details
                 ),
                 (
                     DisplayPoint::new(2, 4),
-                    SelectionGoal::HorizontalPosition(x)
+                    SelectionGoal::HorizontalPosition(x.0)
                 )
             );
 
@@ -1400,7 +1373,9 @@ pub mod tests {
             );
 
             // Re-wrap on font size changes
-            map.update(cx, |map, cx| map.set_font(font_id, font_size + 3., cx));
+            map.update(cx, |map, cx| {
+                map.set_font(font("Helvetica"), px(font_size.0 + 3.), cx)
+            });
 
             let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
             assert_eq!(
@@ -1416,17 +1391,11 @@ pub mod tests {
 
         let text = sample_text(6, 6, 'a');
         let buffer = MultiBuffer::build_simple(&text, cx);
-        let family_id = cx
-            .font_cache()
-            .load_family(&["Helvetica"], &Default::default())
-            .unwrap();
-        let font_id = cx
-            .font_cache()
-            .select_font(family_id, &Default::default())
-            .unwrap();
-        let font_size = 14.0;
-        let map =
-            cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx));
+
+        let font_size = px(14.0);
+        let map = cx.new_model(|cx| {
+            DisplayMap::new(buffer.clone(), font("Helvetica"), font_size, None, 1, 1, cx)
+        });
 
         buffer.update(cx, |buffer, cx| {
             buffer.edit(
@@ -1470,9 +1439,9 @@ pub mod tests {
             }"#
         .unindent();
 
-        let theme = SyntaxTheme::new(vec![
-            ("mod.body".to_string(), Color::red().into()),
-            ("fn.name".to_string(), Color::blue().into()),
+        let theme = SyntaxTheme::new_test(vec![
+            ("mod.body", Hsla::red().into()),
+            ("fn.name", Hsla::blue().into()),
         ]);
         let language = Arc::new(
             Language::new(
@@ -1495,38 +1464,33 @@ pub mod tests {
 
         cx.update(|cx| init_test(cx, |s| s.defaults.tab_size = Some(2.try_into().unwrap())));
 
-        let buffer = cx
-            .add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
-        buffer.condition(cx, |buf, _| !buf.is_parsing()).await;
-        let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+        let buffer = cx.new_model(|cx| {
+            Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
+        });
+        cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
+        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 
-        let font_cache = cx.font_cache();
-        let family_id = font_cache
-            .load_family(&["Helvetica"], &Default::default())
-            .unwrap();
-        let font_id = font_cache
-            .select_font(family_id, &Default::default())
-            .unwrap();
-        let font_size = 14.0;
+        let font_size = px(14.0);
 
-        let map = cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx));
+        let map = cx
+            .new_model(|cx| DisplayMap::new(buffer, font("Helvetica"), font_size, None, 1, 1, cx));
         assert_eq!(
             cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)),
             vec![
                 ("fn ".to_string(), None),
-                ("outer".to_string(), Some(Color::blue())),
+                ("outer".to_string(), Some(Hsla::blue())),
                 ("() {}\n\nmod module ".to_string(), None),
-                ("{\n    fn ".to_string(), Some(Color::red())),
-                ("inner".to_string(), Some(Color::blue())),
-                ("() {}\n}".to_string(), Some(Color::red())),
+                ("{\n    fn ".to_string(), Some(Hsla::red())),
+                ("inner".to_string(), Some(Hsla::blue())),
+                ("() {}\n}".to_string(), Some(Hsla::red())),
             ]
         );
         assert_eq!(
             cx.update(|cx| syntax_chunks(3..5, &map, &theme, cx)),
             vec![
-                ("    fn ".to_string(), Some(Color::red())),
-                ("inner".to_string(), Some(Color::blue())),
-                ("() {}\n}".to_string(), Some(Color::red())),
+                ("    fn ".to_string(), Some(Hsla::red())),
+                ("inner".to_string(), Some(Hsla::blue())),
+                ("() {}\n}".to_string(), Some(Hsla::red())),
             ]
         );
 
@@ -1537,11 +1501,11 @@ pub mod tests {
             cx.update(|cx| syntax_chunks(0..2, &map, &theme, cx)),
             vec![
                 ("fn ".to_string(), None),
-                ("out".to_string(), Some(Color::blue())),
+                ("out".to_string(), Some(Hsla::blue())),
                 ("โ‹ฏ".to_string(), None),
-                ("  fn ".to_string(), Some(Color::red())),
-                ("inner".to_string(), Some(Color::blue())),
-                ("() {}\n}".to_string(), Some(Color::red())),
+                ("  fn ".to_string(), Some(Hsla::red())),
+                ("inner".to_string(), Some(Hsla::blue())),
+                ("() {}\n}".to_string(), Some(Hsla::red())),
             ]
         );
     }
@@ -1550,7 +1514,8 @@ pub mod tests {
     async fn test_chunks_with_soft_wrapping(cx: &mut gpui::TestAppContext) {
         use unindent::Unindent as _;
 
-        cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
+        cx.background_executor
+            .set_block_on_ticks(usize::MAX..=usize::MAX);
 
         let text = r#"
             fn outer() {}
@@ -1560,9 +1525,9 @@ pub mod tests {
             }"#
         .unindent();
 
-        let theme = SyntaxTheme::new(vec![
-            ("mod.body".to_string(), Color::red().into()),
-            ("fn.name".to_string(), Color::blue().into()),
+        let theme = SyntaxTheme::new_test(vec![
+            ("mod.body", Hsla::red().into()),
+            ("fn.name", Hsla::blue().into()),
         ]);
         let language = Arc::new(
             Language::new(
@@ -1585,28 +1550,22 @@ pub mod tests {
 
         cx.update(|cx| init_test(cx, |_| {}));
 
-        let buffer = cx
-            .add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
-        buffer.condition(cx, |buf, _| !buf.is_parsing()).await;
-        let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+        let buffer = cx.new_model(|cx| {
+            Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
+        });
+        cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
+        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 
-        let font_cache = cx.font_cache();
+        let font_size = px(16.0);
 
-        let family_id = font_cache
-            .load_family(&["Courier"], &Default::default())
-            .unwrap();
-        let font_id = font_cache
-            .select_font(family_id, &Default::default())
-            .unwrap();
-        let font_size = 16.0;
-
-        let map =
-            cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, Some(40.0), 1, 1, cx));
+        let map = cx.new_model(|cx| {
+            DisplayMap::new(buffer, font("Courier"), font_size, Some(px(40.0)), 1, 1, cx)
+        });
         assert_eq!(
             cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)),
             [
                 ("fn \n".to_string(), None),
-                ("oute\nr".to_string(), Some(Color::blue())),
+                ("oute\nr".to_string(), Some(Hsla::blue())),
                 ("() \n{}\n\n".to_string(), None),
             ]
         );
@@ -1621,10 +1580,10 @@ pub mod tests {
         assert_eq!(
             cx.update(|cx| syntax_chunks(1..4, &map, &theme, cx)),
             [
-                ("out".to_string(), Some(Color::blue())),
+                ("out".to_string(), Some(Hsla::blue())),
                 ("โ‹ฏ\n".to_string(), None),
-                ("  \nfn ".to_string(), Some(Color::red())),
-                ("i\n".to_string(), Some(Color::blue()))
+                ("  \nfn ".to_string(), Some(Hsla::red())),
+                ("i\n".to_string(), Some(Hsla::blue()))
             ]
         );
     }
@@ -1633,9 +1592,9 @@ pub mod tests {
     async fn test_chunks_with_text_highlights(cx: &mut gpui::TestAppContext) {
         cx.update(|cx| init_test(cx, |_| {}));
 
-        let theme = SyntaxTheme::new(vec![
-            ("operator".to_string(), Color::red().into()),
-            ("string".to_string(), Color::green().into()),
+        let theme = SyntaxTheme::new_test(vec![
+            ("operator", Hsla::red().into()),
+            ("string", Hsla::green().into()),
         ]);
         let language = Arc::new(
             Language::new(
@@ -1658,27 +1617,22 @@ pub mod tests {
 
         let (text, highlighted_ranges) = marked_text_ranges(r#"constห‡ ยซaยป: B = "c ยซdยป""#, false);
 
-        let buffer = cx
-            .add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
-        buffer.condition(cx, |buf, _| !buf.is_parsing()).await;
+        let buffer = cx.new_model(|cx| {
+            Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
+        });
+        cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
 
-        let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
         let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
 
-        let font_cache = cx.font_cache();
-        let family_id = font_cache
-            .load_family(&["Courier"], &Default::default())
-            .unwrap();
-        let font_id = font_cache
-            .select_font(family_id, &Default::default())
-            .unwrap();
-        let font_size = 16.0;
-        let map = cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx));
+        let font_size = px(16.0);
+        let map =
+            cx.new_model(|cx| DisplayMap::new(buffer, font("Courier"), font_size, None, 1, 1, cx));
 
         enum MyType {}
 
         let style = HighlightStyle {
-            color: Some(Color::blue()),
+            color: Some(Hsla::blue()),
             ..Default::default()
         };
 
@@ -1700,12 +1654,12 @@ pub mod tests {
             cx.update(|cx| chunks(0..10, &map, &theme, cx)),
             [
                 ("const ".to_string(), None, None),
-                ("a".to_string(), None, Some(Color::blue())),
-                (":".to_string(), Some(Color::red()), None),
+                ("a".to_string(), None, Some(Hsla::blue())),
+                (":".to_string(), Some(Hsla::red()), None),
                 (" B = ".to_string(), None, None),
-                ("\"c ".to_string(), Some(Color::green()), None),
-                ("d".to_string(), Some(Color::green()), Some(Color::blue())),
-                ("\"".to_string(), Some(Color::green()), None),
+                ("\"c ".to_string(), Some(Hsla::green()), None),
+                ("d".to_string(), Some(Hsla::green()), Some(Hsla::blue())),
+                ("\"".to_string(), Some(Hsla::green()), None),
             ]
         );
     }
@@ -1785,17 +1739,11 @@ pub mod tests {
 
         let text = "โœ…\t\tฮฑ\nฮฒ\t\n๐Ÿ€ฮฒ\t\tฮณ";
         let buffer = MultiBuffer::build_simple(text, cx);
-        let font_cache = cx.font_cache();
-        let family_id = font_cache
-            .load_family(&["Helvetica"], &Default::default())
-            .unwrap();
-        let font_id = font_cache
-            .select_font(family_id, &Default::default())
-            .unwrap();
-        let font_size = 14.0;
+        let font_size = px(14.0);
 
-        let map =
-            cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx));
+        let map = cx.new_model(|cx| {
+            DisplayMap::new(buffer.clone(), font("Helvetica"), font_size, None, 1, 1, cx)
+        });
         let map = map.update(cx, |map, cx| map.snapshot(cx));
         assert_eq!(map.text(), "โœ…       ฮฑ\nฮฒ   \n๐Ÿ€ฮฒ      ฮณ");
         assert_eq!(
@@ -1846,16 +1794,10 @@ pub mod tests {
         init_test(cx, |_| {});
 
         let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", cx);
-        let font_cache = cx.font_cache();
-        let family_id = font_cache
-            .load_family(&["Helvetica"], &Default::default())
-            .unwrap();
-        let font_id = font_cache
-            .select_font(family_id, &Default::default())
-            .unwrap();
-        let font_size = 14.0;
-        let map =
-            cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx));
+        let font_size = px(14.0);
+        let map = cx.new_model(|cx| {
+            DisplayMap::new(buffer.clone(), font("Helvetica"), font_size, None, 1, 1, cx)
+        });
         assert_eq!(
             map.update(cx, |map, cx| map.snapshot(cx)).max_point(),
             DisplayPoint::new(1, 11)
@@ -1864,10 +1806,10 @@ pub mod tests {
 
     fn syntax_chunks<'a>(
         rows: Range<u32>,
-        map: &ModelHandle<DisplayMap>,
+        map: &Model<DisplayMap>,
         theme: &'a SyntaxTheme,
         cx: &mut AppContext,
-    ) -> Vec<(String, Option<Color>)> {
+    ) -> Vec<(String, Option<Hsla>)> {
         chunks(rows, map, theme, cx)
             .into_iter()
             .map(|(text, color, _)| (text, color))
@@ -1876,12 +1818,12 @@ pub mod tests {
 
     fn chunks<'a>(
         rows: Range<u32>,
-        map: &ModelHandle<DisplayMap>,
+        map: &Model<DisplayMap>,
         theme: &'a SyntaxTheme,
         cx: &mut AppContext,
-    ) -> Vec<(String, Option<Color>, Option<Color>)> {
+    ) -> Vec<(String, Option<Hsla>, Option<Hsla>)> {
         let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
-        let mut chunks: Vec<(String, Option<Color>, Option<Color>)> = Vec::new();
+        let mut chunks: Vec<(String, Option<Hsla>, Option<Hsla>)> = Vec::new();
         for chunk in snapshot.chunks(rows, true, None, None) {
             let syntax_color = chunk
                 .syntax_highlight_id
@@ -1899,13 +1841,13 @@ pub mod tests {
     }
 
     fn init_test(cx: &mut AppContext, f: impl Fn(&mut AllLanguageSettingsContent)) {
-        cx.foreground().forbid_parking();
-        cx.set_global(SettingsStore::test(cx));
+        let settings = SettingsStore::test(cx);
+        cx.set_global(settings);
         language::init(cx);
         crate::init(cx);
         Project::init_settings(cx);
-        theme::init((), cx);
-        cx.update_global::<SettingsStore, _, _>(|store, cx| {
+        theme::init(LoadThemes::JustBase, cx);
+        cx.update_global::<SettingsStore, _>(|store, cx| {
             store.update_user_settings::<AllLanguageSettings>(cx, f);
         });
     }

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

@@ -2,9 +2,9 @@ use super::{
     wrap_map::{self, WrapEdit, WrapPoint, WrapSnapshot},
     Highlights,
 };
-use crate::{Anchor, Editor, ExcerptId, ExcerptRange, ToPoint as _};
+use crate::{Anchor, Editor, EditorStyle, ExcerptId, ExcerptRange, ToPoint as _};
 use collections::{Bound, HashMap, HashSet};
-use gpui::{AnyElement, ViewContext};
+use gpui::{AnyElement, Pixels, ViewContext};
 use language::{BufferSnapshot, Chunk, Patch, Point};
 use parking_lot::Mutex;
 use std::{
@@ -50,7 +50,7 @@ struct BlockRow(u32);
 #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
 struct WrapRow(u32);
 
-pub type RenderBlock = Arc<dyn Fn(&mut BlockContext) -> AnyElement<Editor>>;
+pub type RenderBlock = Arc<dyn Fn(&mut BlockContext) -> AnyElement>;
 
 pub struct Block {
     id: BlockId,
@@ -69,7 +69,7 @@ where
     pub position: P,
     pub height: u8,
     pub style: BlockStyle,
-    pub render: Arc<dyn Fn(&mut BlockContext) -> AnyElement<Editor>>,
+    pub render: Arc<dyn Fn(&mut BlockContext) -> AnyElement>,
     pub disposition: BlockDisposition,
 }
 
@@ -80,15 +80,15 @@ pub enum BlockStyle {
     Sticky,
 }
 
-pub struct BlockContext<'a, 'b, 'c> {
-    pub view_context: &'c mut ViewContext<'a, 'b, Editor>,
-    pub anchor_x: f32,
-    pub scroll_x: f32,
-    pub gutter_width: f32,
-    pub gutter_padding: f32,
-    pub em_width: f32,
-    pub line_height: f32,
+pub struct BlockContext<'a, 'b> {
+    pub view_context: &'b mut ViewContext<'a, Editor>,
+    pub anchor_x: Pixels,
+    pub gutter_width: Pixels,
+    pub gutter_padding: Pixels,
+    pub em_width: Pixels,
+    pub line_height: Pixels,
     pub block_id: usize,
+    pub editor_style: &'b EditorStyle,
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
@@ -932,22 +932,22 @@ impl BlockDisposition {
     }
 }
 
-impl<'a, 'b, 'c> Deref for BlockContext<'a, 'b, 'c> {
-    type Target = ViewContext<'a, 'b, Editor>;
+impl<'a> Deref for BlockContext<'a, '_> {
+    type Target = ViewContext<'a, Editor>;
 
     fn deref(&self) -> &Self::Target {
         self.view_context
     }
 }
 
-impl DerefMut for BlockContext<'_, '_, '_> {
+impl DerefMut for BlockContext<'_, '_> {
     fn deref_mut(&mut self) -> &mut Self::Target {
         self.view_context
     }
 }
 
 impl Block {
-    pub fn render(&self, cx: &mut BlockContext) -> AnyElement<Editor> {
+    pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
         self.render.lock()(cx)
     }
 
@@ -993,7 +993,7 @@ mod tests {
     use super::*;
     use crate::display_map::inlay_map::InlayMap;
     use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
-    use gpui::{elements::Empty, Element};
+    use gpui::{div, font, px, Element};
     use multi_buffer::MultiBuffer;
     use rand::prelude::*;
     use settings::SettingsStore;
@@ -1015,27 +1015,19 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_basic_blocks(cx: &mut gpui::AppContext) {
-        init_test(cx);
-
-        let family_id = cx
-            .font_cache()
-            .load_family(&["Helvetica"], &Default::default())
-            .unwrap();
-        let font_id = cx
-            .font_cache()
-            .select_font(family_id, &Default::default())
-            .unwrap();
+    fn test_basic_blocks(cx: &mut gpui::TestAppContext) {
+        cx.update(|cx| init_test(cx));
 
         let text = "aaa\nbbb\nccc\nddd";
 
-        let buffer = MultiBuffer::build_simple(text, cx);
-        let buffer_snapshot = buffer.read(cx).snapshot(cx);
+        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
+        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
         let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
         let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
         let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
         let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
-        let (wrap_map, wraps_snapshot) = WrapMap::new(tab_snapshot, font_id, 14.0, None, cx);
+        let (wrap_map, wraps_snapshot) =
+            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
         let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
 
         let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
@@ -1045,21 +1037,21 @@ mod tests {
                 position: buffer_snapshot.anchor_after(Point::new(1, 0)),
                 height: 1,
                 disposition: BlockDisposition::Above,
-                render: Arc::new(|_| Empty::new().into_any_named("block 1")),
+                render: Arc::new(|_| div().into_any()),
             },
             BlockProperties {
                 style: BlockStyle::Fixed,
                 position: buffer_snapshot.anchor_after(Point::new(1, 2)),
                 height: 2,
                 disposition: BlockDisposition::Above,
-                render: Arc::new(|_| Empty::new().into_any_named("block 2")),
+                render: Arc::new(|_| div().into_any()),
             },
             BlockProperties {
                 style: BlockStyle::Fixed,
                 position: buffer_snapshot.anchor_after(Point::new(3, 3)),
                 height: 3,
                 disposition: BlockDisposition::Below,
-                render: Arc::new(|_| Empty::new().into_any_named("block 3")),
+                render: Arc::new(|_| div().into_any()),
             },
         ]);
 
@@ -1190,26 +1182,21 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_blocks_on_wrapped_lines(cx: &mut gpui::AppContext) {
-        init_test(cx);
-
-        let family_id = cx
-            .font_cache()
-            .load_family(&["Helvetica"], &Default::default())
-            .unwrap();
-        let font_id = cx
-            .font_cache()
-            .select_font(family_id, &Default::default())
-            .unwrap();
+    fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
+        cx.update(|cx| init_test(cx));
+
+        let _font_id = cx.text_system().font_id(&font("Helvetica")).unwrap();
 
         let text = "one two three\nfour five six\nseven eight";
 
-        let buffer = MultiBuffer::build_simple(text, cx);
-        let buffer_snapshot = buffer.read(cx).snapshot(cx);
+        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
+        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
         let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
         let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
         let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
-        let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font_id, 14.0, Some(60.), cx);
+        let (_, wraps_snapshot) = cx.update(|cx| {
+            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(60.)), cx)
+        });
         let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
 
         let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
@@ -1218,14 +1205,14 @@ mod tests {
                 style: BlockStyle::Fixed,
                 position: buffer_snapshot.anchor_after(Point::new(1, 12)),
                 disposition: BlockDisposition::Above,
-                render: Arc::new(|_| Empty::new().into_any_named("block 1")),
+                render: Arc::new(|_| div().into_any()),
                 height: 1,
             },
             BlockProperties {
                 style: BlockStyle::Fixed,
                 position: buffer_snapshot.anchor_after(Point::new(1, 1)),
                 disposition: BlockDisposition::Below,
-                render: Arc::new(|_| Empty::new().into_any_named("block 2")),
+                render: Arc::new(|_| div().into_any()),
                 height: 1,
             },
         ]);
@@ -1240,8 +1227,8 @@ mod tests {
     }
 
     #[gpui::test(iterations = 100)]
-    fn test_random_blocks(cx: &mut gpui::AppContext, mut rng: StdRng) {
-        init_test(cx);
+    fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
+        cx.update(|cx| init_test(cx));
 
         let operations = env::var("OPERATIONS")
             .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
@@ -1250,18 +1237,10 @@ mod tests {
         let wrap_width = if rng.gen_bool(0.2) {
             None
         } else {
-            Some(rng.gen_range(0.0..=100.0))
+            Some(px(rng.gen_range(0.0..=100.0)))
         };
         let tab_size = 1.try_into().unwrap();
-        let family_id = cx
-            .font_cache()
-            .load_family(&["Helvetica"], &Default::default())
-            .unwrap();
-        let font_id = cx
-            .font_cache()
-            .select_font(family_id, &Default::default())
-            .unwrap();
-        let font_size = 14.0;
+        let font_size = px(14.0);
         let buffer_start_header_height = rng.gen_range(1..=5);
         let excerpt_header_height = rng.gen_range(1..=5);
 
@@ -1272,17 +1251,17 @@ mod tests {
             let len = rng.gen_range(0..10);
             let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
             log::info!("initial buffer text: {:?}", text);
-            MultiBuffer::build_simple(&text, cx)
+            cx.update(|cx| MultiBuffer::build_simple(&text, cx))
         } else {
-            MultiBuffer::build_random(&mut rng, cx)
+            cx.update(|cx| MultiBuffer::build_random(&mut rng, cx))
         };
 
-        let mut buffer_snapshot = buffer.read(cx).snapshot(cx);
+        let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
         let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
         let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
         let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
-        let (wrap_map, wraps_snapshot) =
-            WrapMap::new(tab_snapshot, font_id, font_size, wrap_width, cx);
+        let (wrap_map, wraps_snapshot) = cx
+            .update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), font_size, wrap_width, cx));
         let mut block_map = BlockMap::new(
             wraps_snapshot,
             buffer_start_header_height,
@@ -1297,7 +1276,7 @@ mod tests {
                     let wrap_width = if rng.gen_bool(0.2) {
                         None
                     } else {
-                        Some(rng.gen_range(0.0..=100.0))
+                        Some(px(rng.gen_range(0.0..=100.0)))
                     };
                     log::info!("Setting wrap width to {:?}", wrap_width);
                     wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
@@ -1306,7 +1285,7 @@ mod tests {
                     let block_count = rng.gen_range(1..=5);
                     let block_properties = (0..block_count)
                         .map(|_| {
-                            let buffer = buffer.read(cx).read(cx);
+                            let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
                             let position = buffer.anchor_after(
                                 buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left),
                             );
@@ -1328,7 +1307,7 @@ mod tests {
                                 position,
                                 height,
                                 disposition,
-                                render: Arc::new(|_| Empty::new().into_any()),
+                                render: Arc::new(|_| div().into_any()),
                             }
                         })
                         .collect::<Vec<_>>();
@@ -1646,8 +1625,9 @@ mod tests {
     }
 
     fn init_test(cx: &mut gpui::AppContext) {
-        cx.set_global(SettingsStore::test(cx));
-        theme::init((), cx);
+        let settings = SettingsStore::test(cx);
+        cx.set_global(settings);
+        theme::init(theme::LoadThemes::JustBase, cx);
     }
 
     impl TransformBlock {

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

@@ -3,15 +3,16 @@ use super::{
     Highlights,
 };
 use crate::{Anchor, AnchorRangeExt, MultiBufferSnapshot, ToOffset};
-use gpui::{color::Color, fonts::HighlightStyle};
+use gpui::{ElementId, HighlightStyle, Hsla};
 use language::{Chunk, Edit, Point, TextSummary};
 use std::{
     any::TypeId,
     cmp::{self, Ordering},
     iter,
-    ops::{Add, AddAssign, Range, Sub},
+    ops::{Add, AddAssign, Deref, DerefMut, Range, Sub},
 };
 use sum_tree::{Bias, Cursor, FilterCursor, SumTree};
+use util::post_inc;
 
 #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
 pub struct FoldPoint(pub Point);
@@ -90,12 +91,16 @@ impl<'a> FoldMapWriter<'a> {
             }
 
             // For now, ignore any ranges that span an excerpt boundary.
-            let fold = Fold(buffer.anchor_after(range.start)..buffer.anchor_before(range.end));
-            if fold.0.start.excerpt_id != fold.0.end.excerpt_id {
+            let fold_range =
+                FoldRange(buffer.anchor_after(range.start)..buffer.anchor_before(range.end));
+            if fold_range.0.start.excerpt_id != fold_range.0.end.excerpt_id {
                 continue;
             }
 
-            folds.push(fold);
+            folds.push(Fold {
+                id: FoldId(post_inc(&mut self.0.next_fold_id.0)),
+                range: fold_range,
+            });
 
             let inlay_range =
                 snapshot.to_inlay_offset(range.start)..snapshot.to_inlay_offset(range.end);
@@ -106,13 +111,13 @@ impl<'a> FoldMapWriter<'a> {
         }
 
         let buffer = &snapshot.buffer;
-        folds.sort_unstable_by(|a, b| sum_tree::SeekTarget::cmp(a, b, buffer));
+        folds.sort_unstable_by(|a, b| sum_tree::SeekTarget::cmp(&a.range, &b.range, buffer));
 
         self.0.snapshot.folds = {
             let mut new_tree = SumTree::new();
-            let mut cursor = self.0.snapshot.folds.cursor::<Fold>();
+            let mut cursor = self.0.snapshot.folds.cursor::<FoldRange>();
             for fold in folds {
-                new_tree.append(cursor.slice(&fold, Bias::Right, buffer), buffer);
+                new_tree.append(cursor.slice(&fold.range, Bias::Right, buffer), buffer);
                 new_tree.push(fold, buffer);
             }
             new_tree.append(cursor.suffix(buffer), buffer);
@@ -138,7 +143,8 @@ impl<'a> FoldMapWriter<'a> {
             let mut folds_cursor =
                 intersecting_folds(&snapshot, &self.0.snapshot.folds, range, inclusive);
             while let Some(fold) = folds_cursor.item() {
-                let offset_range = fold.0.start.to_offset(buffer)..fold.0.end.to_offset(buffer);
+                let offset_range =
+                    fold.range.start.to_offset(buffer)..fold.range.end.to_offset(buffer);
                 if offset_range.end > offset_range.start {
                     let inlay_range = snapshot.to_inlay_offset(offset_range.start)
                         ..snapshot.to_inlay_offset(offset_range.end);
@@ -174,7 +180,8 @@ impl<'a> FoldMapWriter<'a> {
 
 pub struct FoldMap {
     snapshot: FoldSnapshot,
-    ellipses_color: Option<Color>,
+    ellipses_color: Option<Hsla>,
+    next_fold_id: FoldId,
 }
 
 impl FoldMap {
@@ -197,6 +204,7 @@ impl FoldMap {
                 ellipses_color: None,
             },
             ellipses_color: None,
+            next_fold_id: FoldId::default(),
         };
         let snapshot = this.snapshot.clone();
         (this, snapshot)
@@ -221,7 +229,7 @@ impl FoldMap {
         (FoldMapWriter(self), snapshot, edits)
     }
 
-    pub fn set_ellipses_color(&mut self, color: Color) -> bool {
+    pub fn set_ellipses_color(&mut self, color: Hsla) -> bool {
         if self.ellipses_color != Some(color) {
             self.ellipses_color = Some(color);
             true
@@ -242,8 +250,8 @@ impl FoldMap {
             while let Some(fold) = folds.next() {
                 if let Some(next_fold) = folds.peek() {
                     let comparison = fold
-                        .0
-                        .cmp(&next_fold.0, &self.snapshot.inlay_snapshot.buffer);
+                        .range
+                        .cmp(&next_fold.range, &self.snapshot.inlay_snapshot.buffer);
                     assert!(comparison.is_le());
                 }
             }
@@ -304,9 +312,9 @@ impl FoldMap {
                 let anchor = inlay_snapshot
                     .buffer
                     .anchor_before(inlay_snapshot.to_buffer_offset(edit.new.start));
-                let mut folds_cursor = self.snapshot.folds.cursor::<Fold>();
+                let mut folds_cursor = self.snapshot.folds.cursor::<FoldRange>();
                 folds_cursor.seek(
-                    &Fold(anchor..Anchor::max()),
+                    &FoldRange(anchor..Anchor::max()),
                     Bias::Left,
                     &inlay_snapshot.buffer,
                 );
@@ -315,8 +323,8 @@ impl FoldMap {
                     let inlay_snapshot = &inlay_snapshot;
                     move || {
                         let item = folds_cursor.item().map(|f| {
-                            let buffer_start = f.0.start.to_offset(&inlay_snapshot.buffer);
-                            let buffer_end = f.0.end.to_offset(&inlay_snapshot.buffer);
+                            let buffer_start = f.range.start.to_offset(&inlay_snapshot.buffer);
+                            let buffer_end = f.range.end.to_offset(&inlay_snapshot.buffer);
                             inlay_snapshot.to_inlay_offset(buffer_start)
                                 ..inlay_snapshot.to_inlay_offset(buffer_end)
                         });
@@ -469,7 +477,7 @@ pub struct FoldSnapshot {
     folds: SumTree<Fold>,
     pub inlay_snapshot: InlaySnapshot,
     pub version: usize,
-    pub ellipses_color: Option<Color>,
+    pub ellipses_color: Option<Hsla>,
 }
 
 impl FoldSnapshot {
@@ -596,13 +604,13 @@ impl FoldSnapshot {
         self.transforms.summary().output.longest_row
     }
 
-    pub fn folds_in_range<T>(&self, range: Range<T>) -> impl Iterator<Item = &Range<Anchor>>
+    pub fn folds_in_range<T>(&self, range: Range<T>) -> impl Iterator<Item = &Fold>
     where
         T: ToOffset,
     {
         let mut folds = intersecting_folds(&self.inlay_snapshot, &self.folds, range, false);
         iter::from_fn(move || {
-            let item = folds.item().map(|f| &f.0);
+            let item = folds.item();
             folds.next(&self.inlay_snapshot.buffer);
             item
         })
@@ -830,10 +838,39 @@ impl sum_tree::Summary for TransformSummary {
     }
 }
 
-#[derive(Clone, Debug)]
-struct Fold(Range<Anchor>);
+#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
+pub struct FoldId(usize);
+
+impl Into<ElementId> for FoldId {
+    fn into(self) -> ElementId {
+        ElementId::Integer(self.0)
+    }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Fold {
+    pub id: FoldId,
+    pub range: FoldRange,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct FoldRange(Range<Anchor>);
+
+impl Deref for FoldRange {
+    type Target = Range<Anchor>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl DerefMut for FoldRange {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
 
-impl Default for Fold {
+impl Default for FoldRange {
     fn default() -> Self {
         Self(Anchor::min()..Anchor::max())
     }
@@ -844,17 +881,17 @@ impl sum_tree::Item for Fold {
 
     fn summary(&self) -> Self::Summary {
         FoldSummary {
-            start: self.0.start.clone(),
-            end: self.0.end.clone(),
-            min_start: self.0.start.clone(),
-            max_end: self.0.end.clone(),
+            start: self.range.start.clone(),
+            end: self.range.end.clone(),
+            min_start: self.range.start.clone(),
+            max_end: self.range.end.clone(),
             count: 1,
         }
     }
 }
 
 #[derive(Clone, Debug)]
-struct FoldSummary {
+pub struct FoldSummary {
     start: Anchor,
     end: Anchor,
     min_start: Anchor,
@@ -900,14 +937,14 @@ impl sum_tree::Summary for FoldSummary {
     }
 }
 
-impl<'a> sum_tree::Dimension<'a, FoldSummary> for Fold {
+impl<'a> sum_tree::Dimension<'a, FoldSummary> for FoldRange {
     fn add_summary(&mut self, summary: &'a FoldSummary, _: &MultiBufferSnapshot) {
         self.0.start = summary.start.clone();
         self.0.end = summary.end.clone();
     }
 }
 
-impl<'a> sum_tree::SeekTarget<'a, FoldSummary, Fold> for Fold {
+impl<'a> sum_tree::SeekTarget<'a, FoldSummary, FoldRange> for FoldRange {
     fn cmp(&self, other: &Self, buffer: &MultiBufferSnapshot) -> Ordering {
         self.0.cmp(&other.0, buffer)
     }
@@ -959,7 +996,7 @@ pub struct FoldChunks<'a> {
     inlay_offset: InlayOffset,
     output_offset: usize,
     max_output_offset: usize,
-    ellipses_color: Option<Color>,
+    ellipses_color: Option<Hsla>,
 }
 
 impl<'a> Iterator for FoldChunks<'a> {
@@ -1321,7 +1358,10 @@ mod tests {
         let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]);
         let fold_ranges = snapshot
             .folds_in_range(Point::new(1, 0)..Point::new(1, 3))
-            .map(|fold| fold.start.to_point(&buffer_snapshot)..fold.end.to_point(&buffer_snapshot))
+            .map(|fold| {
+                fold.range.start.to_point(&buffer_snapshot)
+                    ..fold.range.end.to_point(&buffer_snapshot)
+            })
             .collect::<Vec<_>>();
         assert_eq!(
             fold_ranges,
@@ -1553,10 +1593,9 @@ mod tests {
                     .filter(|fold| {
                         let start = buffer_snapshot.anchor_before(start);
                         let end = buffer_snapshot.anchor_after(end);
-                        start.cmp(&fold.0.end, &buffer_snapshot) == Ordering::Less
-                            && end.cmp(&fold.0.start, &buffer_snapshot) == Ordering::Greater
+                        start.cmp(&fold.range.end, &buffer_snapshot) == Ordering::Less
+                            && end.cmp(&fold.range.start, &buffer_snapshot) == Ordering::Greater
                     })
-                    .map(|fold| fold.0)
                     .collect::<Vec<_>>();
 
                 assert_eq!(
@@ -1629,7 +1668,8 @@ mod tests {
     }
 
     fn init_test(cx: &mut gpui::AppContext) {
-        cx.set_global(SettingsStore::test(cx));
+        let store = SettingsStore::test(cx);
+        cx.set_global(store);
     }
 
     impl FoldMap {
@@ -1638,10 +1678,10 @@ mod tests {
             let buffer = &inlay_snapshot.buffer;
             let mut folds = self.snapshot.folds.items(buffer);
             // Ensure sorting doesn't change how folds get merged and displayed.
-            folds.sort_by(|a, b| a.0.cmp(&b.0, buffer));
+            folds.sort_by(|a, b| a.range.cmp(&b.range, buffer));
             let mut fold_ranges = folds
                 .iter()
-                .map(|fold| fold.0.start.to_offset(buffer)..fold.0.end.to_offset(buffer))
+                .map(|fold| fold.range.start.to_offset(buffer)..fold.range.end.to_offset(buffer))
                 .peekable();
 
             let mut merged_ranges = Vec::new();

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

@@ -1,6 +1,6 @@
 use crate::{Anchor, InlayId, MultiBufferSnapshot, ToOffset};
 use collections::{BTreeMap, BTreeSet};
-use gpui::fonts::HighlightStyle;
+use gpui::HighlightStyle;
 use language::{Chunk, Edit, Point, TextSummary};
 use multi_buffer::{MultiBufferChunks, MultiBufferRows};
 use std::{
@@ -1889,7 +1889,8 @@ mod tests {
     }
 
     fn init_test(cx: &mut AppContext) {
-        cx.set_global(SettingsStore::test(cx));
-        theme::init((), cx);
+        let store = SettingsStore::test(cx);
+        cx.set_global(store);
+        theme::init(theme::LoadThemes::JustBase, cx);
     }
 }

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

@@ -4,15 +4,14 @@ use super::{
     Highlights,
 };
 use crate::MultiBufferSnapshot;
-use gpui::{
-    fonts::FontId, text_layout::LineWrapper, AppContext, Entity, ModelContext, ModelHandle, Task,
-};
+use gpui::{AppContext, Context, Font, LineWrapper, Model, ModelContext, Pixels, Task};
 use language::{Chunk, Point};
 use lazy_static::lazy_static;
 use smol::future::yield_now;
 use std::{cmp, collections::VecDeque, mem, ops::Range, time::Duration};
 use sum_tree::{Bias, Cursor, SumTree};
 use text::Patch;
+use util::ResultExt;
 
 pub use super::tab_map::TextSummary;
 pub type WrapEdit = text::Edit<u32>;
@@ -22,13 +21,9 @@ pub struct WrapMap {
     pending_edits: VecDeque<(TabSnapshot, Vec<TabEdit>)>,
     interpolated_edits: Patch<u32>,
     edits_since_sync: Patch<u32>,
-    wrap_width: Option<f32>,
+    wrap_width: Option<Pixels>,
     background_task: Option<Task<()>>,
-    font: (FontId, f32),
-}
-
-impl Entity for WrapMap {
-    type Event = ();
+    font_with_size: (Font, Pixels),
 }
 
 #[derive(Clone)]
@@ -74,14 +69,14 @@ pub struct WrapBufferRows<'a> {
 impl WrapMap {
     pub fn new(
         tab_snapshot: TabSnapshot,
-        font_id: FontId,
-        font_size: f32,
-        wrap_width: Option<f32>,
+        font: Font,
+        font_size: Pixels,
+        wrap_width: Option<Pixels>,
         cx: &mut AppContext,
-    ) -> (ModelHandle<Self>, WrapSnapshot) {
-        let handle = cx.add_model(|cx| {
+    ) -> (Model<Self>, WrapSnapshot) {
+        let handle = cx.new_model(|cx| {
             let mut this = Self {
-                font: (font_id, font_size),
+                font_with_size: (font, font_size),
                 wrap_width: None,
                 pending_edits: Default::default(),
                 interpolated_edits: Default::default(),
@@ -121,14 +116,16 @@ impl WrapMap {
         (self.snapshot.clone(), mem::take(&mut self.edits_since_sync))
     }
 
-    pub fn set_font(
+    pub fn set_font_with_size(
         &mut self,
-        font_id: FontId,
-        font_size: f32,
+        font: Font,
+        font_size: Pixels,
         cx: &mut ModelContext<Self>,
     ) -> bool {
-        if (font_id, font_size) != self.font {
-            self.font = (font_id, font_size);
+        let font_with_size = (font, font_size);
+
+        if font_with_size != self.font_with_size {
+            self.font_with_size = font_with_size;
             self.rewrap(cx);
             true
         } else {
@@ -136,7 +133,11 @@ impl WrapMap {
         }
     }
 
-    pub fn set_wrap_width(&mut self, wrap_width: Option<f32>, cx: &mut ModelContext<Self>) -> bool {
+    pub fn set_wrap_width(
+        &mut self,
+        wrap_width: Option<Pixels>,
+        cx: &mut ModelContext<Self>,
+    ) -> bool {
         if wrap_width == self.wrap_width {
             return false;
         }
@@ -153,34 +154,36 @@ impl WrapMap {
 
         if let Some(wrap_width) = self.wrap_width {
             let mut new_snapshot = self.snapshot.clone();
-            let font_cache = cx.font_cache().clone();
-            let (font_id, font_size) = self.font;
-            let task = cx.background().spawn(async move {
-                let mut line_wrapper = font_cache.line_wrapper(font_id, font_size);
-                let tab_snapshot = new_snapshot.tab_snapshot.clone();
-                let range = TabPoint::zero()..tab_snapshot.max_point();
-                let edits = new_snapshot
-                    .update(
-                        tab_snapshot,
-                        &[TabEdit {
-                            old: range.clone(),
-                            new: range.clone(),
-                        }],
-                        wrap_width,
-                        &mut line_wrapper,
-                    )
-                    .await;
+            let mut edits = Patch::default();
+            let text_system = cx.text_system().clone();
+            let (font, font_size) = self.font_with_size.clone();
+            let task = cx.background_executor().spawn(async move {
+                if let Some(mut line_wrapper) = text_system.line_wrapper(font, font_size).log_err()
+                {
+                    let tab_snapshot = new_snapshot.tab_snapshot.clone();
+                    let range = TabPoint::zero()..tab_snapshot.max_point();
+                    edits = new_snapshot
+                        .update(
+                            tab_snapshot,
+                            &[TabEdit {
+                                old: range.clone(),
+                                new: range.clone(),
+                            }],
+                            wrap_width,
+                            &mut line_wrapper,
+                        )
+                        .await;
+                }
                 (new_snapshot, edits)
             });
 
             match cx
-                .background()
+                .background_executor()
                 .block_with_timeout(Duration::from_millis(5), task)
             {
                 Ok((snapshot, edits)) => {
                     self.snapshot = snapshot;
                     self.edits_since_sync = self.edits_since_sync.compose(&edits);
-                    cx.notify();
                 }
                 Err(wrap_task) => {
                     self.background_task = Some(cx.spawn(|this, mut cx| async move {
@@ -194,7 +197,8 @@ impl WrapMap {
                             this.background_task = None;
                             this.flush_edits(cx);
                             cx.notify();
-                        });
+                        })
+                        .ok();
                     }));
                 }
             }
@@ -237,23 +241,25 @@ impl WrapMap {
             if self.background_task.is_none() {
                 let pending_edits = self.pending_edits.clone();
                 let mut snapshot = self.snapshot.clone();
-                let font_cache = cx.font_cache().clone();
-                let (font_id, font_size) = self.font;
-                let update_task = cx.background().spawn(async move {
-                    let mut line_wrapper = font_cache.line_wrapper(font_id, font_size);
-
+                let text_system = cx.text_system().clone();
+                let (font, font_size) = self.font_with_size.clone();
+                let update_task = cx.background_executor().spawn(async move {
                     let mut edits = Patch::default();
-                    for (tab_snapshot, tab_edits) in pending_edits {
-                        let wrap_edits = snapshot
-                            .update(tab_snapshot, &tab_edits, wrap_width, &mut line_wrapper)
-                            .await;
-                        edits = edits.compose(&wrap_edits);
+                    if let Some(mut line_wrapper) =
+                        text_system.line_wrapper(font, font_size).log_err()
+                    {
+                        for (tab_snapshot, tab_edits) in pending_edits {
+                            let wrap_edits = snapshot
+                                .update(tab_snapshot, &tab_edits, wrap_width, &mut line_wrapper)
+                                .await;
+                            edits = edits.compose(&wrap_edits);
+                        }
                     }
                     (snapshot, edits)
                 });
 
                 match cx
-                    .background()
+                    .background_executor()
                     .block_with_timeout(Duration::from_millis(1), update_task)
                 {
                     Ok((snapshot, output_edits)) => {
@@ -272,7 +278,8 @@ impl WrapMap {
                                 this.background_task = None;
                                 this.flush_edits(cx);
                                 cx.notify();
-                            });
+                            })
+                            .ok();
                         }));
                     }
                 }
@@ -385,7 +392,7 @@ impl WrapSnapshot {
         &mut self,
         new_tab_snapshot: TabSnapshot,
         tab_edits: &[TabEdit],
-        wrap_width: f32,
+        wrap_width: Pixels,
         line_wrapper: &mut LineWrapper,
     ) -> Patch<u32> {
         #[derive(Debug)]
@@ -1026,37 +1033,34 @@ mod tests {
         display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap},
         MultiBuffer,
     };
-    use gpui::test::observe;
+    use gpui::{font, px, test::observe};
     use rand::prelude::*;
     use settings::SettingsStore;
     use smol::stream::StreamExt;
     use std::{cmp, env, num::NonZeroU32};
     use text::Rope;
+    use theme::LoadThemes;
 
     #[gpui::test(iterations = 100)]
     async fn test_random_wraps(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
+        // todo!() this test is flaky
         init_test(cx);
 
-        cx.foreground().set_block_on_ticks(0..=50);
+        cx.background_executor.set_block_on_ticks(0..=50);
         let operations = env::var("OPERATIONS")
             .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
             .unwrap_or(10);
 
-        let font_cache = cx.font_cache().clone();
-        let font_system = cx.platform().fonts();
+        let text_system = cx.read(|cx| cx.text_system().clone());
         let mut wrap_width = if rng.gen_bool(0.1) {
             None
         } else {
-            Some(rng.gen_range(0.0..=1000.0))
+            Some(px(rng.gen_range(0.0..=1000.0)))
         };
         let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap();
-        let family_id = font_cache
-            .load_family(&["Helvetica"], &Default::default())
-            .unwrap();
-        let font_id = font_cache
-            .select_font(family_id, &Default::default())
-            .unwrap();
-        let font_size = 14.0;
+        let font = font("Helvetica");
+        let _font_id = text_system.font_id(&font).unwrap();
+        let font_size = px(14.0);
 
         log::info!("Tab size: {}", tab_size);
         log::info!("Wrap width: {:?}", wrap_width);
@@ -1082,12 +1086,12 @@ mod tests {
         let tabs_snapshot = tab_map.set_max_expansion_column(32);
         log::info!("TabMap text: {:?}", tabs_snapshot.text());
 
-        let mut line_wrapper = LineWrapper::new(font_id, font_size, font_system);
+        let mut line_wrapper = text_system.line_wrapper(font.clone(), font_size).unwrap();
         let unwrapped_text = tabs_snapshot.text();
         let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
 
         let (wrap_map, _) =
-            cx.update(|cx| WrapMap::new(tabs_snapshot.clone(), font_id, font_size, wrap_width, cx));
+            cx.update(|cx| WrapMap::new(tabs_snapshot.clone(), font, font_size, wrap_width, cx));
         let mut notifications = observe(&wrap_map, cx);
 
         if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
@@ -1118,7 +1122,7 @@ mod tests {
                     wrap_width = if rng.gen_bool(0.2) {
                         None
                     } else {
-                        Some(rng.gen_range(0.0..=1000.0))
+                        Some(px(rng.gen_range(0.0..=1000.0)))
                     };
                     log::info!("Setting wrap width to {:?}", wrap_width);
                     wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
@@ -1272,16 +1276,16 @@ mod tests {
     }
 
     fn init_test(cx: &mut gpui::TestAppContext) {
-        cx.foreground().forbid_parking();
         cx.update(|cx| {
-            cx.set_global(SettingsStore::test(cx));
-            theme::init((), cx);
+            let settings = SettingsStore::test(cx);
+            cx.set_global(settings);
+            theme::init(LoadThemes::JustBase, cx);
         });
     }
 
     fn wrap_text(
         unwrapped_text: &str,
-        wrap_width: Option<f32>,
+        wrap_width: Option<Pixels>,
         line_wrapper: &mut LineWrapper,
     ) -> String {
         if let Some(wrap_width) = wrap_width {

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

@@ -20,13 +20,12 @@ pub mod selections_collection;
 mod editor_tests;
 #[cfg(any(test, feature = "test-support"))]
 pub mod test;
-
 use ::git::diff::DiffHunk;
 use aho_corasick::AhoCorasick;
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use blink_manager::BlinkManager;
 use client::{Client, Collaborator, ParticipantIndex, TelemetrySettings};
-use clock::{Global, ReplicaId};
+use clock::ReplicaId;
 use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
 use convert_case::{Case, Casing};
 use copilot::Copilot;
@@ -38,19 +37,14 @@ pub use element::{
 };
 use futures::FutureExt;
 use fuzzy::{StringMatch, StringMatchCandidate};
+use git::diff_hunk_to_display;
 use gpui::{
-    actions,
-    color::Color,
-    elements::*,
-    executor,
-    fonts::{self, HighlightStyle, TextStyle},
-    geometry::vector::{vec2f, Vector2F},
-    impl_actions,
-    keymap_matcher::KeymapContext,
-    platform::{CursorStyle, MouseButton},
-    serde_json, AnyElement, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem,
-    CursorRegion, Element, Entity, ModelHandle, MouseRegion, Subscription, Task, View, ViewContext,
-    ViewHandle, WeakViewHandle, WindowContext,
+    actions, 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,
+    ParentElement, Pixels, Render, SharedString, Styled, StyledText, Subscription, Task, TextStyle,
+    UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WhiteSpace, WindowContext,
 };
 use highlight_matching_bracket::refresh_matching_bracket_highlights;
 use hover_popover::{hide_hover, HoverState};
@@ -61,16 +55,14 @@ pub use language::{char_kind, CharKind};
 use language::{
     language_settings::{self, all_language_settings, InlayHintSettings},
     markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel,
-    Completion, CursorShape, Diagnostic, DiagnosticSeverity, Documentation, File, IndentKind,
-    IndentSize, Language, LanguageRegistry, LanguageServerName, OffsetRangeExt, OffsetUtf16, Point,
-    Selection, SelectionGoal, TransactionId,
-};
-use link_go_to_definition::{
-    hide_link_definition, show_link_definition, GoToDefinitionLink, InlayHighlight,
-    LinkGoToDefinitionState,
+    Completion, CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language,
+    LanguageRegistry, LanguageServerName, OffsetRangeExt, Point, Selection, SelectionGoal,
+    TransactionId,
 };
-use log::error;
-use lsp::LanguageServerId;
+
+use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState};
+use lsp::{DiagnosticSeverity, LanguageServerId};
+use mouse_context_menu::MouseContextMenu;
 use movement::TextLayoutDetails;
 use multi_buffer::ToOffsetUtf16;
 pub use multi_buffer::{
@@ -80,14 +72,14 @@ pub use multi_buffer::{
 use ordered_float::OrderedFloat;
 use parking_lot::RwLock;
 use project::{FormatTrigger, Location, Project, ProjectPath, ProjectTransaction};
-use rand::{seq::SliceRandom, thread_rng};
-use rpc::proto::{self, PeerId};
+use rand::prelude::*;
+use rpc::proto::{self, *};
 use scroll::{
     autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
 };
 use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
 use serde::{Deserialize, Serialize};
-use settings::SettingsStore;
+use settings::{Settings, SettingsStore};
 use smallvec::SmallVec;
 use snippet::Snippet;
 use std::{
@@ -99,16 +91,19 @@ use std::{
     ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive},
     path::Path,
     sync::Arc,
+    sync::Weak,
     time::{Duration, Instant},
 };
 pub use sum_tree::Bias;
 use sum_tree::TreeMap;
-use text::Rope;
-use theme::{DiagnosticStyle, Theme, ThemeSettings};
+use text::{OffsetUtf16, Rope};
+use theme::{ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, ThemeColors, ThemeSettings};
+use ui::{
+    h_stack, ButtonSize, ButtonStyle, Icon, IconButton, ListItem, ListItemSpacing, Popover, Tooltip,
+};
+use ui::{prelude::*, IconSize};
 use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
-use workspace::{ItemNavHistory, SplitDirection, ViewId, Workspace};
-
-use crate::git::diff_hunk_to_display;
+use workspace::{searchable::SearchEvent, ItemNavHistory, Pane, SplitDirection, ViewId, Workspace};
 
 const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
 const MAX_LINE_LEN: usize = 1024;
@@ -120,147 +115,163 @@ pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis
 
 pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
 
-pub fn render_parsed_markdown<Tag: 'static>(
+pub fn render_parsed_markdown(
+    element_id: impl Into<ElementId>,
     parsed: &language::ParsedMarkdown,
     editor_style: &EditorStyle,
-    workspace: Option<WeakViewHandle<Workspace>>,
+    workspace: Option<WeakView<Workspace>>,
     cx: &mut ViewContext<Editor>,
-) -> Text {
-    enum RenderedMarkdown {}
-
-    let parsed = parsed.clone();
-    let view_id = cx.view_id();
-    let code_span_background_color = editor_style.document_highlight_read_background;
-
-    let mut region_id = 0;
-
-    Text::new(parsed.text, editor_style.text.clone())
-        .with_highlights(
-            parsed
-                .highlights
-                .iter()
-                .filter_map(|(range, highlight)| {
-                    let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
-                    Some((range.clone(), highlight))
-                })
-                .collect::<Vec<_>>(),
-        )
-        .with_custom_runs(parsed.region_ranges, move |ix, bounds, cx| {
-            region_id += 1;
-            let region = parsed.regions[ix].clone();
-
-            if let Some(link) = region.link {
-                cx.scene().push_cursor_region(CursorRegion {
-                    bounds,
-                    style: CursorStyle::PointingHand,
-                });
-                cx.scene().push_mouse_region(
-                    MouseRegion::new::<(RenderedMarkdown, Tag)>(view_id, region_id, bounds)
-                        .on_down::<Editor, _>(MouseButton::Left, move |_, _, cx| match &link {
-                            markdown::Link::Web { url } => cx.platform().open_url(url),
-                            markdown::Link::Path { path } => {
-                                if let Some(workspace) = &workspace {
-                                    _ = workspace.update(cx, |workspace, cx| {
-                                        workspace.open_abs_path(path.clone(), false, cx).detach();
-                                    });
-                                }
-                            }
-                        }),
-                );
-            }
-
-            if region.code {
-                cx.scene().push_quad(gpui::Quad {
-                    bounds,
-                    background: Some(code_span_background_color),
-                    border: Default::default(),
-                    corner_radii: (2.0).into(),
-                });
+) -> InteractiveText {
+    let code_span_background_color = cx
+        .theme()
+        .colors()
+        .editor_document_highlight_read_background;
+
+    let highlights = gpui::combine_highlights(
+        parsed.highlights.iter().filter_map(|(range, highlight)| {
+            let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
+            Some((range.clone(), highlight))
+        }),
+        parsed
+            .regions
+            .iter()
+            .zip(&parsed.region_ranges)
+            .filter_map(|(region, range)| {
+                if region.code {
+                    Some((
+                        range.clone(),
+                        HighlightStyle {
+                            background_color: Some(code_span_background_color),
+                            ..Default::default()
+                        },
+                    ))
+                } else {
+                    None
+                }
+            }),
+    );
+
+    let mut links = Vec::new();
+    let mut link_ranges = Vec::new();
+    for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
+        if let Some(link) = region.link.clone() {
+            links.push(link);
+            link_ranges.push(range.clone());
+        }
+    }
+
+    InteractiveText::new(
+        element_id,
+        StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
+    )
+    .on_click(link_ranges, move |clicked_range_ix, cx| {
+        match &links[clicked_range_ix] {
+            markdown::Link::Web { url } => cx.open_url(url),
+            markdown::Link::Path { path } => {
+                if let Some(workspace) = &workspace {
+                    _ = workspace.update(cx, |workspace, cx| {
+                        workspace.open_abs_path(path.clone(), false, cx).detach();
+                    });
+                }
             }
-        })
-        .with_soft_wrap(true)
+        }
+    })
 }
 
-#[derive(Clone, Deserialize, PartialEq, Default)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct SelectNext {
     #[serde(default)]
     pub replace_newest: bool,
 }
 
-#[derive(Clone, Deserialize, PartialEq, Default)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct SelectPrevious {
     #[serde(default)]
     pub replace_newest: bool,
 }
 
-#[derive(Clone, Deserialize, PartialEq, Default)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct SelectAllMatches {
     #[serde(default)]
     pub replace_newest: bool,
 }
 
-#[derive(Clone, Deserialize, PartialEq)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct SelectToBeginningOfLine {
     #[serde(default)]
     stop_at_soft_wraps: bool,
 }
 
-#[derive(Clone, Default, Deserialize, PartialEq)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct MovePageUp {
     #[serde(default)]
     center_cursor: bool,
 }
 
-#[derive(Clone, Default, Deserialize, PartialEq)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct MovePageDown {
     #[serde(default)]
     center_cursor: bool,
 }
 
-#[derive(Clone, Deserialize, PartialEq)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct SelectToEndOfLine {
     #[serde(default)]
     stop_at_soft_wraps: bool,
 }
 
-#[derive(Clone, Deserialize, PartialEq)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct ToggleCodeActions {
     #[serde(default)]
     pub deployed_from_indicator: bool,
 }
 
-#[derive(Clone, Default, Deserialize, PartialEq)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct ConfirmCompletion {
     #[serde(default)]
     pub item_ix: Option<usize>,
 }
 
-#[derive(Clone, Default, Deserialize, PartialEq)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct ConfirmCodeAction {
     #[serde(default)]
     pub item_ix: Option<usize>,
 }
 
-#[derive(Clone, Default, Deserialize, PartialEq)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct ToggleComments {
     #[serde(default)]
     pub advance_downwards: bool,
 }
 
-#[derive(Clone, Default, Deserialize, PartialEq)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct FoldAt {
     pub buffer_row: u32,
 }
 
-#[derive(Clone, Default, Deserialize, PartialEq)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct UnfoldAt {
     pub buffer_row: u32,
 }
 
-#[derive(Clone, Default, Deserialize, PartialEq)]
-pub struct GutterHover {
-    pub hovered: bool,
-}
+impl_actions!(
+    editor,
+    [
+        SelectNext,
+        SelectPrevious,
+        SelectAllMatches,
+        SelectToBeginningOfLine,
+        MovePageUp,
+        MovePageDown,
+        SelectToEndOfLine,
+        ToggleCodeActions,
+        ConfirmCompletion,
+        ConfirmCodeAction,
+        ToggleComments,
+        FoldAt,
+        UnfoldAt
+    ]
+);
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub enum InlayId {
@@ -280,134 +291,122 @@ impl InlayId {
 actions!(
     editor,
     [
-        Cancel,
+        AddSelectionAbove,
+        AddSelectionBelow,
         Backspace,
+        Cancel,
+        ConfirmRename,
+        ContextMenuFirst,
+        ContextMenuLast,
+        ContextMenuNext,
+        ContextMenuPrev,
+        ConvertToKebabCase,
+        ConvertToLowerCamelCase,
+        ConvertToLowerCase,
+        ConvertToSnakeCase,
+        ConvertToTitleCase,
+        ConvertToUpperCamelCase,
+        ConvertToUpperCase,
+        Copy,
+        CopyHighlightJson,
+        CopyPath,
+        CopyRelativePath,
+        Cut,
+        CutToEndOfLine,
         Delete,
-        Newline,
-        NewlineAbove,
-        NewlineBelow,
-        GoToDiagnostic,
-        GoToPrevDiagnostic,
-        GoToHunk,
-        GoToPrevHunk,
-        Indent,
-        Outdent,
         DeleteLine,
-        DeleteToPreviousWordStart,
-        DeleteToPreviousSubwordStart,
-        DeleteToNextWordEnd,
-        DeleteToNextSubwordEnd,
         DeleteToBeginningOfLine,
         DeleteToEndOfLine,
-        CutToEndOfLine,
+        DeleteToNextSubwordEnd,
+        DeleteToNextWordEnd,
+        DeleteToPreviousSubwordStart,
+        DeleteToPreviousWordStart,
         DuplicateLine,
         ExpandMacroRecursively,
-        MoveLineUp,
-        MoveLineDown,
+        FindAllReferences,
+        Fold,
+        FoldSelectedRanges,
+        Format,
+        GoToDefinition,
+        GoToDefinitionSplit,
+        GoToDiagnostic,
+        GoToHunk,
+        GoToPrevDiagnostic,
+        GoToPrevHunk,
+        GoToTypeDefinition,
+        GoToTypeDefinitionSplit,
+        HalfPageDown,
+        HalfPageUp,
+        Hover,
+        Indent,
         JoinLines,
-        SortLinesCaseSensitive,
-        SortLinesCaseInsensitive,
-        ReverseLines,
-        ShuffleLines,
-        ConvertToUpperCase,
-        ConvertToLowerCase,
-        ConvertToTitleCase,
-        ConvertToSnakeCase,
-        ConvertToKebabCase,
-        ConvertToUpperCamelCase,
-        ConvertToLowerCamelCase,
-        Transpose,
-        Cut,
-        Copy,
-        Paste,
-        Undo,
-        Redo,
-        MoveUp,
-        PageUp,
+        LineDown,
+        LineUp,
         MoveDown,
-        PageDown,
         MoveLeft,
+        MoveLineDown,
+        MoveLineUp,
         MoveRight,
-        MoveToPreviousWordStart,
-        MoveToPreviousSubwordStart,
-        MoveToNextWordEnd,
-        MoveToNextSubwordEnd,
+        MoveToBeginning,
         MoveToBeginningOfLine,
+        MoveToEnclosingBracket,
+        MoveToEnd,
         MoveToEndOfLine,
-        MoveToStartOfParagraph,
         MoveToEndOfParagraph,
-        MoveToBeginning,
-        MoveToEnd,
-        SelectUp,
+        MoveToNextSubwordEnd,
+        MoveToNextWordEnd,
+        MoveToPreviousSubwordStart,
+        MoveToPreviousWordStart,
+        MoveToStartOfParagraph,
+        MoveUp,
+        Newline,
+        NewlineAbove,
+        NewlineBelow,
+        NextScreen,
+        OpenExcerpts,
+        Outdent,
+        PageDown,
+        PageUp,
+        Paste,
+        Redo,
+        RedoSelection,
+        Rename,
+        RestartLanguageServer,
+        RevealInFinder,
+        ReverseLines,
+        ScrollCursorBottom,
+        ScrollCursorCenter,
+        ScrollCursorTop,
+        SelectAll,
         SelectDown,
+        SelectLargerSyntaxNode,
         SelectLeft,
+        SelectLine,
         SelectRight,
-        SelectToPreviousWordStart,
-        SelectToPreviousSubwordStart,
-        SelectToNextWordEnd,
-        SelectToNextSubwordEnd,
-        SelectToStartOfParagraph,
-        SelectToEndOfParagraph,
+        SelectSmallerSyntaxNode,
         SelectToBeginning,
         SelectToEnd,
-        SelectAll,
-        SelectLine,
+        SelectToEndOfParagraph,
+        SelectToNextSubwordEnd,
+        SelectToNextWordEnd,
+        SelectToPreviousSubwordStart,
+        SelectToPreviousWordStart,
+        SelectToStartOfParagraph,
+        SelectUp,
+        ShowCharacterPalette,
+        ShowCompletions,
+        ShuffleLines,
+        SortLinesCaseInsensitive,
+        SortLinesCaseSensitive,
         SplitSelectionIntoLines,
-        AddSelectionAbove,
-        AddSelectionBelow,
         Tab,
         TabPrev,
-        ShowCharacterPalette,
-        SelectLargerSyntaxNode,
-        SelectSmallerSyntaxNode,
-        GoToDefinition,
-        GoToDefinitionSplit,
-        GoToTypeDefinition,
-        GoToTypeDefinitionSplit,
-        MoveToEnclosingBracket,
+        ToggleInlayHints,
+        ToggleSoftWrap,
+        Transpose,
+        Undo,
         UndoSelection,
-        RedoSelection,
-        FindAllReferences,
-        Rename,
-        ConfirmRename,
-        Fold,
         UnfoldLines,
-        FoldSelectedRanges,
-        ShowCompletions,
-        OpenExcerpts,
-        RestartLanguageServer,
-        Hover,
-        Format,
-        ToggleSoftWrap,
-        ToggleInlayHints,
-        RevealInFinder,
-        CopyPath,
-        CopyRelativePath,
-        CopyHighlightJson,
-        ContextMenuFirst,
-        ContextMenuPrev,
-        ContextMenuNext,
-        ContextMenuLast,
-    ]
-);
-
-impl_actions!(
-    editor,
-    [
-        SelectNext,
-        SelectPrevious,
-        SelectAllMatches,
-        SelectToBeginningOfLine,
-        SelectToEndOfLine,
-        ToggleCodeActions,
-        MovePageUp,
-        MovePageDown,
-        ConfirmCompletion,
-        ConfirmCodeAction,
-        ToggleComments,
-        FoldAt,
-        UnfoldAt,
-        GutterHover
     ]
 );
 
@@ -422,144 +421,41 @@ pub enum Direction {
 }
 
 pub fn init_settings(cx: &mut AppContext) {
-    settings::register::<EditorSettings>(cx);
+    EditorSettings::register(cx);
 }
 
 pub fn init(cx: &mut AppContext) {
     init_settings(cx);
 
-    rust_analyzer_ext::apply_related_actions(cx);
-    cx.add_action(Editor::new_file);
-    cx.add_action(Editor::new_file_in_direction);
-    cx.add_action(Editor::cancel);
-    cx.add_action(Editor::newline);
-    cx.add_action(Editor::newline_above);
-    cx.add_action(Editor::newline_below);
-    cx.add_action(Editor::backspace);
-    cx.add_action(Editor::delete);
-    cx.add_action(Editor::tab);
-    cx.add_action(Editor::tab_prev);
-    cx.add_action(Editor::indent);
-    cx.add_action(Editor::outdent);
-    cx.add_action(Editor::delete_line);
-    cx.add_action(Editor::join_lines);
-    cx.add_action(Editor::sort_lines_case_sensitive);
-    cx.add_action(Editor::sort_lines_case_insensitive);
-    cx.add_action(Editor::reverse_lines);
-    cx.add_action(Editor::shuffle_lines);
-    cx.add_action(Editor::convert_to_upper_case);
-    cx.add_action(Editor::convert_to_lower_case);
-    cx.add_action(Editor::convert_to_title_case);
-    cx.add_action(Editor::convert_to_snake_case);
-    cx.add_action(Editor::convert_to_kebab_case);
-    cx.add_action(Editor::convert_to_upper_camel_case);
-    cx.add_action(Editor::convert_to_lower_camel_case);
-    cx.add_action(Editor::delete_to_previous_word_start);
-    cx.add_action(Editor::delete_to_previous_subword_start);
-    cx.add_action(Editor::delete_to_next_word_end);
-    cx.add_action(Editor::delete_to_next_subword_end);
-    cx.add_action(Editor::delete_to_beginning_of_line);
-    cx.add_action(Editor::delete_to_end_of_line);
-    cx.add_action(Editor::cut_to_end_of_line);
-    cx.add_action(Editor::duplicate_line);
-    cx.add_action(Editor::move_line_up);
-    cx.add_action(Editor::move_line_down);
-    cx.add_action(Editor::transpose);
-    cx.add_action(Editor::cut);
-    cx.add_action(Editor::copy);
-    cx.add_action(Editor::paste);
-    cx.add_action(Editor::undo);
-    cx.add_action(Editor::redo);
-    cx.add_action(Editor::move_up);
-    cx.add_action(Editor::move_page_up);
-    cx.add_action(Editor::move_down);
-    cx.add_action(Editor::move_page_down);
-    cx.add_action(Editor::next_screen);
-    cx.add_action(Editor::move_left);
-    cx.add_action(Editor::move_right);
-    cx.add_action(Editor::move_to_previous_word_start);
-    cx.add_action(Editor::move_to_previous_subword_start);
-    cx.add_action(Editor::move_to_next_word_end);
-    cx.add_action(Editor::move_to_next_subword_end);
-    cx.add_action(Editor::move_to_beginning_of_line);
-    cx.add_action(Editor::move_to_end_of_line);
-    cx.add_action(Editor::move_to_start_of_paragraph);
-    cx.add_action(Editor::move_to_end_of_paragraph);
-    cx.add_action(Editor::move_to_beginning);
-    cx.add_action(Editor::move_to_end);
-    cx.add_action(Editor::select_up);
-    cx.add_action(Editor::select_down);
-    cx.add_action(Editor::select_left);
-    cx.add_action(Editor::select_right);
-    cx.add_action(Editor::select_to_previous_word_start);
-    cx.add_action(Editor::select_to_previous_subword_start);
-    cx.add_action(Editor::select_to_next_word_end);
-    cx.add_action(Editor::select_to_next_subword_end);
-    cx.add_action(Editor::select_to_beginning_of_line);
-    cx.add_action(Editor::select_to_end_of_line);
-    cx.add_action(Editor::select_to_start_of_paragraph);
-    cx.add_action(Editor::select_to_end_of_paragraph);
-    cx.add_action(Editor::select_to_beginning);
-    cx.add_action(Editor::select_to_end);
-    cx.add_action(Editor::select_all);
-    cx.add_action(Editor::select_all_matches);
-    cx.add_action(Editor::select_line);
-    cx.add_action(Editor::split_selection_into_lines);
-    cx.add_action(Editor::add_selection_above);
-    cx.add_action(Editor::add_selection_below);
-    cx.add_action(Editor::select_next);
-    cx.add_action(Editor::select_previous);
-    cx.add_action(Editor::toggle_comments);
-    cx.add_action(Editor::select_larger_syntax_node);
-    cx.add_action(Editor::select_smaller_syntax_node);
-    cx.add_action(Editor::move_to_enclosing_bracket);
-    cx.add_action(Editor::undo_selection);
-    cx.add_action(Editor::redo_selection);
-    cx.add_action(Editor::go_to_diagnostic);
-    cx.add_action(Editor::go_to_prev_diagnostic);
-    cx.add_action(Editor::go_to_hunk);
-    cx.add_action(Editor::go_to_prev_hunk);
-    cx.add_action(Editor::go_to_definition);
-    cx.add_action(Editor::go_to_definition_split);
-    cx.add_action(Editor::go_to_type_definition);
-    cx.add_action(Editor::go_to_type_definition_split);
-    cx.add_action(Editor::fold);
-    cx.add_action(Editor::fold_at);
-    cx.add_action(Editor::unfold_lines);
-    cx.add_action(Editor::unfold_at);
-    cx.add_action(Editor::gutter_hover);
-    cx.add_action(Editor::fold_selected_ranges);
-    cx.add_action(Editor::show_completions);
-    cx.add_action(Editor::toggle_code_actions);
-    cx.add_action(Editor::open_excerpts);
-    cx.add_action(Editor::toggle_soft_wrap);
-    cx.add_action(Editor::toggle_inlay_hints);
-    cx.add_action(Editor::reveal_in_finder);
-    cx.add_action(Editor::copy_path);
-    cx.add_action(Editor::copy_relative_path);
-    cx.add_action(Editor::copy_highlight_json);
-    cx.add_async_action(Editor::format);
-    cx.add_action(Editor::restart_language_server);
-    cx.add_action(Editor::show_character_palette);
-    cx.add_async_action(Editor::confirm_completion);
-    cx.add_async_action(Editor::confirm_code_action);
-    cx.add_async_action(Editor::rename);
-    cx.add_async_action(Editor::confirm_rename);
-    cx.add_async_action(Editor::find_all_references);
-    cx.add_action(Editor::next_copilot_suggestion);
-    cx.add_action(Editor::previous_copilot_suggestion);
-    cx.add_action(Editor::copilot_suggest);
-    cx.add_action(Editor::context_menu_first);
-    cx.add_action(Editor::context_menu_prev);
-    cx.add_action(Editor::context_menu_next);
-    cx.add_action(Editor::context_menu_last);
-
-    hover_popover::init(cx);
-    scroll::actions::init(cx);
-
     workspace::register_project_item::<Editor>(cx);
     workspace::register_followable_item::<Editor>(cx);
     workspace::register_deserializable_item::<Editor>(cx);
+    cx.observe_new_views(
+        |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
+            workspace.register_action(Editor::new_file);
+            workspace.register_action(Editor::new_file_in_direction);
+        },
+    )
+    .detach();
+
+    cx.on_action(move |_: &workspace::NewFile, cx| {
+        let app_state = cx.global::<Weak<workspace::AppState>>();
+        if let Some(app_state) = app_state.upgrade() {
+            workspace::open_new(&app_state, cx, |workspace, cx| {
+                Editor::new_file(workspace, &Default::default(), cx)
+            })
+            .detach();
+        }
+    });
+    cx.on_action(move |_: &workspace::NewWindow, cx| {
+        let app_state = cx.global::<Weak<workspace::AppState>>();
+        if let Some(app_state) = app_state.upgrade() {
+            workspace::open_new(&app_state, cx, |workspace, cx| {
+                Editor::new_file(workspace, &Default::default(), cx)
+            })
+            .detach();
+        }
+    });
 }
 
 trait InvalidationRegion {
@@ -584,7 +480,7 @@ pub enum SelectPhase {
     Update {
         position: DisplayPoint,
         goal_column: u32,
-        scroll_position: Vector2F,
+        scroll_position: gpui::Point<f32>,
     },
     End,
 }
@@ -611,27 +507,31 @@ pub enum SoftWrap {
     Column(u32),
 }
 
-#[derive(Clone)]
+#[derive(Clone, Default)]
 pub struct EditorStyle {
+    pub background: Hsla,
+    pub local_player: PlayerColor,
     pub text: TextStyle,
-    pub line_height_scalar: f32,
-    pub placeholder_text: Option<TextStyle>,
-    pub theme: theme::Editor,
-    pub theme_id: usize,
+    pub scrollbar_width: Pixels,
+    pub syntax: Arc<SyntaxTheme>,
+    pub status: StatusColors,
+    pub inlays_style: HighlightStyle,
+    pub suggestions_style: HighlightStyle,
 }
 
 type CompletionId = usize;
 
-type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
-type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
+// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
+// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
 
-type BackgroundHighlight = (fn(&Theme) -> Color, Vec<Range<Anchor>>);
-type InlayBackgroundHighlight = (fn(&Theme) -> Color, Vec<InlayHighlight>);
+type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec<Range<Anchor>>);
+type InlayBackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec<InlayHighlight>);
 
 pub struct Editor {
-    handle: WeakViewHandle<Self>,
-    buffer: ModelHandle<MultiBuffer>,
-    display_map: ModelHandle<DisplayMap>,
+    handle: WeakView<Self>,
+    focus_handle: FocusHandle,
+    buffer: Model<MultiBuffer>,
+    display_map: Model<DisplayMap>,
     pub selections: SelectionsCollection,
     pub scroll_manager: ScrollManager,
     columnar_selection_tail: Option<Anchor>,
@@ -645,12 +545,9 @@ pub struct Editor {
     ime_transaction: Option<TransactionId>,
     active_diagnostics: Option<ActiveDiagnosticGroup>,
     soft_wrap_mode_override: Option<language_settings::SoftWrap>,
-    get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
-    override_text_style: Option<Box<OverrideTextStyle>>,
-    project: Option<ModelHandle<Project>>,
+    project: Option<Model<Project>>,
     collaboration_hub: Option<Box<dyn CollaborationHub>>,
-    focused: bool,
-    blink_manager: ModelHandle<BlinkManager>,
+    blink_manager: Model<BlinkManager>,
     pub show_local_selections: bool,
     mode: EditorMode,
     show_gutter: bool,
@@ -661,10 +558,10 @@ pub struct Editor {
     inlay_background_highlights: TreeMap<Option<TypeId>, InlayBackgroundHighlight>,
     nav_history: Option<ItemNavHistory>,
     context_menu: RwLock<Option<ContextMenu>>,
-    mouse_context_menu: ViewHandle<context_menu::ContextMenu>,
+    mouse_context_menu: Option<MouseContextMenu>,
     completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
     next_completion_id: CompletionId,
-    available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
+    available_code_actions: Option<(Model<Buffer>, Arc<[CodeAction]>)>,
     code_actions_task: Option<Task<()>>,
     document_highlights_task: Option<Task<()>>,
     pending_rename: Option<RenameState>,
@@ -672,8 +569,8 @@ pub struct Editor {
     cursor_shape: CursorShape,
     collapse_matches: bool,
     autoindent_mode: Option<AutoindentMode>,
-    workspace: Option<(WeakViewHandle<Workspace>, i64)>,
-    keymap_context_layers: BTreeMap<TypeId, KeymapContext>,
+    workspace: Option<(WeakView<Workspace>, i64)>,
+    keymap_context_layers: BTreeMap<TypeId, KeyContext>,
     input_enabled: bool,
     read_only: bool,
     leader_peer_id: Option<PeerId>,
@@ -685,7 +582,10 @@ pub struct Editor {
     inlay_hint_cache: InlayHintCache,
     next_inlay_id: usize,
     _subscriptions: Vec<Subscription>,
-    pixel_position_of_newest_cursor: Option<Vector2F>,
+    pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
+    gutter_width: Pixels,
+    style: Option<EditorStyle>,
+    editor_actions: Vec<Box<dyn Fn(&mut ViewContext<Self>)>>,
 }
 
 pub struct EditorSnapshot {
@@ -841,7 +741,7 @@ struct SnippetState {
 pub struct RenameState {
     pub range: Range<Anchor>,
     pub old_name: Arc<str>,
-    pub editor: ViewHandle<Editor>,
+    pub editor: View<Editor>,
     block_id: BlockId,
 }
 
@@ -855,7 +755,7 @@ enum ContextMenu {
 impl ContextMenu {
     fn select_first(
         &mut self,
-        project: Option<&ModelHandle<Project>>,
+        project: Option<&Model<Project>>,
         cx: &mut ViewContext<Editor>,
     ) -> bool {
         if self.visible() {
@@ -871,7 +771,7 @@ impl ContextMenu {
 
     fn select_prev(
         &mut self,
-        project: Option<&ModelHandle<Project>>,
+        project: Option<&Model<Project>>,
         cx: &mut ViewContext<Editor>,
     ) -> bool {
         if self.visible() {
@@ -887,7 +787,7 @@ impl ContextMenu {
 
     fn select_next(
         &mut self,
-        project: Option<&ModelHandle<Project>>,
+        project: Option<&Model<Project>>,
         cx: &mut ViewContext<Editor>,
     ) -> bool {
         if self.visible() {
@@ -903,7 +803,7 @@ impl ContextMenu {
 
     fn select_last(
         &mut self,
-        project: Option<&ModelHandle<Project>>,
+        project: Option<&Model<Project>>,
         cx: &mut ViewContext<Editor>,
     ) -> bool {
         if self.visible() {
@@ -927,13 +827,17 @@ impl ContextMenu {
     fn render(
         &self,
         cursor_position: DisplayPoint,
-        style: EditorStyle,
-        workspace: Option<WeakViewHandle<Workspace>>,
+        style: &EditorStyle,
+        max_height: Pixels,
+        workspace: Option<WeakView<Workspace>>,
         cx: &mut ViewContext<Editor>,
-    ) -> (DisplayPoint, AnyElement<Editor>) {
+    ) -> (DisplayPoint, AnyElement) {
         match self {
-            ContextMenu::Completions(menu) => (cursor_position, menu.render(style, workspace, cx)),
-            ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx),
+            ContextMenu::Completions(menu) => (
+                cursor_position,
+                menu.render(style, max_height, workspace, cx),
+            ),
+            ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, max_height, cx),
         }
     }
 }
@@ -942,63 +846,47 @@ impl ContextMenu {
 struct CompletionsMenu {
     id: CompletionId,
     initial_position: Anchor,
-    buffer: ModelHandle<Buffer>,
+    buffer: Model<Buffer>,
     completions: Arc<RwLock<Box<[Completion]>>>,
     match_candidates: Arc<[StringMatchCandidate]>,
     matches: Arc<[StringMatch]>,
     selected_item: usize,
-    list: UniformListState,
+    scroll_handle: UniformListScrollHandle,
 }
 
 impl CompletionsMenu {
-    fn select_first(
-        &mut self,
-        project: Option<&ModelHandle<Project>>,
-        cx: &mut ViewContext<Editor>,
-    ) {
+    fn select_first(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
         self.selected_item = 0;
-        self.list.scroll_to(ScrollTarget::Show(self.selected_item));
+        self.scroll_handle.scroll_to_item(self.selected_item);
         self.attempt_resolve_selected_completion_documentation(project, cx);
         cx.notify();
     }
 
-    fn select_prev(
-        &mut self,
-        project: Option<&ModelHandle<Project>>,
-        cx: &mut ViewContext<Editor>,
-    ) {
+    fn select_prev(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
         if self.selected_item > 0 {
             self.selected_item -= 1;
         } else {
             self.selected_item = self.matches.len() - 1;
         }
-        self.list.scroll_to(ScrollTarget::Show(self.selected_item));
+        self.scroll_handle.scroll_to_item(self.selected_item);
         self.attempt_resolve_selected_completion_documentation(project, cx);
         cx.notify();
     }
 
-    fn select_next(
-        &mut self,
-        project: Option<&ModelHandle<Project>>,
-        cx: &mut ViewContext<Editor>,
-    ) {
+    fn select_next(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
         if self.selected_item + 1 < self.matches.len() {
             self.selected_item += 1;
         } else {
             self.selected_item = 0;
         }
-        self.list.scroll_to(ScrollTarget::Show(self.selected_item));
+        self.scroll_handle.scroll_to_item(self.selected_item);
         self.attempt_resolve_selected_completion_documentation(project, cx);
         cx.notify();
     }
 
-    fn select_last(
-        &mut self,
-        project: Option<&ModelHandle<Project>>,
-        cx: &mut ViewContext<Editor>,
-    ) {
+    fn select_last(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
         self.selected_item = self.matches.len() - 1;
-        self.list.scroll_to(ScrollTarget::Show(self.selected_item));
+        self.scroll_handle.scroll_to_item(self.selected_item);
         self.attempt_resolve_selected_completion_documentation(project, cx);
         cx.notify();
     }
@@ -1008,7 +896,7 @@ impl CompletionsMenu {
         editor: &Editor,
         cx: &mut ViewContext<Editor>,
     ) -> Option<Task<()>> {
-        let settings = settings::get::<EditorSettings>(cx);
+        let settings = EditorSettings::get_global(cx);
         if !settings.show_completion_documentation {
             return None;
         }
@@ -1069,9 +957,12 @@ impl CompletionsMenu {
                     let completion = completion.lsp_completion.clone();
                     drop(completions_guard);
 
-                    let server = project.read_with(&mut cx, |project, _| {
-                        project.language_server_for_id(server_id)
-                    });
+                    let server = project
+                        .read_with(&mut cx, |project, _| {
+                            project.language_server_for_id(server_id)
+                        })
+                        .ok()
+                        .flatten();
                     let Some(server) = server else {
                         return;
                     };

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

@@ -1,8 +1,8 @@
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
-use settings::Setting;
+use settings::Settings;
 
-#[derive(Clone, Deserialize)]
+#[derive(Deserialize)]
 pub struct EditorSettings {
     pub cursor_blink: bool,
     pub hover_popover_enabled: bool,
@@ -57,7 +57,7 @@ pub struct ScrollbarContent {
     pub selections: Option<bool>,
 }
 
-impl Setting for EditorSettings {
+impl Settings for EditorSettings {
     const KEY: Option<&'static str> = None;
 
     type FileContent = EditorSettingsContent;
@@ -65,7 +65,7 @@ impl Setting for EditorSettings {
     fn load(
         default_value: &Self::FileContent,
         user_values: &[&Self::FileContent],
-        _: &gpui::AppContext,
+        _: &mut gpui::AppContext,
     ) -> anyhow::Result<Self> {
         Self::load_via_json_merge(default_value, user_values)
     }

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

@@ -7,14 +7,12 @@ use crate::{
     },
     JoinLines,
 };
-use drag_and_drop::DragAndDrop;
+
 use futures::StreamExt;
 use gpui::{
-    executor::Deterministic,
-    geometry::{rect::RectF, vector::vec2f},
-    platform::{WindowBounds, WindowOptions},
+    div,
     serde_json::{self, json},
-    TestAppContext,
+    TestAppContext, VisualTestContext, WindowBounds, WindowOptions,
 };
 use indoc::indoc;
 use language::{
@@ -34,7 +32,7 @@ use util::{
     test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
 };
 use workspace::{
-    item::{FollowableItem, Item, ItemHandle},
+    item::{FollowEvent, FollowableItem, Item, ItemHandle},
     NavigationEntry, ViewId,
 };
 
@@ -42,127 +40,110 @@ use workspace::{
 fn test_edit_events(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let buffer = cx.add_model(|cx| {
-        let mut buffer = language::Buffer::new(0, cx.model_id() as u64, "123456");
+    let buffer = cx.new_model(|cx| {
+        let mut buffer = language::Buffer::new(0, cx.entity_id().as_u64(), "123456");
         buffer.set_group_interval(Duration::from_secs(1));
         buffer
     });
 
     let events = Rc::new(RefCell::new(Vec::new()));
-    let editor1 = cx
-        .add_window({
-            let events = events.clone();
-            |cx| {
-                cx.subscribe(&cx.handle(), move |_, _, event, _| {
-                    if matches!(
-                        event,
-                        Event::Edited | Event::BufferEdited | Event::DirtyChanged
-                    ) {
-                        events.borrow_mut().push(("editor1", event.clone()));
-                    }
-                })
-                .detach();
-                Editor::for_buffer(buffer.clone(), None, cx)
-            }
-        })
-        .root(cx);
-    let editor2 = cx
-        .add_window({
-            let events = events.clone();
-            |cx| {
-                cx.subscribe(&cx.handle(), move |_, _, event, _| {
-                    if matches!(
-                        event,
-                        Event::Edited | Event::BufferEdited | Event::DirtyChanged
-                    ) {
-                        events.borrow_mut().push(("editor2", event.clone()));
-                    }
-                })
-                .detach();
-                Editor::for_buffer(buffer.clone(), None, cx)
-            }
-        })
-        .root(cx);
+    let editor1 = cx.add_window({
+        let events = events.clone();
+        |cx| {
+            let view = cx.view().clone();
+            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| {
+                if matches!(event, EditorEvent::Edited | EditorEvent::BufferEdited) {
+                    events.borrow_mut().push(("editor1", event.clone()));
+                }
+            })
+            .detach();
+            Editor::for_buffer(buffer.clone(), None, cx)
+        }
+    });
+
+    let editor2 = cx.add_window({
+        let events = events.clone();
+        |cx| {
+            cx.subscribe(&cx.view().clone(), move |_, _, event: &EditorEvent, _| {
+                if matches!(event, EditorEvent::Edited | EditorEvent::BufferEdited) {
+                    events.borrow_mut().push(("editor2", event.clone()));
+                }
+            })
+            .detach();
+            Editor::for_buffer(buffer.clone(), None, cx)
+        }
+    });
+
     assert_eq!(mem::take(&mut *events.borrow_mut()), []);
 
     // Mutating editor 1 will emit an `Edited` event only for that editor.
-    editor1.update(cx, |editor, cx| editor.insert("X", cx));
+    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
     assert_eq!(
         mem::take(&mut *events.borrow_mut()),
         [
-            ("editor1", Event::Edited),
-            ("editor1", Event::BufferEdited),
-            ("editor2", Event::BufferEdited),
-            ("editor1", Event::DirtyChanged),
-            ("editor2", Event::DirtyChanged)
+            ("editor1", EditorEvent::Edited),
+            ("editor1", EditorEvent::BufferEdited),
+            ("editor2", EditorEvent::BufferEdited),
         ]
     );
 
     // Mutating editor 2 will emit an `Edited` event only for that editor.
-    editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
+    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
     assert_eq!(
         mem::take(&mut *events.borrow_mut()),
         [
-            ("editor2", Event::Edited),
-            ("editor1", Event::BufferEdited),
-            ("editor2", Event::BufferEdited),
+            ("editor2", EditorEvent::Edited),
+            ("editor1", EditorEvent::BufferEdited),
+            ("editor2", EditorEvent::BufferEdited),
         ]
     );
 
     // Undoing on editor 1 will emit an `Edited` event only for that editor.
-    editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
+    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
     assert_eq!(
         mem::take(&mut *events.borrow_mut()),
         [
-            ("editor1", Event::Edited),
-            ("editor1", Event::BufferEdited),
-            ("editor2", Event::BufferEdited),
-            ("editor1", Event::DirtyChanged),
-            ("editor2", Event::DirtyChanged),
+            ("editor1", EditorEvent::Edited),
+            ("editor1", EditorEvent::BufferEdited),
+            ("editor2", EditorEvent::BufferEdited),
         ]
     );
 
     // Redoing on editor 1 will emit an `Edited` event only for that editor.
-    editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
+    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
     assert_eq!(
         mem::take(&mut *events.borrow_mut()),
         [
-            ("editor1", Event::Edited),
-            ("editor1", Event::BufferEdited),
-            ("editor2", Event::BufferEdited),
-            ("editor1", Event::DirtyChanged),
-            ("editor2", Event::DirtyChanged),
+            ("editor1", EditorEvent::Edited),
+            ("editor1", EditorEvent::BufferEdited),
+            ("editor2", EditorEvent::BufferEdited),
         ]
     );
 
     // Undoing on editor 2 will emit an `Edited` event only for that editor.
-    editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
+    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
     assert_eq!(
         mem::take(&mut *events.borrow_mut()),
         [
-            ("editor2", Event::Edited),
-            ("editor1", Event::BufferEdited),
-            ("editor2", Event::BufferEdited),
-            ("editor1", Event::DirtyChanged),
-            ("editor2", Event::DirtyChanged),
+            ("editor2", EditorEvent::Edited),
+            ("editor1", EditorEvent::BufferEdited),
+            ("editor2", EditorEvent::BufferEdited),
         ]
     );
 
     // Redoing on editor 2 will emit an `Edited` event only for that editor.
-    editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
+    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
     assert_eq!(
         mem::take(&mut *events.borrow_mut()),
         [
-            ("editor2", Event::Edited),
-            ("editor1", Event::BufferEdited),
-            ("editor2", Event::BufferEdited),
-            ("editor1", Event::DirtyChanged),
-            ("editor2", Event::DirtyChanged),
+            ("editor2", EditorEvent::Edited),
+            ("editor1", EditorEvent::BufferEdited),
+            ("editor2", EditorEvent::BufferEdited),
         ]
     );
 
     // No event is emitted when the mutation is a no-op.
-    editor2.update(cx, |editor, cx| {
+    _ = editor2.update(cx, |editor, cx| {
         editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 
         editor.backspace(&Backspace, cx);
@@ -175,14 +156,12 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
     let mut now = Instant::now();
-    let buffer = cx.add_model(|cx| language::Buffer::new(0, cx.model_id() as u64, "123456"));
-    let group_interval = buffer.read_with(cx, |buffer, _| buffer.transaction_group_interval());
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let editor = cx
-        .add_window(|cx| build_editor(buffer.clone(), cx))
-        .root(cx);
-
-    editor.update(cx, |editor, cx| {
+    let buffer = cx.new_model(|cx| language::Buffer::new(0, cx.entity_id().as_u64(), "123456"));
+    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
+    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
+    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
+
+    _ = editor.update(cx, |editor, cx| {
         editor.start_transaction_at(now, cx);
         editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
 
@@ -202,7 +181,7 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
         editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
 
         // Simulate an edit in another editor
-        buffer.update(cx, |buffer, cx| {
+        _ = buffer.update(cx, |buffer, cx| {
             buffer.start_transaction_at(now, cx);
             buffer.edit([(0..1, "a")], None, cx);
             buffer.edit([(1..1, "b")], None, cx);
@@ -247,14 +226,14 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
 fn test_ime_composition(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let buffer = cx.add_model(|cx| {
-        let mut buffer = language::Buffer::new(0, cx.model_id() as u64, "abcde");
+    let buffer = cx.new_model(|cx| {
+        let mut buffer = language::Buffer::new(0, cx.entity_id().as_u64(), "abcde");
         // Ensure automatic grouping doesn't occur.
         buffer.set_group_interval(Duration::ZERO);
         buffer
     });
 
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
     cx.add_window(|cx| {
         let mut editor = build_editor(buffer.clone(), cx);
 
@@ -350,67 +329,98 @@ fn test_ime_composition(cx: &mut TestAppContext) {
 fn test_selection_with_mouse(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let editor = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-    editor.update(cx, |view, cx| {
+    let editor = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
+        build_editor(buffer, cx)
+    });
+
+    _ = editor.update(cx, |view, cx| {
         view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
     });
     assert_eq!(
-        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
+        editor
+            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .unwrap(),
         [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
     );
 
-    editor.update(cx, |view, cx| {
-        view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
+    _ = editor.update(cx, |view, cx| {
+        view.update_selection(
+            DisplayPoint::new(3, 3),
+            0,
+            gpui::Point::<f32>::default(),
+            cx,
+        );
     });
 
     assert_eq!(
-        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
+        editor
+            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .unwrap(),
         [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
     );
 
-    editor.update(cx, |view, cx| {
-        view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
+    _ = editor.update(cx, |view, cx| {
+        view.update_selection(
+            DisplayPoint::new(1, 1),
+            0,
+            gpui::Point::<f32>::default(),
+            cx,
+        );
     });
 
     assert_eq!(
-        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
+        editor
+            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .unwrap(),
         [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
     );
 
-    editor.update(cx, |view, cx| {
+    _ = editor.update(cx, |view, cx| {
         view.end_selection(cx);
-        view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
+        view.update_selection(
+            DisplayPoint::new(3, 3),
+            0,
+            gpui::Point::<f32>::default(),
+            cx,
+        );
     });
 
     assert_eq!(
-        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
+        editor
+            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .unwrap(),
         [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
     );
 
-    editor.update(cx, |view, cx| {
+    _ = editor.update(cx, |view, cx| {
         view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
-        view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
+        view.update_selection(
+            DisplayPoint::new(0, 0),
+            0,
+            gpui::Point::<f32>::default(),
+            cx,
+        );
     });
 
     assert_eq!(
-        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
+        editor
+            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .unwrap(),
         [
             DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
             DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
         ]
     );
 
-    editor.update(cx, |view, cx| {
+    _ = editor.update(cx, |view, cx| {
         view.end_selection(cx);
     });
 
     assert_eq!(
-        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
+        editor
+            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .unwrap(),
         [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
     );
 }
@@ -419,14 +429,12 @@ fn test_selection_with_mouse(cx: &mut TestAppContext) {
 fn test_canceling_pending_selection(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
+        build_editor(buffer, cx)
+    });
 
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
         assert_eq!(
             view.selections.display_ranges(cx),
@@ -434,17 +442,27 @@ fn test_canceling_pending_selection(cx: &mut TestAppContext) {
         );
     });
 
-    view.update(cx, |view, cx| {
-        view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
+    _ = view.update(cx, |view, cx| {
+        view.update_selection(
+            DisplayPoint::new(3, 3),
+            0,
+            gpui::Point::<f32>::default(),
+            cx,
+        );
         assert_eq!(
             view.selections.display_ranges(cx),
             [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
         );
     });
 
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         view.cancel(&Cancel, cx);
-        view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
+        view.update_selection(
+            DisplayPoint::new(1, 1),
+            0,
+            gpui::Point::<f32>::default(),
+            cx,
+        );
         assert_eq!(
             view.selections.display_ranges(cx),
             [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
@@ -467,14 +485,12 @@ fn test_clone(cx: &mut TestAppContext) {
         true,
     );
 
-    let editor = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(&text, cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let editor = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple(&text, cx);
+        build_editor(buffer, cx)
+    });
 
-    editor.update(cx, |editor, cx| {
+    _ = editor.update(cx, |editor, cx| {
         editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
         editor.fold_ranges(
             [
@@ -488,16 +504,18 @@ fn test_clone(cx: &mut TestAppContext) {
 
     let cloned_editor = editor
         .update(cx, |editor, cx| {
-            cx.add_window(Default::default(), |cx| editor.clone(cx))
+            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
         })
-        .root(cx);
+        .unwrap();
 
-    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
-    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
+    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
+    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
 
     assert_eq!(
-        cloned_editor.update(cx, |e, cx| e.display_text(cx)),
-        editor.update(cx, |e, cx| e.display_text(cx))
+        cloned_editor
+            .update(cx, |e, cx| e.display_text(cx))
+            .unwrap(),
+        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
     );
     assert_eq!(
         cloned_snapshot
@@ -506,127 +524,139 @@ fn test_clone(cx: &mut TestAppContext) {
         snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
     );
     assert_set_eq!(
-        cloned_editor.read_with(cx, |editor, cx| editor.selections.ranges::<Point>(cx)),
-        editor.read_with(cx, |editor, cx| editor.selections.ranges(cx))
+        cloned_editor
+            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
+            .unwrap(),
+        editor
+            .update(cx, |editor, cx| editor.selections.ranges(cx))
+            .unwrap()
     );
     assert_set_eq!(
-        cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
-        editor.update(cx, |e, cx| e.selections.display_ranges(cx))
+        cloned_editor
+            .update(cx, |e, cx| e.selections.display_ranges(cx))
+            .unwrap(),
+        editor
+            .update(cx, |e, cx| e.selections.display_ranges(cx))
+            .unwrap()
     );
 }
 
+//todo!(editor navigate)
 #[gpui::test]
 async fn test_navigation_history(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    cx.set_global(DragAndDrop::<Workspace>::default());
     use workspace::item::Item;
 
-    let fs = FakeFs::new(cx.background());
+    let fs = FakeFs::new(cx.executor());
     let project = Project::test(fs, [], cx).await;
-    let window = cx.add_window(|cx| Workspace::test_new(project, cx));
-    let workspace = window.root(cx);
-    let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
-    window.add_view(cx, |cx| {
-        let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
-        let mut editor = build_editor(buffer.clone(), cx);
-        let handle = cx.handle();
-        editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
+    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
+    let pane = workspace
+        .update(cx, |workspace, _| workspace.active_pane().clone())
+        .unwrap();
 
-        fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
-            editor.nav_history.as_mut().unwrap().pop_backward(cx)
-        }
+    _ = workspace.update(cx, |_v, cx| {
+        cx.new_view(|cx| {
+            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
+            let mut editor = build_editor(buffer.clone(), cx);
+            let handle = cx.view();
+            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
 
-        // Move the cursor a small distance.
-        // Nothing is added to the navigation history.
-        editor.change_selections(None, cx, |s| {
-            s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
-        });
-        editor.change_selections(None, cx, |s| {
-            s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
-        });
-        assert!(pop_history(&mut editor, cx).is_none());
+            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
+                editor.nav_history.as_mut().unwrap().pop_backward(cx)
+            }
 
-        // Move the cursor a large distance.
-        // The history can jump back to the previous position.
-        editor.change_selections(None, cx, |s| {
-            s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
-        });
-        let nav_entry = pop_history(&mut editor, cx).unwrap();
-        editor.navigate(nav_entry.data.unwrap(), cx);
-        assert_eq!(nav_entry.item.id(), cx.view_id());
-        assert_eq!(
-            editor.selections.display_ranges(cx),
-            &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
-        );
-        assert!(pop_history(&mut editor, cx).is_none());
-
-        // Move the cursor a small distance via the mouse.
-        // Nothing is added to the navigation history.
-        editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
-        editor.end_selection(cx);
-        assert_eq!(
-            editor.selections.display_ranges(cx),
-            &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
-        );
-        assert!(pop_history(&mut editor, cx).is_none());
-
-        // Move the cursor a large distance via the mouse.
-        // The history can jump back to the previous position.
-        editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
-        editor.end_selection(cx);
-        assert_eq!(
-            editor.selections.display_ranges(cx),
-            &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
-        );
-        let nav_entry = pop_history(&mut editor, cx).unwrap();
-        editor.navigate(nav_entry.data.unwrap(), cx);
-        assert_eq!(nav_entry.item.id(), cx.view_id());
-        assert_eq!(
-            editor.selections.display_ranges(cx),
-            &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
-        );
-        assert!(pop_history(&mut editor, cx).is_none());
-
-        // Set scroll position to check later
-        editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
-        let original_scroll_position = editor.scroll_manager.anchor();
-
-        // Jump to the end of the document and adjust scroll
-        editor.move_to_end(&MoveToEnd, cx);
-        editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
-        assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
-
-        let nav_entry = pop_history(&mut editor, cx).unwrap();
-        editor.navigate(nav_entry.data.unwrap(), cx);
-        assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
-
-        // Ensure we don't panic when navigation data contains invalid anchors *and* points.
-        let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
-        invalid_anchor.text_anchor.buffer_id = Some(999);
-        let invalid_point = Point::new(9999, 0);
-        editor.navigate(
-            Box::new(NavigationData {
-                cursor_anchor: invalid_anchor,
-                cursor_position: invalid_point,
-                scroll_anchor: ScrollAnchor {
-                    anchor: invalid_anchor,
-                    offset: Default::default(),
-                },
-                scroll_top_row: invalid_point.row,
-            }),
-            cx,
-        );
-        assert_eq!(
-            editor.selections.display_ranges(cx),
-            &[editor.max_point(cx)..editor.max_point(cx)]
-        );
-        assert_eq!(
-            editor.scroll_position(cx),
-            vec2f(0., editor.max_point(cx).row() as f32)
-        );
+            // Move the cursor a small distance.
+            // Nothing is added to the navigation history.
+            editor.change_selections(None, cx, |s| {
+                s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
+            });
+            editor.change_selections(None, cx, |s| {
+                s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
+            });
+            assert!(pop_history(&mut editor, cx).is_none());
 
-        editor
+            // Move the cursor a large distance.
+            // The history can jump back to the previous position.
+            editor.change_selections(None, cx, |s| {
+                s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
+            });
+            let nav_entry = pop_history(&mut editor, cx).unwrap();
+            editor.navigate(nav_entry.data.unwrap(), cx);
+            assert_eq!(nav_entry.item.id(), cx.entity_id());
+            assert_eq!(
+                editor.selections.display_ranges(cx),
+                &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
+            );
+            assert!(pop_history(&mut editor, cx).is_none());
+
+            // Move the cursor a small distance via the mouse.
+            // Nothing is added to the navigation history.
+            editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
+            editor.end_selection(cx);
+            assert_eq!(
+                editor.selections.display_ranges(cx),
+                &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
+            );
+            assert!(pop_history(&mut editor, cx).is_none());
+
+            // Move the cursor a large distance via the mouse.
+            // The history can jump back to the previous position.
+            editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
+            editor.end_selection(cx);
+            assert_eq!(
+                editor.selections.display_ranges(cx),
+                &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
+            );
+            let nav_entry = pop_history(&mut editor, cx).unwrap();
+            editor.navigate(nav_entry.data.unwrap(), cx);
+            assert_eq!(nav_entry.item.id(), cx.entity_id());
+            assert_eq!(
+                editor.selections.display_ranges(cx),
+                &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
+            );
+            assert!(pop_history(&mut editor, cx).is_none());
+
+            // Set scroll position to check later
+            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
+            let original_scroll_position = editor.scroll_manager.anchor();
+
+            // Jump to the end of the document and adjust scroll
+            editor.move_to_end(&MoveToEnd, cx);
+            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
+            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
+
+            let nav_entry = pop_history(&mut editor, cx).unwrap();
+            editor.navigate(nav_entry.data.unwrap(), cx);
+            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
+
+            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
+            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
+            invalid_anchor.text_anchor.buffer_id = Some(999);
+            let invalid_point = Point::new(9999, 0);
+            editor.navigate(
+                Box::new(NavigationData {
+                    cursor_anchor: invalid_anchor,
+                    cursor_position: invalid_point,
+                    scroll_anchor: ScrollAnchor {
+                        anchor: invalid_anchor,
+                        offset: Default::default(),
+                    },
+                    scroll_top_row: invalid_point.row,
+                }),
+                cx,
+            );
+            assert_eq!(
+                editor.selections.display_ranges(cx),
+                &[editor.max_point(cx)..editor.max_point(cx)]
+            );
+            assert_eq!(
+                editor.scroll_position(cx),
+                gpui::Point::new(0., editor.max_point(cx).row() as f32)
+            );
+
+            editor
+        })
     });
 }
 
@@ -634,20 +664,28 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
 fn test_cancel(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
+        build_editor(buffer, cx)
+    });
 
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
-        view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
+        view.update_selection(
+            DisplayPoint::new(1, 1),
+            0,
+            gpui::Point::<f32>::default(),
+            cx,
+        );
         view.end_selection(cx);
 
         view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
-        view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
+        view.update_selection(
+            DisplayPoint::new(0, 3),
+            0,
+            gpui::Point::<f32>::default(),
+            cx,
+        );
         view.end_selection(cx);
         assert_eq!(
             view.selections.display_ranges(cx),
@@ -658,7 +696,7 @@ fn test_cancel(cx: &mut TestAppContext) {
         );
     });
 
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         view.cancel(&Cancel, cx);
         assert_eq!(
             view.selections.display_ranges(cx),
@@ -666,7 +704,7 @@ fn test_cancel(cx: &mut TestAppContext) {
         );
     });
 
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         view.cancel(&Cancel, cx);
         assert_eq!(
             view.selections.display_ranges(cx),
@@ -679,10 +717,9 @@ fn test_cancel(cx: &mut TestAppContext) {
 fn test_fold_action(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(
-                &"
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple(
+            &"
                 impl Foo {
                     // Hello!
 
@@ -699,14 +736,13 @@ fn test_fold_action(cx: &mut TestAppContext) {
                     }
                 }
             "
-                .unindent(),
-                cx,
-            );
-            build_editor(buffer.clone(), cx)
-        })
-        .root(cx);
+            .unindent(),
+            cx,
+        );
+        build_editor(buffer.clone(), cx)
+    });
 
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
         });
@@ -772,11 +808,9 @@ fn test_move_cursor(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
     let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
-    let view = cx
-        .add_window(|cx| build_editor(buffer.clone(), cx))
-        .root(cx);
+    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 
-    buffer.update(cx, |buffer, cx| {
+    _ = buffer.update(cx, |buffer, cx| {
         buffer.edit(
             vec![
                 (Point::new(1, 0)..Point::new(1, 0), "\t"),
@@ -786,7 +820,7 @@ fn test_move_cursor(cx: &mut TestAppContext) {
             cx,
         );
     });
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         assert_eq!(
             view.selections.display_ranges(cx),
             &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
@@ -849,17 +883,15 @@ fn test_move_cursor(cx: &mut TestAppContext) {
 fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("โ“โ“‘โ“’โ““โ“”\nabcde\nฮฑฮฒฮณฮดฮต", cx);
-            build_editor(buffer.clone(), cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("โ“โ“‘โ“’โ““โ“”\nabcde\nฮฑฮฒฮณฮดฮต", cx);
+        build_editor(buffer.clone(), cx)
+    });
 
     assert_eq!('โ“'.len_utf8(), 3);
     assert_eq!('ฮฑ'.len_utf8(), 2);
 
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         view.fold_ranges(
             vec![
                 Point::new(0, 6)..Point::new(0, 12),
@@ -963,17 +995,16 @@ fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
     });
 }
 
+//todo!(finish editor tests)
 #[gpui::test]
 fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("โ“โ“‘โ“’โ““โ“”\nabcd\nฮฑฮฒฮณ\nabcd\nโ“โ“‘โ“’โ““โ“”\n", cx);
-            build_editor(buffer.clone(), cx)
-        })
-        .root(cx);
-    view.update(cx, |view, cx| {
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("โ“โ“‘โ“’โ““โ“”\nabcd\nฮฑฮฒฮณ\nabcd\nโ“โ“‘โ“’โ““โ“”\n", cx);
+        build_editor(buffer.clone(), cx)
+    });
+    _ = view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([empty_range(0, "โ“โ“‘โ“’โ““โ“”".len())]);
         });
@@ -1019,13 +1050,11 @@ fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 fn test_beginning_end_of_line(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("abc\n  def", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-    view.update(cx, |view, cx| {
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
+        build_editor(buffer, cx)
+    });
+    _ = view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
@@ -1034,7 +1063,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
         });
     });
 
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
         assert_eq!(
             view.selections.display_ranges(cx),
@@ -1045,7 +1074,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
         );
     });
 
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
         assert_eq!(
             view.selections.display_ranges(cx),
@@ -1056,7 +1085,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
         );
     });
 
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
         assert_eq!(
             view.selections.display_ranges(cx),
@@ -1067,7 +1096,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
         );
     });
 
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         view.move_to_end_of_line(&MoveToEndOfLine, cx);
         assert_eq!(
             view.selections.display_ranges(cx),
@@ -1079,7 +1108,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
     });
 
     // Moving to the end of line again is a no-op.
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         view.move_to_end_of_line(&MoveToEndOfLine, cx);
         assert_eq!(
             view.selections.display_ranges(cx),
@@ -1090,7 +1119,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
         );
     });
 
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         view.move_left(&MoveLeft, cx);
         view.select_to_beginning_of_line(
             &SelectToBeginningOfLine {
@@ -1107,7 +1136,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
         );
     });
 
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         view.select_to_beginning_of_line(
             &SelectToBeginningOfLine {
                 stop_at_soft_wraps: true,
@@ -1123,7 +1152,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
         );
     });
 
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         view.select_to_beginning_of_line(
             &SelectToBeginningOfLine {
                 stop_at_soft_wraps: true,
@@ -1139,7 +1168,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
         );
     });
 
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         view.select_to_end_of_line(
             &SelectToEndOfLine {
                 stop_at_soft_wraps: true,
@@ -1155,7 +1184,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
         );
     });
 
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
         assert_eq!(view.display_text(cx), "ab\n  de");
         assert_eq!(
@@ -1167,7 +1196,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
         );
     });
 
-    view.update(cx, |view, cx| {
+    _ = view.update(cx, |view, cx| {
         view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
         assert_eq!(view.display_text(cx), "\n");
         assert_eq!(
@@ -1184,13 +1213,11 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-    view.update(cx, |view, cx| {
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
+        build_editor(buffer, cx)
+    });
+    _ = view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),

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

@@ -1,50 +1,47 @@
-use super::{
-    display_map::{BlockContext, ToDisplayPoint},
-    Anchor, DisplayPoint, Editor, EditorMode, EditorSnapshot, SelectPhase, SoftWrap, ToPoint,
-    MAX_LINE_LEN,
-};
 use crate::{
-    display_map::{BlockStyle, DisplaySnapshot, FoldStatus, HighlightedChunk, TransformBlock},
+    display_map::{
+        BlockContext, BlockStyle, DisplaySnapshot, FoldStatus, HighlightedChunk, ToDisplayPoint,
+        TransformBlock,
+    },
     editor_settings::ShowScrollbar,
     git::{diff_hunk_to_display, DisplayDiffHunk},
     hover_popover::{
-        hide_hover, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH,
-        MIN_POPOVER_LINE_HEIGHT,
+        self, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT,
     },
     link_go_to_definition::{
-        go_to_fetched_definition, go_to_fetched_type_definition, update_go_to_definition_link,
-        update_inlay_link_and_hover_points, GoToDefinitionTrigger,
+        go_to_fetched_definition, go_to_fetched_type_definition, show_link_definition,
+        update_go_to_definition_link, update_inlay_link_and_hover_points, GoToDefinitionTrigger,
+        LinkGoToDefinitionState,
     },
-    mouse_context_menu, EditorSettings, EditorStyle, GutterHover, UnfoldAt,
+    mouse_context_menu,
+    scroll::scroll_amount::ScrollAmount,
+    CursorShape, DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle,
+    HalfPageDown, HalfPageUp, LineDown, LineUp, OpenExcerpts, PageDown, PageUp, Point, SelectPhase,
+    Selection, SoftWrap, ToPoint, MAX_LINE_LEN,
 };
+use anyhow::Result;
 use collections::{BTreeMap, HashMap};
 use git::diff::DiffHunkStatus;
 use gpui::{
-    color::Color,
-    elements::*,
-    fonts::TextStyle,
-    geometry::{
-        rect::RectF,
-        vector::{vec2f, Vector2F},
-        PathBuilder,
-    },
-    json::{self, ToJson},
-    platform::{CursorStyle, Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent},
-    text_layout::{self, Line, RunStyle, TextLayoutCache},
-    AnyElement, Axis, CursorRegion, Element, EventContext, FontCache, MouseRegion, Quad,
-    SizeConstraint, ViewContext, WindowContext,
+    div, fill, outline, overlay, point, px, quad, relative, size, transparent_black, Action,
+    AnchorCorner, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Corners,
+    CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Hsla, InteractiveBounds,
+    InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton, MouseDownEvent,
+    MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollWheelEvent, ShapedLine,
+    SharedString, Size, StackingOrder, StatefulInteractiveElement, Style, Styled, TextRun,
+    TextStyle, View, ViewContext, WindowContext,
 };
 use itertools::Itertools;
-use json::json;
-use language::{
-    language_settings::ShowWhitespaceSetting, Bias, CursorShape, OffsetUtf16, Selection,
-};
+use language::language_settings::ShowWhitespaceSetting;
+use multi_buffer::Anchor;
 use project::{
     project_settings::{GitGutterSetting, ProjectSettings},
     ProjectPath,
 };
+use settings::Settings;
 use smallvec::SmallVec;
 use std::{
+    any::TypeId,
     borrow::Cow,
     cmp::{self, Ordering},
     fmt::Write,
@@ -52,12 +49,13 @@ use std::{
     ops::Range,
     sync::Arc,
 };
-use text::Point;
-use theme::SelectionStyle;
+use sum_tree::Bias;
+use theme::{ActiveTheme, PlayerColor};
+use ui::prelude::*;
+use ui::{h_stack, ButtonLike, ButtonStyle, IconButton, Tooltip};
+use util::ResultExt;
 use workspace::item::Item;
 
-enum FoldMarkers {}
-
 struct SelectionLayout {
     head: DisplayPoint,
     cursor_shape: CursorShape,
@@ -119,176 +117,288 @@ impl SelectionLayout {
 }
 
 pub struct EditorElement {
-    style: Arc<EditorStyle>,
+    editor: View<Editor>,
+    style: EditorStyle,
 }
 
 impl EditorElement {
-    pub fn new(style: EditorStyle) -> Self {
+    pub fn new(editor: &View<Editor>, style: EditorStyle) -> Self {
         Self {
-            style: Arc::new(style),
+            editor: editor.clone(),
+            style,
         }
     }
 
-    fn attach_mouse_handlers(
-        position_map: &Arc<PositionMap>,
-        has_popovers: bool,
-        visible_bounds: RectF,
-        text_bounds: RectF,
-        gutter_bounds: RectF,
-        bounds: RectF,
+    fn register_actions(&self, cx: &mut WindowContext) {
+        let view = &self.editor;
+        view.update(cx, |editor, cx| {
+            for action in editor.editor_actions.iter() {
+                (action)(cx)
+            }
+        });
+
+        crate::rust_analyzer_ext::apply_related_actions(view, cx);
+        register_action(view, cx, Editor::move_left);
+        register_action(view, cx, Editor::move_right);
+        register_action(view, cx, Editor::move_down);
+        register_action(view, cx, Editor::move_up);
+        register_action(view, cx, Editor::cancel);
+        register_action(view, cx, Editor::newline);
+        register_action(view, cx, Editor::newline_above);
+        register_action(view, cx, Editor::newline_below);
+        register_action(view, cx, Editor::backspace);
+        register_action(view, cx, Editor::delete);
+        register_action(view, cx, Editor::tab);
+        register_action(view, cx, Editor::tab_prev);
+        register_action(view, cx, Editor::indent);
+        register_action(view, cx, Editor::outdent);
+        register_action(view, cx, Editor::delete_line);
+        register_action(view, cx, Editor::join_lines);
+        register_action(view, cx, Editor::sort_lines_case_sensitive);
+        register_action(view, cx, Editor::sort_lines_case_insensitive);
+        register_action(view, cx, Editor::reverse_lines);
+        register_action(view, cx, Editor::shuffle_lines);
+        register_action(view, cx, Editor::convert_to_upper_case);
+        register_action(view, cx, Editor::convert_to_lower_case);
+        register_action(view, cx, Editor::convert_to_title_case);
+        register_action(view, cx, Editor::convert_to_snake_case);
+        register_action(view, cx, Editor::convert_to_kebab_case);
+        register_action(view, cx, Editor::convert_to_upper_camel_case);
+        register_action(view, cx, Editor::convert_to_lower_camel_case);
+        register_action(view, cx, Editor::delete_to_previous_word_start);
+        register_action(view, cx, Editor::delete_to_previous_subword_start);
+        register_action(view, cx, Editor::delete_to_next_word_end);
+        register_action(view, cx, Editor::delete_to_next_subword_end);
+        register_action(view, cx, Editor::delete_to_beginning_of_line);
+        register_action(view, cx, Editor::delete_to_end_of_line);
+        register_action(view, cx, Editor::cut_to_end_of_line);
+        register_action(view, cx, Editor::duplicate_line);
+        register_action(view, cx, Editor::move_line_up);
+        register_action(view, cx, Editor::move_line_down);
+        register_action(view, cx, Editor::transpose);
+        register_action(view, cx, Editor::cut);
+        register_action(view, cx, Editor::copy);
+        register_action(view, cx, Editor::paste);
+        register_action(view, cx, Editor::undo);
+        register_action(view, cx, Editor::redo);
+        register_action(view, cx, Editor::move_page_up);
+        register_action(view, cx, Editor::move_page_down);
+        register_action(view, cx, Editor::next_screen);
+        register_action(view, cx, Editor::scroll_cursor_top);
+        register_action(view, cx, Editor::scroll_cursor_center);
+        register_action(view, cx, Editor::scroll_cursor_bottom);
+        register_action(view, cx, |editor, _: &LineDown, cx| {
+            editor.scroll_screen(&ScrollAmount::Line(1.), cx)
+        });
+        register_action(view, cx, |editor, _: &LineUp, cx| {
+            editor.scroll_screen(&ScrollAmount::Line(-1.), cx)
+        });
+        register_action(view, cx, |editor, _: &HalfPageDown, cx| {
+            editor.scroll_screen(&ScrollAmount::Page(0.5), cx)
+        });
+        register_action(view, cx, |editor, _: &HalfPageUp, cx| {
+            editor.scroll_screen(&ScrollAmount::Page(-0.5), cx)
+        });
+        register_action(view, cx, |editor, _: &PageDown, cx| {
+            editor.scroll_screen(&ScrollAmount::Page(1.), cx)
+        });
+        register_action(view, cx, |editor, _: &PageUp, cx| {
+            editor.scroll_screen(&ScrollAmount::Page(-1.), cx)
+        });
+        register_action(view, cx, Editor::move_to_previous_word_start);
+        register_action(view, cx, Editor::move_to_previous_subword_start);
+        register_action(view, cx, Editor::move_to_next_word_end);
+        register_action(view, cx, Editor::move_to_next_subword_end);
+        register_action(view, cx, Editor::move_to_beginning_of_line);
+        register_action(view, cx, Editor::move_to_end_of_line);
+        register_action(view, cx, Editor::move_to_start_of_paragraph);
+        register_action(view, cx, Editor::move_to_end_of_paragraph);
+        register_action(view, cx, Editor::move_to_beginning);
+        register_action(view, cx, Editor::move_to_end);
+        register_action(view, cx, Editor::select_up);
+        register_action(view, cx, Editor::select_down);
+        register_action(view, cx, Editor::select_left);
+        register_action(view, cx, Editor::select_right);
+        register_action(view, cx, Editor::select_to_previous_word_start);
+        register_action(view, cx, Editor::select_to_previous_subword_start);
+        register_action(view, cx, Editor::select_to_next_word_end);
+        register_action(view, cx, Editor::select_to_next_subword_end);
+        register_action(view, cx, Editor::select_to_beginning_of_line);
+        register_action(view, cx, Editor::select_to_end_of_line);
+        register_action(view, cx, Editor::select_to_start_of_paragraph);
+        register_action(view, cx, Editor::select_to_end_of_paragraph);
+        register_action(view, cx, Editor::select_to_beginning);
+        register_action(view, cx, Editor::select_to_end);
+        register_action(view, cx, Editor::select_all);
+        register_action(view, cx, |editor, action, cx| {
+            editor.select_all_matches(action, cx).log_err();
+        });
+        register_action(view, cx, Editor::select_line);
+        register_action(view, cx, Editor::split_selection_into_lines);
+        register_action(view, cx, Editor::add_selection_above);
+        register_action(view, cx, Editor::add_selection_below);
+        register_action(view, cx, |editor, action, cx| {
+            editor.select_next(action, cx).log_err();
+        });
+        register_action(view, cx, |editor, action, cx| {
+            editor.select_previous(action, cx).log_err();
+        });
+        register_action(view, cx, Editor::toggle_comments);
+        register_action(view, cx, Editor::select_larger_syntax_node);
+        register_action(view, cx, Editor::select_smaller_syntax_node);
+        register_action(view, cx, Editor::move_to_enclosing_bracket);
+        register_action(view, cx, Editor::undo_selection);
+        register_action(view, cx, Editor::redo_selection);
+        register_action(view, cx, Editor::go_to_diagnostic);
+        register_action(view, cx, Editor::go_to_prev_diagnostic);
+        register_action(view, cx, Editor::go_to_hunk);
+        register_action(view, cx, Editor::go_to_prev_hunk);
+        register_action(view, cx, Editor::go_to_definition);
+        register_action(view, cx, Editor::go_to_definition_split);
+        register_action(view, cx, Editor::go_to_type_definition);
+        register_action(view, cx, Editor::go_to_type_definition_split);
+        register_action(view, cx, Editor::fold);
+        register_action(view, cx, Editor::fold_at);
+        register_action(view, cx, Editor::unfold_lines);
+        register_action(view, cx, Editor::unfold_at);
+        register_action(view, cx, Editor::fold_selected_ranges);
+        register_action(view, cx, Editor::show_completions);
+        register_action(view, cx, Editor::toggle_code_actions);
+        register_action(view, cx, Editor::open_excerpts);
+        register_action(view, cx, Editor::toggle_soft_wrap);
+        register_action(view, cx, Editor::toggle_inlay_hints);
+        register_action(view, cx, hover_popover::hover);
+        register_action(view, cx, Editor::reveal_in_finder);
+        register_action(view, cx, Editor::copy_path);
+        register_action(view, cx, Editor::copy_relative_path);
+        register_action(view, cx, Editor::copy_highlight_json);
+        register_action(view, cx, |editor, action, cx| {
+            if let Some(task) = editor.format(action, cx) {
+                task.detach_and_log_err(cx);
+            } else {
+                cx.propagate();
+            }
+        });
+        register_action(view, cx, Editor::restart_language_server);
+        register_action(view, cx, Editor::show_character_palette);
+        register_action(view, cx, |editor, action, cx| {
+            if let Some(task) = editor.confirm_completion(action, cx) {
+                task.detach_and_log_err(cx);
+            } else {
+                cx.propagate();
+            }
+        });
+        register_action(view, cx, |editor, action, cx| {
+            if let Some(task) = editor.confirm_code_action(action, cx) {
+                task.detach_and_log_err(cx);
+            } else {
+                cx.propagate();
+            }
+        });
+        register_action(view, cx, |editor, action, cx| {
+            if let Some(task) = editor.rename(action, cx) {
+                task.detach_and_log_err(cx);
+            } else {
+                cx.propagate();
+            }
+        });
+        register_action(view, cx, |editor, action, cx| {
+            if let Some(task) = editor.confirm_rename(action, cx) {
+                task.detach_and_log_err(cx);
+            } else {
+                cx.propagate();
+            }
+        });
+        register_action(view, cx, |editor, action, cx| {
+            if let Some(task) = editor.find_all_references(action, cx) {
+                task.detach_and_log_err(cx);
+            } else {
+                cx.propagate();
+            }
+        });
+        register_action(view, cx, Editor::next_copilot_suggestion);
+        register_action(view, cx, Editor::previous_copilot_suggestion);
+        register_action(view, cx, Editor::copilot_suggest);
+        register_action(view, cx, Editor::context_menu_first);
+        register_action(view, cx, Editor::context_menu_prev);
+        register_action(view, cx, Editor::context_menu_next);
+        register_action(view, cx, Editor::context_menu_last);
+    }
+
+    fn register_key_listeners(&self, cx: &mut WindowContext) {
+        cx.on_key_event({
+            let editor = self.editor.clone();
+            move |event: &ModifiersChangedEvent, phase, cx| {
+                if phase != DispatchPhase::Bubble {
+                    return;
+                }
+
+                if editor.update(cx, |editor, cx| Self::modifiers_changed(editor, event, cx)) {
+                    cx.stop_propagation();
+                }
+            }
+        });
+    }
+
+    pub(crate) fn modifiers_changed(
+        editor: &mut Editor,
+        event: &ModifiersChangedEvent,
         cx: &mut ViewContext<Editor>,
-    ) {
-        enum EditorElementMouseHandlers {}
-        let view_id = cx.view_id();
-        cx.scene().push_mouse_region(
-            MouseRegion::new::<EditorElementMouseHandlers>(view_id, view_id, visible_bounds)
-                .on_down(MouseButton::Left, {
-                    let position_map = position_map.clone();
-                    move |event, editor, cx| {
-                        if !Self::mouse_down(
-                            editor,
-                            event.platform_event,
-                            position_map.as_ref(),
-                            text_bounds,
-                            gutter_bounds,
-                            cx,
-                        ) {
-                            cx.propagate_event();
-                        }
-                    }
-                })
-                .on_down(MouseButton::Right, {
-                    let position_map = position_map.clone();
-                    move |event, editor, cx| {
-                        if !Self::mouse_right_down(
-                            editor,
-                            event.position,
-                            position_map.as_ref(),
-                            text_bounds,
-                            cx,
-                        ) {
-                            cx.propagate_event();
-                        }
-                    }
-                })
-                .on_up(MouseButton::Left, {
-                    let position_map = position_map.clone();
-                    move |event, editor, cx| {
-                        if !Self::mouse_up(
-                            editor,
-                            event.position,
-                            event.cmd,
-                            event.shift,
-                            event.alt,
-                            position_map.as_ref(),
-                            text_bounds,
-                            cx,
-                        ) {
-                            cx.propagate_event()
-                        }
-                    }
-                })
-                .on_drag(MouseButton::Left, {
-                    let position_map = position_map.clone();
-                    move |event, editor, cx| {
-                        if event.end {
-                            return;
-                        }
+    ) -> bool {
+        let pending_selection = editor.has_pending_selection();
 
-                        if !Self::mouse_dragged(
-                            editor,
-                            event.platform_event,
-                            position_map.as_ref(),
-                            text_bounds,
-                            cx,
-                        ) {
-                            cx.propagate_event()
-                        }
-                    }
-                })
-                .on_move({
-                    let position_map = position_map.clone();
-                    move |event, editor, cx| {
-                        if !Self::mouse_moved(
-                            editor,
-                            event.platform_event,
-                            &position_map,
-                            text_bounds,
-                            cx,
-                        ) {
-                            cx.propagate_event()
-                        }
-                    }
-                })
-                .on_move_out(move |_, editor: &mut Editor, cx| {
-                    if has_popovers {
-                        hide_hover(editor, cx);
-                    }
-                })
-                .on_scroll({
-                    let position_map = position_map.clone();
-                    move |event, editor, cx| {
-                        if !Self::scroll(
-                            editor,
-                            event.position,
-                            *event.delta.raw(),
-                            event.delta.precise(),
-                            &position_map,
-                            bounds,
-                            cx,
-                        ) {
-                            cx.propagate_event()
-                        }
-                    }
-                }),
-        );
+        if let Some(point) = &editor.link_go_to_definition_state.last_trigger_point {
+            if event.command && !pending_selection {
+                let point = point.clone();
+                let snapshot = editor.snapshot(cx);
+                let kind = point.definition_kind(event.shift);
 
-        enum GutterHandlers {}
-        let view_id = cx.view_id();
-        let region_id = cx.view_id() + 1;
-        cx.scene().push_mouse_region(
-            MouseRegion::new::<GutterHandlers>(view_id, region_id, gutter_bounds).on_hover(
-                |hover, editor: &mut Editor, cx| {
-                    editor.gutter_hover(
-                        &GutterHover {
-                            hovered: hover.started,
-                        },
-                        cx,
-                    );
-                },
-            ),
-        )
+                show_link_definition(kind, editor, point, snapshot, cx);
+                return false;
+            }
+        }
+
+        {
+            if editor.link_go_to_definition_state.symbol_range.is_some()
+                || !editor.link_go_to_definition_state.definitions.is_empty()
+            {
+                editor.link_go_to_definition_state.symbol_range.take();
+                editor.link_go_to_definition_state.definitions.clear();
+                cx.notify();
+            }
+
+            editor.link_go_to_definition_state.task = None;
+
+            editor.clear_highlights::<LinkGoToDefinitionState>(cx);
+        }
+
+        false
     }
 
-    fn mouse_down(
+    fn mouse_left_down(
         editor: &mut Editor,
-        MouseButtonEvent {
-            position,
-            modifiers:
-                Modifiers {
-                    shift,
-                    ctrl,
-                    alt,
-                    cmd,
-                    ..
-                },
-            mut click_count,
-            ..
-        }: MouseButtonEvent,
+        event: &MouseDownEvent,
         position_map: &PositionMap,
-        text_bounds: RectF,
-        gutter_bounds: RectF,
-        cx: &mut EventContext<Editor>,
-    ) -> bool {
-        if gutter_bounds.contains_point(position) {
+        text_bounds: Bounds<Pixels>,
+        gutter_bounds: Bounds<Pixels>,
+        stacking_order: &StackingOrder,
+        cx: &mut ViewContext<Editor>,
+    ) {
+        let mut click_count = event.click_count;
+        let modifiers = event.modifiers;
+
+        if gutter_bounds.contains(&event.position) {
             click_count = 3; // Simulate triple-click when clicking the gutter to select lines
-        } else if !text_bounds.contains_point(position) {
-            return false;
+        } else if !text_bounds.contains(&event.position) {
+            return;
+        }
+        if !cx.was_top_layer(&event.position, stacking_order) {
+            return;
         }
 
-        let point_for_position = position_map.point_for_position(text_bounds, position);
+        let point_for_position = position_map.point_for_position(text_bounds, event.position);
         let position = point_for_position.previous_valid;
-        if shift && alt {
+        if modifiers.shift && modifiers.alt {
             editor.select(
                 SelectPhase::BeginColumnar {
                     position,
@@ -296,7 +406,7 @@ impl EditorElement {
                 },
                 cx,
             );
-        } else if shift && !ctrl && !alt && !cmd {
+        } else if modifiers.shift && !modifiers.control && !modifiers.alt && !modifiers.command {
             editor.select(
                 SelectPhase::Extend {
                     position,
@@ -308,46 +418,44 @@ impl EditorElement {
             editor.select(
                 SelectPhase::Begin {
                     position,
-                    add: alt,
+                    add: modifiers.alt,
                     click_count,
                 },
                 cx,
             );
         }
 
-        true
+        cx.stop_propagation();
     }
 
     fn mouse_right_down(
         editor: &mut Editor,
-        position: Vector2F,
+        event: &MouseDownEvent,
         position_map: &PositionMap,
-        text_bounds: RectF,
-        cx: &mut EventContext<Editor>,
-    ) -> bool {
-        if !text_bounds.contains_point(position) {
-            return false;
+        text_bounds: Bounds<Pixels>,
+        cx: &mut ViewContext<Editor>,
+    ) {
+        if !text_bounds.contains(&event.position) {
+            return;
         }
-        let point_for_position = position_map.point_for_position(text_bounds, position);
+        let point_for_position = position_map.point_for_position(text_bounds, event.position);
         mouse_context_menu::deploy_context_menu(
             editor,
-            position,
+            event.position,
             point_for_position.previous_valid,
             cx,
         );
-        true
+        cx.stop_propagation();
     }
 
     fn mouse_up(
         editor: &mut Editor,
-        position: Vector2F,
-        cmd: bool,
-        shift: bool,
-        alt: bool,
+        event: &MouseUpEvent,
         position_map: &PositionMap,
-        text_bounds: RectF,
-        cx: &mut EventContext<Editor>,
-    ) -> bool {
+        text_bounds: Bounds<Pixels>,
+        stacking_order: &StackingOrder,
+        cx: &mut ViewContext<Editor>,
+    ) {
         let end_selection = editor.has_pending_selection();
         let pending_nonempty_selections = editor.has_pending_nonempty_selection();
 
@@ -355,118 +463,99 @@ impl EditorElement {
             editor.select(SelectPhase::End, cx);
         }
 
-        if !pending_nonempty_selections && cmd && text_bounds.contains_point(position) {
-            let point = position_map.point_for_position(text_bounds, position);
+        if !pending_nonempty_selections
+            && event.modifiers.command
+            && text_bounds.contains(&event.position)
+            && cx.was_top_layer(&event.position, stacking_order)
+        {
+            let point = position_map.point_for_position(text_bounds, event.position);
             let could_be_inlay = point.as_valid().is_none();
-            if shift || could_be_inlay {
-                go_to_fetched_type_definition(editor, point, alt, cx);
+            let split = event.modifiers.alt;
+            if event.modifiers.shift || could_be_inlay {
+                go_to_fetched_type_definition(editor, point, split, cx);
             } else {
-                go_to_fetched_definition(editor, point, alt, cx);
+                go_to_fetched_definition(editor, point, split, cx);
             }
 
-            return true;
+            cx.stop_propagation();
+        } else if end_selection {
+            cx.stop_propagation();
         }
-
-        end_selection
     }
 
     fn mouse_dragged(
         editor: &mut Editor,
-        MouseMovedEvent {
-            modifiers: Modifiers { cmd, shift, .. },
-            position,
-            ..
-        }: MouseMovedEvent,
+        event: &MouseMoveEvent,
         position_map: &PositionMap,
-        text_bounds: RectF,
-        cx: &mut EventContext<Editor>,
-    ) -> bool {
-        // This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed
-        // Don't trigger hover popover if mouse is hovering over context menu
-        let point = if text_bounds.contains_point(position) {
-            position_map
-                .point_for_position(text_bounds, position)
-                .as_valid()
-        } else {
-            None
-        };
-
-        update_go_to_definition_link(
-            editor,
-            point.map(GoToDefinitionTrigger::Text),
-            cmd,
-            shift,
-            cx,
-        );
-
-        if editor.has_pending_selection() {
-            let mut scroll_delta = Vector2F::zero();
-
-            let vertical_margin = position_map.line_height.min(text_bounds.height() / 3.0);
-            let top = text_bounds.origin_y() + vertical_margin;
-            let bottom = text_bounds.lower_left().y() - vertical_margin;
-            if position.y() < top {
-                scroll_delta.set_y(-scale_vertical_mouse_autoscroll_delta(top - position.y()))
-            }
-            if position.y() > bottom {
-                scroll_delta.set_y(scale_vertical_mouse_autoscroll_delta(position.y() - bottom))
-            }
-
-            let horizontal_margin = position_map.line_height.min(text_bounds.width() / 3.0);
-            let left = text_bounds.origin_x() + horizontal_margin;
-            let right = text_bounds.upper_right().x() - horizontal_margin;
-            if position.x() < left {
-                scroll_delta.set_x(-scale_horizontal_mouse_autoscroll_delta(
-                    left - position.x(),
-                ))
-            }
-            if position.x() > right {
-                scroll_delta.set_x(scale_horizontal_mouse_autoscroll_delta(
-                    position.x() - right,
-                ))
-            }
+        text_bounds: Bounds<Pixels>,
+        _gutter_bounds: Bounds<Pixels>,
+        _stacking_order: &StackingOrder,
+        cx: &mut ViewContext<Editor>,
+    ) {
+        if !editor.has_pending_selection() {
+            return;
+        }
 
-            let point_for_position = position_map.point_for_position(text_bounds, position);
+        let point_for_position = position_map.point_for_position(text_bounds, event.position);
+        let mut scroll_delta = gpui::Point::<f32>::default();
+        let vertical_margin = position_map.line_height.min(text_bounds.size.height / 3.0);
+        let top = text_bounds.origin.y + vertical_margin;
+        let bottom = text_bounds.lower_left().y - vertical_margin;
+        if event.position.y < top {
+            scroll_delta.y = -scale_vertical_mouse_autoscroll_delta(top - event.position.y);
+        }
+        if event.position.y > bottom {
+            scroll_delta.y = scale_vertical_mouse_autoscroll_delta(event.position.y - bottom);
+        }
 
-            editor.select(
-                SelectPhase::Update {
-                    position: point_for_position.previous_valid,
-                    goal_column: point_for_position.exact_unclipped.column(),
-                    scroll_position: (position_map.snapshot.scroll_position() + scroll_delta)
-                        .clamp(Vector2F::zero(), position_map.scroll_max),
-                },
-                cx,
-            );
-            hover_at(editor, point, cx);
-            true
-        } else {
-            hover_at(editor, point, cx);
-            false
+        let horizontal_margin = position_map.line_height.min(text_bounds.size.width / 3.0);
+        let left = text_bounds.origin.x + horizontal_margin;
+        let right = text_bounds.upper_right().x - horizontal_margin;
+        if event.position.x < left {
+            scroll_delta.x = -scale_horizontal_mouse_autoscroll_delta(left - event.position.x);
         }
+        if event.position.x > right {
+            scroll_delta.x = scale_horizontal_mouse_autoscroll_delta(event.position.x - right);
+        }
+
+        editor.select(
+            SelectPhase::Update {
+                position: point_for_position.previous_valid,
+                goal_column: point_for_position.exact_unclipped.column(),
+                scroll_position: (position_map.snapshot.scroll_position() + scroll_delta)
+                    .clamp(&gpui::Point::default(), &position_map.scroll_max),
+            },
+            cx,
+        );
     }
 
     fn mouse_moved(
         editor: &mut Editor,
-        MouseMovedEvent {
-            modifiers: Modifiers { shift, cmd, .. },
-            position,
-            ..
-        }: MouseMovedEvent,
+        event: &MouseMoveEvent,
         position_map: &PositionMap,
-        text_bounds: RectF,
+        text_bounds: Bounds<Pixels>,
+        gutter_bounds: Bounds<Pixels>,
+        stacking_order: &StackingOrder,
         cx: &mut ViewContext<Editor>,
-    ) -> bool {
-        // This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed
+    ) {
+        let modifiers = event.modifiers;
+        let text_hovered = text_bounds.contains(&event.position);
+        let gutter_hovered = gutter_bounds.contains(&event.position);
+        let was_top = cx.was_top_layer(&event.position, stacking_order);
+
+        editor.set_gutter_hovered(gutter_hovered, cx);
+
         // Don't trigger hover popover if mouse is hovering over context menu
-        if text_bounds.contains_point(position) {
-            let point_for_position = position_map.point_for_position(text_bounds, position);
+        if text_hovered && was_top {
+            let point_for_position = position_map.point_for_position(text_bounds, event.position);
+
             match point_for_position.as_valid() {
                 Some(point) => {
                     update_go_to_definition_link(
                         editor,
                         Some(GoToDefinitionTrigger::Text(point)),
-                        cmd,
-                        shift,
+                        modifiers.command,
+                        modifiers.shift,
                         cx,
                     );
                     hover_at(editor, Some(point), cx);
@@ -476,76 +565,69 @@ impl EditorElement {
                         &position_map.snapshot,
                         point_for_position,
                         editor,
-                        cmd,
-                        shift,
+                        modifiers.command,
+                        modifiers.shift,
                         cx,
                     );
                 }
             }
         } else {
-            update_go_to_definition_link(editor, None, cmd, shift, cx);
+            update_go_to_definition_link(editor, None, modifiers.command, modifiers.shift, cx);
             hover_at(editor, None, cx);
+            if gutter_hovered && was_top {
+                cx.stop_propagation();
+            }
         }
-
-        true
     }
 
     fn scroll(
         editor: &mut Editor,
-        position: Vector2F,
-        mut delta: Vector2F,
-        precise: bool,
+        event: &ScrollWheelEvent,
         position_map: &PositionMap,
-        bounds: RectF,
+        bounds: &InteractiveBounds,
         cx: &mut ViewContext<Editor>,
-    ) -> bool {
-        if !bounds.contains_point(position) {
-            return false;
+    ) {
+        if !bounds.visibly_contains(&event.position, cx) {
+            return;
         }
 
         let line_height = position_map.line_height;
         let max_glyph_width = position_map.em_width;
+        let (delta, axis) = match event.delta {
+            gpui::ScrollDelta::Pixels(mut pixels) => {
+                //Trackpad
+                let axis = position_map.snapshot.ongoing_scroll.filter(&mut pixels);
+                (pixels, axis)
+            }
 
-        let axis = if precise {
-            //Trackpad
-            position_map.snapshot.ongoing_scroll.filter(&mut delta)
-        } else {
-            //Not trackpad
-            delta *= vec2f(max_glyph_width, line_height);
-            None //Resets ongoing scroll
+            gpui::ScrollDelta::Lines(lines) => {
+                //Not trackpad
+                let pixels = point(lines.x * max_glyph_width, lines.y * line_height);
+                (pixels, None)
+            }
         };
 
         let scroll_position = position_map.snapshot.scroll_position();
-        let x = (scroll_position.x() * max_glyph_width - delta.x()) / max_glyph_width;
-        let y = (scroll_position.y() * line_height - delta.y()) / line_height;
-        let scroll_position = vec2f(x, y).clamp(Vector2F::zero(), position_map.scroll_max);
+        let x = f32::from((scroll_position.x * max_glyph_width - delta.x) / max_glyph_width);
+        let y = f32::from((scroll_position.y * line_height - delta.y) / line_height);
+        let scroll_position = point(x, y).clamp(&point(0., 0.), &position_map.scroll_max);
         editor.scroll(scroll_position, axis, cx);
-
-        true
+        cx.stop_propagation();
     }
 
     fn paint_background(
         &self,
-        gutter_bounds: RectF,
-        text_bounds: RectF,
+        gutter_bounds: Bounds<Pixels>,
+        text_bounds: Bounds<Pixels>,
         layout: &LayoutState,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut WindowContext,
     ) {
-        let bounds = gutter_bounds.union_rect(text_bounds);
+        let bounds = gutter_bounds.union(&text_bounds);
         let scroll_top =
-            layout.position_map.snapshot.scroll_position().y() * layout.position_map.line_height;
-        cx.scene().push_quad(Quad {
-            bounds: gutter_bounds,
-            background: Some(self.style.gutter_background),
-            border: Border::new(0., Color::transparent_black()).into(),
-            corner_radii: Default::default(),
-        });
-        cx.scene().push_quad(Quad {
-            bounds: text_bounds,
-            background: Some(self.style.background),
-            border: Border::new(0., Color::transparent_black()).into(),
-            corner_radii: Default::default(),
-        });
+            layout.position_map.snapshot.scroll_position().y * layout.position_map.line_height;
+        let gutter_bg = cx.theme().colors().editor_gutter_background;
+        cx.paint_quad(fill(gutter_bounds, gutter_bg));
+        cx.paint_quad(fill(text_bounds, self.style.background));
 
         if let EditorMode::Full = layout.mode {
             let mut active_rows = layout.active_rows.iter().peekable();
@@ -559,90 +641,77 @@ impl EditorElement {
                 }
 
                 if !contains_non_empty_selection {
-                    let origin = vec2f(
-                        bounds.origin_x(),
-                        bounds.origin_y() + (layout.position_map.line_height * *start_row as f32)
+                    let origin = point(
+                        bounds.origin.x,
+                        bounds.origin.y + (layout.position_map.line_height * *start_row as f32)
                             - scroll_top,
                     );
-                    let size = vec2f(
-                        bounds.width(),
+                    let size = size(
+                        bounds.size.width,
                         layout.position_map.line_height * (end_row - start_row + 1) as f32,
                     );
-                    cx.scene().push_quad(Quad {
-                        bounds: RectF::new(origin, size),
-                        background: Some(self.style.active_line_background),
-                        border: Border::default().into(),
-                        corner_radii: Default::default(),
-                    });
+                    let active_line_bg = cx.theme().colors().editor_active_line_background;
+                    cx.paint_quad(fill(Bounds { origin, size }, active_line_bg));
                 }
             }
 
             if let Some(highlighted_rows) = &layout.highlighted_rows {
-                let origin = vec2f(
-                    bounds.origin_x(),
-                    bounds.origin_y()
+                let origin = point(
+                    bounds.origin.x,
+                    bounds.origin.y
                         + (layout.position_map.line_height * highlighted_rows.start as f32)
                         - scroll_top,
                 );
-                let size = vec2f(
-                    bounds.width(),
+                let size = size(
+                    bounds.size.width,
                     layout.position_map.line_height * highlighted_rows.len() as f32,
                 );
-                cx.scene().push_quad(Quad {
-                    bounds: RectF::new(origin, size),
-                    background: Some(self.style.highlighted_line_background),
-                    border: Border::default().into(),
-                    corner_radii: Default::default(),
-                });
+                let highlighted_line_bg = cx.theme().colors().editor_highlighted_line_background;
+                cx.paint_quad(fill(Bounds { origin, size }, highlighted_line_bg));
             }
 
             let scroll_left =
-                layout.position_map.snapshot.scroll_position().x() * layout.position_map.em_width;
+                layout.position_map.snapshot.scroll_position().x * layout.position_map.em_width;
 
             for (wrap_position, active) in layout.wrap_guides.iter() {
-                let x =
-                    (text_bounds.origin_x() + wrap_position + layout.position_map.em_width / 2.)
-                        - scroll_left;
+                let x = (text_bounds.origin.x + *wrap_position + layout.position_map.em_width / 2.)
+                    - scroll_left;
 
-                if x < text_bounds.origin_x()
+                if x < text_bounds.origin.x
                     || (layout.show_scrollbars && x > self.scrollbar_left(&bounds))
                 {
                     continue;
                 }
 
                 let color = if *active {
-                    self.style.active_wrap_guide
+                    cx.theme().colors().editor_active_wrap_guide
                 } else {
-                    self.style.wrap_guide
+                    cx.theme().colors().editor_wrap_guide
                 };
-                cx.scene().push_quad(Quad {
-                    bounds: RectF::new(
-                        vec2f(x, text_bounds.origin_y()),
-                        vec2f(1., text_bounds.height()),
-                    ),
-                    background: Some(color),
-                    border: Border::new(0., Color::transparent_black()).into(),
-                    corner_radii: Default::default(),
-                });
+                cx.paint_quad(fill(
+                    Bounds {
+                        origin: point(x, text_bounds.origin.y),
+                        size: size(px(1.), text_bounds.size.height),
+                    },
+                    color,
+                ));
             }
         }
     }
 
     fn paint_gutter(
         &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
+        bounds: Bounds<Pixels>,
         layout: &mut LayoutState,
-        editor: &mut Editor,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut WindowContext,
     ) {
         let line_height = layout.position_map.line_height;
 
         let scroll_position = layout.position_map.snapshot.scroll_position();
-        let scroll_top = scroll_position.y() * line_height;
+        let scroll_top = scroll_position.y * line_height;
 
         let show_gutter = matches!(
-            settings::get::<ProjectSettings>(cx).git.git_gutter,
+            ProjectSettings::get_global(cx).git.git_gutter,
             Some(GitGutterSetting::TrackedFiles)
         );
 

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

@@ -60,8 +60,8 @@ pub fn diff_hunk_to_display(hunk: DiffHunk<u32>, snapshot: &DisplaySnapshot) ->
     let folds_end = Point::new(hunk.buffer_range.end + 2, 0);
     let folds_range = folds_start..folds_end;
 
-    let containing_fold = snapshot.folds_in_range(folds_range).find(|fold_range| {
-        let fold_point_range = fold_range.to_point(&snapshot.buffer_snapshot);
+    let containing_fold = snapshot.folds_in_range(folds_range).find(|fold| {
+        let fold_point_range = fold.range.to_point(&snapshot.buffer_snapshot);
         let fold_point_range = fold_point_range.start..=fold_point_range.end;
 
         let folded_start = fold_point_range.contains(&hunk_start_point);
@@ -72,7 +72,7 @@ pub fn diff_hunk_to_display(hunk: DiffHunk<u32>, snapshot: &DisplaySnapshot) ->
     });
 
     if let Some(fold) = containing_fold {
-        let row = fold.start.to_display_point(snapshot).row();
+        let row = fold.range.start.to_display_point(snapshot).row();
         DisplayDiffHunk::Folded { display_row: row }
     } else {
         let start = hunk_start_point.to_display_point(snapshot).row();
@@ -88,11 +88,11 @@ pub fn diff_hunk_to_display(hunk: DiffHunk<u32>, snapshot: &DisplaySnapshot) ->
     }
 }
 
-#[cfg(any(test, feature = "test_support"))]
+#[cfg(test)]
 mod tests {
     use crate::editor_tests::init_test;
     use crate::Point;
-    use gpui::TestAppContext;
+    use gpui::{Context, TestAppContext};
     use multi_buffer::{ExcerptRange, MultiBuffer};
     use project::{FakeFs, Project};
     use unindent::Unindent;
@@ -101,7 +101,7 @@ mod tests {
         use git::diff::DiffHunkStatus;
         init_test(cx, |_| {});
 
-        let fs = FakeFs::new(cx.background());
+        let fs = FakeFs::new(cx.background_executor.clone());
         let project = Project::test(fs, [], cx).await;
 
         // buffer has two modified hunks with two rows each
@@ -180,9 +180,9 @@ mod tests {
             );
         });
 
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
 
-        let multibuffer = cx.add_model(|cx| {
+        let multibuffer = cx.new_model(|cx| {
             let mut multibuffer = MultiBuffer::new(0);
             multibuffer.push_excerpts(
                 buffer_1.clone(),

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

@@ -24,7 +24,7 @@ pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewCon
                 opening_range.to_anchors(&snapshot.buffer_snapshot),
                 closing_range.to_anchors(&snapshot.buffer_snapshot),
             ],
-            |theme| theme.editor.document_highlight_read_background,
+            |theme| theme.editor_document_highlight_read_background,
             cx,
         )
     }

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

@@ -6,16 +6,17 @@ use crate::{
 };
 use futures::FutureExt;
 use gpui::{
-    actions,
-    elements::{Flex, MouseEventHandler, Padding, ParentElement, Text},
-    platform::{CursorStyle, MouseButton},
-    AnyElement, AppContext, Element, ModelHandle, Task, ViewContext, WeakViewHandle,
-};
-use language::{
-    markdown, Bias, DiagnosticEntry, DiagnosticSeverity, Language, LanguageRegistry, ParsedMarkdown,
+    actions, div, px, AnyElement, CursorStyle, Hsla, InteractiveElement, IntoElement, Model,
+    MouseButton, ParentElement, Pixels, SharedString, Size, StatefulInteractiveElement, Styled,
+    Task, ViewContext, WeakView,
 };
+use language::{markdown, Bias, DiagnosticEntry, Language, LanguageRegistry, ParsedMarkdown};
+
+use lsp::DiagnosticSeverity;
 use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart, Project};
+use settings::Settings;
 use std::{ops::Range, sync::Arc, time::Duration};
+use ui::{StyledExt, Tooltip};
 use util::TryFutureExt;
 use workspace::Workspace;
 
@@ -23,15 +24,11 @@ pub const HOVER_DELAY_MILLIS: u64 = 350;
 pub const HOVER_REQUEST_DELAY_MILLIS: u64 = 200;
 
 pub const MIN_POPOVER_CHARACTER_WIDTH: f32 = 20.;
-pub const MIN_POPOVER_LINE_HEIGHT: f32 = 4.;
-pub const HOVER_POPOVER_GAP: f32 = 10.;
+pub const MIN_POPOVER_LINE_HEIGHT: Pixels = px(4.);
+pub const HOVER_POPOVER_GAP: Pixels = px(10.);
 
 actions!(editor, [Hover]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.add_action(hover);
-}
-
 /// Bindable action which uses the most recent selection head to trigger a hover
 pub fn hover(editor: &mut Editor, _: &Hover, cx: &mut ViewContext<Editor>) {
     let head = editor.selections.newest_display(cx).head();
@@ -41,7 +38,7 @@ pub fn hover(editor: &mut Editor, _: &Hover, cx: &mut ViewContext<Editor>) {
 /// The internal hover action dispatches between `show_hover` or `hide_hover`
 /// depending on whether a point to hover over is provided.
 pub fn hover_at(editor: &mut Editor, point: Option<DisplayPoint>, cx: &mut ViewContext<Editor>) {
-    if settings::get::<EditorSettings>(cx).hover_popover_enabled {
+    if EditorSettings::get_global(cx).hover_popover_enabled {
         if let Some(point) = point {
             show_hover(editor, point, false, cx);
         } else {
@@ -79,7 +76,7 @@ pub fn find_hovered_hint_part(
 }
 
 pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut ViewContext<Editor>) {
-    if settings::get::<EditorSettings>(cx).hover_popover_enabled {
+    if EditorSettings::get_global(cx).hover_popover_enabled {
         if editor.pending_rename.is_some() {
             return;
         }
@@ -100,14 +97,14 @@ pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut Vie
 
         let task = cx.spawn(|this, mut cx| {
             async move {
-                cx.background()
+                cx.background_executor()
                     .timer(Duration::from_millis(HOVER_DELAY_MILLIS))
                     .await;
                 this.update(&mut cx, |this, _| {
                     this.hover_state.diagnostic_popover = None;
                 })?;
 
-                let language_registry = project.update(&mut cx, |p, _| p.languages().clone());
+                let language_registry = project.update(&mut cx, |p, _| p.languages().clone())?;
                 let blocks = vec![inlay_hover.tooltip];
                 let parsed_content = parse_blocks(&blocks, &language_registry, None).await;
 
@@ -122,7 +119,7 @@ pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut Vie
                     // Highlight the selected symbol using a background highlight
                     this.highlight_inlay_background::<HoverState>(
                         vec![inlay_hover.range],
-                        |theme| theme.editor.hover_popover.highlight,
+                        |theme| theme.element_hover, // todo!("use a proper background here")
                         cx,
                     );
                     this.hover_state.info_popover = Some(hover_popover);
@@ -239,11 +236,11 @@ fn show_hover(
             let delay = if !ignore_timeout {
                 // Construct delay task to wait for later
                 let total_delay = Some(
-                    cx.background()
+                    cx.background_executor()
                         .timer(Duration::from_millis(HOVER_DELAY_MILLIS)),
                 );
 
-                cx.background()
+                cx.background_executor()
                     .timer(Duration::from_millis(HOVER_REQUEST_DELAY_MILLIS))
                     .await;
                 total_delay
@@ -252,11 +249,11 @@ 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)
                 })
-            });
+            })?;
 
             if let Some(delay) = delay {
                 delay.await;
@@ -310,7 +307,8 @@ fn show_hover(
                         anchor..anchor
                     };
 
-                    let language_registry = project.update(&mut cx, |p, _| p.languages().clone());
+                    let language_registry =
+                        project.update(&mut cx, |p, _| p.languages().clone())?;
                     let blocks = hover_result.contents;
                     let language = hover_result.language;
                     let parsed_content = parse_blocks(&blocks, &language_registry, language).await;
@@ -334,7 +332,7 @@ fn show_hover(
                     // Highlight the selected symbol using a background highlight
                     this.highlight_background::<HoverState>(
                         vec![symbol_range],
-                        |theme| theme.editor.hover_popover.highlight,
+                        |theme| theme.element_hover, // todo! update theme
                         cx,
                     );
                 } else {
@@ -423,9 +421,10 @@ impl HoverState {
         snapshot: &EditorSnapshot,
         style: &EditorStyle,
         visible_rows: Range<u32>,
-        workspace: Option<WeakViewHandle<Workspace>>,
+        max_size: Size<Pixels>,
+        workspace: Option<WeakView<Workspace>>,
         cx: &mut ViewContext<Editor>,
-    ) -> Option<(DisplayPoint, Vec<AnyElement<Editor>>)> {
+    ) -> Option<(DisplayPoint, Vec<AnyElement>)> {
         // If there is a diagnostic, position the popovers based on that.
         // Otherwise use the start of the hover range
         let anchor = self
@@ -450,10 +449,10 @@ impl HoverState {
         let mut elements = Vec::new();
 
         if let Some(diagnostic_popover) = self.diagnostic_popover.as_ref() {
-            elements.push(diagnostic_popover.render(style, cx));
+            elements.push(diagnostic_popover.render(style, max_size, cx));
         }
         if let Some(info_popover) = self.info_popover.as_mut() {
-            elements.push(info_popover.render(style, workspace, cx));
+            elements.push(info_popover.render(style, max_size, workspace, cx));
         }
 
         Some((point, elements))
@@ -462,7 +461,7 @@ impl HoverState {
 
 #[derive(Debug, Clone)]
 pub struct InfoPopover {
-    pub project: ModelHandle<Project>,
+    pub project: Model<Project>,
     symbol_range: RangeInEditor,
     pub blocks: Vec<HoverBlock>,
     parsed_content: ParsedMarkdown,
@@ -472,29 +471,28 @@ impl InfoPopover {
     pub fn render(
         &mut self,
         style: &EditorStyle,
-        workspace: Option<WeakViewHandle<Workspace>>,
+        max_size: Size<Pixels>,
+        workspace: Option<WeakView<Workspace>>,
         cx: &mut ViewContext<Editor>,
-    ) -> AnyElement<Editor> {
-        MouseEventHandler::new::<InfoPopover, _>(0, cx, |_, cx| {
-            Flex::column()
-                .scrollable::<HoverBlock>(0, None, cx)
-                .with_child(crate::render_parsed_markdown::<HoverBlock>(
-                    &self.parsed_content,
-                    style,
-                    workspace,
-                    cx,
-                ))
-                .contained()
-                .with_style(style.hover_popover.container)
-        })
-        .on_move(|_, _, _| {}) // Consume move events so they don't reach regions underneath.
-        .with_cursor_style(CursorStyle::Arrow)
-        .with_padding(Padding {
-            bottom: HOVER_POPOVER_GAP,
-            top: HOVER_POPOVER_GAP,
-            ..Default::default()
-        })
-        .into_any()
+    ) -> AnyElement {
+        div()
+            .id("info_popover")
+            .elevation_2(cx)
+            .p_2()
+            .overflow_y_scroll()
+            .max_w(max_size.width)
+            .max_h(max_size.height)
+            // Prevent a mouse move on the popover from being propagated to the editor,
+            // because that would dismiss the popover.
+            .on_mouse_move(|_, cx| cx.stop_propagation())
+            .child(crate::render_parsed_markdown(
+                "content",
+                &self.parsed_content,
+                style,
+                workspace,
+                cx,
+            ))
+            .into_any_element()
     }
 }
 
@@ -505,56 +503,74 @@ pub struct DiagnosticPopover {
 }
 
 impl DiagnosticPopover {
-    pub fn render(&self, style: &EditorStyle, cx: &mut ViewContext<Editor>) -> AnyElement<Editor> {
-        enum PrimaryDiagnostic {}
-
-        let mut text_style = style.hover_popover.prose.clone();
-        text_style.font_size = style.text.font_size;
-        let diagnostic_source_style = style.hover_popover.diagnostic_source_highlight.clone();
-
+    pub fn render(
+        &self,
+        style: &EditorStyle,
+        max_size: Size<Pixels>,
+        cx: &mut ViewContext<Editor>,
+    ) -> AnyElement {
         let text = match &self.local_diagnostic.diagnostic.source {
-            Some(source) => Text::new(
-                format!("{source}: {}", self.local_diagnostic.diagnostic.message),
-                text_style,
-            )
-            .with_highlights(vec![(0..source.len(), diagnostic_source_style)]),
-
-            None => Text::new(self.local_diagnostic.diagnostic.message.clone(), text_style),
+            Some(source) => format!("{source}: {}", self.local_diagnostic.diagnostic.message),
+            None => self.local_diagnostic.diagnostic.message.clone(),
         };
 
-        let container_style = match self.local_diagnostic.diagnostic.severity {
-            DiagnosticSeverity::HINT => style.hover_popover.info_container,
-            DiagnosticSeverity::INFORMATION => style.hover_popover.info_container,
-            DiagnosticSeverity::WARNING => style.hover_popover.warning_container,
-            DiagnosticSeverity::ERROR => style.hover_popover.error_container,
-            _ => style.hover_popover.container,
-        };
+        struct DiagnosticColors {
+            pub text: Hsla,
+            pub background: Hsla,
+            pub border: Hsla,
+        }
 
-        let tooltip_style = theme::current(cx).tooltip.clone();
+        let diagnostic_colors = match self.local_diagnostic.diagnostic.severity {
+            DiagnosticSeverity::ERROR => DiagnosticColors {
+                text: style.status.error,
+                background: style.status.error_background,
+                border: style.status.error_border,
+            },
+            DiagnosticSeverity::WARNING => DiagnosticColors {
+                text: style.status.warning,
+                background: style.status.warning_background,
+                border: style.status.warning_border,
+            },
+            DiagnosticSeverity::INFORMATION => DiagnosticColors {
+                text: style.status.info,
+                background: style.status.info_background,
+                border: style.status.info_border,
+            },
+            DiagnosticSeverity::HINT => DiagnosticColors {
+                text: style.status.hint,
+                background: style.status.hint_background,
+                border: style.status.hint_border,
+            },
+            _ => DiagnosticColors {
+                text: style.status.ignored,
+                background: style.status.ignored_background,
+                border: style.status.ignored_border,
+            },
+        };
 
-        MouseEventHandler::new::<DiagnosticPopover, _>(0, cx, |_, _| {
-            text.with_soft_wrap(true)
-                .contained()
-                .with_style(container_style)
-        })
-        .with_padding(Padding {
-            top: HOVER_POPOVER_GAP,
-            bottom: HOVER_POPOVER_GAP,
-            ..Default::default()
-        })
-        .on_move(|_, _, _| {}) // Consume move events so they don't reach regions underneath.
-        .on_click(MouseButton::Left, |_, this, cx| {
-            this.go_to_diagnostic(&Default::default(), cx)
-        })
-        .with_cursor_style(CursorStyle::PointingHand)
-        .with_tooltip::<PrimaryDiagnostic>(
-            0,
-            "Go To Diagnostic".to_string(),
-            Some(Box::new(crate::GoToDiagnostic)),
-            tooltip_style,
-            cx,
-        )
-        .into_any()
+        div()
+            .id("diagnostic")
+            .overflow_y_scroll()
+            .px_2()
+            .py_1()
+            .bg(diagnostic_colors.background)
+            .text_color(diagnostic_colors.text)
+            .border_1()
+            .border_color(diagnostic_colors.border)
+            .rounded_md()
+            .max_w(max_size.width)
+            .max_h(max_size.height)
+            .cursor(CursorStyle::PointingHand)
+            .tooltip(move |cx| Tooltip::for_action("Go To Diagnostic", &crate::GoToDiagnostic, cx))
+            // Prevent a mouse move on the popover from being propagated to the editor,
+            // because that would dismiss the popover.
+            .on_mouse_move(|_, cx| cx.stop_propagation())
+            // Prevent a mouse down on the popover from being propagated to the editor,
+            // because that would move the cursor.
+            .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
+            .on_click(cx.listener(|editor, _, cx| editor.go_to_diagnostic(&Default::default(), cx)))
+            .child(SharedString::from(text))
+            .into_any_element()
     }
 
     pub fn activation_info(&self) -> (usize, Anchor) {
@@ -579,7 +595,7 @@ mod tests {
         InlayId,
     };
     use collections::BTreeSet;
-    use gpui::fonts::{HighlightStyle, Underline, Weight};
+    use gpui::{FontWeight, HighlightStyle, UnderlineStyle};
     use indoc::indoc;
     use language::{language_settings::InlayHintSettings, Diagnostic, DiagnosticSet};
     use lsp::LanguageServerId;
@@ -626,7 +642,7 @@ mod tests {
                     range: Some(symbol_range),
                 }))
             });
-        cx.foreground()
+        cx.background_executor
             .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
         requests.next().await;
 
@@ -649,7 +665,7 @@ mod tests {
             .lsp
             .handle_request::<lsp::request::HoverRequest, _, _>(|_, _| async move { Ok(None) });
         cx.update_editor(|editor, cx| hover_at(editor, Some(hover_point), cx));
-        cx.foreground()
+        cx.background_executor
             .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
         request.next().await;
         cx.editor(|editor, _| {
@@ -853,7 +869,7 @@ mod tests {
 
         // Hover pops diagnostic immediately
         cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
 
         cx.editor(|Editor { hover_state, .. }, _| {
             assert!(hover_state.diagnostic_popover.is_some() && hover_state.info_popover.is_none())
@@ -872,10 +888,10 @@ mod tests {
                 range: Some(range),
             }))
         });
-        cx.foreground()
+        cx.background_executor
             .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
 
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
         cx.editor(|Editor { hover_state, .. }, _| {
             hover_state.diagnostic_popover.is_some() && hover_state.info_task.is_some()
         });
@@ -885,48 +901,49 @@ mod tests {
     fn test_render_blocks(cx: &mut gpui::TestAppContext) {
         init_test(cx, |_| {});
 
-        cx.add_window(|cx| {
-            let editor = Editor::single_line(None, cx);
-            let style = editor.style(cx);
+        let editor = cx.add_window(|cx| Editor::single_line(cx));
+        editor
+            .update(cx, |editor, _cx| {
+                let style = editor.style.clone().unwrap();
 
-            struct Row {
-                blocks: Vec<HoverBlock>,
-                expected_marked_text: String,
-                expected_styles: Vec<HighlightStyle>,
-            }
+                struct Row {
+                    blocks: Vec<HoverBlock>,
+                    expected_marked_text: String,
+                    expected_styles: Vec<HighlightStyle>,
+                }
 
-            let rows = &[
-                // Strong emphasis
-                Row {
-                    blocks: vec![HoverBlock {
-                        text: "one **two** three".to_string(),
-                        kind: HoverBlockKind::Markdown,
-                    }],
-                    expected_marked_text: "one ยซtwoยป three".to_string(),
-                    expected_styles: vec![HighlightStyle {
-                        weight: Some(Weight::BOLD),
-                        ..Default::default()
-                    }],
-                },
-                // Links
-                Row {
-                    blocks: vec![HoverBlock {
-                        text: "one [two](https://the-url) three".to_string(),
-                        kind: HoverBlockKind::Markdown,
-                    }],
-                    expected_marked_text: "one ยซtwoยป three".to_string(),
-                    expected_styles: vec![HighlightStyle {
-                        underline: Some(Underline {
-                            thickness: 1.0.into(),
+                let rows = &[
+                    // Strong emphasis
+                    Row {
+                        blocks: vec![HoverBlock {
+                            text: "one **two** three".to_string(),
+                            kind: HoverBlockKind::Markdown,
+                        }],
+                        expected_marked_text: "one ยซtwoยป three".to_string(),
+                        expected_styles: vec![HighlightStyle {
+                            font_weight: Some(FontWeight::BOLD),
                             ..Default::default()
-                        }),
-                        ..Default::default()
-                    }],
-                },
-                // Lists
-                Row {
-                    blocks: vec![HoverBlock {
-                        text: "
+                        }],
+                    },
+                    // Links
+                    Row {
+                        blocks: vec![HoverBlock {
+                            text: "one [two](https://the-url) three".to_string(),
+                            kind: HoverBlockKind::Markdown,
+                        }],
+                        expected_marked_text: "one ยซtwoยป three".to_string(),
+                        expected_styles: vec![HighlightStyle {
+                            underline: Some(UnderlineStyle {
+                                thickness: 1.0.into(),
+                                ..Default::default()
+                            }),
+                            ..Default::default()
+                        }],
+                    },
+                    // Lists
+                    Row {
+                        blocks: vec![HoverBlock {
+                            text: "
                             lists:
                             * one
                                 - a
@@ -934,10 +951,10 @@ mod tests {
                             * two
                                 - [c](https://the-url)
                                 - d"
-                        .unindent(),
-                        kind: HoverBlockKind::Markdown,
-                    }],
-                    expected_marked_text: "
+                            .unindent(),
+                            kind: HoverBlockKind::Markdown,
+                        }],
+                        expected_marked_text: "
                         lists:
                         - one
                           - a
@@ -945,19 +962,19 @@ mod tests {
                         - two
                           - ยซcยป
                           - d"
-                    .unindent(),
-                    expected_styles: vec![HighlightStyle {
-                        underline: Some(Underline {
-                            thickness: 1.0.into(),
+                        .unindent(),
+                        expected_styles: vec![HighlightStyle {
+                            underline: Some(UnderlineStyle {
+                                thickness: 1.0.into(),
+                                ..Default::default()
+                            }),
                             ..Default::default()
-                        }),
-                        ..Default::default()
-                    }],
-                },
-                // Multi-paragraph list items
-                Row {
-                    blocks: vec![HoverBlock {
-                        text: "
+                        }],
+                    },
+                    // Multi-paragraph list items
+                    Row {
+                        blocks: vec![HoverBlock {
+                            text: "
                             * one two
                               three
 
@@ -968,10 +985,10 @@ mod tests {
                                   nine
                                 * ten
                             * six"
-                            .unindent(),
-                        kind: HoverBlockKind::Markdown,
-                    }],
-                    expected_marked_text: "
+                                .unindent(),
+                            kind: HoverBlockKind::Markdown,
+                        }],
+                        expected_marked_text: "
                         - one two three
                         - four five
                           - six seven eight
@@ -979,52 +996,51 @@ mod tests {
                             nine
                           - ten
                         - six"
-                        .unindent(),
-                    expected_styles: vec![HighlightStyle {
-                        underline: Some(Underline {
-                            thickness: 1.0.into(),
+                            .unindent(),
+                        expected_styles: vec![HighlightStyle {
+                            underline: Some(UnderlineStyle {
+                                thickness: 1.0.into(),
+                                ..Default::default()
+                            }),
                             ..Default::default()
-                        }),
-                        ..Default::default()
-                    }],
-                },
-            ];
-
-            for Row {
-                blocks,
-                expected_marked_text,
-                expected_styles,
-            } in &rows[0..]
-            {
-                let rendered = smol::block_on(parse_blocks(&blocks, &Default::default(), None));
-
-                let (expected_text, ranges) = marked_text_ranges(expected_marked_text, false);
-                let expected_highlights = ranges
-                    .into_iter()
-                    .zip(expected_styles.iter().cloned())
-                    .collect::<Vec<_>>();
-                assert_eq!(
-                    rendered.text, expected_text,
-                    "wrong text for input {blocks:?}"
-                );
-
-                let rendered_highlights: Vec<_> = rendered
-                    .highlights
-                    .iter()
-                    .filter_map(|(range, highlight)| {
-                        let highlight = highlight.to_highlight_style(&style.syntax)?;
-                        Some((range.clone(), highlight))
-                    })
-                    .collect();
+                        }],
+                    },
+                ];
 
-                assert_eq!(
-                    rendered_highlights, expected_highlights,
-                    "wrong highlights for input {blocks:?}"
-                );
-            }
+                for Row {
+                    blocks,
+                    expected_marked_text,
+                    expected_styles,
+                } in &rows[0..]
+                {
+                    let rendered = smol::block_on(parse_blocks(&blocks, &Default::default(), None));
+
+                    let (expected_text, ranges) = marked_text_ranges(expected_marked_text, false);
+                    let expected_highlights = ranges
+                        .into_iter()
+                        .zip(expected_styles.iter().cloned())
+                        .collect::<Vec<_>>();
+                    assert_eq!(
+                        rendered.text, expected_text,
+                        "wrong text for input {blocks:?}"
+                    );
 
-            editor
-        });
+                    let rendered_highlights: Vec<_> = rendered
+                        .highlights
+                        .iter()
+                        .filter_map(|(range, highlight)| {
+                            let highlight = highlight.to_highlight_style(&style.syntax)?;
+                            Some((range.clone(), highlight))
+                        })
+                        .collect();
+
+                    assert_eq!(
+                        rendered_highlights, expected_highlights,
+                        "wrong highlights for input {blocks:?}"
+                    );
+                }
+            })
+            .unwrap();
     }
 
     #[gpui::test]
@@ -1127,7 +1143,7 @@ mod tests {
             })
             .next()
             .await;
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
         cx.update_editor(|editor, cx| {
             let expected_layers = vec![entire_hint_label.to_string()];
             assert_eq!(expected_layers, cached_hint_labels(editor));
@@ -1236,7 +1252,7 @@ mod tests {
             )
             .next()
             .await;
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
 
         cx.update_editor(|editor, cx| {
             update_inlay_link_and_hover_points(
@@ -1248,9 +1264,9 @@ mod tests {
                 cx,
             );
         });
-        cx.foreground()
+        cx.background_executor
             .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
         cx.update_editor(|editor, cx| {
             let hover_state = &editor.hover_state;
             assert!(hover_state.diagnostic_popover.is_none() && hover_state.info_popover.is_some());
@@ -1301,9 +1317,9 @@ mod tests {
                 cx,
             );
         });
-        cx.foreground()
+        cx.background_executor
             .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
         cx.update_editor(|editor, cx| {
             let hover_state = &editor.hover_state;
             assert!(hover_state.diagnostic_popover.is_none() && hover_state.info_popover.is_some());

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

@@ -11,7 +11,7 @@ use crate::{
 use anyhow::Context;
 use clock::Global;
 use futures::future;
-use gpui::{ModelContext, ModelHandle, Task, ViewContext};
+use gpui::{Model, ModelContext, Task, ViewContext};
 use language::{language_settings::InlayHintKind, Buffer, BufferSnapshot};
 use parking_lot::RwLock;
 use project::{InlayHint, ResolveState};
@@ -250,7 +250,7 @@ impl InlayHintCache {
 
     pub fn update_settings(
         &mut self,
-        multi_buffer: &ModelHandle<MultiBuffer>,
+        multi_buffer: &Model<MultiBuffer>,
         new_hint_settings: InlayHintSettings,
         visible_hints: Vec<Inlay>,
         cx: &mut ViewContext<Editor>,
@@ -302,7 +302,7 @@ impl InlayHintCache {
     pub fn spawn_hint_refresh(
         &mut self,
         reason: &'static str,
-        excerpts_to_query: HashMap<ExcerptId, (ModelHandle<Buffer>, Global, Range<usize>)>,
+        excerpts_to_query: HashMap<ExcerptId, (Model<Buffer>, Global, Range<usize>)>,
         invalidate: InvalidationStrategy,
         cx: &mut ViewContext<Editor>,
     ) -> Option<InlaySplice> {
@@ -355,7 +355,7 @@ impl InlayHintCache {
 
     fn new_allowed_hint_kinds_splice(
         &self,
-        multi_buffer: &ModelHandle<MultiBuffer>,
+        multi_buffer: &Model<MultiBuffer>,
         visible_hints: &[Inlay],
         new_kinds: &HashSet<Option<InlayHintKind>>,
         cx: &mut ViewContext<Editor>,
@@ -521,7 +521,7 @@ impl InlayHintCache {
         buffer_id: u64,
         excerpt_id: ExcerptId,
         id: InlayId,
-        cx: &mut ViewContext<'_, '_, Editor>,
+        cx: &mut ViewContext<'_, Editor>,
     ) {
         if let Some(excerpt_hints) = self.hints.get(&excerpt_id) {
             let mut guard = excerpt_hints.write();
@@ -579,10 +579,10 @@ impl InlayHintCache {
 fn spawn_new_update_tasks(
     editor: &mut Editor,
     reason: &'static str,
-    excerpts_to_query: HashMap<ExcerptId, (ModelHandle<Buffer>, Global, Range<usize>)>,
+    excerpts_to_query: HashMap<ExcerptId, (Model<Buffer>, Global, Range<usize>)>,
     invalidate: InvalidationStrategy,
     update_cache_version: usize,
-    cx: &mut ViewContext<'_, '_, Editor>,
+    cx: &mut ViewContext<'_, Editor>,
 ) {
     let visible_hints = Arc::new(editor.visible_inlay_hints(cx));
     for (excerpt_id, (excerpt_buffer, new_task_buffer_version, excerpt_visible_range)) in
@@ -684,7 +684,7 @@ impl QueryRanges {
 fn determine_query_ranges(
     multi_buffer: &mut MultiBuffer,
     excerpt_id: ExcerptId,
-    excerpt_buffer: &ModelHandle<Buffer>,
+    excerpt_buffer: &Model<Buffer>,
     excerpt_visible_range: Range<usize>,
     cx: &mut ModelContext<'_, MultiBuffer>,
 ) -> Option<QueryRanges> {
@@ -760,7 +760,7 @@ fn new_update_task(
     visible_hints: Arc<Vec<Inlay>>,
     cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>,
     lsp_request_limiter: Arc<Semaphore>,
-    cx: &mut ViewContext<'_, '_, Editor>,
+    cx: &mut ViewContext<'_, Editor>,
 ) -> Task<()> {
     cx.spawn(|editor, mut cx| async move {
         let closure_cx = cx.clone();
@@ -789,7 +789,7 @@ fn new_update_task(
         ))
         .await;
 
-        let hint_delay = cx.background().timer(Duration::from_millis(
+        let hint_delay = cx.background_executor().timer(Duration::from_millis(
             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS,
         ));
 
@@ -837,7 +837,7 @@ fn new_update_task(
 }
 
 async fn fetch_and_update_hints(
-    editor: gpui::WeakViewHandle<Editor>,
+    editor: gpui::WeakView<Editor>,
     multi_buffer_snapshot: MultiBufferSnapshot,
     buffer_snapshot: BufferSnapshot,
     visible_hints: Arc<Vec<Inlay>>,
@@ -846,7 +846,7 @@ async fn fetch_and_update_hints(
     invalidate: bool,
     fetch_range: Range<language::Anchor>,
     lsp_request_limiter: Arc<Semaphore>,
-    mut cx: gpui::AsyncAppContext,
+    mut cx: gpui::AsyncWindowContext,
 ) -> anyhow::Result<()> {
     let (lsp_request_guard, got_throttled) = if query.invalidate.should_invalidate() {
         (None, false)
@@ -927,7 +927,7 @@ async fn fetch_and_update_hints(
     let background_task_buffer_snapshot = buffer_snapshot.clone();
     let backround_fetch_range = fetch_range.clone();
     let new_update = cx
-        .background()
+        .background_executor()
         .spawn(async move {
             calculate_hint_updates(
                 query.excerpt_id,
@@ -1071,7 +1071,7 @@ fn apply_hint_update(
     invalidate: bool,
     buffer_snapshot: BufferSnapshot,
     multi_buffer_snapshot: MultiBufferSnapshot,
-    cx: &mut ViewContext<'_, '_, Editor>,
+    cx: &mut ViewContext<'_, Editor>,
 ) {
     let cached_excerpt_hints = editor
         .inlay_hint_cache
@@ -1200,11 +1200,10 @@ pub mod tests {
 
     use crate::{
         scroll::{autoscroll::Autoscroll, scroll_amount::ScrollAmount},
-        serde_json::json,
         ExcerptRange,
     };
     use futures::StreamExt;
-    use gpui::{executor::Deterministic, TestAppContext, ViewHandle};
+    use gpui::{Context, TestAppContext, WindowHandle};
     use itertools::Itertools;
     use language::{
         language_settings::AllLanguageSettingsContent, FakeLspAdapter, Language, LanguageConfig,
@@ -1212,9 +1211,9 @@ pub mod tests {
     use lsp::FakeLanguageServer;
     use parking_lot::Mutex;
     use project::{FakeFs, Project};
+    use serde_json::json;
     use settings::SettingsStore;
     use text::{Point, ToPoint};
-    use workspace::Workspace;
 
     use crate::editor_tests::update_test_language_settings;
 
@@ -1270,10 +1269,10 @@ pub mod tests {
             })
             .next()
             .await;
-        cx.foreground().run_until_parked();
+        cx.executor().run_until_parked();
 
         let mut edits_made = 1;
-        editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, cx| {
             let expected_hints = vec!["0".to_string()];
             assert_eq!(
                 expected_hints,
@@ -1292,13 +1291,13 @@ pub mod tests {
             );
         });
 
-        editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, cx| {
             editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
             editor.handle_input("some change", cx);
             edits_made += 1;
         });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
+        cx.executor().run_until_parked();
+        _ = editor.update(cx, |editor, cx| {
             let expected_hints = vec!["0".to_string(), "1".to_string()];
             assert_eq!(
                 expected_hints,
@@ -1322,8 +1321,8 @@ pub mod tests {
             .await
             .expect("inlay refresh request failed");
         edits_made += 1;
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
+        cx.executor().run_until_parked();
+        _ = editor.update(cx, |editor, cx| {
             let expected_hints = vec!["0".to_string(), "1".to_string(), "2".to_string()];
             assert_eq!(
                 expected_hints,
@@ -1380,10 +1379,10 @@ pub mod tests {
             })
             .next()
             .await;
-        cx.foreground().run_until_parked();
+        cx.executor().run_until_parked();
 
         let mut edits_made = 1;
-        editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, cx| {
             let expected_hints = vec!["0".to_string()];
             assert_eq!(
                 expected_hints,
@@ -1405,16 +1404,16 @@ pub mod tests {
             })
             .await
             .expect("work done progress create request failed");
-        cx.foreground().run_until_parked();
+        cx.executor().run_until_parked();
         fake_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
             token: lsp::ProgressToken::String(progress_token.to_string()),
             value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
                 lsp::WorkDoneProgressBegin::default(),
             )),
         });
-        cx.foreground().run_until_parked();
+        cx.executor().run_until_parked();
 
-        editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, cx| {
             let expected_hints = vec!["0".to_string()];
             assert_eq!(
                 expected_hints,
@@ -1435,10 +1434,10 @@ pub mod tests {
                 lsp::WorkDoneProgressEnd::default(),
             )),
         });
-        cx.foreground().run_until_parked();
+        cx.executor().run_until_parked();
 
         edits_made += 1;
-        editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, cx| {
             let expected_hints = vec!["1".to_string()];
             assert_eq!(
                 expected_hints,
@@ -1465,7 +1464,7 @@ pub mod tests {
             })
         });
 
-        let fs = FakeFs::new(cx.background());
+        let fs = FakeFs::new(cx.background_executor.clone());
         fs.insert_tree(
                     "/a",
                     json!({
@@ -1475,14 +1474,6 @@ pub mod tests {
                 )
                 .await;
         let project = Project::test(fs, ["/a".as_ref()], cx).await;
-        let workspace = cx
-            .add_window(|cx| Workspace::test_new(project.clone(), cx))
-            .root(cx);
-        let worktree_id = workspace.update(cx, |workspace, cx| {
-            workspace.project().read_with(cx, |project, cx| {
-                project.worktrees(cx).next().unwrap().read(cx).id()
-            })
-        });
 
         let mut rs_fake_servers = None;
         let mut md_fake_servers = None;
@@ -1515,23 +1506,17 @@ pub mod tests {
             });
         }
 
-        let _rs_buffer = project
+        let rs_buffer = project
             .update(cx, |project, cx| {
                 project.open_local_buffer("/a/main.rs", cx)
             })
             .await
             .unwrap();
-        cx.foreground().run_until_parked();
-        cx.foreground().start_waiting();
+        cx.executor().run_until_parked();
+        cx.executor().start_waiting();
         let rs_fake_server = rs_fake_servers.unwrap().next().await.unwrap();
-        let rs_editor = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-            })
-            .await
-            .unwrap()
-            .downcast::<Editor>()
-            .unwrap();
+        let rs_editor =
+            cx.add_window(|cx| Editor::for_buffer(rs_buffer, Some(project.clone()), cx));
         let rs_lsp_request_count = Arc::new(AtomicU32::new(0));
         rs_fake_server
             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
@@ -1556,8 +1541,8 @@ pub mod tests {
             })
             .next()
             .await;
-        cx.foreground().run_until_parked();
-        rs_editor.update(cx, |editor, cx| {
+        cx.executor().run_until_parked();
+        _ = rs_editor.update(cx, |editor, cx| {
             let expected_hints = vec!["0".to_string()];
             assert_eq!(
                 expected_hints,
@@ -1572,24 +1557,17 @@ pub mod tests {
             );
         });
 
-        cx.foreground().run_until_parked();
-        let _md_buffer = project
+        cx.executor().run_until_parked();
+        let md_buffer = project
             .update(cx, |project, cx| {
                 project.open_local_buffer("/a/other.md", cx)
             })
             .await
             .unwrap();
-        cx.foreground().run_until_parked();
-        cx.foreground().start_waiting();
+        cx.executor().run_until_parked();
+        cx.executor().start_waiting();
         let md_fake_server = md_fake_servers.unwrap().next().await.unwrap();
-        let md_editor = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_path((worktree_id, "other.md"), None, true, cx)
-            })
-            .await
-            .unwrap()
-            .downcast::<Editor>()
-            .unwrap();
+        let md_editor = cx.add_window(|cx| Editor::for_buffer(md_buffer, Some(project), cx));
         let md_lsp_request_count = Arc::new(AtomicU32::new(0));
         md_fake_server
             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
@@ -1614,8 +1592,8 @@ pub mod tests {
             })
             .next()
             .await;
-        cx.foreground().run_until_parked();
-        md_editor.update(cx, |editor, cx| {
+        cx.executor().run_until_parked();
+        _ = md_editor.update(cx, |editor, cx| {
             let expected_hints = vec!["0".to_string()];
             assert_eq!(
                 expected_hints,
@@ -1626,12 +1604,12 @@ pub mod tests {
             assert_eq!(editor.inlay_hint_cache().version, 1);
         });
 
-        rs_editor.update(cx, |editor, cx| {
+        _ = rs_editor.update(cx, |editor, cx| {
             editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
             editor.handle_input("some rs change", cx);
         });
-        cx.foreground().run_until_parked();
-        rs_editor.update(cx, |editor, cx| {
+        cx.executor().run_until_parked();
+        _ = rs_editor.update(cx, |editor, cx| {
             let expected_hints = vec!["1".to_string()];
             assert_eq!(
                 expected_hints,
@@ -1645,7 +1623,7 @@ pub mod tests {
                 "Every time hint cache changes, cache version should be incremented"
             );
         });
-        md_editor.update(cx, |editor, cx| {
+        _ = md_editor.update(cx, |editor, cx| {
             let expected_hints = vec!["0".to_string()];
             assert_eq!(
                 expected_hints,
@@ -1656,12 +1634,12 @@ pub mod tests {
             assert_eq!(editor.inlay_hint_cache().version, 1);
         });
 
-        md_editor.update(cx, |editor, cx| {
+        _ = md_editor.update(cx, |editor, cx| {
             editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
             editor.handle_input("some md change", cx);
         });
-        cx.foreground().run_until_parked();
-        md_editor.update(cx, |editor, cx| {
+        cx.executor().run_until_parked();
+        _ = md_editor.update(cx, |editor, cx| {
             let expected_hints = vec!["1".to_string()];
             assert_eq!(
                 expected_hints,
@@ -1671,7 +1649,7 @@ pub mod tests {
             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
             assert_eq!(editor.inlay_hint_cache().version, 2);
         });
-        rs_editor.update(cx, |editor, cx| {
+        _ = rs_editor.update(cx, |editor, cx| {
             let expected_hints = vec!["1".to_string()];
             assert_eq!(
                 expected_hints,
@@ -1743,10 +1721,10 @@ pub mod tests {
             })
             .next()
             .await;
-        cx.foreground().run_until_parked();
+        cx.executor().run_until_parked();
 
         let mut edits_made = 1;
-        editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, cx| {
             assert_eq!(
                 lsp_request_count.load(Ordering::Relaxed),
                 1,
@@ -1780,8 +1758,8 @@ pub mod tests {
             .request::<lsp::request::InlayHintRefreshRequest>(())
             .await
             .expect("inlay refresh request failed");
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
+        cx.executor().run_until_parked();
+        _ = editor.update(cx, |editor, cx| {
             assert_eq!(
                 lsp_request_count.load(Ordering::Relaxed),
                 2,
@@ -1852,8 +1830,8 @@ pub mod tests {
                     show_other_hints: new_allowed_hint_kinds.contains(&None),
                 })
             });
-            cx.foreground().run_until_parked();
-            editor.update(cx, |editor, cx| {
+            cx.executor().run_until_parked();
+            _ = editor.update(cx, |editor, cx| {
                 assert_eq!(
                     lsp_request_count.load(Ordering::Relaxed),
                     2,
@@ -1896,8 +1874,8 @@ pub mod tests {
                 show_other_hints: another_allowed_hint_kinds.contains(&None),
             })
         });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
+        cx.executor().run_until_parked();
+        _ = editor.update(cx, |editor, cx| {
             assert_eq!(
                 lsp_request_count.load(Ordering::Relaxed),
                 2,
@@ -1926,8 +1904,8 @@ pub mod tests {
             .request::<lsp::request::InlayHintRefreshRequest>(())
             .await
             .expect("inlay refresh request failed");
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
+        cx.executor().run_until_parked();
+        _ = editor.update(cx, |editor, cx| {
             assert_eq!(
                 lsp_request_count.load(Ordering::Relaxed),
                 2,
@@ -1952,8 +1930,8 @@ pub mod tests {
                 show_other_hints: final_allowed_hint_kinds.contains(&None),
             })
         });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
+        cx.executor().run_until_parked();
+        _ = editor.update(cx, |editor, cx| {
             assert_eq!(
                 lsp_request_count.load(Ordering::Relaxed),
                 3,
@@ -1988,8 +1966,8 @@ pub mod tests {
             .request::<lsp::request::InlayHintRefreshRequest>(())
             .await
             .expect("inlay refresh request failed");
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
+        cx.executor().run_until_parked();
+        _ = editor.update(cx, |editor, cx| {
             assert_eq!(
                 lsp_request_count.load(Ordering::Relaxed),
                 4,
@@ -2056,16 +2034,16 @@ pub mod tests {
             "initial change #2",
             "initial change #3",
         ] {
-            editor.update(cx, |editor, cx| {
+            _ = editor.update(cx, |editor, cx| {
                 editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
                 editor.handle_input(change_after_opening, cx);
             });
             expected_changes.push(change_after_opening);
         }
 
-        cx.foreground().run_until_parked();
+        cx.executor().run_until_parked();
 
-        editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, cx| {
             let current_text = editor.text(cx);
             for change in &expected_changes {
                 assert!(
@@ -2099,18 +2077,17 @@ pub mod tests {
         ] {
             expected_changes.push(async_later_change);
             let task_editor = editor.clone();
-            let mut task_cx = cx.clone();
-            edits.push(cx.foreground().spawn(async move {
-                task_editor.update(&mut task_cx, |editor, cx| {
+            edits.push(cx.spawn(|mut cx| async move {
+                _ = task_editor.update(&mut cx, |editor, cx| {
                     editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
                     editor.handle_input(async_later_change, cx);
                 });
             }));
         }
         let _ = future::join_all(edits).await;
-        cx.foreground().run_until_parked();
+        cx.executor().run_until_parked();
 
-        editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, cx| {
             let current_text = editor.text(cx);
             for change in &expected_changes {
                 assert!(
@@ -2166,7 +2143,7 @@ pub mod tests {
                 ..Default::default()
             }))
             .await;
-        let fs = FakeFs::new(cx.background());
+        let fs = FakeFs::new(cx.background_executor.clone());
         fs.insert_tree(
             "/a",
             json!({
@@ -2177,32 +2154,16 @@ pub mod tests {
         .await;
         let project = Project::test(fs, ["/a".as_ref()], cx).await;
         project.update(cx, |project, _| project.languages().add(Arc::new(language)));
-        let workspace = cx
-            .add_window(|cx| Workspace::test_new(project.clone(), cx))
-            .root(cx);
-        let worktree_id = workspace.update(cx, |workspace, cx| {
-            workspace.project().read_with(cx, |project, cx| {
-                project.worktrees(cx).next().unwrap().read(cx).id()
-            })
-        });
-
-        let _buffer = project
+        let buffer = project
             .update(cx, |project, cx| {
                 project.open_local_buffer("/a/main.rs", cx)
             })
             .await
             .unwrap();
-        cx.foreground().run_until_parked();
-        cx.foreground().start_waiting();
+        cx.executor().run_until_parked();
+        cx.executor().start_waiting();
         let fake_server = fake_servers.next().await.unwrap();
-        let editor = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-            })
-            .await
-            .unwrap()
-            .downcast::<Editor>()
-            .unwrap();
+        let editor = cx.add_window(|cx| Editor::for_buffer(buffer, Some(project), cx));
         let lsp_request_ranges = Arc::new(Mutex::new(Vec::new()));
         let lsp_request_count = Arc::new(AtomicUsize::new(0));
         let closure_lsp_request_ranges = Arc::clone(&lsp_request_ranges);
@@ -2233,13 +2194,16 @@ pub mod tests {
             })
             .next()
             .await;
+
         fn editor_visible_range(
-            editor: &ViewHandle<Editor>,
+            editor: &WindowHandle<Editor>,
             cx: &mut gpui::TestAppContext,
         ) -> Range<Point> {
-            let ranges = editor.update(cx, |editor, cx| {
-                editor.excerpts_for_inlay_hints_query(None, cx)
-            });
+            let ranges = editor
+                .update(cx, |editor, cx| {
+                    editor.excerpts_for_inlay_hints_query(None, cx)
+                })
+                .unwrap();
             assert_eq!(
                 ranges.len(),
                 1,
@@ -2262,10 +2226,10 @@ pub mod tests {
         // in large buffers, requests are made for more than visible range of a buffer.
         // invisible parts are queried later, to avoid excessive requests on quick typing.
         // wait the timeout needed to get all requests.
-        cx.foreground().advance_clock(Duration::from_millis(
+        cx.executor().advance_clock(Duration::from_millis(
             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
         ));
-        cx.foreground().run_until_parked();
+        cx.executor().run_until_parked();
         let initial_visible_range = editor_visible_range(&editor, cx);
         let lsp_initial_visible_range = lsp::Range::new(
             lsp::Position::new(
@@ -2281,7 +2245,7 @@ pub mod tests {
             lsp::Position::new(initial_visible_range.end.row * 2, 2);
         let mut expected_invisible_query_start = lsp_initial_visible_range.end;
         expected_invisible_query_start.character += 1;
-        editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, cx| {
             let ranges = lsp_request_ranges.lock().drain(..).collect::<Vec<_>>();
             assert_eq!(ranges.len(), 2,
                 "When scroll is at the edge of a big document, its visible part and the same range further should be queried in order, but got: {ranges:?}");
@@ -2308,39 +2272,41 @@ pub mod tests {
             );
         });
 
-        editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, cx| {
             editor.scroll_screen(&ScrollAmount::Page(1.0), cx);
             editor.scroll_screen(&ScrollAmount::Page(1.0), cx);
         });
-        cx.foreground().advance_clock(Duration::from_millis(
+        cx.executor().advance_clock(Duration::from_millis(
             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
         ));
-        cx.foreground().run_until_parked();
+        cx.executor().run_until_parked();
         let visible_range_after_scrolls = editor_visible_range(&editor, cx);
-        let visible_line_count =
-            editor.update(cx, |editor, _| editor.visible_line_count().unwrap());
-        let selection_in_cached_range = editor.update(cx, |editor, cx| {
-            let ranges = lsp_request_ranges
-                .lock()
-                .drain(..)
-                .sorted_by_key(|r| r.start)
-                .collect::<Vec<_>>();
-            assert_eq!(
-                ranges.len(),
-                2,
-                "Should query 2 ranges after both scrolls, but got: {ranges:?}"
-            );
-            let first_scroll = &ranges[0];
-            let second_scroll = &ranges[1];
-            assert_eq!(
-                first_scroll.end, second_scroll.start,
-                "Should query 2 adjacent ranges after the scrolls, but got: {ranges:?}"
-            );
-            assert_eq!(
+        let visible_line_count = editor
+            .update(cx, |editor, _| editor.visible_line_count().unwrap())
+            .unwrap();
+        let selection_in_cached_range = editor
+            .update(cx, |editor, cx| {
+                let ranges = lsp_request_ranges
+                    .lock()
+                    .drain(..)
+                    .sorted_by_key(|r| r.start)
+                    .collect::<Vec<_>>();
+                assert_eq!(
+                    ranges.len(),
+                    2,
+                    "Should query 2 ranges after both scrolls, but got: {ranges:?}"
+                );
+                let first_scroll = &ranges[0];
+                let second_scroll = &ranges[1];
+                assert_eq!(
+                    first_scroll.end, second_scroll.start,
+                    "Should query 2 adjacent ranges after the scrolls, but got: {ranges:?}"
+                );
+                assert_eq!(
                 first_scroll.start, expected_initial_query_range_end,
                 "First scroll should start the query right after the end of the original scroll",
             );
-            assert_eq!(
+                assert_eq!(
                 second_scroll.end,
                 lsp::Position::new(
                     visible_range_after_scrolls.end.row
@@ -2350,41 +2316,42 @@ pub mod tests {
                 "Second scroll should query one more screen down after the end of the visible range"
             );
 
-            let lsp_requests = lsp_request_count.load(Ordering::Acquire);
-            assert_eq!(lsp_requests, 4, "Should query for hints after every scroll");
-            let expected_hints = vec![
-                "1".to_string(),
-                "2".to_string(),
-                "3".to_string(),
-                "4".to_string(),
-            ];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should have hints from the new LSP response after the edit"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                lsp_requests,
-                "Should update the cache for every LSP response with hints added"
-            );
+                let lsp_requests = lsp_request_count.load(Ordering::Acquire);
+                assert_eq!(lsp_requests, 4, "Should query for hints after every scroll");
+                let expected_hints = vec![
+                    "1".to_string(),
+                    "2".to_string(),
+                    "3".to_string(),
+                    "4".to_string(),
+                ];
+                assert_eq!(
+                    expected_hints,
+                    cached_hint_labels(editor),
+                    "Should have hints from the new LSP response after the edit"
+                );
+                assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+                assert_eq!(
+                    editor.inlay_hint_cache().version,
+                    lsp_requests,
+                    "Should update the cache for every LSP response with hints added"
+                );
 
-            let mut selection_in_cached_range = visible_range_after_scrolls.end;
-            selection_in_cached_range.row -= visible_line_count.ceil() as u32;
-            selection_in_cached_range
-        });
+                let mut selection_in_cached_range = visible_range_after_scrolls.end;
+                selection_in_cached_range.row -= visible_line_count.ceil() as u32;
+                selection_in_cached_range
+            })
+            .unwrap();
 
-        editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, cx| {
             editor.change_selections(Some(Autoscroll::center()), cx, |s| {
                 s.select_ranges([selection_in_cached_range..selection_in_cached_range])
             });
         });
-        cx.foreground().advance_clock(Duration::from_millis(
+        cx.executor().advance_clock(Duration::from_millis(
             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
         ));
-        cx.foreground().run_until_parked();
-        editor.update(cx, |_, _| {
+        cx.executor().run_until_parked();
+        _ = editor.update(cx, |_, _| {
             let ranges = lsp_request_ranges
                 .lock()
                 .drain(..)
@@ -2394,14 +2361,14 @@ pub mod tests {
             assert_eq!(lsp_request_count.load(Ordering::Acquire), 4);
         });
 
-        editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, cx| {
             editor.handle_input("++++more text++++", cx);
         });
-        cx.foreground().advance_clock(Duration::from_millis(
+        cx.executor().advance_clock(Duration::from_millis(
             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
         ));
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
+        cx.executor().run_until_parked();
+        _ = editor.update(cx, |editor, cx| {
             let mut ranges = lsp_request_ranges.lock().drain(..).collect::<Vec<_>>();
             ranges.sort_by_key(|r| r.start);
 
@@ -2434,10 +2401,7 @@ pub mod tests {
     }
 
     #[gpui::test(iterations = 10)]
-    async fn test_multiple_excerpts_large_multibuffer(
-        deterministic: Arc<Deterministic>,
-        cx: &mut gpui::TestAppContext,
-    ) {
+    async fn test_multiple_excerpts_large_multibuffer(cx: &mut gpui::TestAppContext) {
         init_test(cx, |settings| {
             settings.defaults.inlay_hints = Some(InlayHintSettings {
                 enabled: true,
@@ -2465,26 +2429,21 @@ pub mod tests {
             }))
             .await;
         let language = Arc::new(language);
-        let fs = FakeFs::new(cx.background());
+        let fs = FakeFs::new(cx.background_executor.clone());
         fs.insert_tree(
-            "/a",
-            json!({
-                "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")),
-                "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")),
-            }),
-        )
-        .await;
+                "/a",
+                json!({
+                    "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")),
+                    "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")),
+                }),
+            )
+            .await;
         let project = Project::test(fs, ["/a".as_ref()], cx).await;
         project.update(cx, |project, _| {
             project.languages().add(Arc::clone(&language))
         });
-        let workspace = cx
-            .add_window(|cx| Workspace::test_new(project.clone(), cx))
-            .root(cx);
-        let worktree_id = workspace.update(cx, |workspace, cx| {
-            workspace.project().read_with(cx, |project, cx| {
-                project.worktrees(cx).next().unwrap().read(cx).id()
-            })
+        let worktree_id = project.update(cx, |project, cx| {
+            project.worktrees().next().unwrap().read(cx).id()
         });
 
         let buffer_1 = project
@@ -2499,7 +2458,7 @@ pub mod tests {
             })
             .await
             .unwrap();
-        let multibuffer = cx.add_model(|cx| {
+        let multibuffer = cx.new_model(|cx| {
             let mut multibuffer = MultiBuffer::new(0);
             multibuffer.push_excerpts(
                 buffer_1.clone(),
@@ -2564,11 +2523,9 @@ pub mod tests {
             multibuffer
         });
 
-        deterministic.run_until_parked();
-        cx.foreground().run_until_parked();
-        let editor = cx
-            .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx))
-            .root(cx);
+        cx.executor().run_until_parked();
+        let editor =
+            cx.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx));
         let editor_edited = Arc::new(AtomicBool::new(false));
         let fake_server = fake_servers.next().await.unwrap();
         let closure_editor_edited = Arc::clone(&editor_edited);
@@ -2637,25 +2594,27 @@ pub mod tests {
             })
             .next()
             .await;
-        cx.foreground().run_until_parked();
-
-        editor.update(cx, |editor, cx| {
-            let expected_hints = vec![
-                "main hint #0".to_string(),
-                "main hint #1".to_string(),
-                "main hint #2".to_string(),
-                "main hint #3".to_string(),
-            ];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "When scroll is at the edge of a multibuffer, its visible excerpts only should be queried for inlay hints"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(), "Every visible excerpt hints should bump the verison");
-        });
+        cx.executor().run_until_parked();
+
+        _ = editor.update(cx, |editor, cx| {
+                let expected_hints = vec![
+                    "main hint #0".to_string(),
+                    "main hint #1".to_string(),
+                    "main hint #2".to_string(),
+                    "main hint #3".to_string(),
+                    "main hint #4".to_string(),
+                    "main hint #5".to_string(),
+                ];
+                assert_eq!(
+                    expected_hints,
+                    cached_hint_labels(editor),
+                    "When scroll is at the edge of a multibuffer, its visible excerpts only should be queried for inlay hints"
+                );
+                assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+                assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(), "Every visible excerpt hints should bump the verison");
+            });
 
-        editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, cx| {
             editor.change_selections(Some(Autoscroll::Next), cx, |s| {
                 s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
             });
@@ -2666,93 +2625,94 @@ pub mod tests {
                 s.select_ranges([Point::new(50, 0)..Point::new(50, 0)])
             });
         });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            let expected_hints = vec![
-                "main hint #0".to_string(),
-                "main hint #1".to_string(),
-                "main hint #2".to_string(),
-                "main hint #3".to_string(),
-                "main hint #4".to_string(),
-                "main hint #5".to_string(),
-                "other hint #0".to_string(),
-                "other hint #1".to_string(),
-                "other hint #2".to_string(),
-            ];
-            assert_eq!(expected_hints, cached_hint_labels(editor),
-                "With more scrolls of the multibuffer, more hints should be added into the cache and nothing invalidated without edits");
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(),
-                "Due to every excerpt having one hint, we update cache per new excerpt scrolled");
-        });
+        cx.executor().run_until_parked();
+        _ = editor.update(cx, |editor, cx| {
+                let expected_hints = vec![
+                    "main hint #0".to_string(),
+                    "main hint #1".to_string(),
+                    "main hint #2".to_string(),
+                    "main hint #3".to_string(),
+                    "main hint #4".to_string(),
+                    "main hint #5".to_string(),
+                    "other hint #0".to_string(),
+                    "other hint #1".to_string(),
+                    "other hint #2".to_string(),
+                ];
+                assert_eq!(expected_hints, cached_hint_labels(editor),
+                    "With more scrolls of the multibuffer, more hints should be added into the cache and nothing invalidated without edits");
+                assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+                assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(),
+                    "Due to every excerpt having one hint, we update cache per new excerpt scrolled");
+            });
 
-        editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, cx| {
             editor.change_selections(Some(Autoscroll::Next), cx, |s| {
                 s.select_ranges([Point::new(100, 0)..Point::new(100, 0)])
             });
         });
-        cx.foreground().advance_clock(Duration::from_millis(
+        cx.executor().advance_clock(Duration::from_millis(
             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
         ));
-        cx.foreground().run_until_parked();
+        cx.executor().run_until_parked();
         let last_scroll_update_version = editor.update(cx, |editor, cx| {
-            let expected_hints = vec![
-                "main hint #0".to_string(),
-                "main hint #1".to_string(),
-                "main hint #2".to_string(),
-                "main hint #3".to_string(),
-                "main hint #4".to_string(),
-                "main hint #5".to_string(),
-                "other hint #0".to_string(),
-                "other hint #1".to_string(),
-                "other hint #2".to_string(),
-                "other hint #3".to_string(),
-                "other hint #4".to_string(),
-                "other hint #5".to_string(),
-            ];
-            assert_eq!(expected_hints, cached_hint_labels(editor),
-                "After multibuffer was scrolled to the end, all hints for all excerpts should be fetched");
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, expected_hints.len());
-            expected_hints.len()
-        });
-
-        editor.update(cx, |editor, cx| {
+                let expected_hints = vec![
+                    "main hint #0".to_string(),
+                    "main hint #1".to_string(),
+                    "main hint #2".to_string(),
+                    "main hint #3".to_string(),
+                    "main hint #4".to_string(),
+                    "main hint #5".to_string(),
+                    "other hint #0".to_string(),
+                    "other hint #1".to_string(),
+                    "other hint #2".to_string(),
+                    "other hint #3".to_string(),
+                    "other hint #4".to_string(),
+                    "other hint #5".to_string(),
+                ];
+                assert_eq!(expected_hints, cached_hint_labels(editor),
+                    "After multibuffer was scrolled to the end, all hints for all excerpts should be fetched");
+                assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+                assert_eq!(editor.inlay_hint_cache().version, expected_hints.len());
+                expected_hints.len()
+            }).unwrap();
+
+        _ = editor.update(cx, |editor, cx| {
             editor.change_selections(Some(Autoscroll::Next), cx, |s| {
                 s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
             });
         });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            let expected_hints = vec![
-                "main hint #0".to_string(),
-                "main hint #1".to_string(),
-                "main hint #2".to_string(),
-                "main hint #3".to_string(),
-                "main hint #4".to_string(),
-                "main hint #5".to_string(),
-                "other hint #0".to_string(),
-                "other hint #1".to_string(),
-                "other hint #2".to_string(),
-                "other hint #3".to_string(),
-                "other hint #4".to_string(),
-                "other hint #5".to_string(),
-            ];
-            assert_eq!(expected_hints, cached_hint_labels(editor),
-                "After multibuffer was scrolled to the end, further scrolls up should not bring more hints");
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, last_scroll_update_version, "No updates should happen during scrolling already scolled buffer");
-        });
+        cx.executor().run_until_parked();
+        _ = editor.update(cx, |editor, cx| {
+                let expected_hints = vec![
+                    "main hint #0".to_string(),
+                    "main hint #1".to_string(),
+                    "main hint #2".to_string(),
+                    "main hint #3".to_string(),
+                    "main hint #4".to_string(),
+                    "main hint #5".to_string(),
+                    "other hint #0".to_string(),
+                    "other hint #1".to_string(),
+                    "other hint #2".to_string(),
+                    "other hint #3".to_string(),
+                    "other hint #4".to_string(),
+                    "other hint #5".to_string(),
+                ];
+                assert_eq!(expected_hints, cached_hint_labels(editor),
+                    "After multibuffer was scrolled to the end, further scrolls up should not bring more hints");
+                assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+                assert_eq!(editor.inlay_hint_cache().version, last_scroll_update_version, "No updates should happen during scrolling already scolled buffer");
+            });
 
         editor_edited.store(true, Ordering::Release);
-        editor.update(cx, |editor, cx| {
+        _ = editor.update(cx, |editor, cx| {
             editor.change_selections(None, cx, |s| {
-                s.select_ranges([Point::new(56, 0)..Point::new(56, 0)])
+                // TODO if this gets set to hint boundary (e.g. 56) we sometimes get an extra cache version bump, why?
+                s.select_ranges([Point::new(57, 0)..Point::new(57, 0)])
             });
             editor.handle_input("++++more text++++", cx);
         });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
+        cx.executor().run_until_parked();
+        _ = editor.update(cx, |editor, cx| {
             let expected_hints = vec![
                 "main hint(edited) #0".to_string(),
                 "main hint(edited) #1".to_string(),

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

@@ -1,16 +1,15 @@
 use crate::{
     editor_settings::SeedQuerySetting, link_go_to_definition::hide_link_definition,
-    persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor, EditorSettings, Event,
+    persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor, EditorEvent, EditorSettings,
     ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _,
 };
-use anyhow::{Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use collections::HashSet;
 use futures::future::try_join_all;
 use gpui::{
-    elements::*,
-    geometry::vector::{vec2f, Vector2F},
-    AppContext, AsyncAppContext, Entity, ModelHandle, Subscription, Task, View, ViewContext,
-    ViewHandle, WeakViewHandle,
+    div, point, AnyElement, AppContext, AsyncWindowContext, Context, Entity, EntityId,
+    EventEmitter, IntoElement, Model, ParentElement, Pixels, Render, SharedString, Styled,
+    Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
 };
 use language::{
     proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, CharKind, OffsetRangeExt,
@@ -18,27 +17,29 @@ use language::{
 };
 use project::{search::SearchQuery, FormatTrigger, Item as _, Project, ProjectPath};
 use rpc::proto::{self, update_view, PeerId};
-use smallvec::SmallVec;
+use settings::Settings;
+
+use std::fmt::Write;
 use std::{
     borrow::Cow,
     cmp::{self, Ordering},
-    fmt::Write,
     iter,
     ops::Range,
     path::{Path, PathBuf},
     sync::Arc,
 };
 use text::Selection;
-use util::{
-    paths::{PathExt, FILE_ROW_COLUMN_DELIMITER},
-    ResultExt, TryFutureExt,
+use theme::{ActiveTheme, Theme};
+use ui::{h_stack, prelude::*, Label};
+use util::{paths::PathExt, paths::FILE_ROW_COLUMN_DELIMITER, ResultExt, TryFutureExt};
+use workspace::{
+    item::{BreadcrumbText, FollowEvent, FollowableItemHandle},
+    StatusItemView,
 };
-use workspace::item::{BreadcrumbText, FollowableItemHandle, ItemHandle};
 use workspace::{
-    item::{FollowableItem, Item, ItemEvent, ProjectItem},
+    item::{FollowableItem, Item, ItemEvent, ItemHandle, ProjectItem},
     searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle},
-    ItemId, ItemNavHistory, Pane, StatusItemView, ToolbarItemLocation, ViewId, Workspace,
-    WorkspaceId,
+    ItemId, ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace, WorkspaceId,
 };
 
 pub const MAX_TAB_TITLE_LEN: usize = 24;
@@ -49,12 +50,12 @@ impl FollowableItem for Editor {
     }
 
     fn from_state_proto(
-        pane: ViewHandle<workspace::Pane>,
-        workspace: ViewHandle<Workspace>,
+        pane: View<workspace::Pane>,
+        workspace: View<Workspace>,
         remote_id: ViewId,
         state: &mut Option<proto::view::Variant>,
-        cx: &mut AppContext,
-    ) -> Option<Task<Result<ViewHandle<Self>>>> {
+        cx: &mut WindowContext,
+    ) -> Option<Task<Result<View<Self>>>> {
         let project = workspace.read(cx).project().to_owned();
         let Some(proto::view::Variant::Editor(_)) = state else {
             return None;
@@ -80,7 +81,7 @@ impl FollowableItem for Editor {
         let pane = pane.downgrade();
         Some(cx.spawn(|mut cx| async move {
             let mut buffers = futures::future::try_join_all(buffers).await?;
-            let editor = pane.read_with(&cx, |pane, cx| {
+            let editor = pane.update(&mut cx, |pane, cx| {
                 let mut editors = pane.items_of_type::<Self>();
                 editors.find(|editor| {
                     let ids_match = editor.remote_id(&client, cx) == Some(remote_id);
@@ -95,7 +96,7 @@ impl FollowableItem for Editor {
                 editor
             } else {
                 pane.update(&mut cx, |_, cx| {
-                    let multibuffer = cx.add_model(|cx| {
+                    let multibuffer = cx.new_model(|cx| {
                         let mut multibuffer;
                         if state.singleton && buffers.len() == 1 {
                             multibuffer = MultiBuffer::singleton(buffers.pop().unwrap(), cx)
@@ -128,7 +129,7 @@ impl FollowableItem for Editor {
                         multibuffer
                     });
 
-                    cx.add_view(|cx| {
+                    cx.new_view(|cx| {
                         let mut editor =
                             Editor::for_multibuffer(multibuffer, Some(project.clone()), cx);
                         editor.remote_id = Some(remote_id);
@@ -162,22 +163,20 @@ impl FollowableItem for Editor {
             self.buffer.update(cx, |buffer, cx| {
                 buffer.remove_active_selections(cx);
             });
-        } else {
+        } else if self.focus_handle.is_focused(cx) {
             self.buffer.update(cx, |buffer, cx| {
-                if self.focused {
-                    buffer.set_active_selections(
-                        &self.selections.disjoint_anchors(),
-                        self.selections.line_mode,
-                        self.cursor_shape,
-                        cx,
-                    );
-                }
+                buffer.set_active_selections(
+                    &self.selections.disjoint_anchors(),
+                    self.selections.line_mode,
+                    self.cursor_shape,
+                    cx,
+                );
             });
         }
         cx.notify();
     }
 
-    fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant> {
+    fn to_state_proto(&self, cx: &WindowContext) -> Option<proto::view::Variant> {
         let buffer = self.buffer.read(cx);
         let scroll_anchor = self.scroll_manager.anchor();
         let excerpts = buffer
@@ -204,8 +203,8 @@ impl FollowableItem for Editor {
             title: (!buffer.is_singleton()).then(|| buffer.title(cx).into()),
             excerpts,
             scroll_top_anchor: Some(serialize_anchor(&scroll_anchor.anchor)),
-            scroll_x: scroll_anchor.offset.x(),
-            scroll_y: scroll_anchor.offset.y(),
+            scroll_x: scroll_anchor.offset.x,
+            scroll_y: scroll_anchor.offset.y,
             selections: self
                 .selections
                 .disjoint_anchors()
@@ -220,18 +219,33 @@ impl FollowableItem for Editor {
         }))
     }
 
+    fn to_follow_event(event: &EditorEvent) -> Option<workspace::item::FollowEvent> {
+        match event {
+            EditorEvent::Edited => Some(FollowEvent::Unfollow),
+            EditorEvent::SelectionsChanged { local }
+            | EditorEvent::ScrollPositionChanged { local, .. } => {
+                if *local {
+                    Some(FollowEvent::Unfollow)
+                } else {
+                    None
+                }
+            }
+            _ => None,
+        }
+    }
+
     fn add_event_to_update_proto(
         &self,
-        event: &Self::Event,
+        event: &EditorEvent,
         update: &mut Option<proto::update_view::Variant>,
-        cx: &AppContext,
+        cx: &WindowContext,
     ) -> bool {
         let update =
             update.get_or_insert_with(|| proto::update_view::Variant::Editor(Default::default()));
 
         match update {
             proto::update_view::Variant::Editor(update) => match event {
-                Event::ExcerptsAdded {
+                EditorEvent::ExcerptsAdded {
                     buffer,
                     predecessor,
                     excerpts,
@@ -252,20 +266,20 @@ impl FollowableItem for Editor {
                     }
                     true
                 }
-                Event::ExcerptsRemoved { ids } => {
+                EditorEvent::ExcerptsRemoved { ids } => {
                     update
                         .deleted_excerpts
                         .extend(ids.iter().map(ExcerptId::to_proto));
                     true
                 }
-                Event::ScrollPositionChanged { .. } => {
+                EditorEvent::ScrollPositionChanged { .. } => {
                     let scroll_anchor = self.scroll_manager.anchor();
                     update.scroll_top_anchor = Some(serialize_anchor(&scroll_anchor.anchor));
-                    update.scroll_x = scroll_anchor.offset.x();
-                    update.scroll_y = scroll_anchor.offset.y();
+                    update.scroll_x = scroll_anchor.offset.x;
+                    update.scroll_y = scroll_anchor.offset.y;
                     true
                 }
-                Event::SelectionsChanged { .. } => {
+                EditorEvent::SelectionsChanged { .. } => {
                     update.selections = self
                         .selections
                         .disjoint_anchors()
@@ -286,7 +300,7 @@ impl FollowableItem for Editor {
 
     fn apply_update_proto(
         &mut self,
-        project: &ModelHandle<Project>,
+        project: &Model<Project>,
         message: update_view::Variant,
         cx: &mut ViewContext<Self>,
     ) -> Task<Result<()>> {
@@ -297,25 +311,16 @@ impl FollowableItem for Editor {
         })
     }
 
-    fn should_unfollow_on_event(event: &Self::Event, _: &AppContext) -> bool {
-        match event {
-            Event::Edited => true,
-            Event::SelectionsChanged { local } => *local,
-            Event::ScrollPositionChanged { local, .. } => *local,
-            _ => false,
-        }
-    }
-
-    fn is_project_item(&self, _cx: &AppContext) -> bool {
+    fn is_project_item(&self, _cx: &WindowContext) -> bool {
         true
     }
 }
 
 async fn update_editor_from_message(
-    this: WeakViewHandle<Editor>,
-    project: ModelHandle<Project>,
+    this: WeakView<Editor>,
+    project: Model<Project>,
     message: proto::update_view::Editor,
-    cx: &mut AsyncAppContext,
+    cx: &mut AsyncWindowContext,
 ) -> Result<()> {
     // Open all of the buffers of which excerpts were added to the editor.
     let inserted_excerpt_buffer_ids = message
@@ -328,7 +333,7 @@ async fn update_editor_from_message(
             .into_iter()
             .map(|id| project.open_buffer_by_id(id, cx))
             .collect::<Vec<_>>()
-    });
+    })?;
     let _inserted_excerpt_buffers = try_join_all(inserted_excerpt_buffers).await?;
 
     // Update the editor's excerpts.
@@ -353,7 +358,7 @@ async fn update_editor_from_message(
                     continue;
                 };
                 let buffer_id = excerpt.buffer_id;
-                let Some(buffer) = project.read(cx).buffer_for_id(buffer_id, cx) else {
+                let Some(buffer) = project.read(cx).buffer_for_id(buffer_id) else {
                     continue;
                 };
 
@@ -430,7 +435,7 @@ async fn update_editor_from_message(
             editor.set_scroll_anchor_remote(
                 ScrollAnchor {
                     anchor: scroll_top_anchor,
-                    offset: vec2f(message.scroll_x, message.scroll_y),
+                    offset: point(message.scroll_x, message.scroll_y),
                 },
                 cx,
             );
@@ -516,6 +521,8 @@ fn deserialize_anchor(buffer: &MultiBufferSnapshot, anchor: proto::EditorAnchor)
 }
 
 impl Item for Editor {
+    type Event = EditorEvent;
+
     fn navigate(&mut self, data: Box<dyn std::any::Any>, cx: &mut ViewContext<Self>) -> bool {
         if let Ok(data) = data.downcast::<NavigationData>() {
             let newest_selection = self.selections.newest::<Point>(cx);
@@ -551,7 +558,7 @@ impl Item for Editor {
         }
     }
 
-    fn tab_tooltip_text(&self, cx: &AppContext) -> Option<Cow<str>> {
+    fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString> {
         let file_path = self
             .buffer()
             .read(cx)
@@ -566,53 +573,66 @@ impl Item for Editor {
         Some(file_path.into())
     }
 
-    fn tab_description<'a>(&'a self, detail: usize, cx: &'a AppContext) -> Option<Cow<str>> {
-        match path_for_buffer(&self.buffer, detail, true, cx)? {
-            Cow::Borrowed(path) => Some(path.to_string_lossy()),
-            Cow::Owned(path) => Some(path.to_string_lossy().to_string().into()),
-        }
+    fn tab_description<'a>(&self, detail: usize, cx: &'a AppContext) -> Option<SharedString> {
+        let path = path_for_buffer(&self.buffer, detail, true, cx)?;
+        Some(path.to_string_lossy().to_string().into())
     }
 
-    fn tab_content<T: 'static>(
-        &self,
-        detail: Option<usize>,
-        style: &theme::Tab,
-        cx: &AppContext,
-    ) -> AnyElement<T> {
-        Flex::row()
-            .with_child(Label::new(self.title(cx).to_string(), style.label.clone()).into_any())
-            .with_children(detail.and_then(|detail| {
-                let path = path_for_buffer(&self.buffer, detail, false, cx)?;
-                let description = path.to_string_lossy();
-                Some(
-                    Label::new(
-                        util::truncate_and_trailoff(&description, MAX_TAB_TITLE_LEN),
-                        style.description.text.clone(),
-                    )
-                    .contained()
-                    .with_style(style.description.container)
-                    .aligned(),
-                )
+    fn tab_content(&self, detail: Option<usize>, selected: bool, cx: &WindowContext) -> AnyElement {
+        let _theme = cx.theme();
+
+        let description = detail.and_then(|detail| {
+            let path = path_for_buffer(&self.buffer, detail, false, cx)?;
+            let description = path.to_string_lossy();
+            let description = description.trim();
+
+            if description.is_empty() {
+                return None;
+            }
+
+            Some(util::truncate_and_trailoff(&description, MAX_TAB_TITLE_LEN))
+        });
+
+        h_stack()
+            .gap_2()
+            .child(Label::new(self.title(cx).to_string()).color(if selected {
+                Color::Default
+            } else {
+                Color::Muted
             }))
-            .align_children_center()
-            .into_any()
+            .when_some(description, |this, description| {
+                this.child(
+                    Label::new(description)
+                        .size(LabelSize::XSmall)
+                        .color(Color::Muted),
+                )
+            })
+            .into_any_element()
     }
 
-    fn for_each_project_item(&self, cx: &AppContext, f: &mut dyn FnMut(usize, &dyn project::Item)) {
+    fn for_each_project_item(
+        &self,
+        cx: &AppContext,
+        f: &mut dyn FnMut(EntityId, &dyn project::Item),
+    ) {
         self.buffer
             .read(cx)
-            .for_each_buffer(|buffer| f(buffer.id(), buffer.read(cx)));
+            .for_each_buffer(|buffer| f(buffer.entity_id(), buffer.read(cx)));
     }
 
     fn is_singleton(&self, cx: &AppContext) -> bool {
         self.buffer.read(cx).is_singleton()
     }
 
-    fn clone_on_split(&self, _workspace_id: WorkspaceId, cx: &mut ViewContext<Self>) -> Option<Self>
+    fn clone_on_split(
+        &self,
+        _workspace_id: WorkspaceId,
+        cx: &mut ViewContext<Self>,
+    ) -> Option<View<Editor>>
     where
         Self: Sized,
     {
-        Some(self.clone(cx))
+        Some(cx.new_view(|cx| self.clone(cx)))
     }
 
     fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
@@ -646,11 +666,7 @@ impl Item for Editor {
         }
     }
 
-    fn save(
-        &mut self,
-        project: ModelHandle<Project>,
-        cx: &mut ViewContext<Self>,
-    ) -> Task<Result<()>> {
+    fn save(&mut self, project: Model<Project>, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
         self.report_editor_event("save", None, cx);
         let format = self.perform_format(project.clone(), FormatTrigger::Save, cx);
         let buffers = self.buffer().clone().read(cx).all_buffers();
@@ -659,28 +675,34 @@ impl Item for Editor {
 
             if buffers.len() == 1 {
                 project
-                    .update(&mut cx, |project, cx| project.save_buffers(buffers, cx))
+                    .update(&mut cx, |project, cx| project.save_buffers(buffers, cx))?
                     .await?;
             } else {
                 // For multi-buffers, only save those ones that contain changes. For clean buffers
                 // we simulate saving by calling `Buffer::did_save`, so that language servers or
                 // other downstream listeners of save events get notified.
                 let (dirty_buffers, clean_buffers) = buffers.into_iter().partition(|buffer| {
-                    buffer.read_with(&cx, |buffer, _| buffer.is_dirty() || buffer.has_conflict())
+                    buffer
+                        .update(&mut cx, |buffer, _| {
+                            buffer.is_dirty() || buffer.has_conflict()
+                        })
+                        .unwrap_or(false)
                 });
 
                 project
                     .update(&mut cx, |project, cx| {
                         project.save_buffers(dirty_buffers, cx)
-                    })
+                    })?
                     .await?;
                 for buffer in clean_buffers {
-                    buffer.update(&mut cx, |buffer, cx| {
-                        let version = buffer.saved_version().clone();
-                        let fingerprint = buffer.saved_version_fingerprint();
-                        let mtime = buffer.saved_mtime();
-                        buffer.did_save(version, fingerprint, mtime, cx);
-                    });
+                    buffer
+                        .update(&mut cx, |buffer, cx| {
+                            let version = buffer.saved_version().clone();
+                            let fingerprint = buffer.saved_version_fingerprint();
+                            let mtime = buffer.saved_mtime();
+                            buffer.did_save(version, fingerprint, mtime, cx);
+                        })
+                        .ok();
                 }
             }
 
@@ -690,7 +712,7 @@ impl Item for Editor {
 
     fn save_as(
         &mut self,
-        project: ModelHandle<Project>,
+        project: Model<Project>,
         abs_path: PathBuf,
         cx: &mut ViewContext<Self>,
     ) -> Task<Result<()>> {
@@ -710,11 +732,7 @@ impl Item for Editor {
         })
     }
 
-    fn reload(
-        &mut self,
-        project: ModelHandle<Project>,
-        cx: &mut ViewContext<Self>,
-    ) -> Task<Result<()>> {
+    fn reload(&mut self, project: Model<Project>, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
         let buffer = self.buffer().clone();
         let buffers = self.buffer.read(cx).all_buffers();
         let reload_buffers =
@@ -724,60 +742,36 @@ impl Item for Editor {
             this.update(&mut cx, |editor, cx| {
                 editor.request_autoscroll(Autoscroll::fit(), cx)
             })?;
-            buffer.update(&mut cx, |buffer, cx| {
-                if let Some(transaction) = transaction {
-                    if !buffer.is_singleton() {
-                        buffer.push_transaction(&transaction.0, cx);
+            buffer
+                .update(&mut cx, |buffer, cx| {
+                    if let Some(transaction) = transaction {
+                        if !buffer.is_singleton() {
+                            buffer.push_transaction(&transaction.0, cx);
+                        }
                     }
-                }
-            });
+                })
+                .ok();
             Ok(())
         })
     }
 
-    fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
-        let mut result = SmallVec::new();
-        match event {
-            Event::Closed => result.push(ItemEvent::CloseItem),
-            Event::Saved | Event::TitleChanged => {
-                result.push(ItemEvent::UpdateTab);
-                result.push(ItemEvent::UpdateBreadcrumbs);
-            }
-            Event::Reparsed => {
-                result.push(ItemEvent::UpdateBreadcrumbs);
-            }
-            Event::SelectionsChanged { local } if *local => {
-                result.push(ItemEvent::UpdateBreadcrumbs);
-            }
-            Event::DirtyChanged => {
-                result.push(ItemEvent::UpdateTab);
-            }
-            Event::BufferEdited => {
-                result.push(ItemEvent::Edit);
-                result.push(ItemEvent::UpdateBreadcrumbs);
-            }
-            _ => {}
-        }
-        result
-    }
-
-    fn as_searchable(&self, handle: &ViewHandle<Self>) -> Option<Box<dyn SearchableItemHandle>> {
+    fn as_searchable(&self, handle: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
         Some(Box::new(handle.clone()))
     }
 
-    fn pixel_position_of_cursor(&self, _: &AppContext) -> Option<Vector2F> {
+    fn pixel_position_of_cursor(&self, _: &AppContext) -> Option<gpui::Point<Pixels>> {
         self.pixel_position_of_newest_cursor
     }
 
     fn breadcrumb_location(&self) -> ToolbarItemLocation {
-        ToolbarItemLocation::PrimaryLeft { flex: None }
+        ToolbarItemLocation::PrimaryLeft
     }
 
-    fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
+    fn breadcrumbs(&self, variant: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
         let cursor = self.selections.newest_anchor().head();
         let multibuffer = &self.buffer().read(cx);
         let (buffer_id, symbols) =
-            multibuffer.symbols_containing(cursor, Some(&theme.editor.syntax), cx)?;
+            multibuffer.symbols_containing(cursor, Some(&variant.syntax()), cx)?;
         let buffer = multibuffer.buffer(buffer_id)?;
 
         let buffer = buffer.read(cx);
@@ -806,11 +800,11 @@ impl Item for Editor {
 
     fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
         let workspace_id = workspace.database_id();
-        let item_id = cx.view_id();
+        let item_id = cx.view().item_id().as_u64() as ItemId;
         self.workspace = Some((workspace.weak_handle(), workspace.database_id()));
 
         fn serialize(
-            buffer: ModelHandle<Buffer>,
+            buffer: Model<Buffer>,
             workspace_id: WorkspaceId,
             item_id: ItemId,
             cx: &mut AppContext,
@@ -818,7 +812,7 @@ impl Item for Editor {
             if let Some(file) = buffer.read(cx).file().and_then(|file| file.as_local()) {
                 let path = file.abs_path(cx);
 
-                cx.background()
+                cx.background_executor()
                     .spawn(async move {
                         DB.save_path(item_id, workspace_id, path.clone())
                             .await
@@ -834,7 +828,12 @@ impl Item for Editor {
             cx.subscribe(&buffer, |this, buffer, event, cx| {
                 if let Some((_, workspace_id)) = this.workspace.as_ref() {
                     if let language::Event::FileHandleChanged = event {
-                        serialize(buffer, *workspace_id, cx.view_id(), cx);
+                        serialize(
+                            buffer,
+                            *workspace_id,
+                            cx.view().item_id().as_u64() as ItemId,
+                            cx,
+                        );
                     }
                 }
             })
@@ -846,13 +845,47 @@ impl Item for Editor {
         Some("Editor")
     }
 
+    fn to_item_events(event: &EditorEvent, mut f: impl FnMut(ItemEvent)) {
+        match event {
+            EditorEvent::Closed => f(ItemEvent::CloseItem),
+
+            EditorEvent::Saved | EditorEvent::TitleChanged => {
+                f(ItemEvent::UpdateTab);
+                f(ItemEvent::UpdateBreadcrumbs);
+            }
+
+            EditorEvent::Reparsed => {
+                f(ItemEvent::UpdateBreadcrumbs);
+            }
+
+            EditorEvent::SelectionsChanged { local } if *local => {
+                f(ItemEvent::UpdateBreadcrumbs);
+            }
+
+            EditorEvent::DirtyChanged => {
+                f(ItemEvent::UpdateTab);
+            }
+
+            EditorEvent::BufferEdited => {
+                f(ItemEvent::Edit);
+                f(ItemEvent::UpdateBreadcrumbs);
+            }
+
+            EditorEvent::ExcerptsAdded { .. } | EditorEvent::ExcerptsRemoved { .. } => {
+                f(ItemEvent::Edit);
+            }
+
+            _ => {}
+        }
+    }
+
     fn deserialize(
-        project: ModelHandle<Project>,
-        _workspace: WeakViewHandle<Workspace>,
+        project: Model<Project>,
+        _workspace: WeakView<Workspace>,
         workspace_id: workspace::WorkspaceId,
         item_id: ItemId,
         cx: &mut ViewContext<Pane>,
-    ) -> Task<Result<ViewHandle<Self>>> {
+    ) -> Task<Result<View<Self>>> {
         let project_item: Result<_> = project.update(cx, |project, cx| {
             // Look up the path with this key associated, create a self with that path
             let path = DB
@@ -876,10 +909,11 @@ impl Item for Editor {
                     let (_, project_item) = project_item.await?;
                     let buffer = project_item
                         .downcast::<Buffer>()
-                        .context("Project item at stored path was not a buffer")?;
+                        .map_err(|_| anyhow!("Project item at stored path was not a buffer"))?;
                     Ok(pane.update(&mut cx, |_, cx| {
-                        cx.add_view(|cx| {
+                        cx.new_view(|cx| {
                             let mut editor = Editor::for_buffer(buffer, Some(project), cx);
+
                             editor.read_scroll_position_from_db(item_id, workspace_id, cx);
                             editor
                         })
@@ -894,36 +928,20 @@ impl ProjectItem for Editor {
     type Item = Buffer;
 
     fn for_project_item(
-        project: ModelHandle<Project>,
-        buffer: ModelHandle<Buffer>,
+        project: Model<Project>,
+        buffer: Model<Buffer>,
         cx: &mut ViewContext<Self>,
     ) -> Self {
         Self::for_buffer(buffer, Some(project), cx)
     }
 }
 
+impl EventEmitter<SearchEvent> for Editor {}
+
 pub(crate) enum BufferSearchHighlights {}
 impl SearchableItem for Editor {
     type Match = Range<Anchor>;
 
-    fn to_search_event(
-        &mut self,
-        event: &Self::Event,
-        _: &mut ViewContext<Self>,
-    ) -> Option<SearchEvent> {
-        match event {
-            Event::BufferEdited => Some(SearchEvent::MatchesInvalidated),
-            Event::SelectionsChanged { .. } => {
-                if self.selections.disjoint_anchors().len() == 1 {
-                    Some(SearchEvent::ActiveMatchChanged)
-                } else {
-                    None
-                }
-            }
-            _ => None,
-        }
-    }
-
     fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
         self.clear_background_highlights::<BufferSearchHighlights>(cx);
     }
@@ -931,13 +949,13 @@ impl SearchableItem for Editor {
     fn update_matches(&mut self, matches: Vec<Range<Anchor>>, cx: &mut ViewContext<Self>) {
         self.highlight_background::<BufferSearchHighlights>(
             matches,
-            |theme| theme.search.match_background,
+            |theme| theme.search_match_background,
             cx,
         );
     }
 
     fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
-        let setting = settings::get::<EditorSettings>(cx).seed_search_query_from_cursor;
+        let setting = EditorSettings::get_global(cx).seed_search_query_from_cursor;
         let snapshot = &self.snapshot(cx).buffer_snapshot;
         let selection = self.selections.newest::<usize>(cx);
 
@@ -1060,7 +1078,7 @@ impl SearchableItem for Editor {
         cx: &mut ViewContext<Self>,
     ) -> Task<Vec<Range<Anchor>>> {
         let buffer = self.buffer().read(cx).snapshot(cx);
-        cx.background().spawn(async move {
+        cx.background_executor().spawn(async move {
             let mut ranges = Vec::new();
             if let Some((_, _, excerpt_buffer)) = buffer.as_singleton() {
                 ranges.extend(
@@ -1153,7 +1171,7 @@ impl CursorPosition {
         }
     }
 
-    fn update_position(&mut self, editor: ViewHandle<Editor>, cx: &mut ViewContext<Self>) {
+    fn update_position(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
         let editor = editor.read(cx);
         let buffer = editor.buffer().read(cx).snapshot(cx);
 
@@ -1174,18 +1192,9 @@ impl CursorPosition {
     }
 }
 
-impl Entity for CursorPosition {
-    type Event = ();
-}
-
-impl View for CursorPosition {
-    fn ui_name() -> &'static str {
-        "CursorPosition"
-    }
-
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-        if let Some(position) = self.position {
-            let theme = &theme::current(cx).workspace.status_bar;
+impl Render for CursorPosition {
+    fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
+        div().when_some(self.position, |el, position| {
             let mut text = format!(
                 "{}{FILE_ROW_COLUMN_DELIMITER}{}",
                 position.row + 1,
@@ -1194,10 +1203,9 @@ impl View for CursorPosition {
             if self.selected_count > 0 {
                 write!(text, " ({} selected)", self.selected_count).unwrap();
             }
-            Label::new(text, theme.cursor_position.clone()).into_any()
-        } else {
-            Empty::new().into_any()
-        }
+
+            el.child(Label::new(text).size(LabelSize::Small))
+        })
     }
 }
 
@@ -1220,7 +1228,7 @@ impl StatusItemView for CursorPosition {
 }
 
 fn path_for_buffer<'a>(
-    buffer: &ModelHandle<MultiBuffer>,
+    buffer: &Model<MultiBuffer>,
     height: usize,
     include_filename: bool,
     cx: &'a AppContext,
@@ -2,9 +2,10 @@ use crate::{
     display_map::DisplaySnapshot,
     element::PointForPosition,
     hover_popover::{self, InlayHover},
-    Anchor, DisplayPoint, Editor, EditorSnapshot, InlayId, SelectPhase,
+    Anchor, DisplayPoint, Editor, EditorSnapshot, GoToDefinition, GoToTypeDefinition, InlayId,
+    SelectPhase,
 };
-use gpui::{Task, ViewContext};
+use gpui::{px, Task, ViewContext};
 use language::{Bias, ToOffset};
 use lsp::LanguageServerId;
 use project::{
@@ -12,6 +13,7 @@ use project::{
     ResolveState,
 };
 use std::ops::Range;
+use theme::ActiveTheme as _;
 use util::TryFutureExt;
 
 #[derive(Debug, Default)]
@@ -168,7 +170,7 @@ pub fn update_inlay_link_and_hover_points(
     editor: &mut Editor,
     cmd_held: bool,
     shift_held: bool,
-    cx: &mut ViewContext<'_, '_, Editor>,
+    cx: &mut ViewContext<'_, Editor>,
 ) {
     let hovered_offset = if point_for_position.column_overshoot_after_line_end == 0 {
         Some(snapshot.display_point_to_inlay_offset(point_for_position.exact_unclipped, Bias::Left))
@@ -396,8 +398,8 @@ pub fn show_link_definition(
             let result = match &trigger_point {
                 TriggerPoint::Text(_) => {
                     // query the LSP for definition info
-                    cx.update(|cx| {
-                        project.update(cx, |project, cx| match definition_kind {
+                    project
+                        .update(&mut cx, |project, cx| match definition_kind {
                             LinkDefinitionKind::Symbol => {
                                 project.definition(&buffer, buffer_position, cx)
                             }
@@ -405,29 +407,30 @@ pub fn show_link_definition(
                             LinkDefinitionKind::Type => {
                                 project.type_definition(&buffer, buffer_position, cx)
                             }
+                        })?
+                        .await
+                        .ok()
+                        .map(|definition_result| {
+                            (
+                                definition_result.iter().find_map(|link| {
+                                    link.origin.as_ref().map(|origin| {
+                                        let start = snapshot.buffer_snapshot.anchor_in_excerpt(
+                                            excerpt_id.clone(),
+                                            origin.range.start,
+                                        );
+                                        let end = snapshot.buffer_snapshot.anchor_in_excerpt(
+                                            excerpt_id.clone(),
+                                            origin.range.end,
+                                        );
+                                        RangeInEditor::Text(start..end)
+                                    })
+                                }),
+                                definition_result
+                                    .into_iter()
+                                    .map(GoToDefinitionLink::Text)
+                                    .collect(),
+                            )
                         })
-                    })
-                    .await
-                    .ok()
-                    .map(|definition_result| {
-                        (
-                            definition_result.iter().find_map(|link| {
-                                link.origin.as_ref().map(|origin| {
-                                    let start = snapshot
-                                        .buffer_snapshot
-                                        .anchor_in_excerpt(excerpt_id.clone(), origin.range.start);
-                                    let end = snapshot
-                                        .buffer_snapshot
-                                        .anchor_in_excerpt(excerpt_id.clone(), origin.range.end);
-                                    RangeInEditor::Text(start..end)
-                                })
-                            }),
-                            definition_result
-                                .into_iter()
-                                .map(GoToDefinitionLink::Text)
-                                .collect(),
-                        )
-                    })
                 }
                 TriggerPoint::InlayHint(highlight, lsp_location, server_id) => Some((
                     Some(RangeInEditor::Inlay(highlight.clone())),
@@ -483,8 +486,14 @@ pub fn show_link_definition(
                         });
 
                     if any_definition_does_not_contain_current_location {
-                        // Highlight symbol using theme link definition highlight style
-                        let style = theme::current(cx).editor.link_definition;
+                        let style = gpui::HighlightStyle {
+                            underline: Some(gpui::UnderlineStyle {
+                                thickness: px(1.),
+                                ..Default::default()
+                            }),
+                            color: Some(cx.theme().colors().link_text_hover),
+                            ..Default::default()
+                        };
                         let highlight_range =
                             symbol_range.unwrap_or_else(|| match &trigger_point {
                                 TriggerPoint::Text(trigger_anchor) => {
@@ -575,8 +584,8 @@ fn go_to_fetched_definition_of_kind(
 
     let is_correct_kind = cached_definitions_kind == Some(kind);
     if !cached_definitions.is_empty() && is_correct_kind {
-        if !editor.focused {
-            cx.focus_self();
+        if !editor.focus_handle.is_focused(cx) {
+            cx.focus(&editor.focus_handle);
         }
 
         editor.navigate_to_definitions(cached_definitions, split, cx);
@@ -592,8 +601,8 @@ fn go_to_fetched_definition_of_kind(
 
         if point.as_valid().is_some() {
             match kind {
-                LinkDefinitionKind::Symbol => editor.go_to_definition(&Default::default(), cx),
-                LinkDefinitionKind::Type => editor.go_to_type_definition(&Default::default(), cx),
+                LinkDefinitionKind::Symbol => editor.go_to_definition(&GoToDefinition, cx),
+                LinkDefinitionKind::Type => editor.go_to_type_definition(&GoToTypeDefinition, cx),
             }
         }
     }
@@ -609,10 +618,7 @@ mod tests {
         test::editor_lsp_test_context::EditorLspTestContext,
     };
     use futures::StreamExt;
-    use gpui::{
-        platform::{self, Modifiers, ModifiersChangedEvent},
-        View,
-    };
+    use gpui::{Modifiers, ModifiersChangedEvent};
     use indoc::indoc;
     use language::language_settings::InlayHintSettings;
     use lsp::request::{GotoDefinition, GotoTypeDefinition};
@@ -674,7 +680,7 @@ mod tests {
             );
         });
         requests.next().await;
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
             struct A;
             let ยซvariableยป = A;
@@ -682,10 +688,11 @@ mod tests {
 
         // Unpress shift causes highlight to go away (normal goto-definition is not valid here)
         cx.update_editor(|editor, cx| {
-            editor.modifiers_changed(
-                &platform::ModifiersChangedEvent {
+            crate::element::EditorElement::modifiers_changed(
+                editor,
+                &ModifiersChangedEvent {
                     modifiers: Modifiers {
-                        cmd: true,
+                        command: true,
                         ..Default::default()
                     },
                     ..Default::default()
@@ -725,7 +732,7 @@ mod tests {
             go_to_fetched_type_definition(editor, PointForPosition::valid(hover_point), false, cx);
         });
         requests.next().await;
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
 
         cx.assert_editor_state(indoc! {"
             struct ยซAห‡ยป;
@@ -747,23 +754,23 @@ mod tests {
         .await;
 
         cx.set_state(indoc! {"
-            fn ห‡test() { do_work(); }
-            fn do_work() { test(); }
-        "});
+                fn ห‡test() { do_work(); }
+                fn do_work() { test(); }
+            "});
 
         // Basic hold cmd, expect highlight in region if response contains definition
         let hover_point = cx.display_point(indoc! {"
-            fn test() { do_wห‡ork(); }
-            fn do_work() { test(); }
-        "});
+                fn test() { do_wห‡ork(); }
+                fn do_work() { test(); }
+            "});
         let symbol_range = cx.lsp_range(indoc! {"
-            fn test() { ยซdo_workยป(); }
-            fn do_work() { test(); }
-        "});
+                fn test() { ยซdo_workยป(); }
+                fn do_work() { test(); }
+            "});
         let target_range = cx.lsp_range(indoc! {"
-            fn test() { do_work(); }
-            fn ยซdo_workยป() { test(); }
-        "});
+                fn test() { do_work(); }
+                fn ยซdo_workยป() { test(); }
+            "});
 
         let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
             Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
@@ -786,22 +793,22 @@ mod tests {
             );
         });
         requests.next().await;
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { ยซdo_workยป(); }
-            fn do_work() { test(); }
-        "});
+                fn test() { ยซdo_workยป(); }
+                fn do_work() { test(); }
+            "});
 
         // Unpress cmd causes highlight to go away
         cx.update_editor(|editor, cx| {
-            editor.modifiers_changed(&Default::default(), cx);
+            crate::element::EditorElement::modifiers_changed(editor, &Default::default(), cx);
         });
 
         // Assert no link highlights
         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { test(); }
-        "});
+                fn test() { do_work(); }
+                fn do_work() { test(); }
+            "});
 
         // Response without source range still highlights word
         cx.update_editor(|editor, _| editor.link_go_to_definition_state.last_trigger_point = None);
@@ -826,18 +833,18 @@ mod tests {
             );
         });
         requests.next().await;
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
 
         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { ยซdo_workยป(); }
-            fn do_work() { test(); }
-        "});
+                fn test() { ยซdo_workยป(); }
+                fn do_work() { test(); }
+            "});
 
         // Moving mouse to location with no response dismisses highlight
         let hover_point = cx.display_point(indoc! {"
-            fห‡n test() { do_work(); }
-            fn do_work() { test(); }
-        "});
+                fห‡n test() { do_work(); }
+                fn do_work() { test(); }
+            "});
         let mut requests = cx
             .lsp
             .handle_request::<GotoDefinition, _, _>(move |_, _| async move {
@@ -854,19 +861,19 @@ mod tests {
             );
         });
         requests.next().await;
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
 
         // Assert no link highlights
         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { test(); }
-        "});
+                fn test() { do_work(); }
+                fn do_work() { test(); }
+            "});
 
         // Move mouse without cmd and then pressing cmd triggers highlight
         let hover_point = cx.display_point(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { teห‡st(); }
-        "});
+                fn test() { do_work(); }
+                fn do_work() { teห‡st(); }
+            "});
         cx.update_editor(|editor, cx| {
             update_go_to_definition_link(
                 editor,
@@ -876,22 +883,22 @@ mod tests {
                 cx,
             );
         });
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
 
         // Assert no link highlights
         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { test(); }
-        "});
+                fn test() { do_work(); }
+                fn do_work() { test(); }
+            "});
 
         let symbol_range = cx.lsp_range(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { ยซtestยป(); }
-        "});
+                fn test() { do_work(); }
+                fn do_work() { ยซtestยป(); }
+            "});
         let target_range = cx.lsp_range(indoc! {"
-            fn ยซtestยป() { do_work(); }
-            fn do_work() { test(); }
-        "});
+                fn ยซtestยป() { do_work(); }
+                fn do_work() { test(); }
+            "});
 
         let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
             Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
@@ -904,10 +911,11 @@ mod tests {
             ])))
         });
         cx.update_editor(|editor, cx| {
-            editor.modifiers_changed(
+            crate::element::EditorElement::modifiers_changed(
+                editor,
                 &ModifiersChangedEvent {
                     modifiers: Modifiers {
-                        cmd: true,
+                        command: true,
                         ..Default::default()
                     },
                 },
@@ -915,21 +923,21 @@ mod tests {
             );
         });
         requests.next().await;
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
 
         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { ยซtestยป(); }
-        "});
+                fn test() { do_work(); }
+                fn do_work() { ยซtestยป(); }
+            "});
 
         // Deactivating the window dismisses the highlight
         cx.update_workspace(|workspace, cx| {
-            workspace.on_window_activation_changed(false, cx);
+            workspace.on_window_activation_changed(cx);
         });
         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { test(); }
-        "});
+                fn test() { do_work(); }
+                fn do_work() { test(); }
+            "});
 
         // Moving the mouse restores the highlights.
         cx.update_editor(|editor, cx| {
@@ -941,17 +949,17 @@ mod tests {
                 cx,
             );
         });
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { ยซtestยป(); }
-        "});
+                fn test() { do_work(); }
+                fn do_work() { ยซtestยป(); }
+            "});
 
         // Moving again within the same symbol range doesn't re-request
         let hover_point = cx.display_point(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { tesห‡t(); }
-        "});
+                fn test() { do_work(); }
+                fn do_work() { tesห‡t(); }
+            "});
         cx.update_editor(|editor, cx| {
             update_go_to_definition_link(
                 editor,
@@ -961,11 +969,11 @@ mod tests {
                 cx,
             );
         });
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { ยซtestยป(); }
-        "});
+                fn test() { do_work(); }
+                fn do_work() { ยซtestยป(); }
+            "});
 
         // Cmd click with existing definition doesn't re-request and dismisses highlight
         cx.update_editor(|editor, cx| {
@@ -978,27 +986,27 @@ mod tests {
                 // the cached location instead
                 Ok(Some(lsp::GotoDefinitionResponse::Link(vec![])))
             });
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
         cx.assert_editor_state(indoc! {"
-            fn ยซtestห‡ยป() { do_work(); }
-            fn do_work() { test(); }
-        "});
+                fn ยซtestห‡ยป() { do_work(); }
+                fn do_work() { test(); }
+            "});
 
         // Assert no link highlights after jump
         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { test(); }
-        "});
+                fn test() { do_work(); }
+                fn do_work() { test(); }
+            "});
 
         // Cmd click without existing definition requests and jumps
         let hover_point = cx.display_point(indoc! {"
-            fn test() { do_wห‡ork(); }
-            fn do_work() { test(); }
-        "});
+                fn test() { do_wห‡ork(); }
+                fn do_work() { test(); }
+            "});
         let target_range = cx.lsp_range(indoc! {"
-            fn test() { do_work(); }
-            fn ยซdo_workยป() { test(); }
-        "});
+                fn test() { do_work(); }
+                fn ยซdo_workยป() { test(); }
+            "});
 
         let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
             Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
@@ -1014,22 +1022,22 @@ mod tests {
             go_to_fetched_definition(editor, PointForPosition::valid(hover_point), false, cx);
         });
         requests.next().await;
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
         cx.assert_editor_state(indoc! {"
-            fn test() { do_work(); }
-            fn ยซdo_workห‡ยป() { test(); }
-        "});
+                fn test() { do_work(); }
+                fn ยซdo_workห‡ยป() { test(); }
+            "});
 
         // 1. We have a pending selection, mouse point is over a symbol that we have a response for, hitting cmd and nothing happens
         // 2. Selection is completed, hovering
         let hover_point = cx.display_point(indoc! {"
-            fn test() { do_wห‡ork(); }
-            fn do_work() { test(); }
-        "});
+                fn test() { do_wห‡ork(); }
+                fn do_work() { test(); }
+            "});
         let target_range = cx.lsp_range(indoc! {"
-            fn test() { do_work(); }
-            fn ยซdo_workยป() { test(); }
-        "});
+                fn test() { do_work(); }
+                fn ยซdo_workยป() { test(); }
+            "});
         let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
             Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
                 lsp::LocationLink {
@@ -1043,9 +1051,9 @@ mod tests {
 
         // create a pending selection
         let selection_range = cx.ranges(indoc! {"
-            fn ยซtest() { do_wยปork(); }
-            fn do_work() { test(); }
-        "})[0]
+                fn ยซtest() { do_wยปork(); }
+                fn do_work() { test(); }
+            "})[0]
             .clone();
         cx.update_editor(|editor, cx| {
             let snapshot = editor.buffer().read(cx).snapshot(cx);
@@ -1064,13 +1072,13 @@ mod tests {
                 cx,
             );
         });
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
         assert!(requests.try_next().is_err());
         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { test(); }
-        "});
-        cx.foreground().run_until_parked();
+                fn test() { do_work(); }
+                fn do_work() { test(); }
+            "});
+        cx.background_executor.run_until_parked();
     }
 
     #[gpui::test]
@@ -1093,28 +1101,28 @@ mod tests {
         )
         .await;
         cx.set_state(indoc! {"
-            struct TestStruct;
+                struct TestStruct;
 
-            fn main() {
-                let variableห‡ = TestStruct;
-            }
-        "});
+                fn main() {
+                    let variableห‡ = TestStruct;
+                }
+            "});
         let hint_start_offset = cx.ranges(indoc! {"
-            struct TestStruct;
+                struct TestStruct;
 
-            fn main() {
-                let variableห‡ = TestStruct;
-            }
-        "})[0]
+                fn main() {
+                    let variableห‡ = TestStruct;
+                }
+            "})[0]
             .start;
         let hint_position = cx.to_lsp(hint_start_offset);
         let target_range = cx.lsp_range(indoc! {"
-            struct ยซTestStructยป;
+                struct ยซTestStructยป;
 
-            fn main() {
-                let variable = TestStruct;
-            }
-        "});
+                fn main() {
+                    let variable = TestStruct;
+                }
+            "});
 
         let expected_uri = cx.buffer_lsp_url.clone();
         let hint_label = ": TestStruct";
@@ -1144,7 +1152,7 @@ mod tests {
             })
             .next()
             .await;
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
         cx.update_editor(|editor, cx| {
             let expected_layers = vec![hint_label.to_string()];
             assert_eq!(expected_layers, cached_hint_labels(editor));
@@ -1153,12 +1161,12 @@ mod tests {
 
         let inlay_range = cx
             .ranges(indoc! {"
-            struct TestStruct;
+                struct TestStruct;
 
-            fn main() {
-                let variableยซ ยป= TestStruct;
-            }
-        "})
+                fn main() {
+                    let variableยซ ยป= TestStruct;
+                }
+            "})
             .get(0)
             .cloned()
             .unwrap();
@@ -1190,7 +1198,7 @@ mod tests {
                 cx,
             );
         });
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
         cx.update_editor(|editor, cx| {
             let snapshot = editor.snapshot(cx);
             let actual_highlights = snapshot
@@ -1210,10 +1218,11 @@ mod tests {
 
         // Unpress cmd causes highlight to go away
         cx.update_editor(|editor, cx| {
-            editor.modifiers_changed(
-                &platform::ModifiersChangedEvent {
+            crate::element::EditorElement::modifiers_changed(
+                editor,
+                &ModifiersChangedEvent {
                     modifiers: Modifiers {
-                        cmd: false,
+                        command: false,
                         ..Default::default()
                     },
                     ..Default::default()
@@ -1223,21 +1232,22 @@ mod tests {
         });
         // Assert no link highlights
         cx.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
-            let actual_ranges = snapshot
-                .text_highlight_ranges::<LinkGoToDefinitionState>()
-                .map(|ranges| ranges.as_ref().clone().1)
-                .unwrap_or_default();
+                let snapshot = editor.snapshot(cx);
+                let actual_ranges = snapshot
+                    .text_highlight_ranges::<LinkGoToDefinitionState>()
+                    .map(|ranges| ranges.as_ref().clone().1)
+                    .unwrap_or_default();
 
-            assert!(actual_ranges.is_empty(), "When no cmd is pressed, should have no hint label selected, but got: {actual_ranges:?}");
-        });
+                assert!(actual_ranges.is_empty(), "When no cmd is pressed, should have no hint label selected, but got: {actual_ranges:?}");
+            });
 
         // Cmd+click without existing definition requests and jumps
         cx.update_editor(|editor, cx| {
-            editor.modifiers_changed(
-                &platform::ModifiersChangedEvent {
+            crate::element::EditorElement::modifiers_changed(
+                editor,
+                &ModifiersChangedEvent {
                     modifiers: Modifiers {
-                        cmd: true,
+                        command: true,
                         ..Default::default()
                     },
                     ..Default::default()
@@ -1253,17 +1263,17 @@ mod tests {
                 cx,
             );
         });
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
         cx.update_editor(|editor, cx| {
             go_to_fetched_type_definition(editor, hint_hover_position, false, cx);
         });
-        cx.foreground().run_until_parked();
+        cx.background_executor.run_until_parked();
         cx.assert_editor_state(indoc! {"
-            struct ยซTestStructห‡ยป;
+                struct ยซTestStructห‡ยป;
 
-            fn main() {
-                let variable = TestStruct;
-            }
-        "});
+                fn main() {
+                    let variable = TestStruct;
+                }
+            "});
     }
 }

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

@@ -2,17 +2,22 @@ use crate::{
     DisplayPoint, Editor, EditorMode, FindAllReferences, GoToDefinition, GoToTypeDefinition,
     Rename, RevealInFinder, SelectMode, ToggleCodeActions,
 };
-use context_menu::ContextMenuItem;
-use gpui::{elements::AnchorCorner, geometry::vector::Vector2F, ViewContext};
+use gpui::{DismissEvent, Pixels, Point, Subscription, View, ViewContext};
+
+pub struct MouseContextMenu {
+    pub(crate) position: Point<Pixels>,
+    pub(crate) context_menu: View<ui::ContextMenu>,
+    _subscription: Subscription,
+}
 
 pub fn deploy_context_menu(
     editor: &mut Editor,
-    position: Vector2F,
+    position: Point<Pixels>,
     point: DisplayPoint,
     cx: &mut ViewContext<Editor>,
 ) {
-    if !editor.focused {
-        cx.focus_self();
+    if !editor.is_focused(cx) {
+        editor.focus(cx);
     }
 
     // Don't show context menu for inline editors
@@ -31,26 +36,34 @@ pub fn deploy_context_menu(
         s.set_pending_display_range(point..point, SelectMode::Character);
     });
 
-    editor.mouse_context_menu.update(cx, |menu, cx| {
-        menu.show(
-            position,
-            AnchorCorner::TopLeft,
-            vec![
-                ContextMenuItem::action("Rename Symbol", Rename),
-                ContextMenuItem::action("Go to Definition", GoToDefinition),
-                ContextMenuItem::action("Go to Type Definition", GoToTypeDefinition),
-                ContextMenuItem::action("Find All References", FindAllReferences),
-                ContextMenuItem::action(
-                    "Code Actions",
-                    ToggleCodeActions {
-                        deployed_from_indicator: false,
-                    },
-                ),
-                ContextMenuItem::Separator,
-                ContextMenuItem::action("Reveal in Finder", RevealInFinder),
-            ],
-            cx,
-        );
+    let context_menu = ui::ContextMenu::build(cx, |menu, _cx| {
+        menu.action("Rename Symbol", Box::new(Rename))
+            .action("Go to Definition", Box::new(GoToDefinition))
+            .action("Go to Type Definition", Box::new(GoToTypeDefinition))
+            .action("Find All References", Box::new(FindAllReferences))
+            .action(
+                "Code Actions",
+                Box::new(ToggleCodeActions {
+                    deployed_from_indicator: false,
+                }),
+            )
+            .separator()
+            .action("Reveal in Finder", Box::new(RevealInFinder))
+    });
+    let context_menu_focus = context_menu.focus_handle(cx);
+    cx.focus(&context_menu_focus);
+
+    let _subscription = cx.subscribe(&context_menu, move |this, _, _event: &DismissEvent, cx| {
+        this.mouse_context_menu.take();
+        if context_menu_focus.contains_focused(cx) {
+            this.focus(cx);
+        }
+    });
+
+    editor.mouse_context_menu = Some(MouseContextMenu {
+        position,
+        context_menu,
+        _subscription,
     });
     cx.notify();
 }
@@ -84,6 +97,7 @@ mod tests {
                 do_wห‡ork();
             }
         "});
+        cx.editor(|editor, _app| assert!(editor.mouse_context_menu.is_none()));
         cx.update_editor(|editor, cx| deploy_context_menu(editor, Default::default(), point, cx));
 
         cx.assert_editor_state(indoc! {"
@@ -91,6 +105,6 @@ mod tests {
                 do_wห‡ork();
             }
         "});
-        cx.editor(|editor, app| assert!(editor.mouse_context_menu.read(app).visible()));
+        cx.editor(|editor, _app| assert!(editor.mouse_context_menu.is_some()));
     }
 }

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

@@ -1,7 +1,8 @@
 use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
 use crate::{char_kind, CharKind, EditorStyle, ToOffset, ToPoint};
-use gpui::{FontCache, TextLayoutCache};
+use gpui::{px, Pixels, TextSystem};
 use language::Point;
+
 use std::{ops::Range, sync::Arc};
 
 #[derive(Debug, PartialEq)]
@@ -13,9 +14,9 @@ pub enum FindRange {
 /// TextLayoutDetails encompasses everything we need to move vertically
 /// taking into account variable width characters.
 pub struct TextLayoutDetails {
-    pub font_cache: Arc<FontCache>,
-    pub text_layout_cache: Arc<TextLayoutCache>,
+    pub text_system: Arc<TextSystem>,
     pub editor_style: EditorStyle,
+    pub rem_size: Pixels,
 }
 
 pub fn left(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint {
@@ -94,10 +95,10 @@ pub fn up_by_rows(
     text_layout_details: &TextLayoutDetails,
 ) -> (DisplayPoint, SelectionGoal) {
     let mut goal_x = match goal {
-        SelectionGoal::HorizontalPosition(x) => x,
-        SelectionGoal::WrappedHorizontalPosition((_, x)) => x,
-        SelectionGoal::HorizontalRange { end, .. } => end,
-        _ => map.x_for_point(start, text_layout_details),
+        SelectionGoal::HorizontalPosition(x) => x.into(), // todo!("Can the fields in SelectionGoal by Pixels? We should extract a geometry crate and depend on that.")
+        SelectionGoal::WrappedHorizontalPosition((_, x)) => x.into(),
+        SelectionGoal::HorizontalRange { end, .. } => end.into(),
+        _ => map.x_for_display_point(start, text_layout_details),
     };
 
     let prev_row = start.row().saturating_sub(row_count);
@@ -106,19 +107,22 @@ pub fn up_by_rows(
         Bias::Left,
     );
     if point.row() < start.row() {
-        *point.column_mut() = map.column_for_x(point.row(), goal_x, text_layout_details)
+        *point.column_mut() = map.display_column_for_x(point.row(), goal_x, text_layout_details)
     } else if preserve_column_at_start {
         return (start, goal);
     } else {
         point = DisplayPoint::new(0, 0);
-        goal_x = 0.0;
+        goal_x = px(0.);
     }
 
     let mut clipped_point = map.clip_point(point, Bias::Left);
     if clipped_point.row() < point.row() {
         clipped_point = map.clip_point(point, Bias::Right);
     }
-    (clipped_point, SelectionGoal::HorizontalPosition(goal_x))
+    (
+        clipped_point,
+        SelectionGoal::HorizontalPosition(goal_x.into()),
+    )
 }
 
 pub fn down_by_rows(
@@ -130,28 +134,31 @@ pub fn down_by_rows(
     text_layout_details: &TextLayoutDetails,
 ) -> (DisplayPoint, SelectionGoal) {
     let mut goal_x = match goal {
-        SelectionGoal::HorizontalPosition(x) => x,
-        SelectionGoal::WrappedHorizontalPosition((_, x)) => x,
-        SelectionGoal::HorizontalRange { end, .. } => end,
-        _ => map.x_for_point(start, text_layout_details),
+        SelectionGoal::HorizontalPosition(x) => x.into(),
+        SelectionGoal::WrappedHorizontalPosition((_, x)) => x.into(),
+        SelectionGoal::HorizontalRange { end, .. } => end.into(),
+        _ => map.x_for_display_point(start, text_layout_details),
     };
 
     let new_row = start.row() + row_count;
     let mut point = map.clip_point(DisplayPoint::new(new_row, 0), Bias::Right);
     if point.row() > start.row() {
-        *point.column_mut() = map.column_for_x(point.row(), goal_x, text_layout_details)
+        *point.column_mut() = map.display_column_for_x(point.row(), goal_x, text_layout_details)
     } else if preserve_column_at_end {
         return (start, goal);
     } else {
         point = map.max_point();
-        goal_x = map.x_for_point(point, text_layout_details)
+        goal_x = map.x_for_display_point(point, text_layout_details)
     }
 
     let mut clipped_point = map.clip_point(point, Bias::Right);
     if clipped_point.row() > point.row() {
         clipped_point = map.clip_point(point, Bias::Left);
     }
-    (clipped_point, SelectionGoal::HorizontalPosition(goal_x))
+    (
+        clipped_point,
+        SelectionGoal::HorizontalPosition(goal_x.into()),
+    )
 }
 
 pub fn line_beginning(
@@ -453,6 +460,7 @@ mod tests {
         test::{editor_test_context::EditorTestContext, marked_display_snapshot},
         Buffer, DisplayMap, ExcerptRange, InlayId, MultiBuffer,
     };
+    use gpui::{font, Context as _};
     use project::Project;
     use settings::SettingsStore;
     use util::post_inc;
@@ -563,19 +571,12 @@ mod tests {
         init_test(cx);
 
         let input_text = "abcdefghijklmnopqrstuvwxys";
-        let family_id = cx
-            .font_cache()
-            .load_family(&["Helvetica"], &Default::default())
-            .unwrap();
-        let font_id = cx
-            .font_cache()
-            .select_font(family_id, &Default::default())
-            .unwrap();
-        let font_size = 14.0;
+        let font = font("Helvetica");
+        let font_size = px(14.0);
         let buffer = MultiBuffer::build_simple(input_text, cx);
         let buffer_snapshot = buffer.read(cx).snapshot(cx);
         let display_map =
-            cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx));
+            cx.new_model(|cx| DisplayMap::new(buffer, font, font_size, None, 1, 1, cx));
 
         // add all kinds of inlays between two word boundaries: we should be able to cross them all, when looking for another boundary
         let mut id = 0;
@@ -756,22 +757,15 @@ mod tests {
         let mut cx = EditorTestContext::new(cx).await;
         let editor = cx.editor.clone();
         let window = cx.window.clone();
-        cx.update_window(window, |cx| {
+        _ = cx.update_window(window, |_, cx| {
             let text_layout_details =
-                editor.read_with(cx, |editor, cx| editor.text_layout_details(cx));
+                editor.update(cx, |editor, cx| editor.text_layout_details(cx));
 
-            let family_id = cx
-                .font_cache()
-                .load_family(&["Helvetica"], &Default::default())
-                .unwrap();
-            let font_id = cx
-                .font_cache()
-                .select_font(family_id, &Default::default())
-                .unwrap();
+            let font = font("Helvetica");
 
             let buffer =
-                cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "abc\ndefg\nhijkl\nmn"));
-            let multibuffer = cx.add_model(|cx| {
+                cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abc\ndefg\nhijkl\nmn"));
+            let multibuffer = cx.new_model(|cx| {
                 let mut multibuffer = MultiBuffer::new(0);
                 multibuffer.push_excerpts(
                     buffer.clone(),
@@ -790,19 +784,20 @@ mod tests {
                 multibuffer
             });
             let display_map =
-                cx.add_model(|cx| DisplayMap::new(multibuffer, font_id, 14.0, None, 2, 2, cx));
+                cx.new_model(|cx| DisplayMap::new(multibuffer, font, px(14.0), None, 2, 2, cx));
             let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
 
             assert_eq!(snapshot.text(), "\n\nabc\ndefg\n\n\nhijkl\nmn");
 
-            let col_2_x = snapshot.x_for_point(DisplayPoint::new(2, 2), &text_layout_details);
+            let col_2_x =
+                snapshot.x_for_display_point(DisplayPoint::new(2, 2), &text_layout_details);
 
             // Can't move up into the first excerpt's header
             assert_eq!(
                 up(
                     &snapshot,
                     DisplayPoint::new(2, 2),
-                    SelectionGoal::HorizontalPosition(col_2_x),
+                    SelectionGoal::HorizontalPosition(col_2_x.0),
                     false,
                     &text_layout_details
                 ),
@@ -825,67 +820,70 @@ mod tests {
                 ),
             );
 
-            let col_4_x = snapshot.x_for_point(DisplayPoint::new(3, 4), &text_layout_details);
+            let col_4_x =
+                snapshot.x_for_display_point(DisplayPoint::new(3, 4), &text_layout_details);
 
             // Move up and down within first excerpt
             assert_eq!(
                 up(
                     &snapshot,
                     DisplayPoint::new(3, 4),
-                    SelectionGoal::HorizontalPosition(col_4_x),
+                    SelectionGoal::HorizontalPosition(col_4_x.0),
                     false,
                     &text_layout_details
                 ),
                 (
                     DisplayPoint::new(2, 3),
-                    SelectionGoal::HorizontalPosition(col_4_x)
+                    SelectionGoal::HorizontalPosition(col_4_x.0)
                 ),
             );
             assert_eq!(
                 down(
                     &snapshot,
                     DisplayPoint::new(2, 3),
-                    SelectionGoal::HorizontalPosition(col_4_x),
+                    SelectionGoal::HorizontalPosition(col_4_x.0),
                     false,
                     &text_layout_details
                 ),
                 (
                     DisplayPoint::new(3, 4),
-                    SelectionGoal::HorizontalPosition(col_4_x)
+                    SelectionGoal::HorizontalPosition(col_4_x.0)
                 ),
             );
 
-            let col_5_x = snapshot.x_for_point(DisplayPoint::new(6, 5), &text_layout_details);
+            let col_5_x =
+                snapshot.x_for_display_point(DisplayPoint::new(6, 5), &text_layout_details);
 
             // Move up and down across second excerpt's header
             assert_eq!(
                 up(
                     &snapshot,
                     DisplayPoint::new(6, 5),
-                    SelectionGoal::HorizontalPosition(col_5_x),
+                    SelectionGoal::HorizontalPosition(col_5_x.0),
                     false,
                     &text_layout_details
                 ),
                 (
                     DisplayPoint::new(3, 4),
-                    SelectionGoal::HorizontalPosition(col_5_x)
+                    SelectionGoal::HorizontalPosition(col_5_x.0)
                 ),
             );
             assert_eq!(
                 down(
                     &snapshot,
                     DisplayPoint::new(3, 4),
-                    SelectionGoal::HorizontalPosition(col_5_x),
+                    SelectionGoal::HorizontalPosition(col_5_x.0),
                     false,
                     &text_layout_details
                 ),
                 (
                     DisplayPoint::new(6, 5),
-                    SelectionGoal::HorizontalPosition(col_5_x)
+                    SelectionGoal::HorizontalPosition(col_5_x.0)
                 ),
             );
 
-            let max_point_x = snapshot.x_for_point(DisplayPoint::new(7, 2), &text_layout_details);
+            let max_point_x =
+                snapshot.x_for_display_point(DisplayPoint::new(7, 2), &text_layout_details);
 
             // Can't move down off the end
             assert_eq!(
@@ -898,28 +896,29 @@ mod tests {
                 ),
                 (
                     DisplayPoint::new(7, 2),
-                    SelectionGoal::HorizontalPosition(max_point_x)
+                    SelectionGoal::HorizontalPosition(max_point_x.0)
                 ),
             );
             assert_eq!(
                 down(
                     &snapshot,
                     DisplayPoint::new(7, 2),
-                    SelectionGoal::HorizontalPosition(max_point_x),
+                    SelectionGoal::HorizontalPosition(max_point_x.0),
                     false,
                     &text_layout_details
                 ),
                 (
                     DisplayPoint::new(7, 2),
-                    SelectionGoal::HorizontalPosition(max_point_x)
+                    SelectionGoal::HorizontalPosition(max_point_x.0)
                 ),
             );
         });
     }
 
     fn init_test(cx: &mut gpui::AppContext) {
-        cx.set_global(SettingsStore::test(cx));
-        theme::init((), cx);
+        let settings_store = SettingsStore::test(cx);
+        cx.set_global(settings_store);
+        theme::init(theme::LoadThemes::JustBase, cx);
         language::init(cx);
         crate::init(cx);
         Project::init_settings(cx);

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

@@ -1,31 +1,50 @@
 use std::sync::Arc;
 
 use anyhow::Context as _;
-use gpui::{AppContext, Task, ViewContext};
+use gpui::{Context, View, ViewContext, VisualContext, WindowContext};
 use language::Language;
 use multi_buffer::MultiBuffer;
 use project::lsp_ext_command::ExpandMacro;
 use text::ToPointUtf16;
 
-use crate::{Editor, ExpandMacroRecursively};
+use crate::{element::register_action, Editor, ExpandMacroRecursively};
 
-pub fn apply_related_actions(cx: &mut AppContext) {
-    cx.add_async_action(expand_macro_recursively);
+pub fn apply_related_actions(editor: &View<Editor>, cx: &mut WindowContext) {
+    let is_rust_related = editor.update(cx, |editor, cx| {
+        editor
+            .buffer()
+            .read(cx)
+            .all_buffers()
+            .iter()
+            .any(|b| match b.read(cx).language() {
+                Some(l) => is_rust_language(l),
+                None => false,
+            })
+    });
+
+    if is_rust_related {
+        register_action(editor, cx, expand_macro_recursively);
+    }
 }
 
 pub fn expand_macro_recursively(
     editor: &mut Editor,
     _: &ExpandMacroRecursively,
-    cx: &mut ViewContext<'_, '_, Editor>,
-) -> Option<Task<anyhow::Result<()>>> {
+    cx: &mut ViewContext<'_, Editor>,
+) {
     if editor.selections.count() == 0 {
-        return None;
+        return;
     }
-    let project = editor.project.as_ref()?;
-    let workspace = editor.workspace(cx)?;
+    let Some(project) = &editor.project else {
+        return;
+    };
+    let Some(workspace) = editor.workspace() else {
+        return;
+    };
+
     let multibuffer = editor.buffer().read(cx);
 
-    let (trigger_anchor, rust_language, server_to_query, buffer) = editor
+    let Some((trigger_anchor, rust_language, server_to_query, buffer)) = editor
         .selections
         .disjoint_anchors()
         .into_iter()
@@ -56,7 +75,10 @@ pub fn expand_macro_recursively(
                         None
                     }
                 })
-        })?;
+        })
+    else {
+        return;
+    };
 
     let project = project.clone();
     let buffer_snapshot = buffer.read(cx).snapshot();
@@ -69,7 +91,7 @@ pub fn expand_macro_recursively(
             cx,
         )
     });
-    Some(cx.spawn(|_, mut cx| async move {
+    cx.spawn(|_editor, mut cx| async move {
         let macro_expansion = expand_macro_task.await.context("expand macro")?;
         if macro_expansion.is_empty() {
             log::info!("Empty macro expansion for position {position:?}");
@@ -78,19 +100,18 @@ pub fn expand_macro_recursively(
 
         let buffer = project.update(&mut cx, |project, cx| {
             project.create_buffer(&macro_expansion.expansion, Some(rust_language), cx)
-        })?;
+        })??;
         workspace.update(&mut cx, |workspace, cx| {
-            let buffer = cx.add_model(|cx| {
+            let buffer = cx.new_model(|cx| {
                 MultiBuffer::singleton(buffer, cx).with_title(macro_expansion.name)
             });
             workspace.add_item(
-                Box::new(cx.add_view(|cx| Editor::for_multibuffer(buffer, Some(project), cx))),
+                Box::new(cx.new_view(|cx| Editor::for_multibuffer(buffer, Some(project), cx))),
                 cx,
             );
-        });
-
-        anyhow::Ok(())
-    }))
+        })
+    })
+    .detach_and_log_err(cx);
 }
 
 fn is_rust_language(language: &Language) -> bool {

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

@@ -2,26 +2,21 @@ pub mod actions;
 pub mod autoscroll;
 pub mod scroll_amount;
 
-use std::{
-    cmp::Ordering,
-    time::{Duration, Instant},
-};
-
-use gpui::{
-    geometry::vector::{vec2f, Vector2F},
-    AppContext, Axis, Task, ViewContext,
-};
-use language::{Bias, Point};
-use util::ResultExt;
-use workspace::WorkspaceId;
-
 use crate::{
     display_map::{DisplaySnapshot, ToDisplayPoint},
     hover_popover::hide_hover,
     persistence::DB,
-    Anchor, DisplayPoint, Editor, EditorMode, Event, InlayHintRefreshReason, MultiBufferSnapshot,
-    ToPoint,
+    Anchor, DisplayPoint, Editor, EditorEvent, EditorMode, InlayHintRefreshReason,
+    MultiBufferSnapshot, ToPoint,
+};
+use gpui::{point, px, AppContext, Entity, Pixels, Task, ViewContext};
+use language::{Bias, Point};
+use std::{
+    cmp::Ordering,
+    time::{Duration, Instant},
 };
+use util::ResultExt;
+use workspace::{ItemId, WorkspaceId};
 
 use self::{
     autoscroll::{Autoscroll, AutoscrollStrategy},
@@ -37,25 +32,25 @@ pub struct ScrollbarAutoHide(pub bool);
 
 #[derive(Clone, Copy, Debug, PartialEq)]
 pub struct ScrollAnchor {
-    pub offset: Vector2F,
+    pub offset: gpui::Point<f32>,
     pub anchor: Anchor,
 }
 
 impl ScrollAnchor {
     fn new() -> Self {
         Self {
-            offset: Vector2F::zero(),
+            offset: gpui::Point::default(),
             anchor: Anchor::min(),
         }
     }
 
-    pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> Vector2F {
+    pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> gpui::Point<f32> {
         let mut scroll_position = self.offset;
         if self.anchor != Anchor::min() {
             let scroll_top = self.anchor.to_display_point(snapshot).row() as f32;
-            scroll_position.set_y(scroll_top + scroll_position.y());
+            scroll_position.y = scroll_top + scroll_position.y;
         } else {
-            scroll_position.set_y(0.);
+            scroll_position.y = 0.;
         }
         scroll_position
     }
@@ -65,6 +60,12 @@ impl ScrollAnchor {
     }
 }
 
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum Axis {
+    Vertical,
+    Horizontal,
+}
+
 #[derive(Clone, Copy, Debug)]
 pub struct OngoingScroll {
     last_event: Instant,
@@ -79,13 +80,13 @@ impl OngoingScroll {
         }
     }
 
-    pub fn filter(&self, delta: &mut Vector2F) -> Option<Axis> {
+    pub fn filter(&self, delta: &mut gpui::Point<Pixels>) -> Option<Axis> {
         const UNLOCK_PERCENT: f32 = 1.9;
-        const UNLOCK_LOWER_BOUND: f32 = 6.;
+        const UNLOCK_LOWER_BOUND: Pixels = px(6.);
         let mut axis = self.axis;
 
-        let x = delta.x().abs();
-        let y = delta.y().abs();
+        let x = delta.x.abs();
+        let y = delta.y.abs();
         let duration = Instant::now().duration_since(self.last_event);
         if duration > SCROLL_EVENT_SEPARATION {
             //New ongoing scroll will start, determine axis
@@ -114,8 +115,12 @@ impl OngoingScroll {
         }
 
         match axis {
-            Some(Axis::Vertical) => *delta = vec2f(0., delta.y()),
-            Some(Axis::Horizontal) => *delta = vec2f(delta.x(), 0.),
+            Some(Axis::Vertical) => {
+                *delta = point(px(0.), delta.y);
+            }
+            Some(Axis::Horizontal) => {
+                *delta = point(delta.x, px(0.));
+            }
             None => {}
         }
 
@@ -128,9 +133,10 @@ pub struct ScrollManager {
     anchor: ScrollAnchor,
     ongoing: OngoingScroll,
     autoscroll_request: Option<(Autoscroll, bool)>,
-    last_autoscroll: Option<(Vector2F, f32, f32, AutoscrollStrategy)>,
+    last_autoscroll: Option<(gpui::Point<f32>, f32, f32, AutoscrollStrategy)>,
     show_scrollbars: bool,
     hide_scrollbar_task: Option<Task<()>>,
+    dragging_scrollbar: bool,
     visible_line_count: Option<f32>,
 }
 
@@ -143,6 +149,7 @@ impl ScrollManager {
             autoscroll_request: None,
             show_scrollbars: true,
             hide_scrollbar_task: None,
+            dragging_scrollbar: false,
             last_autoscroll: None,
             visible_line_count: None,
         }
@@ -166,30 +173,30 @@ impl ScrollManager {
         self.ongoing.axis = axis;
     }
 
-    pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> Vector2F {
+    pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> gpui::Point<f32> {
         self.anchor.scroll_position(snapshot)
     }
 
     fn set_scroll_position(
         &mut self,
-        scroll_position: Vector2F,
+        scroll_position: gpui::Point<f32>,
         map: &DisplaySnapshot,
         local: bool,
         autoscroll: bool,
         workspace_id: Option<i64>,
         cx: &mut ViewContext<Editor>,
     ) {
-        let (new_anchor, top_row) = if scroll_position.y() <= 0. {
+        let (new_anchor, top_row) = if scroll_position.y <= 0. {
             (
                 ScrollAnchor {
                     anchor: Anchor::min(),
-                    offset: scroll_position.max(vec2f(0., 0.)),
+                    offset: scroll_position.max(&gpui::Point::default()),
                 },
                 0,
             )
         } else {
             let scroll_top_buffer_point =
-                DisplayPoint::new(scroll_position.y() as u32, 0).to_point(&map);
+                DisplayPoint::new(scroll_position.y as u32, 0).to_point(&map);
             let top_anchor = map
                 .buffer_snapshot
                 .anchor_at(scroll_top_buffer_point, Bias::Right);
@@ -197,9 +204,9 @@ impl ScrollManager {
             (
                 ScrollAnchor {
                     anchor: top_anchor,
-                    offset: vec2f(
-                        scroll_position.x(),
-                        scroll_position.y() - top_anchor.to_display_point(&map).row() as f32,
+                    offset: point(
+                        scroll_position.x,
+                        scroll_position.y - top_anchor.to_display_point(&map).row() as f32,
                     ),
                 },
                 scroll_top_buffer_point.row,
@@ -219,20 +226,20 @@ impl ScrollManager {
         cx: &mut ViewContext<Editor>,
     ) {
         self.anchor = anchor;
-        cx.emit(Event::ScrollPositionChanged { local, autoscroll });
+        cx.emit(EditorEvent::ScrollPositionChanged { local, autoscroll });
         self.show_scrollbar(cx);
         self.autoscroll_request.take();
         if let Some(workspace_id) = workspace_id {
-            let item_id = cx.view_id();
+            let item_id = cx.view().entity_id().as_u64() as ItemId;
 
-            cx.background()
+            cx.foreground_executor()
                 .spawn(async move {
                     DB.save_scroll_position(
                         item_id,
                         workspace_id,
                         top_row,
-                        anchor.offset.x(),
-                        anchor.offset.y(),
+                        anchor.offset.x,
+                        anchor.offset.y,
                     )
                     .await
                     .log_err()
@@ -250,7 +257,9 @@ impl ScrollManager {
 
         if cx.default_global::<ScrollbarAutoHide>().0 {
             self.hide_scrollbar_task = Some(cx.spawn(|editor, mut cx| async move {
-                cx.background().timer(SCROLLBAR_SHOW_INTERVAL).await;
+                cx.background_executor()
+                    .timer(SCROLLBAR_SHOW_INTERVAL)
+                    .await;
                 editor
                     .update(&mut cx, |editor, cx| {
                         editor.scroll_manager.show_scrollbars = false;
@@ -271,9 +280,20 @@ impl ScrollManager {
         self.autoscroll_request.is_some()
     }
 
+    pub fn is_dragging_scrollbar(&self) -> bool {
+        self.dragging_scrollbar
+    }
+
+    pub fn set_is_dragging_scrollbar(&mut self, dragging: bool, cx: &mut ViewContext<Editor>) {
+        if dragging != self.dragging_scrollbar {
+            self.dragging_scrollbar = dragging;
+            cx.notify();
+        }
+    }
+
     pub fn clamp_scroll_left(&mut self, max: f32) -> bool {
-        if max < self.anchor.offset.x() {
-            self.anchor.offset.set_x(max);
+        if max < self.anchor.offset.x {
+            self.anchor.offset.x = max;
             true
         } else {
             false
@@ -310,13 +330,17 @@ impl Editor {
         }
     }
 
-    pub fn set_scroll_position(&mut self, scroll_position: Vector2F, cx: &mut ViewContext<Self>) {
+    pub fn set_scroll_position(
+        &mut self,
+        scroll_position: gpui::Point<f32>,
+        cx: &mut ViewContext<Self>,
+    ) {
         self.set_scroll_position_internal(scroll_position, true, false, cx);
     }
 
     pub(crate) fn set_scroll_position_internal(
         &mut self,
-        scroll_position: Vector2F,
+        scroll_position: gpui::Point<f32>,
         local: bool,
         autoscroll: bool,
         cx: &mut ViewContext<Self>,
@@ -337,7 +361,7 @@ impl Editor {
         self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
     }
 
-    pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Vector2F {
+    pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> gpui::Point<f32> {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         self.scroll_manager.anchor.scroll_position(&display_map)
     }
@@ -370,7 +394,7 @@ impl Editor {
 
     pub fn scroll_screen(&mut self, amount: &ScrollAmount, cx: &mut ViewContext<Self>) {
         if matches!(self.mode, EditorMode::SingleLine) {
-            cx.propagate_action();
+            cx.propagate();
             return;
         }
 
@@ -379,7 +403,7 @@ impl Editor {
         }
 
         let cur_position = self.scroll_position(cx);
-        let new_pos = cur_position + vec2f(0., amount.lines(self));
+        let new_pos = cur_position + point(0., amount.lines(self));
         self.set_scroll_position(new_pos, cx);
     }
 
@@ -415,7 +439,7 @@ impl Editor {
 
     pub fn read_scroll_position_from_db(
         &mut self,
-        item_id: usize,
+        item_id: u64,
         workspace_id: WorkspaceId,
         cx: &mut ViewContext<Editor>,
     ) {
@@ -427,7 +451,7 @@ impl Editor {
                 .snapshot(cx)
                 .anchor_at(Point::new(top_row as u32, 0), Bias::Left);
             let scroll_anchor = ScrollAnchor {
-                offset: Vector2F::new(x, y),
+                offset: gpui::Point::new(x, y),
                 anchor: top_anchor,
             };
             self.set_scroll_anchor(scroll_anchor, cx);

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

@@ -1,72 +1,31 @@
-use gpui::{actions, geometry::vector::Vector2F, AppContext, Axis, ViewContext};
-use language::Bias;
-
-use crate::{Editor, EditorMode};
-
-use super::{autoscroll::Autoscroll, scroll_amount::ScrollAmount, ScrollAnchor};
-
-actions!(
-    editor,
-    [
-        LineDown,
-        LineUp,
-        HalfPageDown,
-        HalfPageUp,
-        PageDown,
-        PageUp,
-        NextScreen,
-        ScrollCursorTop,
-        ScrollCursorCenter,
-        ScrollCursorBottom,
-    ]
-);
-
-pub fn init(cx: &mut AppContext) {
-    cx.add_action(Editor::next_screen);
-    cx.add_action(Editor::scroll_cursor_top);
-    cx.add_action(Editor::scroll_cursor_center);
-    cx.add_action(Editor::scroll_cursor_bottom);
-    cx.add_action(|this: &mut Editor, _: &LineDown, cx| {
-        this.scroll_screen(&ScrollAmount::Line(1.), cx)
-    });
-    cx.add_action(|this: &mut Editor, _: &LineUp, cx| {
-        this.scroll_screen(&ScrollAmount::Line(-1.), cx)
-    });
-    cx.add_action(|this: &mut Editor, _: &HalfPageDown, cx| {
-        this.scroll_screen(&ScrollAmount::Page(0.5), cx)
-    });
-    cx.add_action(|this: &mut Editor, _: &HalfPageUp, cx| {
-        this.scroll_screen(&ScrollAmount::Page(-0.5), cx)
-    });
-    cx.add_action(|this: &mut Editor, _: &PageDown, cx| {
-        this.scroll_screen(&ScrollAmount::Page(1.), cx)
-    });
-    cx.add_action(|this: &mut Editor, _: &PageUp, cx| {
-        this.scroll_screen(&ScrollAmount::Page(-1.), cx)
-    });
-}
+use super::Axis;
+use crate::{
+    Autoscroll, Bias, Editor, EditorMode, NextScreen, ScrollAnchor, ScrollCursorBottom,
+    ScrollCursorCenter, ScrollCursorTop,
+};
+use gpui::{Point, ViewContext};
 
 impl Editor {
-    pub fn next_screen(&mut self, _: &NextScreen, cx: &mut ViewContext<Editor>) -> Option<()> {
+    pub fn next_screen(&mut self, _: &NextScreen, cx: &mut ViewContext<Editor>) {
         if self.take_rename(true, cx).is_some() {
-            return None;
+            return;
         }
 
-        if self.mouse_context_menu.read(cx).visible() {
-            return None;
-        }
+        // todo!()
+        // if self.mouse_context_menu.read(cx).visible() {
+        //     return None;
+        // }
 
         if matches!(self.mode, EditorMode::SingleLine) {
-            cx.propagate_action();
-            return None;
+            cx.propagate();
+            return;
         }
         self.request_autoscroll(Autoscroll::Next, cx);
-        Some(())
     }
 
     pub fn scroll(
         &mut self,
-        scroll_position: Vector2F,
+        scroll_position: Point<f32>,
         axis: Option<Axis>,
         cx: &mut ViewContext<Self>,
     ) {
@@ -74,17 +33,17 @@ impl Editor {
         self.set_scroll_position(scroll_position, cx);
     }
 
-    fn scroll_cursor_top(editor: &mut Editor, _: &ScrollCursorTop, cx: &mut ViewContext<Editor>) {
-        let snapshot = editor.snapshot(cx).display_snapshot;
-        let scroll_margin_rows = editor.vertical_scroll_margin() as u32;
+    pub fn scroll_cursor_top(&mut self, _: &ScrollCursorTop, cx: &mut ViewContext<Editor>) {
+        let snapshot = self.snapshot(cx).display_snapshot;
+        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
 
-        let mut new_screen_top = editor.selections.newest_display(cx).head();
+        let mut new_screen_top = self.selections.newest_display(cx).head();
         *new_screen_top.row_mut() = new_screen_top.row().saturating_sub(scroll_margin_rows);
         *new_screen_top.column_mut() = 0;
         let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
         let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
 
-        editor.set_scroll_anchor(
+        self.set_scroll_anchor(
             ScrollAnchor {
                 anchor: new_anchor,
                 offset: Default::default(),
@@ -93,25 +52,21 @@ impl Editor {
         )
     }
 
-    fn scroll_cursor_center(
-        editor: &mut Editor,
-        _: &ScrollCursorCenter,
-        cx: &mut ViewContext<Editor>,
-    ) {
-        let snapshot = editor.snapshot(cx).display_snapshot;
-        let visible_rows = if let Some(visible_rows) = editor.visible_line_count() {
+    pub fn scroll_cursor_center(&mut self, _: &ScrollCursorCenter, cx: &mut ViewContext<Editor>) {
+        let snapshot = self.snapshot(cx).display_snapshot;
+        let visible_rows = if let Some(visible_rows) = self.visible_line_count() {
             visible_rows as u32
         } else {
             return;
         };
 
-        let mut new_screen_top = editor.selections.newest_display(cx).head();
+        let mut new_screen_top = self.selections.newest_display(cx).head();
         *new_screen_top.row_mut() = new_screen_top.row().saturating_sub(visible_rows / 2);
         *new_screen_top.column_mut() = 0;
         let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
         let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
 
-        editor.set_scroll_anchor(
+        self.set_scroll_anchor(
             ScrollAnchor {
                 anchor: new_anchor,
                 offset: Default::default(),
@@ -120,20 +75,16 @@ impl Editor {
         )
     }
 
-    fn scroll_cursor_bottom(
-        editor: &mut Editor,
-        _: &ScrollCursorBottom,
-        cx: &mut ViewContext<Editor>,
-    ) {
-        let snapshot = editor.snapshot(cx).display_snapshot;
-        let scroll_margin_rows = editor.vertical_scroll_margin() as u32;
-        let visible_rows = if let Some(visible_rows) = editor.visible_line_count() {
+    pub fn scroll_cursor_bottom(&mut self, _: &ScrollCursorBottom, cx: &mut ViewContext<Editor>) {
+        let snapshot = self.snapshot(cx).display_snapshot;
+        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
+        let visible_rows = if let Some(visible_rows) = self.visible_line_count() {
             visible_rows as u32
         } else {
             return;
         };
 
-        let mut new_screen_top = editor.selections.newest_display(cx).head();
+        let mut new_screen_top = self.selections.newest_display(cx).head();
         *new_screen_top.row_mut() = new_screen_top
             .row()
             .saturating_sub(visible_rows.saturating_sub(scroll_margin_rows));
@@ -141,7 +92,7 @@ impl Editor {
         let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
         let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
 
-        editor.set_scroll_anchor(
+        self.set_scroll_anchor(
             ScrollAnchor {
                 anchor: new_anchor,
                 offset: Default::default(),

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

@@ -1,6 +1,6 @@
-use std::cmp;
+use std::{cmp, f32};
 
-use gpui::ViewContext;
+use gpui::{px, Pixels, ViewContext};
 use language::Point;
 
 use crate::{display_map::ToDisplayPoint, Editor, EditorMode, LineWithInvisibles};
@@ -48,11 +48,11 @@ impl AutoscrollStrategy {
 impl Editor {
     pub fn autoscroll_vertically(
         &mut self,
-        viewport_height: f32,
-        line_height: f32,
+        viewport_height: Pixels,
+        line_height: Pixels,
         cx: &mut ViewContext<Editor>,
     ) -> bool {
-        let visible_lines = viewport_height / line_height;
+        let visible_lines = f32::from(viewport_height / line_height);
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let mut scroll_position = self.scroll_manager.scroll_position(&display_map);
         let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
@@ -60,8 +60,8 @@ impl Editor {
         } else {
             display_map.max_point().row() as f32
         };
-        if scroll_position.y() > max_scroll_top {
-            scroll_position.set_y(max_scroll_top);
+        if scroll_position.y > max_scroll_top {
+            scroll_position.y = max_scroll_top;
             self.set_scroll_position(scroll_position, cx);
         }
 
@@ -136,31 +136,31 @@ impl Editor {
                 let margin = margin.min(self.scroll_manager.vertical_scroll_margin);
                 let target_top = (target_top - margin).max(0.0);
                 let target_bottom = target_bottom + margin;
-                let start_row = scroll_position.y();
+                let start_row = scroll_position.y;
                 let end_row = start_row + visible_lines;
 
                 let needs_scroll_up = target_top < start_row;
                 let needs_scroll_down = target_bottom >= end_row;
 
                 if needs_scroll_up && !needs_scroll_down {
-                    scroll_position.set_y(target_top);
+                    scroll_position.y = target_top;
                     self.set_scroll_position_internal(scroll_position, local, true, cx);
                 }
                 if !needs_scroll_up && needs_scroll_down {
-                    scroll_position.set_y(target_bottom - visible_lines);
+                    scroll_position.y = target_bottom - visible_lines;
                     self.set_scroll_position_internal(scroll_position, local, true, cx);
                 }
             }
             AutoscrollStrategy::Center => {
-                scroll_position.set_y((target_top - margin).max(0.0));
+                scroll_position.y = (target_top - margin).max(0.0);
                 self.set_scroll_position_internal(scroll_position, local, true, cx);
             }
             AutoscrollStrategy::Top => {
-                scroll_position.set_y((target_top).max(0.0));
+                scroll_position.y = (target_top).max(0.0);
                 self.set_scroll_position_internal(scroll_position, local, true, cx);
             }
             AutoscrollStrategy::Bottom => {
-                scroll_position.set_y((target_bottom - visible_lines).max(0.0));
+                scroll_position.y = (target_bottom - visible_lines).max(0.0);
                 self.set_scroll_position_internal(scroll_position, local, true, cx);
             }
         }
@@ -178,9 +178,9 @@ impl Editor {
     pub fn autoscroll_horizontally(
         &mut self,
         start_row: u32,
-        viewport_width: f32,
-        scroll_width: f32,
-        max_glyph_width: f32,
+        viewport_width: Pixels,
+        scroll_width: Pixels,
+        max_glyph_width: Pixels,
         layouts: &[LineWithInvisibles],
         cx: &mut ViewContext<Self>,
     ) -> bool {
@@ -191,11 +191,11 @@ impl Editor {
         let mut target_right;
 
         if self.highlighted_rows.is_some() {
-            target_left = 0.0_f32;
-            target_right = 0.0_f32;
+            target_left = px(0.);
+            target_right = px(0.);
         } else {
-            target_left = std::f32::INFINITY;
-            target_right = 0.0_f32;
+            target_left = px(f32::INFINITY);
+            target_right = px(0.);
             for selection in selections {
                 let head = selection.head().to_display_point(&display_map);
                 if head.row() >= start_row && head.row() < start_row + layouts.len() as u32 {
@@ -222,20 +222,15 @@ impl Editor {
             return false;
         }
 
-        let scroll_left = self.scroll_manager.anchor.offset.x() * max_glyph_width;
+        let scroll_left = self.scroll_manager.anchor.offset.x * max_glyph_width;
         let scroll_right = scroll_left + viewport_width;
 
         if target_left < scroll_left {
-            self.scroll_manager
-                .anchor
-                .offset
-                .set_x(target_left / max_glyph_width);
+            self.scroll_manager.anchor.offset.x = (target_left / max_glyph_width).into();
             true
         } else if target_right > scroll_right {
-            self.scroll_manager
-                .anchor
-                .offset
-                .set_x((target_right - viewport_width) / max_glyph_width);
+            self.scroll_manager.anchor.offset.x =
+                ((target_right - viewport_width) / max_glyph_width).into();
             true
         } else {
             false

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

@@ -6,7 +6,7 @@ use std::{
 };
 
 use collections::HashMap;
-use gpui::{AppContext, ModelHandle};
+use gpui::{AppContext, Model, Pixels};
 use itertools::Itertools;
 use language::{Bias, Point, Selection, SelectionGoal, TextDimension, ToPoint};
 use util::post_inc;
@@ -25,8 +25,8 @@ pub struct PendingSelection {
 
 #[derive(Debug, Clone)]
 pub struct SelectionsCollection {
-    display_map: ModelHandle<DisplayMap>,
-    buffer: ModelHandle<MultiBuffer>,
+    display_map: Model<DisplayMap>,
+    buffer: Model<MultiBuffer>,
     pub next_selection_id: usize,
     pub line_mode: bool,
     disjoint: Arc<[Selection<Anchor>]>,
@@ -34,7 +34,7 @@ pub struct SelectionsCollection {
 }
 
 impl SelectionsCollection {
-    pub fn new(display_map: ModelHandle<DisplayMap>, buffer: ModelHandle<MultiBuffer>) -> Self {
+    pub fn new(display_map: Model<DisplayMap>, buffer: Model<MultiBuffer>) -> Self {
         Self {
             display_map,
             buffer,
@@ -306,19 +306,19 @@ impl SelectionsCollection {
         &mut self,
         display_map: &DisplaySnapshot,
         row: u32,
-        positions: &Range<f32>,
+        positions: &Range<Pixels>,
         reversed: bool,
         text_layout_details: &TextLayoutDetails,
     ) -> Option<Selection<Point>> {
         let is_empty = positions.start == positions.end;
         let line_len = display_map.line_len(row);
 
-        let layed_out_line = display_map.lay_out_line_for_row(row, &text_layout_details);
+        let line = display_map.layout_row(row, &text_layout_details);
 
-        let start_col = layed_out_line.closest_index_for_x(positions.start) as u32;
-        if start_col < line_len || (is_empty && positions.start == layed_out_line.width()) {
+        let start_col = line.closest_index_for_x(positions.start) as u32;
+        if start_col < line_len || (is_empty && positions.start == line.width) {
             let start = DisplayPoint::new(row, start_col);
-            let end_col = layed_out_line.closest_index_for_x(positions.end) as u32;
+            let end_col = line.closest_index_for_x(positions.end) as u32;
             let end = DisplayPoint::new(row, end_col);
 
             Some(Selection {
@@ -327,8 +327,8 @@ impl SelectionsCollection {
                 end: end.to_point(display_map),
                 reversed,
                 goal: SelectionGoal::HorizontalRange {
-                    start: positions.start,
-                    end: positions.end,
+                    start: positions.start.into(),
+                    end: positions.end.into(),
                 },
             })
         } else {
@@ -592,7 +592,10 @@ impl<'a> MutableSelectionsCollection<'a> {
         self.select(selections)
     }
 
-    pub fn select_anchor_ranges<I: IntoIterator<Item = Range<Anchor>>>(&mut self, ranges: I) {
+    pub fn select_anchor_ranges<I>(&mut self, ranges: I)
+    where
+        I: IntoIterator<Item = Range<Anchor>>,
+    {
         let buffer = self.buffer.read(self.cx).snapshot(self.cx);
         let selections = ranges
             .into_iter()
@@ -614,7 +617,6 @@ impl<'a> MutableSelectionsCollection<'a> {
                 }
             })
             .collect::<Vec<_>>();
-
         self.select_anchors(selections)
     }
 

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

@@ -6,7 +6,7 @@ use crate::{
     DisplayPoint, Editor, EditorMode, MultiBuffer,
 };
 
-use gpui::{ModelHandle, ViewContext};
+use gpui::{Context, Model, Pixels, ViewContext};
 
 use project::Project;
 use util::test::{marked_text_offsets, marked_text_ranges};
@@ -26,19 +26,11 @@ pub fn marked_display_snapshot(
 ) -> (DisplaySnapshot, Vec<DisplayPoint>) {
     let (unmarked_text, markers) = marked_text_offsets(text);
 
-    let family_id = cx
-        .font_cache()
-        .load_family(&["Helvetica"], &Default::default())
-        .unwrap();
-    let font_id = cx
-        .font_cache()
-        .select_font(family_id, &Default::default())
-        .unwrap();
-    let font_size = 14.0;
+    let font = cx.text_style().font();
+    let font_size: Pixels = 14usize.into();
 
     let buffer = MultiBuffer::build_simple(&unmarked_text, cx);
-    let display_map =
-        cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx));
+    let display_map = cx.new_model(|cx| DisplayMap::new(buffer, font, font_size, None, 1, 1, cx));
     let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
     let markers = markers
         .into_iter()
@@ -67,17 +59,16 @@ pub fn assert_text_with_selections(
 // RA thinks this is dead code even though it is used in a whole lot of tests
 #[allow(dead_code)]
 #[cfg(any(test, feature = "test-support"))]
-pub(crate) fn build_editor(
-    buffer: ModelHandle<MultiBuffer>,
-    cx: &mut ViewContext<Editor>,
-) -> Editor {
-    Editor::new(EditorMode::Full, buffer, None, None, cx)
+pub(crate) fn build_editor(buffer: Model<MultiBuffer>, cx: &mut ViewContext<Editor>) -> Editor {
+    // todo!()
+    Editor::new(EditorMode::Full, buffer, None, /*None,*/ cx)
 }
 
 pub(crate) fn build_editor_with_project(
-    project: ModelHandle<Project>,
-    buffer: ModelHandle<MultiBuffer>,
+    project: Model<Project>,
+    buffer: Model<MultiBuffer>,
     cx: &mut ViewContext<Editor>,
 ) -> Editor {
-    Editor::new(EditorMode::Full, buffer, Some(project), None, cx)
+    // todo!()
+    Editor::new(EditorMode::Full, buffer, Some(project), /*None,*/ cx)
 }

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

@@ -5,11 +5,12 @@ use std::{
 };
 
 use anyhow::Result;
+use serde_json::json;
 
 use crate::{Editor, ToPoint};
 use collections::HashSet;
 use futures::Future;
-use gpui::{json, ViewContext, ViewHandle};
+use gpui::{View, ViewContext, VisualTestContext};
 use indoc::indoc;
 use language::{point_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageQueries};
 use lsp::{notification, request};
@@ -18,12 +19,12 @@ use project::Project;
 use smol::stream::StreamExt;
 use workspace::{AppState, Workspace, WorkspaceHandle};
 
-use super::editor_test_context::EditorTestContext;
+use super::editor_test_context::{AssertionContextManager, EditorTestContext};
 
 pub struct EditorLspTestContext<'a> {
     pub cx: EditorTestContext<'a>,
     pub lsp: lsp::FakeLanguageServer,
-    pub workspace: ViewHandle<Workspace>,
+    pub workspace: View<Workspace>,
     pub buffer_lsp_url: lsp::Url,
 }
 
@@ -33,8 +34,6 @@ impl<'a> EditorLspTestContext<'a> {
         capabilities: lsp::ServerCapabilities,
         cx: &'a mut gpui::TestAppContext,
     ) -> EditorLspTestContext<'a> {
-        use json::json;
-
         let app_state = cx.update(AppState::test);
 
         cx.update(|cx| {
@@ -60,6 +59,7 @@ impl<'a> EditorLspTestContext<'a> {
             .await;
 
         let project = Project::test(app_state.fs.clone(), [], cx).await;
+
         project.update(cx, |project, _| project.languages().add(Arc::new(language)));
 
         app_state
@@ -69,37 +69,38 @@ impl<'a> EditorLspTestContext<'a> {
             .await;
 
         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-        let workspace = window.root(cx);
+
+        let workspace = window.root_view(cx).unwrap();
+
+        let mut cx = VisualTestContext::from_window(*window.deref(), cx);
         project
-            .update(cx, |project, cx| {
+            .update(&mut cx, |project, cx| {
                 project.find_or_create_local_worktree("/root", true, cx)
             })
             .await
             .unwrap();
         cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
             .await;
-
         let file = cx.read(|cx| workspace.file_project_paths(cx)[0].clone());
         let item = workspace
-            .update(cx, |workspace, cx| {
+            .update(&mut cx, |workspace, cx| {
                 workspace.open_path(file, None, true, cx)
             })
             .await
             .expect("Could not open test file");
-
         let editor = cx.update(|cx| {
             item.act_as::<Editor>(cx)
                 .expect("Opened test file wasn't an editor")
         });
-        editor.update(cx, |_, cx| cx.focus_self());
+        editor.update(&mut cx, |editor, cx| editor.focus(cx));
 
         let lsp = fake_servers.next().await.unwrap();
-
         Self {
             cx: EditorTestContext {
                 cx,
                 window: window.into(),
                 editor,
+                assertion_cx: AssertionContextManager::new(),
             },
             lsp,
             workspace,
@@ -257,7 +258,7 @@ impl<'a> EditorLspTestContext<'a> {
     where
         F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
     {
-        self.workspace.update(self.cx.cx, update)
+        self.workspace.update(&mut self.cx.cx, update)
     }
 
     pub fn handle_request<T, F, Fut>(

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

@@ -1,17 +1,23 @@
 use crate::{
     display_map::ToDisplayPoint, AnchorRangeExt, Autoscroll, DisplayPoint, Editor, MultiBuffer,
 };
+use collections::BTreeMap;
 use futures::Future;
 use gpui::{
-    executor::Foreground, keymap_matcher::Keystroke, AnyWindowHandle, AppContext, ContextHandle,
-    ModelContext, ViewContext, ViewHandle,
+    AnyWindowHandle, AppContext, Keystroke, ModelContext, View, ViewContext, VisualTestContext,
 };
 use indoc::indoc;
+use itertools::Itertools;
 use language::{Buffer, BufferSnapshot};
+use parking_lot::RwLock;
 use project::{FakeFs, Project};
 use std::{
     any::TypeId,
     ops::{Deref, DerefMut, Range},
+    sync::{
+        atomic::{AtomicUsize, Ordering},
+        Arc,
+    },
 };
 use util::{
     assert_set_eq,
@@ -21,14 +27,15 @@ use util::{
 use super::build_editor_with_project;
 
 pub struct EditorTestContext<'a> {
-    pub cx: &'a mut gpui::TestAppContext,
+    pub cx: gpui::VisualTestContext<'a>,
     pub window: AnyWindowHandle,
-    pub editor: ViewHandle<Editor>,
+    pub editor: View<Editor>,
+    pub assertion_cx: AssertionContextManager,
 }
 
 impl<'a> EditorTestContext<'a> {
     pub async fn new(cx: &'a mut gpui::TestAppContext) -> EditorTestContext<'a> {
-        let fs = FakeFs::new(cx.background());
+        let fs = FakeFs::new(cx.executor());
         // fs.insert_file("/file", "".to_owned()).await;
         fs.insert_tree(
             "/root",
@@ -44,15 +51,18 @@ impl<'a> EditorTestContext<'a> {
             })
             .await
             .unwrap();
-        let window = cx.add_window(|cx| {
-            cx.focus_self();
-            build_editor_with_project(project, MultiBuffer::build_from_buffer(buffer, cx), cx)
+        let editor = cx.add_window(|cx| {
+            let editor =
+                build_editor_with_project(project, MultiBuffer::build_from_buffer(buffer, cx), cx);
+            editor.focus(cx);
+            editor
         });
-        let editor = window.root(cx);
+        let editor_view = editor.root_view(cx).unwrap();
         Self {
-            cx,
-            window: window.into(),
-            editor,
+            cx: VisualTestContext::from_window(*editor.deref(), cx),
+            window: editor.into(),
+            editor: editor_view,
+            assertion_cx: AssertionContextManager::new(),
         }
     }
 
@@ -60,24 +70,28 @@ impl<'a> EditorTestContext<'a> {
         &self,
         predicate: impl FnMut(&Editor, &AppContext) -> bool,
     ) -> impl Future<Output = ()> {
-        self.editor.condition(self.cx, predicate)
+        self.editor
+            .condition::<crate::EditorEvent>(&self.cx, predicate)
     }
 
-    pub fn editor<F, T>(&self, read: F) -> T
+    #[track_caller]
+    pub fn editor<F, T>(&mut self, read: F) -> T
     where
         F: FnOnce(&Editor, &ViewContext<Editor>) -> T,
     {
-        self.editor.read_with(self.cx, read)
+        self.editor
+            .update(&mut self.cx, |this, cx| read(&this, &cx))
     }
 
+    #[track_caller]
     pub fn update_editor<F, T>(&mut self, update: F) -> T
     where
         F: FnOnce(&mut Editor, &mut ViewContext<Editor>) -> T,
     {
-        self.editor.update(self.cx, update)
+        self.editor.update(&mut self.cx, update)
     }
 
-    pub fn multibuffer<F, T>(&self, read: F) -> T
+    pub fn multibuffer<F, T>(&mut self, read: F) -> T
     where
         F: FnOnce(&MultiBuffer, &AppContext) -> T,
     {
@@ -91,11 +105,11 @@ impl<'a> EditorTestContext<'a> {
         self.update_editor(|editor, cx| editor.buffer().update(cx, update))
     }
 
-    pub fn buffer_text(&self) -> String {
+    pub fn buffer_text(&mut self) -> String {
         self.multibuffer(|buffer, cx| buffer.snapshot(cx).text())
     }
 
-    pub fn buffer<F, T>(&self, read: F) -> T
+    pub fn buffer<F, T>(&mut self, read: F) -> T
     where
         F: FnOnce(&Buffer, &AppContext) -> T,
     {
@@ -115,10 +129,18 @@ impl<'a> EditorTestContext<'a> {
         })
     }
 
-    pub fn buffer_snapshot(&self) -> BufferSnapshot {
+    pub fn buffer_snapshot(&mut self) -> BufferSnapshot {
         self.buffer(|buffer, _| buffer.snapshot())
     }
 
+    pub fn add_assertion_context(&self, context: String) -> ContextHandle {
+        self.assertion_cx.add_context(context)
+    }
+
+    pub fn assertion_context(&self) -> String {
+        self.assertion_cx.context()
+    }
+
     pub fn simulate_keystroke(&mut self, keystroke_text: &str) -> ContextHandle {
         let keystroke_under_test_handle =
             self.add_assertion_context(format!("Simulated Keystroke: {:?}", keystroke_text));
@@ -142,16 +164,16 @@ impl<'a> EditorTestContext<'a> {
         // before returning.
         // NOTE: we don't do this in simulate_keystroke() because a possible cause of bugs is that typing too
         // quickly races with async actions.
-        if let Foreground::Deterministic { cx_id: _, executor } = self.cx.foreground().as_ref() {
-            executor.run_until_parked();
-        } else {
-            unreachable!();
-        }
+        self.cx.background_executor.run_until_parked();
 
         keystrokes_under_test_handle
     }
 
-    pub fn ranges(&self, marked_text: &str) -> Vec<Range<usize>> {
+    pub fn run_until_parked(&mut self) {
+        self.cx.background_executor.run_until_parked();
+    }
+
+    pub fn ranges(&mut self, marked_text: &str) -> Vec<Range<usize>> {
         let (unmarked_text, ranges) = marked_text_ranges(marked_text, false);
         assert_eq!(self.buffer_text(), unmarked_text);
         ranges
@@ -161,12 +183,12 @@ impl<'a> EditorTestContext<'a> {
         let ranges = self.ranges(marked_text);
         let snapshot = self
             .editor
-            .update(self.cx, |editor, cx| editor.snapshot(cx));
+            .update(&mut self.cx, |editor, cx| editor.snapshot(cx));
         ranges[0].start.to_display_point(&snapshot)
     }
 
     // Returns anchors for the current buffer using `ยซ` and `ยป`
-    pub fn text_anchor_range(&self, marked_text: &str) -> Range<language::Anchor> {
+    pub fn text_anchor_range(&mut self, marked_text: &str) -> Range<language::Anchor> {
         let ranges = self.ranges(marked_text);
         let snapshot = self.buffer_snapshot();
         snapshot.anchor_before(ranges[0].start)..snapshot.anchor_after(ranges[0].end)
@@ -191,7 +213,7 @@ impl<'a> EditorTestContext<'a> {
             marked_text.escape_debug().to_string()
         ));
         let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
-        self.editor.update(self.cx, |editor, cx| {
+        self.editor.update(&mut self.cx, |editor, cx| {
             editor.set_text(unmarked_text, cx);
             editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
                 s.select_ranges(selection_ranges)
@@ -207,7 +229,7 @@ impl<'a> EditorTestContext<'a> {
             marked_text.escape_debug().to_string()
         ));
         let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
-        self.editor.update(self.cx, |editor, cx| {
+        self.editor.update(&mut self.cx, |editor, cx| {
             assert_eq!(editor.text(cx), unmarked_text);
             editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
                 s.select_ranges(selection_ranges)
@@ -274,9 +296,12 @@ impl<'a> EditorTestContext<'a> {
         self.assert_selections(expected_selections, expected_marked_text)
     }
 
-    fn editor_selections(&self) -> Vec<Range<usize>> {
+    #[track_caller]
+    fn editor_selections(&mut self) -> Vec<Range<usize>> {
         self.editor
-            .read_with(self.cx, |editor, cx| editor.selections.all::<usize>(cx))
+            .update(&mut self.cx, |editor, cx| {
+                editor.selections.all::<usize>(cx)
+            })
             .into_iter()
             .map(|s| {
                 if s.reversed {
@@ -301,14 +326,14 @@ impl<'a> EditorTestContext<'a> {
             panic!(
                 indoc! {"
 
-                    {}Editor has unexpected selections.
+                {}Editor has unexpected selections.
 
-                    Expected selections:
-                    {}
+                Expected selections:
+                {}
 
-                    Actual selections:
-                    {}
-                "},
+                Actual selections:
+                {}
+            "},
                 self.assertion_context(),
                 expected_marked_text,
                 actual_marked_text,
@@ -321,7 +346,7 @@ impl<'a> Deref for EditorTestContext<'a> {
     type Target = gpui::TestAppContext;
 
     fn deref(&self) -> &Self::Target {
-        self.cx
+        &self.cx
     }
 }
 
@@ -330,3 +355,50 @@ impl<'a> DerefMut for EditorTestContext<'a> {
         &mut self.cx
     }
 }
+
+/// Tracks string context to be printed when assertions fail.
+/// Often this is done by storing a context string in the manager and returning the handle.
+#[derive(Clone)]
+pub struct AssertionContextManager {
+    id: Arc<AtomicUsize>,
+    contexts: Arc<RwLock<BTreeMap<usize, String>>>,
+}
+
+impl AssertionContextManager {
+    pub fn new() -> Self {
+        Self {
+            id: Arc::new(AtomicUsize::new(0)),
+            contexts: Arc::new(RwLock::new(BTreeMap::new())),
+        }
+    }
+
+    pub fn add_context(&self, context: String) -> ContextHandle {
+        let id = self.id.fetch_add(1, Ordering::Relaxed);
+        let mut contexts = self.contexts.write();
+        contexts.insert(id, context);
+        ContextHandle {
+            id,
+            manager: self.clone(),
+        }
+    }
+
+    pub fn context(&self) -> String {
+        let contexts = self.contexts.read();
+        format!("\n{}\n", contexts.values().join("\n"))
+    }
+}
+
+/// Used to track the lifetime of a piece of context so that it can be provided when an assertion fails.
+/// For example, in the EditorTestContext, `set_state` returns a context handle so that if an assertion fails,
+/// the state that was set initially for the failure can be printed in the error message
+pub struct ContextHandle {
+    id: usize,
+    manager: AssertionContextManager,
+}
+
+impl Drop for ContextHandle {
+    fn drop(&mut self) {
+        let mut contexts = self.manager.contexts.write();
+        contexts.remove(&self.id);
+    }
+}

crates/editor2/Cargo.toml ๐Ÿ”—

@@ -1,93 +0,0 @@
-[package]
-name = "editor2"
-version = "0.1.0"
-edition = "2021"
-publish = false
-
-[lib]
-path = "src/editor.rs"
-doctest = false
-
-[features]
-test-support = [
-    "copilot/test-support",
-    "text/test-support",
-    "language/test-support",
-    "gpui/test-support",
-    "multi_buffer/test-support",
-    "project/test-support",
-    "util/test-support",
-    "workspace/test-support",
-    "tree-sitter-rust",
-    "tree-sitter-typescript"
-]
-
-[dependencies]
-client = { package = "client2", path = "../client2" }
-clock = { path = "../clock" }
-copilot = { package="copilot2", path = "../copilot2" }
-db = { package="db2", path = "../db2" }
-collections = { path = "../collections" }
-# context_menu = { path = "../context_menu" }
-fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
-git = { package = "git3", path = "../git3" }
-gpui = { package = "gpui2", path = "../gpui2" }
-language = { package = "language2", path = "../language2" }
-lsp = { package = "lsp2", path = "../lsp2" }
-multi_buffer = { package = "multi_buffer2", path = "../multi_buffer2" }
-project = { package = "project2", path = "../project2" }
-rpc = { package = "rpc2", path = "../rpc2" }
-rich_text = { package = "rich_text2", path = "../rich_text2" }
-settings = { package="settings2", path = "../settings2" }
-snippet = { path = "../snippet" }
-sum_tree = { path = "../sum_tree" }
-text = { package="text2", path = "../text2" }
-theme = { package="theme2", path = "../theme2" }
-ui = { package = "ui2", path = "../ui2" }
-util = { path = "../util" }
-sqlez = { path = "../sqlez" }
-workspace = { package = "workspace2", path = "../workspace2" }
-
-aho-corasick = "1.1"
-anyhow.workspace = true
-convert_case = "0.6.0"
-futures.workspace = true
-indoc = "1.0.4"
-itertools = "0.10"
-lazy_static.workspace = true
-log.workspace = true
-ordered-float.workspace = true
-parking_lot.workspace = true
-postage.workspace = true
-rand.workspace = true
-schemars.workspace = true
-serde.workspace = true
-serde_json.workspace = true
-serde_derive.workspace = true
-smallvec.workspace = true
-smol.workspace = true
-
-tree-sitter-rust = { workspace = true, optional = true }
-tree-sitter-html = { workspace = true, optional = true }
-tree-sitter-typescript = { workspace = true, optional = true }
-
-[dev-dependencies]
-copilot = { package="copilot2", path = "../copilot2", features = ["test-support"] }
-text = { package="text2", path = "../text2", features = ["test-support"] }
-language = { package="language2", path = "../language2", features = ["test-support"] }
-lsp = { package = "lsp2", path = "../lsp2", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
-util = { path = "../util", features = ["test-support"] }
-project = { package = "project2", path = "../project2", features = ["test-support"] }
-settings = { package = "settings2", path = "../settings2", features = ["test-support"] }
-workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
-multi_buffer = { package = "multi_buffer2", path = "../multi_buffer2", features = ["test-support"] }
-
-ctor.workspace = true
-env_logger.workspace = true
-rand.workspace = true
-unindent.workspace = true
-tree-sitter.workspace = true
-tree-sitter-rust.workspace = true
-tree-sitter-html.workspace = true
-tree-sitter-typescript.workspace = true
@@ -1,107 +0,0 @@
-use crate::EditorSettings;
-use gpui::ModelContext;
-use settings::Settings;
-use settings::SettingsStore;
-use smol::Timer;
-use std::time::Duration;
-
-pub struct BlinkManager {
-    blink_interval: Duration,
-
-    blink_epoch: usize,
-    blinking_paused: bool,
-    visible: bool,
-    enabled: bool,
-}
-
-impl BlinkManager {
-    pub fn new(blink_interval: Duration, cx: &mut ModelContext<Self>) -> Self {
-        // Make sure we blink the cursors if the setting is re-enabled
-        cx.observe_global::<SettingsStore>(move |this, cx| {
-            this.blink_cursors(this.blink_epoch, cx)
-        })
-        .detach();
-
-        Self {
-            blink_interval,
-
-            blink_epoch: 0,
-            blinking_paused: false,
-            visible: true,
-            enabled: false,
-        }
-    }
-
-    fn next_blink_epoch(&mut self) -> usize {
-        self.blink_epoch += 1;
-        self.blink_epoch
-    }
-
-    pub fn pause_blinking(&mut self, cx: &mut ModelContext<Self>) {
-        self.show_cursor(cx);
-
-        let epoch = self.next_blink_epoch();
-        let interval = self.blink_interval;
-        cx.spawn(|this, mut cx| async move {
-            Timer::after(interval).await;
-            this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
-        })
-        .detach();
-    }
-
-    fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ModelContext<Self>) {
-        if epoch == self.blink_epoch {
-            self.blinking_paused = false;
-            self.blink_cursors(epoch, cx);
-        }
-    }
-
-    fn blink_cursors(&mut self, epoch: usize, cx: &mut ModelContext<Self>) {
-        if EditorSettings::get_global(cx).cursor_blink {
-            if epoch == self.blink_epoch && self.enabled && !self.blinking_paused {
-                self.visible = !self.visible;
-                cx.notify();
-
-                let epoch = self.next_blink_epoch();
-                let interval = self.blink_interval;
-                cx.spawn(|this, mut cx| async move {
-                    Timer::after(interval).await;
-                    if let Some(this) = this.upgrade() {
-                        this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx))
-                            .ok();
-                    }
-                })
-                .detach();
-            }
-        } else {
-            self.show_cursor(cx);
-        }
-    }
-
-    pub fn show_cursor(&mut self, cx: &mut ModelContext<'_, BlinkManager>) {
-        if !self.visible {
-            self.visible = true;
-            cx.notify();
-        }
-    }
-
-    pub fn enable(&mut self, cx: &mut ModelContext<Self>) {
-        if self.enabled {
-            return;
-        }
-
-        self.enabled = true;
-        // Set cursors as invisible and start blinking: this causes cursors
-        // to be visible during the next render.
-        self.visible = false;
-        self.blink_cursors(self.blink_epoch, cx);
-    }
-
-    pub fn disable(&mut self, _cx: &mut ModelContext<Self>) {
-        self.enabled = false;
-    }
-
-    pub fn visible(&self) -> bool {
-        self.visible
-    }
-}

crates/editor2/src/display_map.rs ๐Ÿ”—

@@ -1,1854 +0,0 @@
-mod block_map;
-mod fold_map;
-mod inlay_map;
-mod tab_map;
-mod wrap_map;
-
-use crate::EditorStyle;
-use crate::{
-    link_go_to_definition::InlayHighlight, movement::TextLayoutDetails, Anchor, AnchorRangeExt,
-    InlayId, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint,
-};
-pub use block_map::{BlockMap, BlockPoint};
-use collections::{BTreeMap, HashMap, HashSet};
-use fold_map::FoldMap;
-use gpui::{Font, HighlightStyle, Hsla, LineLayout, Model, ModelContext, Pixels, UnderlineStyle};
-use inlay_map::InlayMap;
-use language::{
-    language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription,
-};
-use lsp::DiagnosticSeverity;
-use std::{any::TypeId, borrow::Cow, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc};
-use sum_tree::{Bias, TreeMap};
-use tab_map::TabMap;
-
-use wrap_map::WrapMap;
-
-pub use block_map::{
-    BlockBufferRows as DisplayBufferRows, BlockChunks as DisplayChunks, BlockContext,
-    BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock, TransformBlock,
-};
-
-pub use self::fold_map::{Fold, FoldPoint};
-pub use self::inlay_map::{Inlay, InlayOffset, InlayPoint};
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum FoldStatus {
-    Folded,
-    Foldable,
-}
-
-const UNNECESSARY_CODE_FADE: f32 = 0.3;
-
-pub trait ToDisplayPoint {
-    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint;
-}
-
-type TextHighlights = TreeMap<Option<TypeId>, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>;
-type InlayHighlights = BTreeMap<TypeId, HashMap<InlayId, (HighlightStyle, InlayHighlight)>>;
-
-pub struct DisplayMap {
-    buffer: Model<MultiBuffer>,
-    buffer_subscription: BufferSubscription,
-    fold_map: FoldMap,
-    inlay_map: InlayMap,
-    tab_map: TabMap,
-    wrap_map: Model<WrapMap>,
-    block_map: BlockMap,
-    text_highlights: TextHighlights,
-    inlay_highlights: InlayHighlights,
-    pub clip_at_line_ends: bool,
-}
-
-impl DisplayMap {
-    pub fn new(
-        buffer: Model<MultiBuffer>,
-        font: Font,
-        font_size: Pixels,
-        wrap_width: Option<Pixels>,
-        buffer_header_height: u8,
-        excerpt_header_height: u8,
-        cx: &mut ModelContext<Self>,
-    ) -> Self {
-        let buffer_subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
-
-        let tab_size = Self::tab_size(&buffer, cx);
-        let (inlay_map, snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));
-        let (fold_map, snapshot) = FoldMap::new(snapshot);
-        let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
-        let (wrap_map, snapshot) = WrapMap::new(snapshot, font, font_size, wrap_width, cx);
-        let block_map = BlockMap::new(snapshot, buffer_header_height, excerpt_header_height);
-        cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach();
-        DisplayMap {
-            buffer,
-            buffer_subscription,
-            fold_map,
-            inlay_map,
-            tab_map,
-            wrap_map,
-            block_map,
-            text_highlights: Default::default(),
-            inlay_highlights: Default::default(),
-            clip_at_line_ends: false,
-        }
-    }
-
-    pub fn snapshot(&mut self, cx: &mut ModelContext<Self>) -> DisplaySnapshot {
-        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
-        let edits = self.buffer_subscription.consume().into_inner();
-        let (inlay_snapshot, edits) = self.inlay_map.sync(buffer_snapshot, edits);
-        let (fold_snapshot, edits) = self.fold_map.read(inlay_snapshot.clone(), edits);
-        let tab_size = Self::tab_size(&self.buffer, cx);
-        let (tab_snapshot, edits) = self.tab_map.sync(fold_snapshot.clone(), edits, tab_size);
-        let (wrap_snapshot, edits) = self
-            .wrap_map
-            .update(cx, |map, cx| map.sync(tab_snapshot.clone(), edits, cx));
-        let block_snapshot = self.block_map.read(wrap_snapshot.clone(), edits);
-
-        DisplaySnapshot {
-            buffer_snapshot: self.buffer.read(cx).snapshot(cx),
-            fold_snapshot,
-            inlay_snapshot,
-            tab_snapshot,
-            wrap_snapshot,
-            block_snapshot,
-            text_highlights: self.text_highlights.clone(),
-            inlay_highlights: self.inlay_highlights.clone(),
-            clip_at_line_ends: self.clip_at_line_ends,
-        }
-    }
-
-    pub fn set_state(&mut self, other: &DisplaySnapshot, cx: &mut ModelContext<Self>) {
-        self.fold(
-            other
-                .folds_in_range(0..other.buffer_snapshot.len())
-                .map(|fold| fold.range.to_offset(&other.buffer_snapshot)),
-            cx,
-        );
-    }
-
-    pub fn fold<T: ToOffset>(
-        &mut self,
-        ranges: impl IntoIterator<Item = Range<T>>,
-        cx: &mut ModelContext<Self>,
-    ) {
-        let snapshot = self.buffer.read(cx).snapshot(cx);
-        let edits = self.buffer_subscription.consume().into_inner();
-        let tab_size = Self::tab_size(&self.buffer, cx);
-        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
-        let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
-        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
-        let (snapshot, edits) = self
-            .wrap_map
-            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
-        self.block_map.read(snapshot, edits);
-        let (snapshot, edits) = fold_map.fold(ranges);
-        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
-        let (snapshot, edits) = self
-            .wrap_map
-            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
-        self.block_map.read(snapshot, edits);
-    }
-
-    pub fn unfold<T: ToOffset>(
-        &mut self,
-        ranges: impl IntoIterator<Item = Range<T>>,
-        inclusive: bool,
-        cx: &mut ModelContext<Self>,
-    ) {
-        let snapshot = self.buffer.read(cx).snapshot(cx);
-        let edits = self.buffer_subscription.consume().into_inner();
-        let tab_size = Self::tab_size(&self.buffer, cx);
-        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
-        let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
-        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
-        let (snapshot, edits) = self
-            .wrap_map
-            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
-        self.block_map.read(snapshot, edits);
-        let (snapshot, edits) = fold_map.unfold(ranges, inclusive);
-        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
-        let (snapshot, edits) = self
-            .wrap_map
-            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
-        self.block_map.read(snapshot, edits);
-    }
-
-    pub fn insert_blocks(
-        &mut self,
-        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
-        cx: &mut ModelContext<Self>,
-    ) -> Vec<BlockId> {
-        let snapshot = self.buffer.read(cx).snapshot(cx);
-        let edits = self.buffer_subscription.consume().into_inner();
-        let tab_size = Self::tab_size(&self.buffer, cx);
-        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
-        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
-        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
-        let (snapshot, edits) = self
-            .wrap_map
-            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
-        let mut block_map = self.block_map.write(snapshot, edits);
-        block_map.insert(blocks)
-    }
-
-    pub fn replace_blocks(&mut self, styles: HashMap<BlockId, RenderBlock>) {
-        self.block_map.replace(styles);
-    }
-
-    pub fn remove_blocks(&mut self, ids: HashSet<BlockId>, cx: &mut ModelContext<Self>) {
-        let snapshot = self.buffer.read(cx).snapshot(cx);
-        let edits = self.buffer_subscription.consume().into_inner();
-        let tab_size = Self::tab_size(&self.buffer, cx);
-        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
-        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
-        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
-        let (snapshot, edits) = self
-            .wrap_map
-            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
-        let mut block_map = self.block_map.write(snapshot, edits);
-        block_map.remove(ids);
-    }
-
-    pub fn highlight_text(
-        &mut self,
-        type_id: TypeId,
-        ranges: Vec<Range<Anchor>>,
-        style: HighlightStyle,
-    ) {
-        self.text_highlights
-            .insert(Some(type_id), Arc::new((style, ranges)));
-    }
-
-    pub fn highlight_inlays(
-        &mut self,
-        type_id: TypeId,
-        highlights: Vec<InlayHighlight>,
-        style: HighlightStyle,
-    ) {
-        for highlight in highlights {
-            self.inlay_highlights
-                .entry(type_id)
-                .or_default()
-                .insert(highlight.inlay, (style, highlight));
-        }
-    }
-
-    pub fn text_highlights(&self, type_id: TypeId) -> Option<(HighlightStyle, &[Range<Anchor>])> {
-        let highlights = self.text_highlights.get(&Some(type_id))?;
-        Some((highlights.0, &highlights.1))
-    }
-    pub fn clear_highlights(&mut self, type_id: TypeId) -> bool {
-        let mut cleared = self.text_highlights.remove(&Some(type_id)).is_some();
-        cleared |= self.inlay_highlights.remove(&type_id).is_none();
-        cleared
-    }
-
-    pub fn set_font(&self, font: Font, font_size: Pixels, cx: &mut ModelContext<Self>) -> bool {
-        self.wrap_map
-            .update(cx, |map, cx| map.set_font_with_size(font, font_size, cx))
-    }
-
-    pub fn set_fold_ellipses_color(&mut self, color: Hsla) -> bool {
-        self.fold_map.set_ellipses_color(color)
-    }
-
-    pub fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut ModelContext<Self>) -> bool {
-        self.wrap_map
-            .update(cx, |map, cx| map.set_wrap_width(width, cx))
-    }
-
-    pub fn current_inlays(&self) -> impl Iterator<Item = &Inlay> {
-        self.inlay_map.current_inlays()
-    }
-
-    pub fn splice_inlays(
-        &mut self,
-        to_remove: Vec<InlayId>,
-        to_insert: Vec<Inlay>,
-        cx: &mut ModelContext<Self>,
-    ) {
-        if to_remove.is_empty() && to_insert.is_empty() {
-            return;
-        }
-        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
-        let edits = self.buffer_subscription.consume().into_inner();
-        let (snapshot, edits) = self.inlay_map.sync(buffer_snapshot, edits);
-        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
-        let tab_size = Self::tab_size(&self.buffer, cx);
-        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
-        let (snapshot, edits) = self
-            .wrap_map
-            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
-        self.block_map.read(snapshot, edits);
-
-        let (snapshot, edits) = self.inlay_map.splice(to_remove, to_insert);
-        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
-        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
-        let (snapshot, edits) = self
-            .wrap_map
-            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
-        self.block_map.read(snapshot, edits);
-    }
-
-    fn tab_size(buffer: &Model<MultiBuffer>, cx: &mut ModelContext<Self>) -> NonZeroU32 {
-        let language = buffer
-            .read(cx)
-            .as_singleton()
-            .and_then(|buffer| buffer.read(cx).language());
-        language_settings(language.as_deref(), None, cx).tab_size
-    }
-
-    #[cfg(test)]
-    pub fn is_rewrapping(&self, cx: &gpui::AppContext) -> bool {
-        self.wrap_map.read(cx).is_rewrapping()
-    }
-}
-
-#[derive(Debug, Default)]
-pub struct Highlights<'a> {
-    pub text_highlights: Option<&'a TextHighlights>,
-    pub inlay_highlights: Option<&'a InlayHighlights>,
-    pub inlay_highlight_style: Option<HighlightStyle>,
-    pub suggestion_highlight_style: Option<HighlightStyle>,
-}
-
-pub struct HighlightedChunk<'a> {
-    pub chunk: &'a str,
-    pub style: Option<HighlightStyle>,
-    pub is_tab: bool,
-}
-
-pub struct DisplaySnapshot {
-    pub buffer_snapshot: MultiBufferSnapshot,
-    pub fold_snapshot: fold_map::FoldSnapshot,
-    inlay_snapshot: inlay_map::InlaySnapshot,
-    tab_snapshot: tab_map::TabSnapshot,
-    wrap_snapshot: wrap_map::WrapSnapshot,
-    block_snapshot: block_map::BlockSnapshot,
-    text_highlights: TextHighlights,
-    inlay_highlights: InlayHighlights,
-    clip_at_line_ends: bool,
-}
-
-impl DisplaySnapshot {
-    #[cfg(test)]
-    pub fn fold_count(&self) -> usize {
-        self.fold_snapshot.fold_count()
-    }
-
-    pub fn is_empty(&self) -> bool {
-        self.buffer_snapshot.len() == 0
-    }
-
-    pub fn buffer_rows(&self, start_row: u32) -> DisplayBufferRows {
-        self.block_snapshot.buffer_rows(start_row)
-    }
-
-    pub fn max_buffer_row(&self) -> u32 {
-        self.buffer_snapshot.max_buffer_row()
-    }
-
-    pub fn prev_line_boundary(&self, mut point: Point) -> (Point, DisplayPoint) {
-        loop {
-            let mut inlay_point = self.inlay_snapshot.to_inlay_point(point);
-            let mut fold_point = self.fold_snapshot.to_fold_point(inlay_point, Bias::Left);
-            fold_point.0.column = 0;
-            inlay_point = fold_point.to_inlay_point(&self.fold_snapshot);
-            point = self.inlay_snapshot.to_buffer_point(inlay_point);
-
-            let mut display_point = self.point_to_display_point(point, Bias::Left);
-            *display_point.column_mut() = 0;
-            let next_point = self.display_point_to_point(display_point, Bias::Left);
-            if next_point == point {
-                return (point, display_point);
-            }
-            point = next_point;
-        }
-    }
-
-    pub fn next_line_boundary(&self, mut point: Point) -> (Point, DisplayPoint) {
-        loop {
-            let mut inlay_point = self.inlay_snapshot.to_inlay_point(point);
-            let mut fold_point = self.fold_snapshot.to_fold_point(inlay_point, Bias::Right);
-            fold_point.0.column = self.fold_snapshot.line_len(fold_point.row());
-            inlay_point = fold_point.to_inlay_point(&self.fold_snapshot);
-            point = self.inlay_snapshot.to_buffer_point(inlay_point);
-
-            let mut display_point = self.point_to_display_point(point, Bias::Right);
-            *display_point.column_mut() = self.line_len(display_point.row());
-            let next_point = self.display_point_to_point(display_point, Bias::Right);
-            if next_point == point {
-                return (point, display_point);
-            }
-            point = next_point;
-        }
-    }
-
-    // used by line_mode selections and tries to match vim behaviour
-    pub fn expand_to_line(&self, range: Range<Point>) -> Range<Point> {
-        let new_start = if range.start.row == 0 {
-            Point::new(0, 0)
-        } else if range.start.row == self.max_buffer_row()
-            || (range.end.column > 0 && range.end.row == self.max_buffer_row())
-        {
-            Point::new(range.start.row - 1, self.line_len(range.start.row - 1))
-        } else {
-            self.prev_line_boundary(range.start).0
-        };
-
-        let new_end = if range.end.column == 0 {
-            range.end
-        } else if range.end.row < self.max_buffer_row() {
-            self.buffer_snapshot
-                .clip_point(Point::new(range.end.row + 1, 0), Bias::Left)
-        } else {
-            self.buffer_snapshot.max_point()
-        };
-
-        new_start..new_end
-    }
-
-    fn point_to_display_point(&self, point: Point, bias: Bias) -> DisplayPoint {
-        let inlay_point = self.inlay_snapshot.to_inlay_point(point);
-        let fold_point = self.fold_snapshot.to_fold_point(inlay_point, bias);
-        let tab_point = self.tab_snapshot.to_tab_point(fold_point);
-        let wrap_point = self.wrap_snapshot.tab_point_to_wrap_point(tab_point);
-        let block_point = self.block_snapshot.to_block_point(wrap_point);
-        DisplayPoint(block_point)
-    }
-
-    fn display_point_to_point(&self, point: DisplayPoint, bias: Bias) -> Point {
-        self.inlay_snapshot
-            .to_buffer_point(self.display_point_to_inlay_point(point, bias))
-    }
-
-    pub fn display_point_to_inlay_offset(&self, point: DisplayPoint, bias: Bias) -> InlayOffset {
-        self.inlay_snapshot
-            .to_offset(self.display_point_to_inlay_point(point, bias))
-    }
-
-    pub fn anchor_to_inlay_offset(&self, anchor: Anchor) -> InlayOffset {
-        self.inlay_snapshot
-            .to_inlay_offset(anchor.to_offset(&self.buffer_snapshot))
-    }
-
-    fn display_point_to_inlay_point(&self, point: DisplayPoint, bias: Bias) -> InlayPoint {
-        let block_point = point.0;
-        let wrap_point = self.block_snapshot.to_wrap_point(block_point);
-        let tab_point = self.wrap_snapshot.to_tab_point(wrap_point);
-        let fold_point = self.tab_snapshot.to_fold_point(tab_point, bias).0;
-        fold_point.to_inlay_point(&self.fold_snapshot)
-    }
-
-    pub fn display_point_to_fold_point(&self, point: DisplayPoint, bias: Bias) -> FoldPoint {
-        let block_point = point.0;
-        let wrap_point = self.block_snapshot.to_wrap_point(block_point);
-        let tab_point = self.wrap_snapshot.to_tab_point(wrap_point);
-        self.tab_snapshot.to_fold_point(tab_point, bias).0
-    }
-
-    pub fn fold_point_to_display_point(&self, fold_point: FoldPoint) -> DisplayPoint {
-        let tab_point = self.tab_snapshot.to_tab_point(fold_point);
-        let wrap_point = self.wrap_snapshot.tab_point_to_wrap_point(tab_point);
-        let block_point = self.block_snapshot.to_block_point(wrap_point);
-        DisplayPoint(block_point)
-    }
-
-    pub fn max_point(&self) -> DisplayPoint {
-        DisplayPoint(self.block_snapshot.max_point())
-    }
-
-    /// Returns text chunks starting at the given display row until the end of the file
-    pub fn text_chunks(&self, display_row: u32) -> impl Iterator<Item = &str> {
-        self.block_snapshot
-            .chunks(
-                display_row..self.max_point().row() + 1,
-                false,
-                Highlights::default(),
-            )
-            .map(|h| h.text)
-    }
-
-    /// Returns text chunks starting at the end of the given display row in reverse until the start of the file
-    pub fn reverse_text_chunks(&self, display_row: u32) -> impl Iterator<Item = &str> {
-        (0..=display_row).into_iter().rev().flat_map(|row| {
-            self.block_snapshot
-                .chunks(row..row + 1, false, Highlights::default())
-                .map(|h| h.text)
-                .collect::<Vec<_>>()
-                .into_iter()
-                .rev()
-        })
-    }
-
-    pub fn chunks<'a>(
-        &'a self,
-        display_rows: Range<u32>,
-        language_aware: bool,
-        inlay_highlight_style: Option<HighlightStyle>,
-        suggestion_highlight_style: Option<HighlightStyle>,
-    ) -> DisplayChunks<'a> {
-        self.block_snapshot.chunks(
-            display_rows,
-            language_aware,
-            Highlights {
-                text_highlights: Some(&self.text_highlights),
-                inlay_highlights: Some(&self.inlay_highlights),
-                inlay_highlight_style,
-                suggestion_highlight_style,
-            },
-        )
-    }
-
-    pub fn highlighted_chunks<'a>(
-        &'a self,
-        display_rows: Range<u32>,
-        language_aware: bool,
-        editor_style: &'a EditorStyle,
-    ) -> impl Iterator<Item = HighlightedChunk<'a>> {
-        self.chunks(
-            display_rows,
-            language_aware,
-            Some(editor_style.inlays_style),
-            Some(editor_style.suggestions_style),
-        )
-        .map(|chunk| {
-            let mut highlight_style = chunk
-                .syntax_highlight_id
-                .and_then(|id| id.style(&editor_style.syntax));
-
-            if let Some(chunk_highlight) = chunk.highlight_style {
-                if let Some(highlight_style) = highlight_style.as_mut() {
-                    highlight_style.highlight(chunk_highlight);
-                } else {
-                    highlight_style = Some(chunk_highlight);
-                }
-            }
-
-            let mut diagnostic_highlight = HighlightStyle::default();
-
-            if chunk.is_unnecessary {
-                diagnostic_highlight.fade_out = Some(UNNECESSARY_CODE_FADE);
-            }
-
-            if let Some(severity) = chunk.diagnostic_severity {
-                // Omit underlines for HINT/INFO diagnostics on 'unnecessary' code.
-                if severity <= DiagnosticSeverity::WARNING || !chunk.is_unnecessary {
-                    let diagnostic_color =
-                        super::diagnostic_style(severity, true, &editor_style.status);
-                    diagnostic_highlight.underline = Some(UnderlineStyle {
-                        color: Some(diagnostic_color),
-                        thickness: 1.0.into(),
-                        wavy: true,
-                    });
-                }
-            }
-
-            if let Some(highlight_style) = highlight_style.as_mut() {
-                highlight_style.highlight(diagnostic_highlight);
-            } else {
-                highlight_style = Some(diagnostic_highlight);
-            }
-
-            HighlightedChunk {
-                chunk: chunk.text,
-                style: highlight_style,
-                is_tab: chunk.is_tab,
-            }
-        })
-    }
-
-    pub fn layout_row(
-        &self,
-        display_row: u32,
-        TextLayoutDetails {
-            text_system,
-            editor_style,
-            rem_size,
-        }: &TextLayoutDetails,
-    ) -> Arc<LineLayout> {
-        let mut runs = Vec::new();
-        let mut line = String::new();
-
-        let range = display_row..display_row + 1;
-        for chunk in self.highlighted_chunks(range, false, &editor_style) {
-            line.push_str(chunk.chunk);
-
-            let text_style = if let Some(style) = chunk.style {
-                Cow::Owned(editor_style.text.clone().highlight(style))
-            } else {
-                Cow::Borrowed(&editor_style.text)
-            };
-
-            runs.push(text_style.to_run(chunk.chunk.len()))
-        }
-
-        if line.ends_with('\n') {
-            line.pop();
-            if let Some(last_run) = runs.last_mut() {
-                last_run.len -= 1;
-                if last_run.len == 0 {
-                    runs.pop();
-                }
-            }
-        }
-
-        let font_size = editor_style.text.font_size.to_pixels(*rem_size);
-        text_system
-            .layout_line(&line, font_size, &runs)
-            .expect("we expect the font to be loaded because it's rendered by the editor")
-    }
-
-    pub fn x_for_display_point(
-        &self,
-        display_point: DisplayPoint,
-        text_layout_details: &TextLayoutDetails,
-    ) -> Pixels {
-        let line = self.layout_row(display_point.row(), text_layout_details);
-        line.x_for_index(display_point.column() as usize)
-    }
-
-    pub fn display_column_for_x(
-        &self,
-        display_row: u32,
-        x: Pixels,
-        details: &TextLayoutDetails,
-    ) -> u32 {
-        let layout_line = self.layout_row(display_row, details);
-        layout_line.closest_index_for_x(x) as u32
-    }
-
-    pub fn chars_at(
-        &self,
-        mut point: DisplayPoint,
-    ) -> impl Iterator<Item = (char, DisplayPoint)> + '_ {
-        point = DisplayPoint(self.block_snapshot.clip_point(point.0, Bias::Left));
-        self.text_chunks(point.row())
-            .flat_map(str::chars)
-            .skip_while({
-                let mut column = 0;
-                move |char| {
-                    let at_point = column >= point.column();
-                    column += char.len_utf8() as u32;
-                    !at_point
-                }
-            })
-            .map(move |ch| {
-                let result = (ch, point);
-                if ch == '\n' {
-                    *point.row_mut() += 1;
-                    *point.column_mut() = 0;
-                } else {
-                    *point.column_mut() += ch.len_utf8() as u32;
-                }
-                result
-            })
-    }
-
-    pub fn reverse_chars_at(
-        &self,
-        mut point: DisplayPoint,
-    ) -> impl Iterator<Item = (char, DisplayPoint)> + '_ {
-        point = DisplayPoint(self.block_snapshot.clip_point(point.0, Bias::Left));
-        self.reverse_text_chunks(point.row())
-            .flat_map(|chunk| chunk.chars().rev())
-            .skip_while({
-                let mut column = self.line_len(point.row());
-                if self.max_point().row() > point.row() {
-                    column += 1;
-                }
-
-                move |char| {
-                    let at_point = column <= point.column();
-                    column = column.saturating_sub(char.len_utf8() as u32);
-                    !at_point
-                }
-            })
-            .map(move |ch| {
-                if ch == '\n' {
-                    *point.row_mut() -= 1;
-                    *point.column_mut() = self.line_len(point.row());
-                } else {
-                    *point.column_mut() = point.column().saturating_sub(ch.len_utf8() as u32);
-                }
-                (ch, point)
-            })
-    }
-
-    pub fn column_to_chars(&self, display_row: u32, target: u32) -> u32 {
-        let mut count = 0;
-        let mut column = 0;
-        for (c, _) in self.chars_at(DisplayPoint::new(display_row, 0)) {
-            if column >= target {
-                break;
-            }
-            count += 1;
-            column += c.len_utf8() as u32;
-        }
-        count
-    }
-
-    pub fn column_from_chars(&self, display_row: u32, char_count: u32) -> u32 {
-        let mut column = 0;
-
-        for (count, (c, _)) in self.chars_at(DisplayPoint::new(display_row, 0)).enumerate() {
-            if c == '\n' || count >= char_count as usize {
-                break;
-            }
-            column += c.len_utf8() as u32;
-        }
-
-        column
-    }
-
-    pub fn clip_point(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
-        let mut clipped = self.block_snapshot.clip_point(point.0, bias);
-        if self.clip_at_line_ends {
-            clipped = self.clip_at_line_end(DisplayPoint(clipped)).0
-        }
-        DisplayPoint(clipped)
-    }
-
-    pub fn clip_at_line_end(&self, point: DisplayPoint) -> DisplayPoint {
-        let mut point = point.0;
-        if point.column == self.line_len(point.row) {
-            point.column = point.column.saturating_sub(1);
-            point = self.block_snapshot.clip_point(point, Bias::Left);
-        }
-        DisplayPoint(point)
-    }
-
-    pub fn folds_in_range<T>(&self, range: Range<T>) -> impl Iterator<Item = &Fold>
-    where
-        T: ToOffset,
-    {
-        self.fold_snapshot.folds_in_range(range)
-    }
-
-    pub fn blocks_in_range(
-        &self,
-        rows: Range<u32>,
-    ) -> impl Iterator<Item = (u32, &TransformBlock)> {
-        self.block_snapshot.blocks_in_range(rows)
-    }
-
-    pub fn intersects_fold<T: ToOffset>(&self, offset: T) -> bool {
-        self.fold_snapshot.intersects_fold(offset)
-    }
-
-    pub fn is_line_folded(&self, buffer_row: u32) -> bool {
-        self.fold_snapshot.is_line_folded(buffer_row)
-    }
-
-    pub fn is_block_line(&self, display_row: u32) -> bool {
-        self.block_snapshot.is_block_line(display_row)
-    }
-
-    pub fn soft_wrap_indent(&self, display_row: u32) -> Option<u32> {
-        let wrap_row = self
-            .block_snapshot
-            .to_wrap_point(BlockPoint::new(display_row, 0))
-            .row();
-        self.wrap_snapshot.soft_wrap_indent(wrap_row)
-    }
-
-    pub fn text(&self) -> String {
-        self.text_chunks(0).collect()
-    }
-
-    pub fn line(&self, display_row: u32) -> String {
-        let mut result = String::new();
-        for chunk in self.text_chunks(display_row) {
-            if let Some(ix) = chunk.find('\n') {
-                result.push_str(&chunk[0..ix]);
-                break;
-            } else {
-                result.push_str(chunk);
-            }
-        }
-        result
-    }
-
-    pub fn line_indent(&self, display_row: u32) -> (u32, bool) {
-        let mut indent = 0;
-        let mut is_blank = true;
-        for (c, _) in self.chars_at(DisplayPoint::new(display_row, 0)) {
-            if c == ' ' {
-                indent += 1;
-            } else {
-                is_blank = c == '\n';
-                break;
-            }
-        }
-        (indent, is_blank)
-    }
-
-    pub fn line_indent_for_buffer_row(&self, buffer_row: u32) -> (u32, bool) {
-        let (buffer, range) = self
-            .buffer_snapshot
-            .buffer_line_for_row(buffer_row)
-            .unwrap();
-
-        let mut indent_size = 0;
-        let mut is_blank = false;
-        for c in buffer.chars_at(Point::new(range.start.row, 0)) {
-            if c == ' ' || c == '\t' {
-                indent_size += 1;
-            } else {
-                if c == '\n' {
-                    is_blank = true;
-                }
-                break;
-            }
-        }
-
-        (indent_size, is_blank)
-    }
-
-    pub fn line_len(&self, row: u32) -> u32 {
-        self.block_snapshot.line_len(row)
-    }
-
-    pub fn longest_row(&self) -> u32 {
-        self.block_snapshot.longest_row()
-    }
-
-    pub fn fold_for_line(self: &Self, buffer_row: u32) -> Option<FoldStatus> {
-        if self.is_line_folded(buffer_row) {
-            Some(FoldStatus::Folded)
-        } else if self.is_foldable(buffer_row) {
-            Some(FoldStatus::Foldable)
-        } else {
-            None
-        }
-    }
-
-    pub fn is_foldable(self: &Self, buffer_row: u32) -> bool {
-        let max_row = self.buffer_snapshot.max_buffer_row();
-        if buffer_row >= max_row {
-            return false;
-        }
-
-        let (indent_size, is_blank) = self.line_indent_for_buffer_row(buffer_row);
-        if is_blank {
-            return false;
-        }
-
-        for next_row in (buffer_row + 1)..=max_row {
-            let (next_indent_size, next_line_is_blank) = self.line_indent_for_buffer_row(next_row);
-            if next_indent_size > indent_size {
-                return true;
-            } else if !next_line_is_blank {
-                break;
-            }
-        }
-
-        false
-    }
-
-    pub fn foldable_range(self: &Self, buffer_row: u32) -> Option<Range<Point>> {
-        let start = Point::new(buffer_row, self.buffer_snapshot.line_len(buffer_row));
-        if self.is_foldable(start.row) && !self.is_line_folded(start.row) {
-            let (start_indent, _) = self.line_indent_for_buffer_row(buffer_row);
-            let max_point = self.buffer_snapshot.max_point();
-            let mut end = None;
-
-            for row in (buffer_row + 1)..=max_point.row {
-                let (indent, is_blank) = self.line_indent_for_buffer_row(row);
-                if !is_blank && indent <= start_indent {
-                    let prev_row = row - 1;
-                    end = Some(Point::new(
-                        prev_row,
-                        self.buffer_snapshot.line_len(prev_row),
-                    ));
-                    break;
-                }
-            }
-            let end = end.unwrap_or(max_point);
-            Some(start..end)
-        } else {
-            None
-        }
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn text_highlight_ranges<Tag: ?Sized + 'static>(
-        &self,
-    ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
-        let type_id = TypeId::of::<Tag>();
-        self.text_highlights.get(&Some(type_id)).cloned()
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn inlay_highlights<Tag: ?Sized + 'static>(
-        &self,
-    ) -> Option<&HashMap<InlayId, (HighlightStyle, InlayHighlight)>> {
-        let type_id = TypeId::of::<Tag>();
-        self.inlay_highlights.get(&type_id)
-    }
-}
-
-#[derive(Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq)]
-pub struct DisplayPoint(BlockPoint);
-
-impl Debug for DisplayPoint {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.write_fmt(format_args!(
-            "DisplayPoint({}, {})",
-            self.row(),
-            self.column()
-        ))
-    }
-}
-
-impl DisplayPoint {
-    pub fn new(row: u32, column: u32) -> Self {
-        Self(BlockPoint(Point::new(row, column)))
-    }
-
-    pub fn zero() -> Self {
-        Self::new(0, 0)
-    }
-
-    pub fn is_zero(&self) -> bool {
-        self.0.is_zero()
-    }
-
-    pub fn row(self) -> u32 {
-        self.0.row
-    }
-
-    pub fn column(self) -> u32 {
-        self.0.column
-    }
-
-    pub fn row_mut(&mut self) -> &mut u32 {
-        &mut self.0.row
-    }
-
-    pub fn column_mut(&mut self) -> &mut u32 {
-        &mut self.0.column
-    }
-
-    pub fn to_point(self, map: &DisplaySnapshot) -> Point {
-        map.display_point_to_point(self, Bias::Left)
-    }
-
-    pub fn to_offset(self, map: &DisplaySnapshot, bias: Bias) -> usize {
-        let wrap_point = map.block_snapshot.to_wrap_point(self.0);
-        let tab_point = map.wrap_snapshot.to_tab_point(wrap_point);
-        let fold_point = map.tab_snapshot.to_fold_point(tab_point, bias).0;
-        let inlay_point = fold_point.to_inlay_point(&map.fold_snapshot);
-        map.inlay_snapshot
-            .to_buffer_offset(map.inlay_snapshot.to_offset(inlay_point))
-    }
-}
-
-impl ToDisplayPoint for usize {
-    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
-        map.point_to_display_point(self.to_point(&map.buffer_snapshot), Bias::Left)
-    }
-}
-
-impl ToDisplayPoint for OffsetUtf16 {
-    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
-        self.to_offset(&map.buffer_snapshot).to_display_point(map)
-    }
-}
-
-impl ToDisplayPoint for Point {
-    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
-        map.point_to_display_point(*self, Bias::Left)
-    }
-}
-
-impl ToDisplayPoint for Anchor {
-    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
-        self.to_point(&map.buffer_snapshot).to_display_point(map)
-    }
-}
-
-pub fn next_rows(display_row: u32, display_map: &DisplaySnapshot) -> impl Iterator<Item = u32> {
-    let max_row = display_map.max_point().row();
-    let start_row = display_row + 1;
-    let mut current = None;
-    std::iter::from_fn(move || {
-        if current == None {
-            current = Some(start_row);
-        } else {
-            current = Some(current.unwrap() + 1)
-        }
-        if current.unwrap() > max_row {
-            None
-        } else {
-            current
-        }
-    })
-}
-
-#[cfg(test)]
-pub mod tests {
-    use super::*;
-    use crate::{
-        movement,
-        test::{editor_test_context::EditorTestContext, marked_display_snapshot},
-    };
-    use gpui::{div, font, observe, px, AppContext, Context, Element, Hsla};
-    use language::{
-        language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
-        Buffer, Language, LanguageConfig, SelectionGoal,
-    };
-    use project::Project;
-    use rand::{prelude::*, Rng};
-    use settings::SettingsStore;
-    use smol::stream::StreamExt;
-    use std::{env, sync::Arc};
-    use theme::{LoadThemes, SyntaxTheme};
-    use util::test::{marked_text_ranges, sample_text};
-    use Bias::*;
-
-    #[gpui::test(iterations = 100)]
-    async fn test_random_display_map(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
-        cx.background_executor.set_block_on_ticks(0..=50);
-        let operations = env::var("OPERATIONS")
-            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
-            .unwrap_or(10);
-
-        let _test_platform = &cx.test_platform;
-        let mut tab_size = rng.gen_range(1..=4);
-        let buffer_start_excerpt_header_height = rng.gen_range(1..=5);
-        let excerpt_header_height = rng.gen_range(1..=5);
-        let font_size = px(14.0);
-        let max_wrap_width = 300.0;
-        let mut wrap_width = if rng.gen_bool(0.1) {
-            None
-        } else {
-            Some(px(rng.gen_range(0.0..=max_wrap_width)))
-        };
-
-        log::info!("tab size: {}", tab_size);
-        log::info!("wrap width: {:?}", wrap_width);
-
-        cx.update(|cx| {
-            init_test(cx, |s| s.defaults.tab_size = NonZeroU32::new(tab_size));
-        });
-
-        let buffer = cx.update(|cx| {
-            if rng.gen() {
-                let len = rng.gen_range(0..10);
-                let text = util::RandomCharIter::new(&mut rng)
-                    .take(len)
-                    .collect::<String>();
-                MultiBuffer::build_simple(&text, cx)
-            } else {
-                MultiBuffer::build_random(&mut rng, cx)
-            }
-        });
-
-        let map = cx.new_model(|cx| {
-            DisplayMap::new(
-                buffer.clone(),
-                font("Helvetica"),
-                font_size,
-                wrap_width,
-                buffer_start_excerpt_header_height,
-                excerpt_header_height,
-                cx,
-            )
-        });
-        let mut notifications = observe(&map, cx);
-        let mut fold_count = 0;
-        let mut blocks = Vec::new();
-
-        let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
-        log::info!("buffer text: {:?}", snapshot.buffer_snapshot.text());
-        log::info!("fold text: {:?}", snapshot.fold_snapshot.text());
-        log::info!("tab text: {:?}", snapshot.tab_snapshot.text());
-        log::info!("wrap text: {:?}", snapshot.wrap_snapshot.text());
-        log::info!("block text: {:?}", snapshot.block_snapshot.text());
-        log::info!("display text: {:?}", snapshot.text());
-
-        for _i in 0..operations {
-            match rng.gen_range(0..100) {
-                0..=19 => {
-                    wrap_width = if rng.gen_bool(0.2) {
-                        None
-                    } else {
-                        Some(px(rng.gen_range(0.0..=max_wrap_width)))
-                    };
-                    log::info!("setting wrap width to {:?}", wrap_width);
-                    map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
-                }
-                20..=29 => {
-                    let mut tab_sizes = vec![1, 2, 3, 4];
-                    tab_sizes.remove((tab_size - 1) as usize);
-                    tab_size = *tab_sizes.choose(&mut rng).unwrap();
-                    log::info!("setting tab size to {:?}", tab_size);
-                    cx.update(|cx| {
-                        cx.update_global::<SettingsStore, _>(|store, cx| {
-                            store.update_user_settings::<AllLanguageSettings>(cx, |s| {
-                                s.defaults.tab_size = NonZeroU32::new(tab_size);
-                            });
-                        });
-                    });
-                }
-                30..=44 => {
-                    map.update(cx, |map, cx| {
-                        if rng.gen() || blocks.is_empty() {
-                            let buffer = map.snapshot(cx).buffer_snapshot;
-                            let block_properties = (0..rng.gen_range(1..=1))
-                                .map(|_| {
-                                    let position =
-                                        buffer.anchor_after(buffer.clip_offset(
-                                            rng.gen_range(0..=buffer.len()),
-                                            Bias::Left,
-                                        ));
-
-                                    let disposition = if rng.gen() {
-                                        BlockDisposition::Above
-                                    } else {
-                                        BlockDisposition::Below
-                                    };
-                                    let height = rng.gen_range(1..5);
-                                    log::info!(
-                                        "inserting block {:?} {:?} with height {}",
-                                        disposition,
-                                        position.to_point(&buffer),
-                                        height
-                                    );
-                                    BlockProperties {
-                                        style: BlockStyle::Fixed,
-                                        position,
-                                        height,
-                                        disposition,
-                                        render: Arc::new(|_| div().into_any()),
-                                    }
-                                })
-                                .collect::<Vec<_>>();
-                            blocks.extend(map.insert_blocks(block_properties, cx));
-                        } else {
-                            blocks.shuffle(&mut rng);
-                            let remove_count = rng.gen_range(1..=4.min(blocks.len()));
-                            let block_ids_to_remove = (0..remove_count)
-                                .map(|_| blocks.remove(rng.gen_range(0..blocks.len())))
-                                .collect();
-                            log::info!("removing block ids {:?}", block_ids_to_remove);
-                            map.remove_blocks(block_ids_to_remove, cx);
-                        }
-                    });
-                }
-                45..=79 => {
-                    let mut ranges = Vec::new();
-                    for _ in 0..rng.gen_range(1..=3) {
-                        buffer.read_with(cx, |buffer, cx| {
-                            let buffer = buffer.read(cx);
-                            let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
-                            let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
-                            ranges.push(start..end);
-                        });
-                    }
-
-                    if rng.gen() && fold_count > 0 {
-                        log::info!("unfolding ranges: {:?}", ranges);
-                        map.update(cx, |map, cx| {
-                            map.unfold(ranges, true, cx);
-                        });
-                    } else {
-                        log::info!("folding ranges: {:?}", ranges);
-                        map.update(cx, |map, cx| {
-                            map.fold(ranges, cx);
-                        });
-                    }
-                }
-                _ => {
-                    buffer.update(cx, |buffer, cx| buffer.randomly_mutate(&mut rng, 5, cx));
-                }
-            }
-
-            if map.read_with(cx, |map, cx| map.is_rewrapping(cx)) {
-                notifications.next().await.unwrap();
-            }
-
-            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
-            fold_count = snapshot.fold_count();
-            log::info!("buffer text: {:?}", snapshot.buffer_snapshot.text());
-            log::info!("fold text: {:?}", snapshot.fold_snapshot.text());
-            log::info!("tab text: {:?}", snapshot.tab_snapshot.text());
-            log::info!("wrap text: {:?}", snapshot.wrap_snapshot.text());
-            log::info!("block text: {:?}", snapshot.block_snapshot.text());
-            log::info!("display text: {:?}", snapshot.text());
-
-            // Line boundaries
-            let buffer = &snapshot.buffer_snapshot;
-            for _ in 0..5 {
-                let row = rng.gen_range(0..=buffer.max_point().row);
-                let column = rng.gen_range(0..=buffer.line_len(row));
-                let point = buffer.clip_point(Point::new(row, column), Left);
-
-                let (prev_buffer_bound, prev_display_bound) = snapshot.prev_line_boundary(point);
-                let (next_buffer_bound, next_display_bound) = snapshot.next_line_boundary(point);
-
-                assert!(prev_buffer_bound <= point);
-                assert!(next_buffer_bound >= point);
-                assert_eq!(prev_buffer_bound.column, 0);
-                assert_eq!(prev_display_bound.column(), 0);
-                if next_buffer_bound < buffer.max_point() {
-                    assert_eq!(buffer.chars_at(next_buffer_bound).next(), Some('\n'));
-                }
-
-                assert_eq!(
-                    prev_display_bound,
-                    prev_buffer_bound.to_display_point(&snapshot),
-                    "row boundary before {:?}. reported buffer row boundary: {:?}",
-                    point,
-                    prev_buffer_bound
-                );
-                assert_eq!(
-                    next_display_bound,
-                    next_buffer_bound.to_display_point(&snapshot),
-                    "display row boundary after {:?}. reported buffer row boundary: {:?}",
-                    point,
-                    next_buffer_bound
-                );
-                assert_eq!(
-                    prev_buffer_bound,
-                    prev_display_bound.to_point(&snapshot),
-                    "row boundary before {:?}. reported display row boundary: {:?}",
-                    point,
-                    prev_display_bound
-                );
-                assert_eq!(
-                    next_buffer_bound,
-                    next_display_bound.to_point(&snapshot),
-                    "row boundary after {:?}. reported display row boundary: {:?}",
-                    point,
-                    next_display_bound
-                );
-            }
-
-            // Movement
-            let min_point = snapshot.clip_point(DisplayPoint::new(0, 0), Left);
-            let max_point = snapshot.clip_point(snapshot.max_point(), Right);
-            for _ in 0..5 {
-                let row = rng.gen_range(0..=snapshot.max_point().row());
-                let column = rng.gen_range(0..=snapshot.line_len(row));
-                let point = snapshot.clip_point(DisplayPoint::new(row, column), Left);
-
-                log::info!("Moving from point {:?}", point);
-
-                let moved_right = movement::right(&snapshot, point);
-                log::info!("Right {:?}", moved_right);
-                if point < max_point {
-                    assert!(moved_right > point);
-                    if point.column() == snapshot.line_len(point.row())
-                        || snapshot.soft_wrap_indent(point.row()).is_some()
-                            && point.column() == snapshot.line_len(point.row()) - 1
-                    {
-                        assert!(moved_right.row() > point.row());
-                    }
-                } else {
-                    assert_eq!(moved_right, point);
-                }
-
-                let moved_left = movement::left(&snapshot, point);
-                log::info!("Left {:?}", moved_left);
-                if point > min_point {
-                    assert!(moved_left < point);
-                    if point.column() == 0 {
-                        assert!(moved_left.row() < point.row());
-                    }
-                } else {
-                    assert_eq!(moved_left, point);
-                }
-            }
-        }
-    }
-
-    #[gpui::test(retries = 5)]
-    async fn test_soft_wraps(cx: &mut gpui::TestAppContext) {
-        cx.background_executor
-            .set_block_on_ticks(usize::MAX..=usize::MAX);
-        cx.update(|cx| {
-            init_test(cx, |_| {});
-        });
-
-        let mut cx = EditorTestContext::new(cx).await;
-        let editor = cx.editor.clone();
-        let window = cx.window.clone();
-
-        _ = cx.update_window(window, |_, cx| {
-            let text_layout_details =
-                editor.update(cx, |editor, cx| editor.text_layout_details(cx));
-
-            let font_size = px(12.0);
-            let wrap_width = Some(px(64.));
-
-            let text = "one two three four five\nsix seven eight";
-            let buffer = MultiBuffer::build_simple(text, cx);
-            let map = cx.new_model(|cx| {
-                DisplayMap::new(
-                    buffer.clone(),
-                    font("Helvetica"),
-                    font_size,
-                    wrap_width,
-                    1,
-                    1,
-                    cx,
-                )
-            });
-
-            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
-            assert_eq!(
-                snapshot.text_chunks(0).collect::<String>(),
-                "one two \nthree four \nfive\nsix seven \neight"
-            );
-            assert_eq!(
-                snapshot.clip_point(DisplayPoint::new(0, 8), Bias::Left),
-                DisplayPoint::new(0, 7)
-            );
-            assert_eq!(
-                snapshot.clip_point(DisplayPoint::new(0, 8), Bias::Right),
-                DisplayPoint::new(1, 0)
-            );
-            assert_eq!(
-                movement::right(&snapshot, DisplayPoint::new(0, 7)),
-                DisplayPoint::new(1, 0)
-            );
-            assert_eq!(
-                movement::left(&snapshot, DisplayPoint::new(1, 0)),
-                DisplayPoint::new(0, 7)
-            );
-
-            let x = snapshot.x_for_display_point(DisplayPoint::new(1, 10), &text_layout_details);
-            assert_eq!(
-                movement::up(
-                    &snapshot,
-                    DisplayPoint::new(1, 10),
-                    SelectionGoal::None,
-                    false,
-                    &text_layout_details,
-                ),
-                (
-                    DisplayPoint::new(0, 7),
-                    SelectionGoal::HorizontalPosition(x.0)
-                )
-            );
-            assert_eq!(
-                movement::down(
-                    &snapshot,
-                    DisplayPoint::new(0, 7),
-                    SelectionGoal::HorizontalPosition(x.0),
-                    false,
-                    &text_layout_details
-                ),
-                (
-                    DisplayPoint::new(1, 10),
-                    SelectionGoal::HorizontalPosition(x.0)
-                )
-            );
-            assert_eq!(
-                movement::down(
-                    &snapshot,
-                    DisplayPoint::new(1, 10),
-                    SelectionGoal::HorizontalPosition(x.0),
-                    false,
-                    &text_layout_details
-                ),
-                (
-                    DisplayPoint::new(2, 4),
-                    SelectionGoal::HorizontalPosition(x.0)
-                )
-            );
-
-            let ix = snapshot.buffer_snapshot.text().find("seven").unwrap();
-            buffer.update(cx, |buffer, cx| {
-                buffer.edit([(ix..ix, "and ")], None, cx);
-            });
-
-            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
-            assert_eq!(
-                snapshot.text_chunks(1).collect::<String>(),
-                "three four \nfive\nsix and \nseven eight"
-            );
-
-            // Re-wrap on font size changes
-            map.update(cx, |map, cx| {
-                map.set_font(font("Helvetica"), px(font_size.0 + 3.), cx)
-            });
-
-            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
-            assert_eq!(
-                snapshot.text_chunks(1).collect::<String>(),
-                "three \nfour five\nsix and \nseven \neight"
-            )
-        });
-    }
-
-    #[gpui::test]
-    fn test_text_chunks(cx: &mut gpui::AppContext) {
-        init_test(cx, |_| {});
-
-        let text = sample_text(6, 6, 'a');
-        let buffer = MultiBuffer::build_simple(&text, cx);
-
-        let font_size = px(14.0);
-        let map = cx.new_model(|cx| {
-            DisplayMap::new(buffer.clone(), font("Helvetica"), font_size, None, 1, 1, cx)
-        });
-
-        buffer.update(cx, |buffer, cx| {
-            buffer.edit(
-                vec![
-                    (Point::new(1, 0)..Point::new(1, 0), "\t"),
-                    (Point::new(1, 1)..Point::new(1, 1), "\t"),
-                    (Point::new(2, 1)..Point::new(2, 1), "\t"),
-                ],
-                None,
-                cx,
-            )
-        });
-
-        assert_eq!(
-            map.update(cx, |map, cx| map.snapshot(cx))
-                .text_chunks(1)
-                .collect::<String>()
-                .lines()
-                .next(),
-            Some("    b   bbbbb")
-        );
-        assert_eq!(
-            map.update(cx, |map, cx| map.snapshot(cx))
-                .text_chunks(2)
-                .collect::<String>()
-                .lines()
-                .next(),
-            Some("c   ccccc")
-        );
-    }
-
-    #[gpui::test]
-    async fn test_chunks(cx: &mut gpui::TestAppContext) {
-        use unindent::Unindent as _;
-
-        let text = r#"
-            fn outer() {}
-
-            mod module {
-                fn inner() {}
-            }"#
-        .unindent();
-
-        let theme = SyntaxTheme::new_test(vec![
-            ("mod.body", Hsla::red().into()),
-            ("fn.name", Hsla::blue().into()),
-        ]);
-        let language = Arc::new(
-            Language::new(
-                LanguageConfig {
-                    name: "Test".into(),
-                    path_suffixes: vec![".test".to_string()],
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            )
-            .with_highlights_query(
-                r#"
-                (mod_item name: (identifier) body: _ @mod.body)
-                (function_item name: (identifier) @fn.name)
-                "#,
-            )
-            .unwrap(),
-        );
-        language.set_theme(&theme);
-
-        cx.update(|cx| init_test(cx, |s| s.defaults.tab_size = Some(2.try_into().unwrap())));
-
-        let buffer = cx.new_model(|cx| {
-            Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
-        });
-        cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-
-        let font_size = px(14.0);
-
-        let map = cx
-            .new_model(|cx| DisplayMap::new(buffer, font("Helvetica"), font_size, None, 1, 1, cx));
-        assert_eq!(
-            cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)),
-            vec![
-                ("fn ".to_string(), None),
-                ("outer".to_string(), Some(Hsla::blue())),
-                ("() {}\n\nmod module ".to_string(), None),
-                ("{\n    fn ".to_string(), Some(Hsla::red())),
-                ("inner".to_string(), Some(Hsla::blue())),
-                ("() {}\n}".to_string(), Some(Hsla::red())),
-            ]
-        );
-        assert_eq!(
-            cx.update(|cx| syntax_chunks(3..5, &map, &theme, cx)),
-            vec![
-                ("    fn ".to_string(), Some(Hsla::red())),
-                ("inner".to_string(), Some(Hsla::blue())),
-                ("() {}\n}".to_string(), Some(Hsla::red())),
-            ]
-        );
-
-        map.update(cx, |map, cx| {
-            map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], cx)
-        });
-        assert_eq!(
-            cx.update(|cx| syntax_chunks(0..2, &map, &theme, cx)),
-            vec![
-                ("fn ".to_string(), None),
-                ("out".to_string(), Some(Hsla::blue())),
-                ("โ‹ฏ".to_string(), None),
-                ("  fn ".to_string(), Some(Hsla::red())),
-                ("inner".to_string(), Some(Hsla::blue())),
-                ("() {}\n}".to_string(), Some(Hsla::red())),
-            ]
-        );
-    }
-
-    #[gpui::test]
-    async fn test_chunks_with_soft_wrapping(cx: &mut gpui::TestAppContext) {
-        use unindent::Unindent as _;
-
-        cx.background_executor
-            .set_block_on_ticks(usize::MAX..=usize::MAX);
-
-        let text = r#"
-            fn outer() {}
-
-            mod module {
-                fn inner() {}
-            }"#
-        .unindent();
-
-        let theme = SyntaxTheme::new_test(vec![
-            ("mod.body", Hsla::red().into()),
-            ("fn.name", Hsla::blue().into()),
-        ]);
-        let language = Arc::new(
-            Language::new(
-                LanguageConfig {
-                    name: "Test".into(),
-                    path_suffixes: vec![".test".to_string()],
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            )
-            .with_highlights_query(
-                r#"
-                (mod_item name: (identifier) body: _ @mod.body)
-                (function_item name: (identifier) @fn.name)
-                "#,
-            )
-            .unwrap(),
-        );
-        language.set_theme(&theme);
-
-        cx.update(|cx| init_test(cx, |_| {}));
-
-        let buffer = cx.new_model(|cx| {
-            Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
-        });
-        cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-
-        let font_size = px(16.0);
-
-        let map = cx.new_model(|cx| {
-            DisplayMap::new(buffer, font("Courier"), font_size, Some(px(40.0)), 1, 1, cx)
-        });
-        assert_eq!(
-            cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)),
-            [
-                ("fn \n".to_string(), None),
-                ("oute\nr".to_string(), Some(Hsla::blue())),
-                ("() \n{}\n\n".to_string(), None),
-            ]
-        );
-        assert_eq!(
-            cx.update(|cx| syntax_chunks(3..5, &map, &theme, cx)),
-            [("{}\n\n".to_string(), None)]
-        );
-
-        map.update(cx, |map, cx| {
-            map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], cx)
-        });
-        assert_eq!(
-            cx.update(|cx| syntax_chunks(1..4, &map, &theme, cx)),
-            [
-                ("out".to_string(), Some(Hsla::blue())),
-                ("โ‹ฏ\n".to_string(), None),
-                ("  \nfn ".to_string(), Some(Hsla::red())),
-                ("i\n".to_string(), Some(Hsla::blue()))
-            ]
-        );
-    }
-
-    #[gpui::test]
-    async fn test_chunks_with_text_highlights(cx: &mut gpui::TestAppContext) {
-        cx.update(|cx| init_test(cx, |_| {}));
-
-        let theme = SyntaxTheme::new_test(vec![
-            ("operator", Hsla::red().into()),
-            ("string", Hsla::green().into()),
-        ]);
-        let language = Arc::new(
-            Language::new(
-                LanguageConfig {
-                    name: "Test".into(),
-                    path_suffixes: vec![".test".to_string()],
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            )
-            .with_highlights_query(
-                r#"
-                ":" @operator
-                (string_literal) @string
-                "#,
-            )
-            .unwrap(),
-        );
-        language.set_theme(&theme);
-
-        let (text, highlighted_ranges) = marked_text_ranges(r#"constห‡ ยซaยป: B = "c ยซdยป""#, false);
-
-        let buffer = cx.new_model(|cx| {
-            Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
-        });
-        cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
-
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-        let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
-
-        let font_size = px(16.0);
-        let map =
-            cx.new_model(|cx| DisplayMap::new(buffer, font("Courier"), font_size, None, 1, 1, cx));
-
-        enum MyType {}
-
-        let style = HighlightStyle {
-            color: Some(Hsla::blue()),
-            ..Default::default()
-        };
-
-        map.update(cx, |map, _cx| {
-            map.highlight_text(
-                TypeId::of::<MyType>(),
-                highlighted_ranges
-                    .into_iter()
-                    .map(|range| {
-                        buffer_snapshot.anchor_before(range.start)
-                            ..buffer_snapshot.anchor_before(range.end)
-                    })
-                    .collect(),
-                style,
-            );
-        });
-
-        assert_eq!(
-            cx.update(|cx| chunks(0..10, &map, &theme, cx)),
-            [
-                ("const ".to_string(), None, None),
-                ("a".to_string(), None, Some(Hsla::blue())),
-                (":".to_string(), Some(Hsla::red()), None),
-                (" B = ".to_string(), None, None),
-                ("\"c ".to_string(), Some(Hsla::green()), None),
-                ("d".to_string(), Some(Hsla::green()), Some(Hsla::blue())),
-                ("\"".to_string(), Some(Hsla::green()), None),
-            ]
-        );
-    }
-
-    #[gpui::test]
-    fn test_clip_point(cx: &mut gpui::AppContext) {
-        init_test(cx, |_| {});
-
-        fn assert(text: &str, shift_right: bool, bias: Bias, cx: &mut gpui::AppContext) {
-            let (unmarked_snapshot, mut markers) = marked_display_snapshot(text, cx);
-
-            match bias {
-                Bias::Left => {
-                    if shift_right {
-                        *markers[1].column_mut() += 1;
-                    }
-
-                    assert_eq!(unmarked_snapshot.clip_point(markers[1], bias), markers[0])
-                }
-                Bias::Right => {
-                    if shift_right {
-                        *markers[0].column_mut() += 1;
-                    }
-
-                    assert_eq!(unmarked_snapshot.clip_point(markers[0], bias), markers[1])
-                }
-            };
-        }
-
-        use Bias::{Left, Right};
-        assert("ห‡ห‡ฮฑ", false, Left, cx);
-        assert("ห‡ห‡ฮฑ", true, Left, cx);
-        assert("ห‡ห‡ฮฑ", false, Right, cx);
-        assert("ห‡ฮฑห‡", true, Right, cx);
-        assert("ห‡ห‡โœ‹", false, Left, cx);
-        assert("ห‡ห‡โœ‹", true, Left, cx);
-        assert("ห‡ห‡โœ‹", false, Right, cx);
-        assert("ห‡โœ‹ห‡", true, Right, cx);
-        assert("ห‡ห‡๐Ÿ", false, Left, cx);
-        assert("ห‡ห‡๐Ÿ", true, Left, cx);
-        assert("ห‡ห‡๐Ÿ", false, Right, cx);
-        assert("ห‡๐Ÿห‡", true, Right, cx);
-        assert("ห‡ห‡\t", false, Left, cx);
-        assert("ห‡ห‡\t", true, Left, cx);
-        assert("ห‡ห‡\t", false, Right, cx);
-        assert("ห‡\tห‡", true, Right, cx);
-        assert(" ห‡ห‡\t", false, Left, cx);
-        assert(" ห‡ห‡\t", true, Left, cx);
-        assert(" ห‡ห‡\t", false, Right, cx);
-        assert(" ห‡\tห‡", true, Right, cx);
-        assert("   ห‡ห‡\t", false, Left, cx);
-        assert("   ห‡ห‡\t", false, Right, cx);
-    }
-
-    #[gpui::test]
-    fn test_clip_at_line_ends(cx: &mut gpui::AppContext) {
-        init_test(cx, |_| {});
-
-        fn assert(text: &str, cx: &mut gpui::AppContext) {
-            let (mut unmarked_snapshot, markers) = marked_display_snapshot(text, cx);
-            unmarked_snapshot.clip_at_line_ends = true;
-            assert_eq!(
-                unmarked_snapshot.clip_point(markers[1], Bias::Left),
-                markers[0]
-            );
-        }
-
-        assert("ห‡ห‡", cx);
-        assert("ห‡aห‡", cx);
-        assert("aห‡bห‡", cx);
-        assert("aห‡ฮฑห‡", cx);
-    }
-
-    #[gpui::test]
-    fn test_tabs_with_multibyte_chars(cx: &mut gpui::AppContext) {
-        init_test(cx, |_| {});
-
-        let text = "โœ…\t\tฮฑ\nฮฒ\t\n๐Ÿ€ฮฒ\t\tฮณ";
-        let buffer = MultiBuffer::build_simple(text, cx);
-        let font_size = px(14.0);
-
-        let map = cx.new_model(|cx| {
-            DisplayMap::new(buffer.clone(), font("Helvetica"), font_size, None, 1, 1, cx)
-        });
-        let map = map.update(cx, |map, cx| map.snapshot(cx));
-        assert_eq!(map.text(), "โœ…       ฮฑ\nฮฒ   \n๐Ÿ€ฮฒ      ฮณ");
-        assert_eq!(
-            map.text_chunks(0).collect::<String>(),
-            "โœ…       ฮฑ\nฮฒ   \n๐Ÿ€ฮฒ      ฮณ"
-        );
-        assert_eq!(map.text_chunks(1).collect::<String>(), "ฮฒ   \n๐Ÿ€ฮฒ      ฮณ");
-        assert_eq!(map.text_chunks(2).collect::<String>(), "๐Ÿ€ฮฒ      ฮณ");
-
-        let point = Point::new(0, "โœ…\t\t".len() as u32);
-        let display_point = DisplayPoint::new(0, "โœ…       ".len() as u32);
-        assert_eq!(point.to_display_point(&map), display_point);
-        assert_eq!(display_point.to_point(&map), point);
-
-        let point = Point::new(1, "ฮฒ\t".len() as u32);
-        let display_point = DisplayPoint::new(1, "ฮฒ   ".len() as u32);
-        assert_eq!(point.to_display_point(&map), display_point);
-        assert_eq!(display_point.to_point(&map), point,);
-
-        let point = Point::new(2, "๐Ÿ€ฮฒ\t\t".len() as u32);
-        let display_point = DisplayPoint::new(2, "๐Ÿ€ฮฒ      ".len() as u32);
-        assert_eq!(point.to_display_point(&map), display_point);
-        assert_eq!(display_point.to_point(&map), point,);
-
-        // Display points inside of expanded tabs
-        assert_eq!(
-            DisplayPoint::new(0, "โœ…      ".len() as u32).to_point(&map),
-            Point::new(0, "โœ…\t".len() as u32),
-        );
-        assert_eq!(
-            DisplayPoint::new(0, "โœ… ".len() as u32).to_point(&map),
-            Point::new(0, "โœ…".len() as u32),
-        );
-
-        // Clipping display points inside of multi-byte characters
-        assert_eq!(
-            map.clip_point(DisplayPoint::new(0, "โœ…".len() as u32 - 1), Left),
-            DisplayPoint::new(0, 0)
-        );
-        assert_eq!(
-            map.clip_point(DisplayPoint::new(0, "โœ…".len() as u32 - 1), Bias::Right),
-            DisplayPoint::new(0, "โœ…".len() as u32)
-        );
-    }
-
-    #[gpui::test]
-    fn test_max_point(cx: &mut gpui::AppContext) {
-        init_test(cx, |_| {});
-
-        let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", cx);
-        let font_size = px(14.0);
-        let map = cx.new_model(|cx| {
-            DisplayMap::new(buffer.clone(), font("Helvetica"), font_size, None, 1, 1, cx)
-        });
-        assert_eq!(
-            map.update(cx, |map, cx| map.snapshot(cx)).max_point(),
-            DisplayPoint::new(1, 11)
-        )
-    }
-
-    fn syntax_chunks<'a>(
-        rows: Range<u32>,
-        map: &Model<DisplayMap>,
-        theme: &'a SyntaxTheme,
-        cx: &mut AppContext,
-    ) -> Vec<(String, Option<Hsla>)> {
-        chunks(rows, map, theme, cx)
-            .into_iter()
-            .map(|(text, color, _)| (text, color))
-            .collect()
-    }
-
-    fn chunks<'a>(
-        rows: Range<u32>,
-        map: &Model<DisplayMap>,
-        theme: &'a SyntaxTheme,
-        cx: &mut AppContext,
-    ) -> Vec<(String, Option<Hsla>, Option<Hsla>)> {
-        let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
-        let mut chunks: Vec<(String, Option<Hsla>, Option<Hsla>)> = Vec::new();
-        for chunk in snapshot.chunks(rows, true, None, None) {
-            let syntax_color = chunk
-                .syntax_highlight_id
-                .and_then(|id| id.style(theme)?.color);
-            let highlight_color = chunk.highlight_style.and_then(|style| style.color);
-            if let Some((last_chunk, last_syntax_color, last_highlight_color)) = chunks.last_mut() {
-                if syntax_color == *last_syntax_color && highlight_color == *last_highlight_color {
-                    last_chunk.push_str(chunk.text);
-                    continue;
-                }
-            }
-            chunks.push((chunk.text.to_string(), syntax_color, highlight_color));
-        }
-        chunks
-    }
-
-    fn init_test(cx: &mut AppContext, f: impl Fn(&mut AllLanguageSettingsContent)) {
-        let settings = SettingsStore::test(cx);
-        cx.set_global(settings);
-        language::init(cx);
-        crate::init(cx);
-        Project::init_settings(cx);
-        theme::init(LoadThemes::JustBase, cx);
-        cx.update_global::<SettingsStore, _>(|store, cx| {
-            store.update_user_settings::<AllLanguageSettings>(cx, f);
-        });
-    }
-}

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

@@ -1,1647 +0,0 @@
-use super::{
-    wrap_map::{self, WrapEdit, WrapPoint, WrapSnapshot},
-    Highlights,
-};
-use crate::{Anchor, Editor, EditorStyle, ExcerptId, ExcerptRange, ToPoint as _};
-use collections::{Bound, HashMap, HashSet};
-use gpui::{AnyElement, Pixels, ViewContext};
-use language::{BufferSnapshot, Chunk, Patch, Point};
-use parking_lot::Mutex;
-use std::{
-    cell::RefCell,
-    cmp::{self, Ordering},
-    fmt::Debug,
-    ops::{Deref, DerefMut, Range},
-    sync::{
-        atomic::{AtomicUsize, Ordering::SeqCst},
-        Arc,
-    },
-};
-use sum_tree::{Bias, SumTree};
-use text::Edit;
-
-const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
-
-pub struct BlockMap {
-    next_block_id: AtomicUsize,
-    wrap_snapshot: RefCell<WrapSnapshot>,
-    blocks: Vec<Arc<Block>>,
-    transforms: RefCell<SumTree<Transform>>,
-    buffer_header_height: u8,
-    excerpt_header_height: u8,
-}
-
-pub struct BlockMapWriter<'a>(&'a mut BlockMap);
-
-pub struct BlockSnapshot {
-    wrap_snapshot: WrapSnapshot,
-    transforms: SumTree<Transform>,
-}
-
-#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct BlockId(usize);
-
-#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
-pub struct BlockPoint(pub Point);
-
-#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
-struct BlockRow(u32);
-
-#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
-struct WrapRow(u32);
-
-pub type RenderBlock = Arc<dyn Fn(&mut BlockContext) -> AnyElement>;
-
-pub struct Block {
-    id: BlockId,
-    position: Anchor,
-    height: u8,
-    style: BlockStyle,
-    render: Mutex<RenderBlock>,
-    disposition: BlockDisposition,
-}
-
-#[derive(Clone)]
-pub struct BlockProperties<P>
-where
-    P: Clone,
-{
-    pub position: P,
-    pub height: u8,
-    pub style: BlockStyle,
-    pub render: Arc<dyn Fn(&mut BlockContext) -> AnyElement>,
-    pub disposition: BlockDisposition,
-}
-
-#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
-pub enum BlockStyle {
-    Fixed,
-    Flex,
-    Sticky,
-}
-
-pub struct BlockContext<'a, 'b> {
-    pub view_context: &'b mut ViewContext<'a, Editor>,
-    pub anchor_x: Pixels,
-    pub gutter_width: Pixels,
-    pub gutter_padding: Pixels,
-    pub em_width: Pixels,
-    pub line_height: Pixels,
-    pub block_id: usize,
-    pub editor_style: &'b EditorStyle,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
-pub enum BlockDisposition {
-    Above,
-    Below,
-}
-
-#[derive(Clone, Debug)]
-struct Transform {
-    summary: TransformSummary,
-    block: Option<TransformBlock>,
-}
-
-#[allow(clippy::large_enum_variant)]
-#[derive(Clone)]
-pub enum TransformBlock {
-    Custom(Arc<Block>),
-    ExcerptHeader {
-        id: ExcerptId,
-        buffer: BufferSnapshot,
-        range: ExcerptRange<text::Anchor>,
-        height: u8,
-        starts_new_buffer: bool,
-    },
-}
-
-impl TransformBlock {
-    fn disposition(&self) -> BlockDisposition {
-        match self {
-            TransformBlock::Custom(block) => block.disposition,
-            TransformBlock::ExcerptHeader { .. } => BlockDisposition::Above,
-        }
-    }
-
-    pub fn height(&self) -> u8 {
-        match self {
-            TransformBlock::Custom(block) => block.height,
-            TransformBlock::ExcerptHeader { height, .. } => *height,
-        }
-    }
-}
-
-impl Debug for TransformBlock {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            Self::Custom(block) => f.debug_struct("Custom").field("block", block).finish(),
-            Self::ExcerptHeader { buffer, .. } => f
-                .debug_struct("ExcerptHeader")
-                .field("path", &buffer.file().map(|f| f.path()))
-                .finish(),
-        }
-    }
-}
-
-#[derive(Clone, Debug, Default)]
-struct TransformSummary {
-    input_rows: u32,
-    output_rows: u32,
-}
-
-pub struct BlockChunks<'a> {
-    transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
-    input_chunks: wrap_map::WrapChunks<'a>,
-    input_chunk: Chunk<'a>,
-    output_row: u32,
-    max_output_row: u32,
-}
-
-#[derive(Clone)]
-pub struct BlockBufferRows<'a> {
-    transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
-    input_buffer_rows: wrap_map::WrapBufferRows<'a>,
-    output_row: u32,
-    started: bool,
-}
-
-impl BlockMap {
-    pub fn new(
-        wrap_snapshot: WrapSnapshot,
-        buffer_header_height: u8,
-        excerpt_header_height: u8,
-    ) -> Self {
-        let row_count = wrap_snapshot.max_point().row() + 1;
-        let map = Self {
-            next_block_id: AtomicUsize::new(0),
-            blocks: Vec::new(),
-            transforms: RefCell::new(SumTree::from_item(Transform::isomorphic(row_count), &())),
-            wrap_snapshot: RefCell::new(wrap_snapshot.clone()),
-            buffer_header_height,
-            excerpt_header_height,
-        };
-        map.sync(
-            &wrap_snapshot,
-            Patch::new(vec![Edit {
-                old: 0..row_count,
-                new: 0..row_count,
-            }]),
-        );
-        map
-    }
-
-    pub fn read(&self, wrap_snapshot: WrapSnapshot, edits: Patch<u32>) -> BlockSnapshot {
-        self.sync(&wrap_snapshot, edits);
-        *self.wrap_snapshot.borrow_mut() = wrap_snapshot.clone();
-        BlockSnapshot {
-            wrap_snapshot,
-            transforms: self.transforms.borrow().clone(),
-        }
-    }
-
-    pub fn write(&mut self, wrap_snapshot: WrapSnapshot, edits: Patch<u32>) -> BlockMapWriter {
-        self.sync(&wrap_snapshot, edits);
-        *self.wrap_snapshot.borrow_mut() = wrap_snapshot;
-        BlockMapWriter(self)
-    }
-
-    fn sync(&self, wrap_snapshot: &WrapSnapshot, mut edits: Patch<u32>) {
-        let buffer = wrap_snapshot.buffer_snapshot();
-
-        // Handle changing the last excerpt if it is empty.
-        if buffer.trailing_excerpt_update_count()
-            != self
-                .wrap_snapshot
-                .borrow()
-                .buffer_snapshot()
-                .trailing_excerpt_update_count()
-        {
-            let max_point = wrap_snapshot.max_point();
-            let edit_start = wrap_snapshot.prev_row_boundary(max_point);
-            let edit_end = max_point.row() + 1;
-            edits = edits.compose([WrapEdit {
-                old: edit_start..edit_end,
-                new: edit_start..edit_end,
-            }]);
-        }
-
-        let edits = edits.into_inner();
-        if edits.is_empty() {
-            return;
-        }
-
-        let mut transforms = self.transforms.borrow_mut();
-        let mut new_transforms = SumTree::new();
-        let old_row_count = transforms.summary().input_rows;
-        let new_row_count = wrap_snapshot.max_point().row() + 1;
-        let mut cursor = transforms.cursor::<WrapRow>();
-        let mut last_block_ix = 0;
-        let mut blocks_in_edit = Vec::new();
-        let mut edits = edits.into_iter().peekable();
-
-        while let Some(edit) = edits.next() {
-            // Preserve any old transforms that precede this edit.
-            let old_start = WrapRow(edit.old.start);
-            let new_start = WrapRow(edit.new.start);
-            new_transforms.append(cursor.slice(&old_start, Bias::Left, &()), &());
-            if let Some(transform) = cursor.item() {
-                if transform.is_isomorphic() && old_start == cursor.end(&()) {
-                    new_transforms.push(transform.clone(), &());
-                    cursor.next(&());
-                    while let Some(transform) = cursor.item() {
-                        if transform
-                            .block
-                            .as_ref()
-                            .map_or(false, |b| b.disposition().is_below())
-                        {
-                            new_transforms.push(transform.clone(), &());
-                            cursor.next(&());
-                        } else {
-                            break;
-                        }
-                    }
-                }
-            }
-
-            // Preserve any portion of an old transform that precedes this edit.
-            let extent_before_edit = old_start.0 - cursor.start().0;
-            push_isomorphic(&mut new_transforms, extent_before_edit);
-
-            // Skip over any old transforms that intersect this edit.
-            let mut old_end = WrapRow(edit.old.end);
-            let mut new_end = WrapRow(edit.new.end);
-            cursor.seek(&old_end, Bias::Left, &());
-            cursor.next(&());
-            if old_end == *cursor.start() {
-                while let Some(transform) = cursor.item() {
-                    if transform
-                        .block
-                        .as_ref()
-                        .map_or(false, |b| b.disposition().is_below())
-                    {
-                        cursor.next(&());
-                    } else {
-                        break;
-                    }
-                }
-            }
-
-            // Combine this edit with any subsequent edits that intersect the same transform.
-            while let Some(next_edit) = edits.peek() {
-                if next_edit.old.start <= cursor.start().0 {
-                    old_end = WrapRow(next_edit.old.end);
-                    new_end = WrapRow(next_edit.new.end);
-                    cursor.seek(&old_end, Bias::Left, &());
-                    cursor.next(&());
-                    if old_end == *cursor.start() {
-                        while let Some(transform) = cursor.item() {
-                            if transform
-                                .block
-                                .as_ref()
-                                .map_or(false, |b| b.disposition().is_below())
-                            {
-                                cursor.next(&());
-                            } else {
-                                break;
-                            }
-                        }
-                    }
-                    edits.next();
-                } else {
-                    break;
-                }
-            }
-
-            // Find the blocks within this edited region.
-            let new_buffer_start =
-                wrap_snapshot.to_point(WrapPoint::new(new_start.0, 0), Bias::Left);
-            let start_bound = Bound::Included(new_buffer_start);
-            let start_block_ix = match self.blocks[last_block_ix..].binary_search_by(|probe| {
-                probe
-                    .position
-                    .to_point(buffer)
-                    .cmp(&new_buffer_start)
-                    .then(Ordering::Greater)
-            }) {
-                Ok(ix) | Err(ix) => last_block_ix + ix,
-            };
-
-            let end_bound;
-            let end_block_ix = if new_end.0 > wrap_snapshot.max_point().row() {
-                end_bound = Bound::Unbounded;
-                self.blocks.len()
-            } else {
-                let new_buffer_end =
-                    wrap_snapshot.to_point(WrapPoint::new(new_end.0, 0), Bias::Left);
-                end_bound = Bound::Excluded(new_buffer_end);
-                match self.blocks[start_block_ix..].binary_search_by(|probe| {
-                    probe
-                        .position
-                        .to_point(buffer)
-                        .cmp(&new_buffer_end)
-                        .then(Ordering::Greater)
-                }) {
-                    Ok(ix) | Err(ix) => start_block_ix + ix,
-                }
-            };
-            last_block_ix = end_block_ix;
-
-            debug_assert!(blocks_in_edit.is_empty());
-            blocks_in_edit.extend(
-                self.blocks[start_block_ix..end_block_ix]
-                    .iter()
-                    .map(|block| {
-                        let mut position = block.position.to_point(buffer);
-                        match block.disposition {
-                            BlockDisposition::Above => position.column = 0,
-                            BlockDisposition::Below => {
-                                position.column = buffer.line_len(position.row)
-                            }
-                        }
-                        let position = wrap_snapshot.make_wrap_point(position, Bias::Left);
-                        (position.row(), TransformBlock::Custom(block.clone()))
-                    }),
-            );
-            blocks_in_edit.extend(
-                buffer
-                    .excerpt_boundaries_in_range((start_bound, end_bound))
-                    .map(|excerpt_boundary| {
-                        (
-                            wrap_snapshot
-                                .make_wrap_point(Point::new(excerpt_boundary.row, 0), Bias::Left)
-                                .row(),
-                            TransformBlock::ExcerptHeader {
-                                id: excerpt_boundary.id,
-                                buffer: excerpt_boundary.buffer,
-                                range: excerpt_boundary.range,
-                                height: if excerpt_boundary.starts_new_buffer {
-                                    self.buffer_header_height
-                                } else {
-                                    self.excerpt_header_height
-                                },
-                                starts_new_buffer: excerpt_boundary.starts_new_buffer,
-                            },
-                        )
-                    }),
-            );
-
-            // Place excerpt headers above custom blocks on the same row.
-            blocks_in_edit.sort_unstable_by(|(row_a, block_a), (row_b, block_b)| {
-                row_a.cmp(row_b).then_with(|| match (block_a, block_b) {
-                    (
-                        TransformBlock::ExcerptHeader { .. },
-                        TransformBlock::ExcerptHeader { .. },
-                    ) => Ordering::Equal,
-                    (TransformBlock::ExcerptHeader { .. }, _) => Ordering::Less,
-                    (_, TransformBlock::ExcerptHeader { .. }) => Ordering::Greater,
-                    (TransformBlock::Custom(block_a), TransformBlock::Custom(block_b)) => block_a
-                        .disposition
-                        .cmp(&block_b.disposition)
-                        .then_with(|| block_a.id.cmp(&block_b.id)),
-                })
-            });
-
-            // For each of these blocks, insert a new isomorphic transform preceding the block,
-            // and then insert the block itself.
-            for (block_row, block) in blocks_in_edit.drain(..) {
-                let insertion_row = match block.disposition() {
-                    BlockDisposition::Above => block_row,
-                    BlockDisposition::Below => block_row + 1,
-                };
-                let extent_before_block = insertion_row - new_transforms.summary().input_rows;
-                push_isomorphic(&mut new_transforms, extent_before_block);
-                new_transforms.push(Transform::block(block), &());
-            }
-
-            old_end = WrapRow(old_end.0.min(old_row_count));
-            new_end = WrapRow(new_end.0.min(new_row_count));
-
-            // Insert an isomorphic transform after the final block.
-            let extent_after_last_block = new_end.0 - new_transforms.summary().input_rows;
-            push_isomorphic(&mut new_transforms, extent_after_last_block);
-
-            // Preserve any portion of the old transform after this edit.
-            let extent_after_edit = cursor.start().0 - old_end.0;
-            push_isomorphic(&mut new_transforms, extent_after_edit);
-        }
-
-        new_transforms.append(cursor.suffix(&()), &());
-        debug_assert_eq!(
-            new_transforms.summary().input_rows,
-            wrap_snapshot.max_point().row() + 1
-        );
-
-        drop(cursor);
-        *transforms = new_transforms;
-    }
-
-    pub fn replace(&mut self, mut renderers: HashMap<BlockId, RenderBlock>) {
-        for block in &self.blocks {
-            if let Some(render) = renderers.remove(&block.id) {
-                *block.render.lock() = render;
-            }
-        }
-    }
-}
-
-fn push_isomorphic(tree: &mut SumTree<Transform>, rows: u32) {
-    if rows == 0 {
-        return;
-    }
-
-    let mut extent = Some(rows);
-    tree.update_last(
-        |last_transform| {
-            if last_transform.is_isomorphic() {
-                let extent = extent.take().unwrap();
-                last_transform.summary.input_rows += extent;
-                last_transform.summary.output_rows += extent;
-            }
-        },
-        &(),
-    );
-    if let Some(extent) = extent {
-        tree.push(Transform::isomorphic(extent), &());
-    }
-}
-
-impl BlockPoint {
-    pub fn new(row: u32, column: u32) -> Self {
-        Self(Point::new(row, column))
-    }
-}
-
-impl Deref for BlockPoint {
-    type Target = Point;
-
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-
-impl std::ops::DerefMut for BlockPoint {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.0
-    }
-}
-
-impl<'a> BlockMapWriter<'a> {
-    pub fn insert(
-        &mut self,
-        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
-    ) -> Vec<BlockId> {
-        let mut ids = Vec::new();
-        let mut edits = Patch::default();
-        let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
-        let buffer = wrap_snapshot.buffer_snapshot();
-
-        for block in blocks {
-            let id = BlockId(self.0.next_block_id.fetch_add(1, SeqCst));
-            ids.push(id);
-
-            let position = block.position;
-            let point = position.to_point(buffer);
-            let wrap_row = wrap_snapshot
-                .make_wrap_point(Point::new(point.row, 0), Bias::Left)
-                .row();
-            let start_row = wrap_snapshot.prev_row_boundary(WrapPoint::new(wrap_row, 0));
-            let end_row = wrap_snapshot
-                .next_row_boundary(WrapPoint::new(wrap_row, 0))
-                .unwrap_or(wrap_snapshot.max_point().row() + 1);
-
-            let block_ix = match self
-                .0
-                .blocks
-                .binary_search_by(|probe| probe.position.cmp(&position, buffer))
-            {
-                Ok(ix) | Err(ix) => ix,
-            };
-            self.0.blocks.insert(
-                block_ix,
-                Arc::new(Block {
-                    id,
-                    position,
-                    height: block.height,
-                    render: Mutex::new(block.render),
-                    disposition: block.disposition,
-                    style: block.style,
-                }),
-            );
-
-            edits = edits.compose([Edit {
-                old: start_row..end_row,
-                new: start_row..end_row,
-            }]);
-        }
-
-        self.0.sync(wrap_snapshot, edits);
-        ids
-    }
-
-    pub fn remove(&mut self, block_ids: HashSet<BlockId>) {
-        let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
-        let buffer = wrap_snapshot.buffer_snapshot();
-        let mut edits = Patch::default();
-        let mut last_block_buffer_row = None;
-        self.0.blocks.retain(|block| {
-            if block_ids.contains(&block.id) {
-                let buffer_row = block.position.to_point(buffer).row;
-                if last_block_buffer_row != Some(buffer_row) {
-                    last_block_buffer_row = Some(buffer_row);
-                    let wrap_row = wrap_snapshot
-                        .make_wrap_point(Point::new(buffer_row, 0), Bias::Left)
-                        .row();
-                    let start_row = wrap_snapshot.prev_row_boundary(WrapPoint::new(wrap_row, 0));
-                    let end_row = wrap_snapshot
-                        .next_row_boundary(WrapPoint::new(wrap_row, 0))
-                        .unwrap_or(wrap_snapshot.max_point().row() + 1);
-                    edits.push(Edit {
-                        old: start_row..end_row,
-                        new: start_row..end_row,
-                    })
-                }
-                false
-            } else {
-                true
-            }
-        });
-        self.0.sync(wrap_snapshot, edits);
-    }
-}
-
-impl BlockSnapshot {
-    #[cfg(test)]
-    pub fn text(&self) -> String {
-        self.chunks(
-            0..self.transforms.summary().output_rows,
-            false,
-            Highlights::default(),
-        )
-        .map(|chunk| chunk.text)
-        .collect()
-    }
-
-    pub fn chunks<'a>(
-        &'a self,
-        rows: Range<u32>,
-        language_aware: bool,
-        highlights: Highlights<'a>,
-    ) -> BlockChunks<'a> {
-        let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
-        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
-        let input_end = {
-            cursor.seek(&BlockRow(rows.end), Bias::Right, &());
-            let overshoot = if cursor
-                .item()
-                .map_or(false, |transform| transform.is_isomorphic())
-            {
-                rows.end - cursor.start().0 .0
-            } else {
-                0
-            };
-            cursor.start().1 .0 + overshoot
-        };
-        let input_start = {
-            cursor.seek(&BlockRow(rows.start), Bias::Right, &());
-            let overshoot = if cursor
-                .item()
-                .map_or(false, |transform| transform.is_isomorphic())
-            {
-                rows.start - cursor.start().0 .0
-            } else {
-                0
-            };
-            cursor.start().1 .0 + overshoot
-        };
-        BlockChunks {
-            input_chunks: self.wrap_snapshot.chunks(
-                input_start..input_end,
-                language_aware,
-                highlights,
-            ),
-            input_chunk: Default::default(),
-            transforms: cursor,
-            output_row: rows.start,
-            max_output_row,
-        }
-    }
-
-    pub fn buffer_rows(&self, start_row: u32) -> BlockBufferRows {
-        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
-        cursor.seek(&BlockRow(start_row), Bias::Right, &());
-        let (output_start, input_start) = cursor.start();
-        let overshoot = if cursor.item().map_or(false, |t| t.is_isomorphic()) {
-            start_row - output_start.0
-        } else {
-            0
-        };
-        let input_start_row = input_start.0 + overshoot;
-        BlockBufferRows {
-            transforms: cursor,
-            input_buffer_rows: self.wrap_snapshot.buffer_rows(input_start_row),
-            output_row: start_row,
-            started: false,
-        }
-    }
-
-    pub fn blocks_in_range(
-        &self,
-        rows: Range<u32>,
-    ) -> impl Iterator<Item = (u32, &TransformBlock)> {
-        let mut cursor = self.transforms.cursor::<BlockRow>();
-        cursor.seek(&BlockRow(rows.start), Bias::Right, &());
-        std::iter::from_fn(move || {
-            while let Some(transform) = cursor.item() {
-                let start_row = cursor.start().0;
-                if start_row >= rows.end {
-                    break;
-                }
-                if let Some(block) = &transform.block {
-                    cursor.next(&());
-                    return Some((start_row, block));
-                } else {
-                    cursor.next(&());
-                }
-            }
-            None
-        })
-    }
-
-    pub fn max_point(&self) -> BlockPoint {
-        let row = self.transforms.summary().output_rows - 1;
-        BlockPoint::new(row, self.line_len(row))
-    }
-
-    pub fn longest_row(&self) -> u32 {
-        let input_row = self.wrap_snapshot.longest_row();
-        self.to_block_point(WrapPoint::new(input_row, 0)).row
-    }
-
-    pub fn line_len(&self, row: u32) -> u32 {
-        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
-        cursor.seek(&BlockRow(row), Bias::Right, &());
-        if let Some(transform) = cursor.item() {
-            let (output_start, input_start) = cursor.start();
-            let overshoot = row - output_start.0;
-            if transform.block.is_some() {
-                0
-            } else {
-                self.wrap_snapshot.line_len(input_start.0 + overshoot)
-            }
-        } else {
-            panic!("row out of range");
-        }
-    }
-
-    pub fn is_block_line(&self, row: u32) -> bool {
-        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
-        cursor.seek(&BlockRow(row), Bias::Right, &());
-        cursor.item().map_or(false, |t| t.block.is_some())
-    }
-
-    pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
-        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
-        cursor.seek(&BlockRow(point.row), Bias::Right, &());
-
-        let max_input_row = WrapRow(self.transforms.summary().input_rows);
-        let mut search_left =
-            (bias == Bias::Left && cursor.start().1 .0 > 0) || cursor.end(&()).1 == max_input_row;
-        let mut reversed = false;
-
-        loop {
-            if let Some(transform) = cursor.item() {
-                if transform.is_isomorphic() {
-                    let (output_start_row, input_start_row) = cursor.start();
-                    let (output_end_row, input_end_row) = cursor.end(&());
-                    let output_start = Point::new(output_start_row.0, 0);
-                    let input_start = Point::new(input_start_row.0, 0);
-                    let input_end = Point::new(input_end_row.0, 0);
-                    let input_point = if point.row >= output_end_row.0 {
-                        let line_len = self.wrap_snapshot.line_len(input_end_row.0 - 1);
-                        self.wrap_snapshot
-                            .clip_point(WrapPoint::new(input_end_row.0 - 1, line_len), bias)
-                    } else {
-                        let output_overshoot = point.0.saturating_sub(output_start);
-                        self.wrap_snapshot
-                            .clip_point(WrapPoint(input_start + output_overshoot), bias)
-                    };
-
-                    if (input_start..input_end).contains(&input_point.0) {
-                        let input_overshoot = input_point.0.saturating_sub(input_start);
-                        return BlockPoint(output_start + input_overshoot);
-                    }
-                }
-
-                if search_left {
-                    cursor.prev(&());
-                } else {
-                    cursor.next(&());
-                }
-            } else if reversed {
-                return self.max_point();
-            } else {
-                reversed = true;
-                search_left = !search_left;
-                cursor.seek(&BlockRow(point.row), Bias::Right, &());
-            }
-        }
-    }
-
-    pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
-        let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>();
-        cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
-        if let Some(transform) = cursor.item() {
-            debug_assert!(transform.is_isomorphic());
-        } else {
-            return self.max_point();
-        }
-
-        let (input_start_row, output_start_row) = cursor.start();
-        let input_start = Point::new(input_start_row.0, 0);
-        let output_start = Point::new(output_start_row.0, 0);
-        let input_overshoot = wrap_point.0 - input_start;
-        BlockPoint(output_start + input_overshoot)
-    }
-
-    pub fn to_wrap_point(&self, block_point: BlockPoint) -> WrapPoint {
-        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
-        cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
-        if let Some(transform) = cursor.item() {
-            match transform.block.as_ref().map(|b| b.disposition()) {
-                Some(BlockDisposition::Above) => WrapPoint::new(cursor.start().1 .0, 0),
-                Some(BlockDisposition::Below) => {
-                    let wrap_row = cursor.start().1 .0 - 1;
-                    WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
-                }
-                None => {
-                    let overshoot = block_point.row - cursor.start().0 .0;
-                    let wrap_row = cursor.start().1 .0 + overshoot;
-                    WrapPoint::new(wrap_row, block_point.column)
-                }
-            }
-        } else {
-            self.wrap_snapshot.max_point()
-        }
-    }
-}
-
-impl Transform {
-    fn isomorphic(rows: u32) -> Self {
-        Self {
-            summary: TransformSummary {
-                input_rows: rows,
-                output_rows: rows,
-            },
-            block: None,
-        }
-    }
-
-    fn block(block: TransformBlock) -> Self {
-        Self {
-            summary: TransformSummary {
-                input_rows: 0,
-                output_rows: block.height() as u32,
-            },
-            block: Some(block),
-        }
-    }
-
-    fn is_isomorphic(&self) -> bool {
-        self.block.is_none()
-    }
-}
-
-impl<'a> Iterator for BlockChunks<'a> {
-    type Item = Chunk<'a>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.output_row >= self.max_output_row {
-            return None;
-        }
-
-        let transform = self.transforms.item()?;
-        if transform.block.is_some() {
-            let block_start = self.transforms.start().0 .0;
-            let mut block_end = self.transforms.end(&()).0 .0;
-            self.transforms.next(&());
-            if self.transforms.item().is_none() {
-                block_end -= 1;
-            }
-
-            let start_in_block = self.output_row - block_start;
-            let end_in_block = cmp::min(self.max_output_row, block_end) - block_start;
-            let line_count = end_in_block - start_in_block;
-            self.output_row += line_count;
-
-            return Some(Chunk {
-                text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..line_count as usize]) },
-                ..Default::default()
-            });
-        }
-
-        if self.input_chunk.text.is_empty() {
-            if let Some(input_chunk) = self.input_chunks.next() {
-                self.input_chunk = input_chunk;
-            } else {
-                self.output_row += 1;
-                if self.output_row < self.max_output_row {
-                    self.transforms.next(&());
-                    return Some(Chunk {
-                        text: "\n",
-                        ..Default::default()
-                    });
-                } else {
-                    return None;
-                }
-            }
-        }
-
-        let transform_end = self.transforms.end(&()).0 .0;
-        let (prefix_rows, prefix_bytes) =
-            offset_for_row(self.input_chunk.text, transform_end - self.output_row);
-        self.output_row += prefix_rows;
-        let (prefix, suffix) = self.input_chunk.text.split_at(prefix_bytes);
-        self.input_chunk.text = suffix;
-        if self.output_row == transform_end {
-            self.transforms.next(&());
-        }
-
-        Some(Chunk {
-            text: prefix,
-            ..self.input_chunk
-        })
-    }
-}
-
-impl<'a> Iterator for BlockBufferRows<'a> {
-    type Item = Option<u32>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.started {
-            self.output_row += 1;
-        } else {
-            self.started = true;
-        }
-
-        if self.output_row >= self.transforms.end(&()).0 .0 {
-            self.transforms.next(&());
-        }
-
-        let transform = self.transforms.item()?;
-        if transform.block.is_some() {
-            Some(None)
-        } else {
-            Some(self.input_buffer_rows.next().unwrap())
-        }
-    }
-}
-
-impl sum_tree::Item for Transform {
-    type Summary = TransformSummary;
-
-    fn summary(&self) -> Self::Summary {
-        self.summary.clone()
-    }
-}
-
-impl sum_tree::Summary for TransformSummary {
-    type Context = ();
-
-    fn add_summary(&mut self, summary: &Self, _: &()) {
-        self.input_rows += summary.input_rows;
-        self.output_rows += summary.output_rows;
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
-    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
-        self.0 += summary.input_rows;
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
-    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
-        self.0 += summary.output_rows;
-    }
-}
-
-impl BlockDisposition {
-    fn is_below(&self) -> bool {
-        matches!(self, BlockDisposition::Below)
-    }
-}
-
-impl<'a> Deref for BlockContext<'a, '_> {
-    type Target = ViewContext<'a, Editor>;
-
-    fn deref(&self) -> &Self::Target {
-        self.view_context
-    }
-}
-
-impl DerefMut for BlockContext<'_, '_> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        self.view_context
-    }
-}
-
-impl Block {
-    pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
-        self.render.lock()(cx)
-    }
-
-    pub fn position(&self) -> &Anchor {
-        &self.position
-    }
-
-    pub fn style(&self) -> BlockStyle {
-        self.style
-    }
-}
-
-impl Debug for Block {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("Block")
-            .field("id", &self.id)
-            .field("position", &self.position)
-            .field("disposition", &self.disposition)
-            .finish()
-    }
-}
-
-// Count the number of bytes prior to a target point. If the string doesn't contain the target
-// point, return its total extent. Otherwise return the target point itself.
-fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
-    let mut row = 0;
-    let mut offset = 0;
-    for (ix, line) in s.split('\n').enumerate() {
-        if ix > 0 {
-            row += 1;
-            offset += 1;
-        }
-        if row >= target {
-            break;
-        }
-        offset += line.len() as usize;
-    }
-    (row, offset)
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::display_map::inlay_map::InlayMap;
-    use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
-    use gpui::{div, font, px, Element};
-    use multi_buffer::MultiBuffer;
-    use rand::prelude::*;
-    use settings::SettingsStore;
-    use std::env;
-    use util::RandomCharIter;
-
-    #[gpui::test]
-    fn test_offset_for_row() {
-        assert_eq!(offset_for_row("", 0), (0, 0));
-        assert_eq!(offset_for_row("", 1), (0, 0));
-        assert_eq!(offset_for_row("abcd", 0), (0, 0));
-        assert_eq!(offset_for_row("abcd", 1), (0, 4));
-        assert_eq!(offset_for_row("\n", 0), (0, 0));
-        assert_eq!(offset_for_row("\n", 1), (1, 1));
-        assert_eq!(offset_for_row("abc\ndef\nghi", 0), (0, 0));
-        assert_eq!(offset_for_row("abc\ndef\nghi", 1), (1, 4));
-        assert_eq!(offset_for_row("abc\ndef\nghi", 2), (2, 8));
-        assert_eq!(offset_for_row("abc\ndef\nghi", 3), (2, 11));
-    }
-
-    #[gpui::test]
-    fn test_basic_blocks(cx: &mut gpui::TestAppContext) {
-        cx.update(|cx| init_test(cx));
-
-        let text = "aaa\nbbb\nccc\nddd";
-
-        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
-        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
-        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
-        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
-        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
-        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
-        let (wrap_map, wraps_snapshot) =
-            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
-        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
-
-        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
-        let block_ids = writer.insert(vec![
-            BlockProperties {
-                style: BlockStyle::Fixed,
-                position: buffer_snapshot.anchor_after(Point::new(1, 0)),
-                height: 1,
-                disposition: BlockDisposition::Above,
-                render: Arc::new(|_| div().into_any()),
-            },
-            BlockProperties {
-                style: BlockStyle::Fixed,
-                position: buffer_snapshot.anchor_after(Point::new(1, 2)),
-                height: 2,
-                disposition: BlockDisposition::Above,
-                render: Arc::new(|_| div().into_any()),
-            },
-            BlockProperties {
-                style: BlockStyle::Fixed,
-                position: buffer_snapshot.anchor_after(Point::new(3, 3)),
-                height: 3,
-                disposition: BlockDisposition::Below,
-                render: Arc::new(|_| div().into_any()),
-            },
-        ]);
-
-        let snapshot = block_map.read(wraps_snapshot, Default::default());
-        assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
-
-        let blocks = snapshot
-            .blocks_in_range(0..8)
-            .map(|(start_row, block)| {
-                let block = block.as_custom().unwrap();
-                (start_row..start_row + block.height as u32, block.id)
-            })
-            .collect::<Vec<_>>();
-
-        // When multiple blocks are on the same line, the newer blocks appear first.
-        assert_eq!(
-            blocks,
-            &[
-                (1..2, block_ids[0]),
-                (2..4, block_ids[1]),
-                (7..10, block_ids[2]),
-            ]
-        );
-
-        assert_eq!(
-            snapshot.to_block_point(WrapPoint::new(0, 3)),
-            BlockPoint::new(0, 3)
-        );
-        assert_eq!(
-            snapshot.to_block_point(WrapPoint::new(1, 0)),
-            BlockPoint::new(4, 0)
-        );
-        assert_eq!(
-            snapshot.to_block_point(WrapPoint::new(3, 3)),
-            BlockPoint::new(6, 3)
-        );
-
-        assert_eq!(
-            snapshot.to_wrap_point(BlockPoint::new(0, 3)),
-            WrapPoint::new(0, 3)
-        );
-        assert_eq!(
-            snapshot.to_wrap_point(BlockPoint::new(1, 0)),
-            WrapPoint::new(1, 0)
-        );
-        assert_eq!(
-            snapshot.to_wrap_point(BlockPoint::new(3, 0)),
-            WrapPoint::new(1, 0)
-        );
-        assert_eq!(
-            snapshot.to_wrap_point(BlockPoint::new(7, 0)),
-            WrapPoint::new(3, 3)
-        );
-
-        assert_eq!(
-            snapshot.clip_point(BlockPoint::new(1, 0), Bias::Left),
-            BlockPoint::new(0, 3)
-        );
-        assert_eq!(
-            snapshot.clip_point(BlockPoint::new(1, 0), Bias::Right),
-            BlockPoint::new(4, 0)
-        );
-        assert_eq!(
-            snapshot.clip_point(BlockPoint::new(1, 1), Bias::Left),
-            BlockPoint::new(0, 3)
-        );
-        assert_eq!(
-            snapshot.clip_point(BlockPoint::new(1, 1), Bias::Right),
-            BlockPoint::new(4, 0)
-        );
-        assert_eq!(
-            snapshot.clip_point(BlockPoint::new(4, 0), Bias::Left),
-            BlockPoint::new(4, 0)
-        );
-        assert_eq!(
-            snapshot.clip_point(BlockPoint::new(4, 0), Bias::Right),
-            BlockPoint::new(4, 0)
-        );
-        assert_eq!(
-            snapshot.clip_point(BlockPoint::new(6, 3), Bias::Left),
-            BlockPoint::new(6, 3)
-        );
-        assert_eq!(
-            snapshot.clip_point(BlockPoint::new(6, 3), Bias::Right),
-            BlockPoint::new(6, 3)
-        );
-        assert_eq!(
-            snapshot.clip_point(BlockPoint::new(7, 0), Bias::Left),
-            BlockPoint::new(6, 3)
-        );
-        assert_eq!(
-            snapshot.clip_point(BlockPoint::new(7, 0), Bias::Right),
-            BlockPoint::new(6, 3)
-        );
-
-        assert_eq!(
-            snapshot.buffer_rows(0).collect::<Vec<_>>(),
-            &[
-                Some(0),
-                None,
-                None,
-                None,
-                Some(1),
-                Some(2),
-                Some(3),
-                None,
-                None,
-                None
-            ]
-        );
-
-        // Insert a line break, separating two block decorations into separate lines.
-        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
-            buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
-            buffer.snapshot(cx)
-        });
-
-        let (inlay_snapshot, inlay_edits) =
-            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
-        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
-        let (tab_snapshot, tab_edits) =
-            tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
-        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
-            wrap_map.sync(tab_snapshot, tab_edits, cx)
-        });
-        let snapshot = block_map.read(wraps_snapshot, wrap_edits);
-        assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
-    }
-
-    #[gpui::test]
-    fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
-        cx.update(|cx| init_test(cx));
-
-        let _font_id = cx.text_system().font_id(&font("Helvetica")).unwrap();
-
-        let text = "one two three\nfour five six\nseven eight";
-
-        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
-        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
-        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
-        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
-        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
-        let (_, wraps_snapshot) = cx.update(|cx| {
-            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(60.)), cx)
-        });
-        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
-
-        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
-        writer.insert(vec![
-            BlockProperties {
-                style: BlockStyle::Fixed,
-                position: buffer_snapshot.anchor_after(Point::new(1, 12)),
-                disposition: BlockDisposition::Above,
-                render: Arc::new(|_| div().into_any()),
-                height: 1,
-            },
-            BlockProperties {
-                style: BlockStyle::Fixed,
-                position: buffer_snapshot.anchor_after(Point::new(1, 1)),
-                disposition: BlockDisposition::Below,
-                render: Arc::new(|_| div().into_any()),
-                height: 1,
-            },
-        ]);
-
-        // Blocks with an 'above' disposition go above their corresponding buffer line.
-        // Blocks with a 'below' disposition go below their corresponding buffer line.
-        let snapshot = block_map.read(wraps_snapshot, Default::default());
-        assert_eq!(
-            snapshot.text(),
-            "one two \nthree\n\nfour five \nsix\n\nseven \neight"
-        );
-    }
-
-    #[gpui::test(iterations = 100)]
-    fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
-        cx.update(|cx| init_test(cx));
-
-        let operations = env::var("OPERATIONS")
-            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
-            .unwrap_or(10);
-
-        let wrap_width = if rng.gen_bool(0.2) {
-            None
-        } else {
-            Some(px(rng.gen_range(0.0..=100.0)))
-        };
-        let tab_size = 1.try_into().unwrap();
-        let font_size = px(14.0);
-        let buffer_start_header_height = rng.gen_range(1..=5);
-        let excerpt_header_height = rng.gen_range(1..=5);
-
-        log::info!("Wrap width: {:?}", wrap_width);
-        log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
-
-        let buffer = if rng.gen() {
-            let len = rng.gen_range(0..10);
-            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
-            log::info!("initial buffer text: {:?}", text);
-            cx.update(|cx| MultiBuffer::build_simple(&text, cx))
-        } else {
-            cx.update(|cx| MultiBuffer::build_random(&mut rng, cx))
-        };
-
-        let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
-        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
-        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
-        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
-        let (wrap_map, wraps_snapshot) = cx
-            .update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), font_size, wrap_width, cx));
-        let mut block_map = BlockMap::new(
-            wraps_snapshot,
-            buffer_start_header_height,
-            excerpt_header_height,
-        );
-        let mut custom_blocks = Vec::new();
-
-        for _ in 0..operations {
-            let mut buffer_edits = Vec::new();
-            match rng.gen_range(0..=100) {
-                0..=19 => {
-                    let wrap_width = if rng.gen_bool(0.2) {
-                        None
-                    } else {
-                        Some(px(rng.gen_range(0.0..=100.0)))
-                    };
-                    log::info!("Setting wrap width to {:?}", wrap_width);
-                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
-                }
-                20..=39 => {
-                    let block_count = rng.gen_range(1..=5);
-                    let block_properties = (0..block_count)
-                        .map(|_| {
-                            let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
-                            let position = buffer.anchor_after(
-                                buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left),
-                            );
-
-                            let disposition = if rng.gen() {
-                                BlockDisposition::Above
-                            } else {
-                                BlockDisposition::Below
-                            };
-                            let height = rng.gen_range(1..5);
-                            log::info!(
-                                "inserting block {:?} {:?} with height {}",
-                                disposition,
-                                position.to_point(&buffer),
-                                height
-                            );
-                            BlockProperties {
-                                style: BlockStyle::Fixed,
-                                position,
-                                height,
-                                disposition,
-                                render: Arc::new(|_| div().into_any()),
-                            }
-                        })
-                        .collect::<Vec<_>>();
-
-                    let (inlay_snapshot, inlay_edits) =
-                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
-                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
-                    let (tab_snapshot, tab_edits) =
-                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
-                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
-                        wrap_map.sync(tab_snapshot, tab_edits, cx)
-                    });
-                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
-                    let block_ids = block_map.insert(block_properties.clone());
-                    for (block_id, props) in block_ids.into_iter().zip(block_properties) {
-                        custom_blocks.push((block_id, props));
-                    }
-                }
-                40..=59 if !custom_blocks.is_empty() => {
-                    let block_count = rng.gen_range(1..=4.min(custom_blocks.len()));
-                    let block_ids_to_remove = (0..block_count)
-                        .map(|_| {
-                            custom_blocks
-                                .remove(rng.gen_range(0..custom_blocks.len()))
-                                .0
-                        })
-                        .collect();
-
-                    let (inlay_snapshot, inlay_edits) =
-                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
-                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
-                    let (tab_snapshot, tab_edits) =
-                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
-                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
-                        wrap_map.sync(tab_snapshot, tab_edits, cx)
-                    });
-                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
-                    block_map.remove(block_ids_to_remove);
-                }
-                _ => {
-                    buffer.update(cx, |buffer, cx| {
-                        let mutation_count = rng.gen_range(1..=5);
-                        let subscription = buffer.subscribe();
-                        buffer.randomly_mutate(&mut rng, mutation_count, cx);
-                        buffer_snapshot = buffer.snapshot(cx);
-                        buffer_edits.extend(subscription.consume());
-                        log::info!("buffer text: {:?}", buffer_snapshot.text());
-                    });
-                }
-            }
-
-            let (inlay_snapshot, inlay_edits) =
-                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
-            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
-            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
-            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
-                wrap_map.sync(tab_snapshot, tab_edits, cx)
-            });
-            let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
-            assert_eq!(
-                blocks_snapshot.transforms.summary().input_rows,
-                wraps_snapshot.max_point().row() + 1
-            );
-            log::info!("blocks text: {:?}", blocks_snapshot.text());
-
-            let mut expected_blocks = Vec::new();
-            expected_blocks.extend(custom_blocks.iter().map(|(id, block)| {
-                let mut position = block.position.to_point(&buffer_snapshot);
-                match block.disposition {
-                    BlockDisposition::Above => {
-                        position.column = 0;
-                    }
-                    BlockDisposition::Below => {
-                        position.column = buffer_snapshot.line_len(position.row);
-                    }
-                };
-                let row = wraps_snapshot.make_wrap_point(position, Bias::Left).row();
-                (
-                    row,
-                    ExpectedBlock::Custom {
-                        disposition: block.disposition,
-                        id: *id,
-                        height: block.height,
-                    },
-                )
-            }));
-            expected_blocks.extend(buffer_snapshot.excerpt_boundaries_in_range(0..).map(
-                |boundary| {
-                    let position =
-                        wraps_snapshot.make_wrap_point(Point::new(boundary.row, 0), Bias::Left);
-                    (
-                        position.row(),
-                        ExpectedBlock::ExcerptHeader {
-                            height: if boundary.starts_new_buffer {
-                                buffer_start_header_height
-                            } else {
-                                excerpt_header_height
-                            },
-                            starts_new_buffer: boundary.starts_new_buffer,
-                        },
-                    )
-                },
-            ));
-            expected_blocks.sort_unstable();
-            let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
-
-            let input_buffer_rows = buffer_snapshot.buffer_rows(0).collect::<Vec<_>>();
-            let mut expected_buffer_rows = Vec::new();
-            let mut expected_text = String::new();
-            let mut expected_block_positions = Vec::new();
-            let input_text = wraps_snapshot.text();
-            for (row, input_line) in input_text.split('\n').enumerate() {
-                let row = row as u32;
-                if row > 0 {
-                    expected_text.push('\n');
-                }
-
-                let buffer_row = input_buffer_rows[wraps_snapshot
-                    .to_point(WrapPoint::new(row, 0), Bias::Left)
-                    .row as usize];
-
-                while let Some((block_row, block)) = sorted_blocks_iter.peek() {
-                    if *block_row == row && block.disposition() == BlockDisposition::Above {
-                        let (_, block) = sorted_blocks_iter.next().unwrap();
-                        let height = block.height() as usize;
-                        expected_block_positions
-                            .push((expected_text.matches('\n').count() as u32, block));
-                        let text = "\n".repeat(height);
-                        expected_text.push_str(&text);
-                        for _ in 0..height {
-                            expected_buffer_rows.push(None);
-                        }
-                    } else {
-                        break;
-                    }
-                }
-
-                let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0;
-                expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
-                expected_text.push_str(input_line);
-
-                while let Some((block_row, block)) = sorted_blocks_iter.peek() {
-                    if *block_row == row && block.disposition() == BlockDisposition::Below {
-                        let (_, block) = sorted_blocks_iter.next().unwrap();
-                        let height = block.height() as usize;
-                        expected_block_positions
-                            .push((expected_text.matches('\n').count() as u32 + 1, block));
-                        let text = "\n".repeat(height);
-                        expected_text.push_str(&text);
-                        for _ in 0..height {
-                            expected_buffer_rows.push(None);
-                        }
-                    } else {
-                        break;
-                    }
-                }
-            }
-
-            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
-            let expected_row_count = expected_lines.len();
-            for start_row in 0..expected_row_count {
-                let expected_text = expected_lines[start_row..].join("\n");
-                let actual_text = blocks_snapshot
-                    .chunks(
-                        start_row as u32..blocks_snapshot.max_point().row + 1,
-                        false,
-                        Highlights::default(),
-                    )
-                    .map(|chunk| chunk.text)
-                    .collect::<String>();
-                assert_eq!(
-                    actual_text, expected_text,
-                    "incorrect text starting from row {}",
-                    start_row
-                );
-                assert_eq!(
-                    blocks_snapshot
-                        .buffer_rows(start_row as u32)
-                        .collect::<Vec<_>>(),
-                    &expected_buffer_rows[start_row..]
-                );
-            }
-
-            assert_eq!(
-                blocks_snapshot
-                    .blocks_in_range(0..(expected_row_count as u32))
-                    .map(|(row, block)| (row, block.clone().into()))
-                    .collect::<Vec<_>>(),
-                expected_block_positions
-            );
-
-            let mut expected_longest_rows = Vec::new();
-            let mut longest_line_len = -1_isize;
-            for (row, line) in expected_lines.iter().enumerate() {
-                let row = row as u32;
-
-                assert_eq!(
-                    blocks_snapshot.line_len(row),
-                    line.len() as u32,
-                    "invalid line len for row {}",
-                    row
-                );
-
-                let line_char_count = line.chars().count() as isize;
-                match line_char_count.cmp(&longest_line_len) {
-                    Ordering::Less => {}
-                    Ordering::Equal => expected_longest_rows.push(row),
-                    Ordering::Greater => {
-                        longest_line_len = line_char_count;
-                        expected_longest_rows.clear();
-                        expected_longest_rows.push(row);
-                    }
-                }
-            }
-
-            let longest_row = blocks_snapshot.longest_row();
-            assert!(
-                expected_longest_rows.contains(&longest_row),
-                "incorrect longest row {}. expected {:?} with length {}",
-                longest_row,
-                expected_longest_rows,
-                longest_line_len,
-            );
-
-            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
-                let wrap_point = WrapPoint::new(row, 0);
-                let block_point = blocks_snapshot.to_block_point(wrap_point);
-                assert_eq!(blocks_snapshot.to_wrap_point(block_point), wrap_point);
-            }
-
-            let mut block_point = BlockPoint::new(0, 0);
-            for c in expected_text.chars() {
-                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
-                let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
-                assert_eq!(
-                    blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(left_point)),
-                    left_point
-                );
-                assert_eq!(
-                    left_buffer_point,
-                    buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
-                    "{:?} is not valid in buffer coordinates",
-                    left_point
-                );
-
-                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
-                let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
-                assert_eq!(
-                    blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(right_point)),
-                    right_point
-                );
-                assert_eq!(
-                    right_buffer_point,
-                    buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
-                    "{:?} is not valid in buffer coordinates",
-                    right_point
-                );
-
-                if c == '\n' {
-                    block_point.0 += Point::new(1, 0);
-                } else {
-                    block_point.column += c.len_utf8() as u32;
-                }
-            }
-        }
-
-        #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
-        enum ExpectedBlock {
-            ExcerptHeader {
-                height: u8,
-                starts_new_buffer: bool,
-            },
-            Custom {
-                disposition: BlockDisposition,
-                id: BlockId,
-                height: u8,
-            },
-        }
-
-        impl ExpectedBlock {
-            fn height(&self) -> u8 {
-                match self {
-                    ExpectedBlock::ExcerptHeader { height, .. } => *height,
-                    ExpectedBlock::Custom { height, .. } => *height,
-                }
-            }
-
-            fn disposition(&self) -> BlockDisposition {
-                match self {
-                    ExpectedBlock::ExcerptHeader { .. } => BlockDisposition::Above,
-                    ExpectedBlock::Custom { disposition, .. } => *disposition,
-                }
-            }
-        }
-
-        impl From<TransformBlock> for ExpectedBlock {
-            fn from(block: TransformBlock) -> Self {
-                match block {
-                    TransformBlock::Custom(block) => ExpectedBlock::Custom {
-                        id: block.id,
-                        disposition: block.disposition,
-                        height: block.height,
-                    },
-                    TransformBlock::ExcerptHeader {
-                        height,
-                        starts_new_buffer,
-                        ..
-                    } => ExpectedBlock::ExcerptHeader {
-                        height,
-                        starts_new_buffer,
-                    },
-                }
-            }
-        }
-    }
-
-    fn init_test(cx: &mut gpui::AppContext) {
-        let settings = SettingsStore::test(cx);
-        cx.set_global(settings);
-        theme::init(theme::LoadThemes::JustBase, cx);
-    }
-
-    impl TransformBlock {
-        fn as_custom(&self) -> Option<&Block> {
-            match self {
-                TransformBlock::Custom(block) => Some(block),
-                TransformBlock::ExcerptHeader { .. } => None,
-            }
-        }
-    }
-
-    impl BlockSnapshot {
-        fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
-            self.wrap_snapshot.to_point(self.to_wrap_point(point), bias)
-        }
-    }
-}

crates/editor2/src/display_map/fold_map.rs ๐Ÿ”—

@@ -1,1746 +0,0 @@
-use super::{
-    inlay_map::{InlayBufferRows, InlayChunks, InlayEdit, InlayOffset, InlayPoint, InlaySnapshot},
-    Highlights,
-};
-use crate::{Anchor, AnchorRangeExt, MultiBufferSnapshot, ToOffset};
-use gpui::{ElementId, HighlightStyle, Hsla};
-use language::{Chunk, Edit, Point, TextSummary};
-use std::{
-    any::TypeId,
-    cmp::{self, Ordering},
-    iter,
-    ops::{Add, AddAssign, Deref, DerefMut, Range, Sub},
-};
-use sum_tree::{Bias, Cursor, FilterCursor, SumTree};
-use util::post_inc;
-
-#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
-pub struct FoldPoint(pub Point);
-
-impl FoldPoint {
-    pub fn new(row: u32, column: u32) -> Self {
-        Self(Point::new(row, column))
-    }
-
-    pub fn row(self) -> u32 {
-        self.0.row
-    }
-
-    pub fn column(self) -> u32 {
-        self.0.column
-    }
-
-    pub fn row_mut(&mut self) -> &mut u32 {
-        &mut self.0.row
-    }
-
-    #[cfg(test)]
-    pub fn column_mut(&mut self) -> &mut u32 {
-        &mut self.0.column
-    }
-
-    pub fn to_inlay_point(self, snapshot: &FoldSnapshot) -> InlayPoint {
-        let mut cursor = snapshot.transforms.cursor::<(FoldPoint, InlayPoint)>();
-        cursor.seek(&self, Bias::Right, &());
-        let overshoot = self.0 - cursor.start().0 .0;
-        InlayPoint(cursor.start().1 .0 + overshoot)
-    }
-
-    pub fn to_offset(self, snapshot: &FoldSnapshot) -> FoldOffset {
-        let mut cursor = snapshot
-            .transforms
-            .cursor::<(FoldPoint, TransformSummary)>();
-        cursor.seek(&self, Bias::Right, &());
-        let overshoot = self.0 - cursor.start().1.output.lines;
-        let mut offset = cursor.start().1.output.len;
-        if !overshoot.is_zero() {
-            let transform = cursor.item().expect("display point out of range");
-            assert!(transform.output_text.is_none());
-            let end_inlay_offset = snapshot
-                .inlay_snapshot
-                .to_offset(InlayPoint(cursor.start().1.input.lines + overshoot));
-            offset += end_inlay_offset.0 - cursor.start().1.input.len;
-        }
-        FoldOffset(offset)
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, TransformSummary> for FoldPoint {
-    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
-        self.0 += &summary.output.lines;
-    }
-}
-
-pub struct FoldMapWriter<'a>(&'a mut FoldMap);
-
-impl<'a> FoldMapWriter<'a> {
-    pub fn fold<T: ToOffset>(
-        &mut self,
-        ranges: impl IntoIterator<Item = Range<T>>,
-    ) -> (FoldSnapshot, Vec<FoldEdit>) {
-        let mut edits = Vec::new();
-        let mut folds = Vec::new();
-        let snapshot = self.0.snapshot.inlay_snapshot.clone();
-        for range in ranges.into_iter() {
-            let buffer = &snapshot.buffer;
-            let range = range.start.to_offset(&buffer)..range.end.to_offset(&buffer);
-
-            // Ignore any empty ranges.
-            if range.start == range.end {
-                continue;
-            }
-
-            // For now, ignore any ranges that span an excerpt boundary.
-            let fold_range =
-                FoldRange(buffer.anchor_after(range.start)..buffer.anchor_before(range.end));
-            if fold_range.0.start.excerpt_id != fold_range.0.end.excerpt_id {
-                continue;
-            }
-
-            folds.push(Fold {
-                id: FoldId(post_inc(&mut self.0.next_fold_id.0)),
-                range: fold_range,
-            });
-
-            let inlay_range =
-                snapshot.to_inlay_offset(range.start)..snapshot.to_inlay_offset(range.end);
-            edits.push(InlayEdit {
-                old: inlay_range.clone(),
-                new: inlay_range,
-            });
-        }
-
-        let buffer = &snapshot.buffer;
-        folds.sort_unstable_by(|a, b| sum_tree::SeekTarget::cmp(&a.range, &b.range, buffer));
-
-        self.0.snapshot.folds = {
-            let mut new_tree = SumTree::new();
-            let mut cursor = self.0.snapshot.folds.cursor::<FoldRange>();
-            for fold in folds {
-                new_tree.append(cursor.slice(&fold.range, Bias::Right, buffer), buffer);
-                new_tree.push(fold, buffer);
-            }
-            new_tree.append(cursor.suffix(buffer), buffer);
-            new_tree
-        };
-
-        consolidate_inlay_edits(&mut edits);
-        let edits = self.0.sync(snapshot.clone(), edits);
-        (self.0.snapshot.clone(), edits)
-    }
-
-    pub fn unfold<T: ToOffset>(
-        &mut self,
-        ranges: impl IntoIterator<Item = Range<T>>,
-        inclusive: bool,
-    ) -> (FoldSnapshot, Vec<FoldEdit>) {
-        let mut edits = Vec::new();
-        let mut fold_ixs_to_delete = Vec::new();
-        let snapshot = self.0.snapshot.inlay_snapshot.clone();
-        let buffer = &snapshot.buffer;
-        for range in ranges.into_iter() {
-            // Remove intersecting folds and add their ranges to edits that are passed to sync.
-            let mut folds_cursor =
-                intersecting_folds(&snapshot, &self.0.snapshot.folds, range, inclusive);
-            while let Some(fold) = folds_cursor.item() {
-                let offset_range =
-                    fold.range.start.to_offset(buffer)..fold.range.end.to_offset(buffer);
-                if offset_range.end > offset_range.start {
-                    let inlay_range = snapshot.to_inlay_offset(offset_range.start)
-                        ..snapshot.to_inlay_offset(offset_range.end);
-                    edits.push(InlayEdit {
-                        old: inlay_range.clone(),
-                        new: inlay_range,
-                    });
-                }
-                fold_ixs_to_delete.push(*folds_cursor.start());
-                folds_cursor.next(buffer);
-            }
-        }
-
-        fold_ixs_to_delete.sort_unstable();
-        fold_ixs_to_delete.dedup();
-
-        self.0.snapshot.folds = {
-            let mut cursor = self.0.snapshot.folds.cursor::<usize>();
-            let mut folds = SumTree::new();
-            for fold_ix in fold_ixs_to_delete {
-                folds.append(cursor.slice(&fold_ix, Bias::Right, buffer), buffer);
-                cursor.next(buffer);
-            }
-            folds.append(cursor.suffix(buffer), buffer);
-            folds
-        };
-
-        consolidate_inlay_edits(&mut edits);
-        let edits = self.0.sync(snapshot.clone(), edits);
-        (self.0.snapshot.clone(), edits)
-    }
-}
-
-pub struct FoldMap {
-    snapshot: FoldSnapshot,
-    ellipses_color: Option<Hsla>,
-    next_fold_id: FoldId,
-}
-
-impl FoldMap {
-    pub fn new(inlay_snapshot: InlaySnapshot) -> (Self, FoldSnapshot) {
-        let this = Self {
-            snapshot: FoldSnapshot {
-                folds: Default::default(),
-                transforms: SumTree::from_item(
-                    Transform {
-                        summary: TransformSummary {
-                            input: inlay_snapshot.text_summary(),
-                            output: inlay_snapshot.text_summary(),
-                        },
-                        output_text: None,
-                    },
-                    &(),
-                ),
-                inlay_snapshot: inlay_snapshot.clone(),
-                version: 0,
-                ellipses_color: None,
-            },
-            ellipses_color: None,
-            next_fold_id: FoldId::default(),
-        };
-        let snapshot = this.snapshot.clone();
-        (this, snapshot)
-    }
-
-    pub fn read(
-        &mut self,
-        inlay_snapshot: InlaySnapshot,
-        edits: Vec<InlayEdit>,
-    ) -> (FoldSnapshot, Vec<FoldEdit>) {
-        let edits = self.sync(inlay_snapshot, edits);
-        self.check_invariants();
-        (self.snapshot.clone(), edits)
-    }
-
-    pub fn write(
-        &mut self,
-        inlay_snapshot: InlaySnapshot,
-        edits: Vec<InlayEdit>,
-    ) -> (FoldMapWriter, FoldSnapshot, Vec<FoldEdit>) {
-        let (snapshot, edits) = self.read(inlay_snapshot, edits);
-        (FoldMapWriter(self), snapshot, edits)
-    }
-
-    pub fn set_ellipses_color(&mut self, color: Hsla) -> bool {
-        if self.ellipses_color != Some(color) {
-            self.ellipses_color = Some(color);
-            true
-        } else {
-            false
-        }
-    }
-
-    fn check_invariants(&self) {
-        if cfg!(test) {
-            assert_eq!(
-                self.snapshot.transforms.summary().input.len,
-                self.snapshot.inlay_snapshot.len().0,
-                "transform tree does not match inlay snapshot's length"
-            );
-
-            let mut folds = self.snapshot.folds.iter().peekable();
-            while let Some(fold) = folds.next() {
-                if let Some(next_fold) = folds.peek() {
-                    let comparison = fold
-                        .range
-                        .cmp(&next_fold.range, &self.snapshot.inlay_snapshot.buffer);
-                    assert!(comparison.is_le());
-                }
-            }
-        }
-    }
-
-    fn sync(
-        &mut self,
-        inlay_snapshot: InlaySnapshot,
-        inlay_edits: Vec<InlayEdit>,
-    ) -> Vec<FoldEdit> {
-        if inlay_edits.is_empty() {
-            if self.snapshot.inlay_snapshot.version != inlay_snapshot.version {
-                self.snapshot.version += 1;
-            }
-            self.snapshot.inlay_snapshot = inlay_snapshot;
-            Vec::new()
-        } else {
-            let mut inlay_edits_iter = inlay_edits.iter().cloned().peekable();
-
-            let mut new_transforms = SumTree::new();
-            let mut cursor = self.snapshot.transforms.cursor::<InlayOffset>();
-            cursor.seek(&InlayOffset(0), Bias::Right, &());
-
-            while let Some(mut edit) = inlay_edits_iter.next() {
-                new_transforms.append(cursor.slice(&edit.old.start, Bias::Left, &()), &());
-                edit.new.start -= edit.old.start - *cursor.start();
-                edit.old.start = *cursor.start();
-
-                cursor.seek(&edit.old.end, Bias::Right, &());
-                cursor.next(&());
-
-                let mut delta = edit.new_len().0 as isize - edit.old_len().0 as isize;
-                loop {
-                    edit.old.end = *cursor.start();
-
-                    if let Some(next_edit) = inlay_edits_iter.peek() {
-                        if next_edit.old.start > edit.old.end {
-                            break;
-                        }
-
-                        let next_edit = inlay_edits_iter.next().unwrap();
-                        delta += next_edit.new_len().0 as isize - next_edit.old_len().0 as isize;
-
-                        if next_edit.old.end >= edit.old.end {
-                            edit.old.end = next_edit.old.end;
-                            cursor.seek(&edit.old.end, Bias::Right, &());
-                            cursor.next(&());
-                        }
-                    } else {
-                        break;
-                    }
-                }
-
-                edit.new.end =
-                    InlayOffset(((edit.new.start + edit.old_len()).0 as isize + delta) as usize);
-
-                let anchor = inlay_snapshot
-                    .buffer
-                    .anchor_before(inlay_snapshot.to_buffer_offset(edit.new.start));
-                let mut folds_cursor = self.snapshot.folds.cursor::<FoldRange>();
-                folds_cursor.seek(
-                    &FoldRange(anchor..Anchor::max()),
-                    Bias::Left,
-                    &inlay_snapshot.buffer,
-                );
-
-                let mut folds = iter::from_fn({
-                    let inlay_snapshot = &inlay_snapshot;
-                    move || {
-                        let item = folds_cursor.item().map(|f| {
-                            let buffer_start = f.range.start.to_offset(&inlay_snapshot.buffer);
-                            let buffer_end = f.range.end.to_offset(&inlay_snapshot.buffer);
-                            inlay_snapshot.to_inlay_offset(buffer_start)
-                                ..inlay_snapshot.to_inlay_offset(buffer_end)
-                        });
-                        folds_cursor.next(&inlay_snapshot.buffer);
-                        item
-                    }
-                })
-                .peekable();
-
-                while folds.peek().map_or(false, |fold| fold.start < edit.new.end) {
-                    let mut fold = folds.next().unwrap();
-                    let sum = new_transforms.summary();
-
-                    assert!(fold.start.0 >= sum.input.len);
-
-                    while folds
-                        .peek()
-                        .map_or(false, |next_fold| next_fold.start <= fold.end)
-                    {
-                        let next_fold = folds.next().unwrap();
-                        if next_fold.end > fold.end {
-                            fold.end = next_fold.end;
-                        }
-                    }
-
-                    if fold.start.0 > sum.input.len {
-                        let text_summary = inlay_snapshot
-                            .text_summary_for_range(InlayOffset(sum.input.len)..fold.start);
-                        new_transforms.push(
-                            Transform {
-                                summary: TransformSummary {
-                                    output: text_summary.clone(),
-                                    input: text_summary,
-                                },
-                                output_text: None,
-                            },
-                            &(),
-                        );
-                    }
-
-                    if fold.end > fold.start {
-                        let output_text = "โ‹ฏ";
-                        new_transforms.push(
-                            Transform {
-                                summary: TransformSummary {
-                                    output: TextSummary::from(output_text),
-                                    input: inlay_snapshot
-                                        .text_summary_for_range(fold.start..fold.end),
-                                },
-                                output_text: Some(output_text),
-                            },
-                            &(),
-                        );
-                    }
-                }
-
-                let sum = new_transforms.summary();
-                if sum.input.len < edit.new.end.0 {
-                    let text_summary = inlay_snapshot
-                        .text_summary_for_range(InlayOffset(sum.input.len)..edit.new.end);
-                    new_transforms.push(
-                        Transform {
-                            summary: TransformSummary {
-                                output: text_summary.clone(),
-                                input: text_summary,
-                            },
-                            output_text: None,
-                        },
-                        &(),
-                    );
-                }
-            }
-
-            new_transforms.append(cursor.suffix(&()), &());
-            if new_transforms.is_empty() {
-                let text_summary = inlay_snapshot.text_summary();
-                new_transforms.push(
-                    Transform {
-                        summary: TransformSummary {
-                            output: text_summary.clone(),
-                            input: text_summary,
-                        },
-                        output_text: None,
-                    },
-                    &(),
-                );
-            }
-
-            drop(cursor);
-
-            let mut fold_edits = Vec::with_capacity(inlay_edits.len());
-            {
-                let mut old_transforms = self
-                    .snapshot
-                    .transforms
-                    .cursor::<(InlayOffset, FoldOffset)>();
-                let mut new_transforms = new_transforms.cursor::<(InlayOffset, FoldOffset)>();
-
-                for mut edit in inlay_edits {
-                    old_transforms.seek(&edit.old.start, Bias::Left, &());
-                    if old_transforms.item().map_or(false, |t| t.is_fold()) {
-                        edit.old.start = old_transforms.start().0;
-                    }
-                    let old_start =
-                        old_transforms.start().1 .0 + (edit.old.start - old_transforms.start().0).0;
-
-                    old_transforms.seek_forward(&edit.old.end, Bias::Right, &());
-                    if old_transforms.item().map_or(false, |t| t.is_fold()) {
-                        old_transforms.next(&());
-                        edit.old.end = old_transforms.start().0;
-                    }
-                    let old_end =
-                        old_transforms.start().1 .0 + (edit.old.end - old_transforms.start().0).0;
-
-                    new_transforms.seek(&edit.new.start, Bias::Left, &());
-                    if new_transforms.item().map_or(false, |t| t.is_fold()) {
-                        edit.new.start = new_transforms.start().0;
-                    }
-                    let new_start =
-                        new_transforms.start().1 .0 + (edit.new.start - new_transforms.start().0).0;
-
-                    new_transforms.seek_forward(&edit.new.end, Bias::Right, &());
-                    if new_transforms.item().map_or(false, |t| t.is_fold()) {
-                        new_transforms.next(&());
-                        edit.new.end = new_transforms.start().0;
-                    }
-                    let new_end =
-                        new_transforms.start().1 .0 + (edit.new.end - new_transforms.start().0).0;
-
-                    fold_edits.push(FoldEdit {
-                        old: FoldOffset(old_start)..FoldOffset(old_end),
-                        new: FoldOffset(new_start)..FoldOffset(new_end),
-                    });
-                }
-
-                consolidate_fold_edits(&mut fold_edits);
-            }
-
-            self.snapshot.transforms = new_transforms;
-            self.snapshot.inlay_snapshot = inlay_snapshot;
-            self.snapshot.version += 1;
-            fold_edits
-        }
-    }
-}
-
-#[derive(Clone)]
-pub struct FoldSnapshot {
-    transforms: SumTree<Transform>,
-    folds: SumTree<Fold>,
-    pub inlay_snapshot: InlaySnapshot,
-    pub version: usize,
-    pub ellipses_color: Option<Hsla>,
-}
-
-impl FoldSnapshot {
-    #[cfg(test)]
-    pub fn text(&self) -> String {
-        self.chunks(FoldOffset(0)..self.len(), false, Highlights::default())
-            .map(|c| c.text)
-            .collect()
-    }
-
-    #[cfg(test)]
-    pub fn fold_count(&self) -> usize {
-        self.folds.items(&self.inlay_snapshot.buffer).len()
-    }
-
-    pub fn text_summary_for_range(&self, range: Range<FoldPoint>) -> TextSummary {
-        let mut summary = TextSummary::default();
-
-        let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>();
-        cursor.seek(&range.start, Bias::Right, &());
-        if let Some(transform) = cursor.item() {
-            let start_in_transform = range.start.0 - cursor.start().0 .0;
-            let end_in_transform = cmp::min(range.end, cursor.end(&()).0).0 - cursor.start().0 .0;
-            if let Some(output_text) = transform.output_text {
-                summary = TextSummary::from(
-                    &output_text
-                        [start_in_transform.column as usize..end_in_transform.column as usize],
-                );
-            } else {
-                let inlay_start = self
-                    .inlay_snapshot
-                    .to_offset(InlayPoint(cursor.start().1 .0 + start_in_transform));
-                let inlay_end = self
-                    .inlay_snapshot
-                    .to_offset(InlayPoint(cursor.start().1 .0 + end_in_transform));
-                summary = self
-                    .inlay_snapshot
-                    .text_summary_for_range(inlay_start..inlay_end);
-            }
-        }
-
-        if range.end > cursor.end(&()).0 {
-            cursor.next(&());
-            summary += &cursor
-                .summary::<_, TransformSummary>(&range.end, Bias::Right, &())
-                .output;
-            if let Some(transform) = cursor.item() {
-                let end_in_transform = range.end.0 - cursor.start().0 .0;
-                if let Some(output_text) = transform.output_text {
-                    summary += TextSummary::from(&output_text[..end_in_transform.column as usize]);
-                } else {
-                    let inlay_start = self.inlay_snapshot.to_offset(cursor.start().1);
-                    let inlay_end = self
-                        .inlay_snapshot
-                        .to_offset(InlayPoint(cursor.start().1 .0 + end_in_transform));
-                    summary += self
-                        .inlay_snapshot
-                        .text_summary_for_range(inlay_start..inlay_end);
-                }
-            }
-        }
-
-        summary
-    }
-
-    pub fn to_fold_point(&self, point: InlayPoint, bias: Bias) -> FoldPoint {
-        let mut cursor = self.transforms.cursor::<(InlayPoint, FoldPoint)>();
-        cursor.seek(&point, Bias::Right, &());
-        if cursor.item().map_or(false, |t| t.is_fold()) {
-            if bias == Bias::Left || point == cursor.start().0 {
-                cursor.start().1
-            } else {
-                cursor.end(&()).1
-            }
-        } else {
-            let overshoot = point.0 - cursor.start().0 .0;
-            FoldPoint(cmp::min(
-                cursor.start().1 .0 + overshoot,
-                cursor.end(&()).1 .0,
-            ))
-        }
-    }
-
-    pub fn len(&self) -> FoldOffset {
-        FoldOffset(self.transforms.summary().output.len)
-    }
-
-    pub fn line_len(&self, row: u32) -> u32 {
-        let line_start = FoldPoint::new(row, 0).to_offset(self).0;
-        let line_end = if row >= self.max_point().row() {
-            self.len().0
-        } else {
-            FoldPoint::new(row + 1, 0).to_offset(self).0 - 1
-        };
-        (line_end - line_start) as u32
-    }
-
-    pub fn buffer_rows(&self, start_row: u32) -> FoldBufferRows {
-        if start_row > self.transforms.summary().output.lines.row {
-            panic!("invalid display row {}", start_row);
-        }
-
-        let fold_point = FoldPoint::new(start_row, 0);
-        let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>();
-        cursor.seek(&fold_point, Bias::Left, &());
-
-        let overshoot = fold_point.0 - cursor.start().0 .0;
-        let inlay_point = InlayPoint(cursor.start().1 .0 + overshoot);
-        let input_buffer_rows = self.inlay_snapshot.buffer_rows(inlay_point.row());
-
-        FoldBufferRows {
-            fold_point,
-            input_buffer_rows,
-            cursor,
-        }
-    }
-
-    pub fn max_point(&self) -> FoldPoint {
-        FoldPoint(self.transforms.summary().output.lines)
-    }
-
-    #[cfg(test)]
-    pub fn longest_row(&self) -> u32 {
-        self.transforms.summary().output.longest_row
-    }
-
-    pub fn folds_in_range<T>(&self, range: Range<T>) -> impl Iterator<Item = &Fold>
-    where
-        T: ToOffset,
-    {
-        let mut folds = intersecting_folds(&self.inlay_snapshot, &self.folds, range, false);
-        iter::from_fn(move || {
-            let item = folds.item();
-            folds.next(&self.inlay_snapshot.buffer);
-            item
-        })
-    }
-
-    pub fn intersects_fold<T>(&self, offset: T) -> bool
-    where
-        T: ToOffset,
-    {
-        let buffer_offset = offset.to_offset(&self.inlay_snapshot.buffer);
-        let inlay_offset = self.inlay_snapshot.to_inlay_offset(buffer_offset);
-        let mut cursor = self.transforms.cursor::<InlayOffset>();
-        cursor.seek(&inlay_offset, Bias::Right, &());
-        cursor.item().map_or(false, |t| t.output_text.is_some())
-    }
-
-    pub fn is_line_folded(&self, buffer_row: u32) -> bool {
-        let mut inlay_point = self
-            .inlay_snapshot
-            .to_inlay_point(Point::new(buffer_row, 0));
-        let mut cursor = self.transforms.cursor::<InlayPoint>();
-        cursor.seek(&inlay_point, Bias::Right, &());
-        loop {
-            match cursor.item() {
-                Some(transform) => {
-                    let buffer_point = self.inlay_snapshot.to_buffer_point(inlay_point);
-                    if buffer_point.row != buffer_row {
-                        return false;
-                    } else if transform.output_text.is_some() {
-                        return true;
-                    }
-                }
-                None => return false,
-            }
-
-            if cursor.end(&()).row() == inlay_point.row() {
-                cursor.next(&());
-            } else {
-                inlay_point.0 += Point::new(1, 0);
-                cursor.seek(&inlay_point, Bias::Right, &());
-            }
-        }
-    }
-
-    pub fn chunks<'a>(
-        &'a self,
-        range: Range<FoldOffset>,
-        language_aware: bool,
-        highlights: Highlights<'a>,
-    ) -> FoldChunks<'a> {
-        let mut transform_cursor = self.transforms.cursor::<(FoldOffset, InlayOffset)>();
-
-        let inlay_end = {
-            transform_cursor.seek(&range.end, Bias::Right, &());
-            let overshoot = range.end.0 - transform_cursor.start().0 .0;
-            transform_cursor.start().1 + InlayOffset(overshoot)
-        };
-
-        let inlay_start = {
-            transform_cursor.seek(&range.start, Bias::Right, &());
-            let overshoot = range.start.0 - transform_cursor.start().0 .0;
-            transform_cursor.start().1 + InlayOffset(overshoot)
-        };
-
-        FoldChunks {
-            transform_cursor,
-            inlay_chunks: self.inlay_snapshot.chunks(
-                inlay_start..inlay_end,
-                language_aware,
-                highlights,
-            ),
-            inlay_chunk: None,
-            inlay_offset: inlay_start,
-            output_offset: range.start.0,
-            max_output_offset: range.end.0,
-            ellipses_color: self.ellipses_color,
-        }
-    }
-
-    pub fn chars_at(&self, start: FoldPoint) -> impl '_ + Iterator<Item = char> {
-        self.chunks(
-            start.to_offset(self)..self.len(),
-            false,
-            Highlights::default(),
-        )
-        .flat_map(|chunk| chunk.text.chars())
-    }
-
-    #[cfg(test)]
-    pub fn clip_offset(&self, offset: FoldOffset, bias: Bias) -> FoldOffset {
-        if offset > self.len() {
-            self.len()
-        } else {
-            self.clip_point(offset.to_point(self), bias).to_offset(self)
-        }
-    }
-
-    pub fn clip_point(&self, point: FoldPoint, bias: Bias) -> FoldPoint {
-        let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>();
-        cursor.seek(&point, Bias::Right, &());
-        if let Some(transform) = cursor.item() {
-            let transform_start = cursor.start().0 .0;
-            if transform.output_text.is_some() {
-                if point.0 == transform_start || matches!(bias, Bias::Left) {
-                    FoldPoint(transform_start)
-                } else {
-                    FoldPoint(cursor.end(&()).0 .0)
-                }
-            } else {
-                let overshoot = InlayPoint(point.0 - transform_start);
-                let inlay_point = cursor.start().1 + overshoot;
-                let clipped_inlay_point = self.inlay_snapshot.clip_point(inlay_point, bias);
-                FoldPoint(cursor.start().0 .0 + (clipped_inlay_point - cursor.start().1).0)
-            }
-        } else {
-            FoldPoint(self.transforms.summary().output.lines)
-        }
-    }
-}
-
-fn intersecting_folds<'a, T>(
-    inlay_snapshot: &'a InlaySnapshot,
-    folds: &'a SumTree<Fold>,
-    range: Range<T>,
-    inclusive: bool,
-) -> FilterCursor<'a, impl 'a + FnMut(&FoldSummary) -> bool, Fold, usize>
-where
-    T: ToOffset,
-{
-    let buffer = &inlay_snapshot.buffer;
-    let start = buffer.anchor_before(range.start.to_offset(buffer));
-    let end = buffer.anchor_after(range.end.to_offset(buffer));
-    let mut cursor = folds.filter::<_, usize>(move |summary| {
-        let start_cmp = start.cmp(&summary.max_end, buffer);
-        let end_cmp = end.cmp(&summary.min_start, buffer);
-
-        if inclusive {
-            start_cmp <= Ordering::Equal && end_cmp >= Ordering::Equal
-        } else {
-            start_cmp == Ordering::Less && end_cmp == Ordering::Greater
-        }
-    });
-    cursor.next(buffer);
-    cursor
-}
-
-fn consolidate_inlay_edits(edits: &mut Vec<InlayEdit>) {
-    edits.sort_unstable_by(|a, b| {
-        a.old
-            .start
-            .cmp(&b.old.start)
-            .then_with(|| b.old.end.cmp(&a.old.end))
-    });
-
-    let mut i = 1;
-    while i < edits.len() {
-        let edit = edits[i].clone();
-        let prev_edit = &mut edits[i - 1];
-        if prev_edit.old.end >= edit.old.start {
-            prev_edit.old.end = prev_edit.old.end.max(edit.old.end);
-            prev_edit.new.start = prev_edit.new.start.min(edit.new.start);
-            prev_edit.new.end = prev_edit.new.end.max(edit.new.end);
-            edits.remove(i);
-            continue;
-        }
-        i += 1;
-    }
-}
-
-fn consolidate_fold_edits(edits: &mut Vec<FoldEdit>) {
-    edits.sort_unstable_by(|a, b| {
-        a.old
-            .start
-            .cmp(&b.old.start)
-            .then_with(|| b.old.end.cmp(&a.old.end))
-    });
-
-    let mut i = 1;
-    while i < edits.len() {
-        let edit = edits[i].clone();
-        let prev_edit = &mut edits[i - 1];
-        if prev_edit.old.end >= edit.old.start {
-            prev_edit.old.end = prev_edit.old.end.max(edit.old.end);
-            prev_edit.new.start = prev_edit.new.start.min(edit.new.start);
-            prev_edit.new.end = prev_edit.new.end.max(edit.new.end);
-            edits.remove(i);
-            continue;
-        }
-        i += 1;
-    }
-}
-
-#[derive(Clone, Debug, Default, Eq, PartialEq)]
-struct Transform {
-    summary: TransformSummary,
-    output_text: Option<&'static str>,
-}
-
-impl Transform {
-    fn is_fold(&self) -> bool {
-        self.output_text.is_some()
-    }
-}
-
-#[derive(Clone, Debug, Default, Eq, PartialEq)]
-struct TransformSummary {
-    output: TextSummary,
-    input: TextSummary,
-}
-
-impl sum_tree::Item for Transform {
-    type Summary = TransformSummary;
-
-    fn summary(&self) -> Self::Summary {
-        self.summary.clone()
-    }
-}
-
-impl sum_tree::Summary for TransformSummary {
-    type Context = ();
-
-    fn add_summary(&mut self, other: &Self, _: &()) {
-        self.input += &other.input;
-        self.output += &other.output;
-    }
-}
-
-#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
-pub struct FoldId(usize);
-
-impl Into<ElementId> for FoldId {
-    fn into(self) -> ElementId {
-        ElementId::Integer(self.0)
-    }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct Fold {
-    pub id: FoldId,
-    pub range: FoldRange,
-}
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct FoldRange(Range<Anchor>);
-
-impl Deref for FoldRange {
-    type Target = Range<Anchor>;
-
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-
-impl DerefMut for FoldRange {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.0
-    }
-}
-
-impl Default for FoldRange {
-    fn default() -> Self {
-        Self(Anchor::min()..Anchor::max())
-    }
-}
-
-impl sum_tree::Item for Fold {
-    type Summary = FoldSummary;
-
-    fn summary(&self) -> Self::Summary {
-        FoldSummary {
-            start: self.range.start.clone(),
-            end: self.range.end.clone(),
-            min_start: self.range.start.clone(),
-            max_end: self.range.end.clone(),
-            count: 1,
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct FoldSummary {
-    start: Anchor,
-    end: Anchor,
-    min_start: Anchor,
-    max_end: Anchor,
-    count: usize,
-}
-
-impl Default for FoldSummary {
-    fn default() -> Self {
-        Self {
-            start: Anchor::min(),
-            end: Anchor::max(),
-            min_start: Anchor::max(),
-            max_end: Anchor::min(),
-            count: 0,
-        }
-    }
-}
-
-impl sum_tree::Summary for FoldSummary {
-    type Context = MultiBufferSnapshot;
-
-    fn add_summary(&mut self, other: &Self, buffer: &Self::Context) {
-        if other.min_start.cmp(&self.min_start, buffer) == Ordering::Less {
-            self.min_start = other.min_start.clone();
-        }
-        if other.max_end.cmp(&self.max_end, buffer) == Ordering::Greater {
-            self.max_end = other.max_end.clone();
-        }
-
-        #[cfg(debug_assertions)]
-        {
-            let start_comparison = self.start.cmp(&other.start, buffer);
-            assert!(start_comparison <= Ordering::Equal);
-            if start_comparison == Ordering::Equal {
-                assert!(self.end.cmp(&other.end, buffer) >= Ordering::Equal);
-            }
-        }
-
-        self.start = other.start.clone();
-        self.end = other.end.clone();
-        self.count += other.count;
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, FoldSummary> for FoldRange {
-    fn add_summary(&mut self, summary: &'a FoldSummary, _: &MultiBufferSnapshot) {
-        self.0.start = summary.start.clone();
-        self.0.end = summary.end.clone();
-    }
-}
-
-impl<'a> sum_tree::SeekTarget<'a, FoldSummary, FoldRange> for FoldRange {
-    fn cmp(&self, other: &Self, buffer: &MultiBufferSnapshot) -> Ordering {
-        self.0.cmp(&other.0, buffer)
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize {
-    fn add_summary(&mut self, summary: &'a FoldSummary, _: &MultiBufferSnapshot) {
-        *self += summary.count;
-    }
-}
-
-#[derive(Clone)]
-pub struct FoldBufferRows<'a> {
-    cursor: Cursor<'a, Transform, (FoldPoint, InlayPoint)>,
-    input_buffer_rows: InlayBufferRows<'a>,
-    fold_point: FoldPoint,
-}
-
-impl<'a> Iterator for FoldBufferRows<'a> {
-    type Item = Option<u32>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let mut traversed_fold = false;
-        while self.fold_point > self.cursor.end(&()).0 {
-            self.cursor.next(&());
-            traversed_fold = true;
-            if self.cursor.item().is_none() {
-                break;
-            }
-        }
-
-        if self.cursor.item().is_some() {
-            if traversed_fold {
-                self.input_buffer_rows.seek(self.cursor.start().1.row());
-                self.input_buffer_rows.next();
-            }
-            *self.fold_point.row_mut() += 1;
-            self.input_buffer_rows.next()
-        } else {
-            None
-        }
-    }
-}
-
-pub struct FoldChunks<'a> {
-    transform_cursor: Cursor<'a, Transform, (FoldOffset, InlayOffset)>,
-    inlay_chunks: InlayChunks<'a>,
-    inlay_chunk: Option<(InlayOffset, Chunk<'a>)>,
-    inlay_offset: InlayOffset,
-    output_offset: usize,
-    max_output_offset: usize,
-    ellipses_color: Option<Hsla>,
-}
-
-impl<'a> Iterator for FoldChunks<'a> {
-    type Item = Chunk<'a>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.output_offset >= self.max_output_offset {
-            return None;
-        }
-
-        let transform = self.transform_cursor.item()?;
-
-        // If we're in a fold, then return the fold's display text and
-        // advance the transform and buffer cursors to the end of the fold.
-        if let Some(output_text) = transform.output_text {
-            self.inlay_chunk.take();
-            self.inlay_offset += InlayOffset(transform.summary.input.len);
-            self.inlay_chunks.seek(self.inlay_offset);
-
-            while self.inlay_offset >= self.transform_cursor.end(&()).1
-                && self.transform_cursor.item().is_some()
-            {
-                self.transform_cursor.next(&());
-            }
-
-            self.output_offset += output_text.len();
-            return Some(Chunk {
-                text: output_text,
-                highlight_style: self.ellipses_color.map(|color| HighlightStyle {
-                    color: Some(color),
-                    ..Default::default()
-                }),
-                ..Default::default()
-            });
-        }
-
-        // Retrieve a chunk from the current location in the buffer.
-        if self.inlay_chunk.is_none() {
-            let chunk_offset = self.inlay_chunks.offset();
-            self.inlay_chunk = self.inlay_chunks.next().map(|chunk| (chunk_offset, chunk));
-        }
-
-        // Otherwise, take a chunk from the buffer's text.
-        if let Some((buffer_chunk_start, mut chunk)) = self.inlay_chunk {
-            let buffer_chunk_end = buffer_chunk_start + InlayOffset(chunk.text.len());
-            let transform_end = self.transform_cursor.end(&()).1;
-            let chunk_end = buffer_chunk_end.min(transform_end);
-
-            chunk.text = &chunk.text
-                [(self.inlay_offset - buffer_chunk_start).0..(chunk_end - buffer_chunk_start).0];
-
-            if chunk_end == transform_end {
-                self.transform_cursor.next(&());
-            } else if chunk_end == buffer_chunk_end {
-                self.inlay_chunk.take();
-            }
-
-            self.inlay_offset = chunk_end;
-            self.output_offset += chunk.text.len();
-            return Some(chunk);
-        }
-
-        None
-    }
-}
-
-#[derive(Copy, Clone, Eq, PartialEq)]
-struct HighlightEndpoint {
-    offset: InlayOffset,
-    is_start: bool,
-    tag: Option<TypeId>,
-    style: HighlightStyle,
-}
-
-impl PartialOrd for HighlightEndpoint {
-    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl Ord for HighlightEndpoint {
-    fn cmp(&self, other: &Self) -> Ordering {
-        self.offset
-            .cmp(&other.offset)
-            .then_with(|| other.is_start.cmp(&self.is_start))
-    }
-}
-
-#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
-pub struct FoldOffset(pub usize);
-
-impl FoldOffset {
-    pub fn to_point(self, snapshot: &FoldSnapshot) -> FoldPoint {
-        let mut cursor = snapshot
-            .transforms
-            .cursor::<(FoldOffset, TransformSummary)>();
-        cursor.seek(&self, Bias::Right, &());
-        let overshoot = if cursor.item().map_or(true, |t| t.is_fold()) {
-            Point::new(0, (self.0 - cursor.start().0 .0) as u32)
-        } else {
-            let inlay_offset = cursor.start().1.input.len + self.0 - cursor.start().0 .0;
-            let inlay_point = snapshot.inlay_snapshot.to_point(InlayOffset(inlay_offset));
-            inlay_point.0 - cursor.start().1.input.lines
-        };
-        FoldPoint(cursor.start().1.output.lines + overshoot)
-    }
-
-    #[cfg(test)]
-    pub fn to_inlay_offset(self, snapshot: &FoldSnapshot) -> InlayOffset {
-        let mut cursor = snapshot.transforms.cursor::<(FoldOffset, InlayOffset)>();
-        cursor.seek(&self, Bias::Right, &());
-        let overshoot = self.0 - cursor.start().0 .0;
-        InlayOffset(cursor.start().1 .0 + overshoot)
-    }
-}
-
-impl Add for FoldOffset {
-    type Output = Self;
-
-    fn add(self, rhs: Self) -> Self::Output {
-        Self(self.0 + rhs.0)
-    }
-}
-
-impl AddAssign for FoldOffset {
-    fn add_assign(&mut self, rhs: Self) {
-        self.0 += rhs.0;
-    }
-}
-
-impl Sub for FoldOffset {
-    type Output = Self;
-
-    fn sub(self, rhs: Self) -> Self::Output {
-        Self(self.0 - rhs.0)
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, TransformSummary> for FoldOffset {
-    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
-        self.0 += &summary.output.len;
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayPoint {
-    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
-        self.0 += &summary.input.lines;
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayOffset {
-    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
-        self.0 += &summary.input.len;
-    }
-}
-
-pub type FoldEdit = Edit<FoldOffset>;
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{display_map::inlay_map::InlayMap, MultiBuffer, ToPoint};
-    use collections::HashSet;
-    use rand::prelude::*;
-    use settings::SettingsStore;
-    use std::{env, mem};
-    use text::Patch;
-    use util::test::sample_text;
-    use util::RandomCharIter;
-    use Bias::{Left, Right};
-
-    #[gpui::test]
-    fn test_basic_folds(cx: &mut gpui::AppContext) {
-        init_test(cx);
-        let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
-        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
-        let buffer_snapshot = buffer.read(cx).snapshot(cx);
-        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
-        let mut map = FoldMap::new(inlay_snapshot.clone()).0;
-
-        let (mut writer, _, _) = map.write(inlay_snapshot, vec![]);
-        let (snapshot2, edits) = writer.fold(vec![
-            Point::new(0, 2)..Point::new(2, 2),
-            Point::new(2, 4)..Point::new(4, 1),
-        ]);
-        assert_eq!(snapshot2.text(), "aaโ‹ฏccโ‹ฏeeeee");
-        assert_eq!(
-            edits,
-            &[
-                FoldEdit {
-                    old: FoldOffset(2)..FoldOffset(16),
-                    new: FoldOffset(2)..FoldOffset(5),
-                },
-                FoldEdit {
-                    old: FoldOffset(18)..FoldOffset(29),
-                    new: FoldOffset(7)..FoldOffset(10)
-                },
-            ]
-        );
-
-        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
-            buffer.edit(
-                vec![
-                    (Point::new(0, 0)..Point::new(0, 1), "123"),
-                    (Point::new(2, 3)..Point::new(2, 3), "123"),
-                ],
-                None,
-                cx,
-            );
-            buffer.snapshot(cx)
-        });
-
-        let (inlay_snapshot, inlay_edits) =
-            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
-        let (snapshot3, edits) = map.read(inlay_snapshot, inlay_edits);
-        assert_eq!(snapshot3.text(), "123aโ‹ฏc123cโ‹ฏeeeee");
-        assert_eq!(
-            edits,
-            &[
-                FoldEdit {
-                    old: FoldOffset(0)..FoldOffset(1),
-                    new: FoldOffset(0)..FoldOffset(3),
-                },
-                FoldEdit {
-                    old: FoldOffset(6)..FoldOffset(6),
-                    new: FoldOffset(8)..FoldOffset(11),
-                },
-            ]
-        );
-
-        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
-            buffer.edit([(Point::new(2, 6)..Point::new(4, 3), "456")], None, cx);
-            buffer.snapshot(cx)
-        });
-        let (inlay_snapshot, inlay_edits) =
-            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
-        let (snapshot4, _) = map.read(inlay_snapshot.clone(), inlay_edits);
-        assert_eq!(snapshot4.text(), "123aโ‹ฏc123456eee");
-
-        let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
-        writer.unfold(Some(Point::new(0, 4)..Point::new(0, 4)), false);
-        let (snapshot5, _) = map.read(inlay_snapshot.clone(), vec![]);
-        assert_eq!(snapshot5.text(), "123aโ‹ฏc123456eee");
-
-        let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
-        writer.unfold(Some(Point::new(0, 4)..Point::new(0, 4)), true);
-        let (snapshot6, _) = map.read(inlay_snapshot, vec![]);
-        assert_eq!(snapshot6.text(), "123aaaaa\nbbbbbb\nccc123456eee");
-    }
-
-    #[gpui::test]
-    fn test_adjacent_folds(cx: &mut gpui::AppContext) {
-        init_test(cx);
-        let buffer = MultiBuffer::build_simple("abcdefghijkl", cx);
-        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
-        let buffer_snapshot = buffer.read(cx).snapshot(cx);
-        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
-
-        {
-            let mut map = FoldMap::new(inlay_snapshot.clone()).0;
-
-            let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
-            writer.fold(vec![5..8]);
-            let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]);
-            assert_eq!(snapshot.text(), "abcdeโ‹ฏijkl");
-
-            // Create an fold adjacent to the start of the first fold.
-            let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
-            writer.fold(vec![0..1, 2..5]);
-            let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]);
-            assert_eq!(snapshot.text(), "โ‹ฏbโ‹ฏijkl");
-
-            // Create an fold adjacent to the end of the first fold.
-            let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
-            writer.fold(vec![11..11, 8..10]);
-            let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]);
-            assert_eq!(snapshot.text(), "โ‹ฏbโ‹ฏkl");
-        }
-
-        {
-            let mut map = FoldMap::new(inlay_snapshot.clone()).0;
-
-            // Create two adjacent folds.
-            let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
-            writer.fold(vec![0..2, 2..5]);
-            let (snapshot, _) = map.read(inlay_snapshot, vec![]);
-            assert_eq!(snapshot.text(), "โ‹ฏfghijkl");
-
-            // Edit within one of the folds.
-            let buffer_snapshot = buffer.update(cx, |buffer, cx| {
-                buffer.edit([(0..1, "12345")], None, cx);
-                buffer.snapshot(cx)
-            });
-            let (inlay_snapshot, inlay_edits) =
-                inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
-            let (snapshot, _) = map.read(inlay_snapshot, inlay_edits);
-            assert_eq!(snapshot.text(), "12345โ‹ฏfghijkl");
-        }
-    }
-
-    #[gpui::test]
-    fn test_overlapping_folds(cx: &mut gpui::AppContext) {
-        let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
-        let buffer_snapshot = buffer.read(cx).snapshot(cx);
-        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
-        let mut map = FoldMap::new(inlay_snapshot.clone()).0;
-        let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
-        writer.fold(vec![
-            Point::new(0, 2)..Point::new(2, 2),
-            Point::new(0, 4)..Point::new(1, 0),
-            Point::new(1, 2)..Point::new(3, 2),
-            Point::new(3, 1)..Point::new(4, 1),
-        ]);
-        let (snapshot, _) = map.read(inlay_snapshot, vec![]);
-        assert_eq!(snapshot.text(), "aaโ‹ฏeeeee");
-    }
-
-    #[gpui::test]
-    fn test_merging_folds_via_edit(cx: &mut gpui::AppContext) {
-        init_test(cx);
-        let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
-        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
-        let buffer_snapshot = buffer.read(cx).snapshot(cx);
-        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
-        let mut map = FoldMap::new(inlay_snapshot.clone()).0;
-
-        let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
-        writer.fold(vec![
-            Point::new(0, 2)..Point::new(2, 2),
-            Point::new(3, 1)..Point::new(4, 1),
-        ]);
-        let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]);
-        assert_eq!(snapshot.text(), "aaโ‹ฏcccc\ndโ‹ฏeeeee");
-
-        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
-            buffer.edit([(Point::new(2, 2)..Point::new(3, 1), "")], None, cx);
-            buffer.snapshot(cx)
-        });
-        let (inlay_snapshot, inlay_edits) =
-            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
-        let (snapshot, _) = map.read(inlay_snapshot, inlay_edits);
-        assert_eq!(snapshot.text(), "aaโ‹ฏeeeee");
-    }
-
-    #[gpui::test]
-    fn test_folds_in_range(cx: &mut gpui::AppContext) {
-        let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
-        let buffer_snapshot = buffer.read(cx).snapshot(cx);
-        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
-        let mut map = FoldMap::new(inlay_snapshot.clone()).0;
-
-        let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
-        writer.fold(vec![
-            Point::new(0, 2)..Point::new(2, 2),
-            Point::new(0, 4)..Point::new(1, 0),
-            Point::new(1, 2)..Point::new(3, 2),
-            Point::new(3, 1)..Point::new(4, 1),
-        ]);
-        let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]);
-        let fold_ranges = snapshot
-            .folds_in_range(Point::new(1, 0)..Point::new(1, 3))
-            .map(|fold| {
-                fold.range.start.to_point(&buffer_snapshot)
-                    ..fold.range.end.to_point(&buffer_snapshot)
-            })
-            .collect::<Vec<_>>();
-        assert_eq!(
-            fold_ranges,
-            vec![
-                Point::new(0, 2)..Point::new(2, 2),
-                Point::new(1, 2)..Point::new(3, 2)
-            ]
-        );
-    }
-
-    #[gpui::test(iterations = 100)]
-    fn test_random_folds(cx: &mut gpui::AppContext, mut rng: StdRng) {
-        init_test(cx);
-        let operations = env::var("OPERATIONS")
-            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
-            .unwrap_or(10);
-
-        let len = rng.gen_range(0..10);
-        let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
-        let buffer = if rng.gen() {
-            MultiBuffer::build_simple(&text, cx)
-        } else {
-            MultiBuffer::build_random(&mut rng, cx)
-        };
-        let mut buffer_snapshot = buffer.read(cx).snapshot(cx);
-        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
-        let mut map = FoldMap::new(inlay_snapshot.clone()).0;
-
-        let (mut initial_snapshot, _) = map.read(inlay_snapshot.clone(), vec![]);
-        let mut snapshot_edits = Vec::new();
-
-        let mut next_inlay_id = 0;
-        for _ in 0..operations {
-            log::info!("text: {:?}", buffer_snapshot.text());
-            let mut buffer_edits = Vec::new();
-            let mut inlay_edits = Vec::new();
-            match rng.gen_range(0..=100) {
-                0..=39 => {
-                    snapshot_edits.extend(map.randomly_mutate(&mut rng));
-                }
-                40..=59 => {
-                    let (_, edits) = inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng);
-                    inlay_edits = edits;
-                }
-                _ => buffer.update(cx, |buffer, cx| {
-                    let subscription = buffer.subscribe();
-                    let edit_count = rng.gen_range(1..=5);
-                    buffer.randomly_mutate(&mut rng, edit_count, cx);
-                    buffer_snapshot = buffer.snapshot(cx);
-                    let edits = subscription.consume().into_inner();
-                    log::info!("editing {:?}", edits);
-                    buffer_edits.extend(edits);
-                }),
-            };
-
-            let (inlay_snapshot, new_inlay_edits) =
-                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
-            log::info!("inlay text {:?}", inlay_snapshot.text());
-
-            let inlay_edits = Patch::new(inlay_edits)
-                .compose(new_inlay_edits)
-                .into_inner();
-            let (snapshot, edits) = map.read(inlay_snapshot.clone(), inlay_edits);
-            snapshot_edits.push((snapshot.clone(), edits));
-
-            let mut expected_text: String = inlay_snapshot.text().to_string();
-            for fold_range in map.merged_fold_ranges().into_iter().rev() {
-                let fold_inlay_start = inlay_snapshot.to_inlay_offset(fold_range.start);
-                let fold_inlay_end = inlay_snapshot.to_inlay_offset(fold_range.end);
-                expected_text.replace_range(fold_inlay_start.0..fold_inlay_end.0, "โ‹ฏ");
-            }
-
-            assert_eq!(snapshot.text(), expected_text);
-            log::info!(
-                "fold text {:?} ({} lines)",
-                expected_text,
-                expected_text.matches('\n').count() + 1
-            );
-
-            let mut prev_row = 0;
-            let mut expected_buffer_rows = Vec::new();
-            for fold_range in map.merged_fold_ranges().into_iter() {
-                let fold_start = inlay_snapshot
-                    .to_point(inlay_snapshot.to_inlay_offset(fold_range.start))
-                    .row();
-                let fold_end = inlay_snapshot
-                    .to_point(inlay_snapshot.to_inlay_offset(fold_range.end))
-                    .row();
-                expected_buffer_rows.extend(
-                    inlay_snapshot
-                        .buffer_rows(prev_row)
-                        .take((1 + fold_start - prev_row) as usize),
-                );
-                prev_row = 1 + fold_end;
-            }
-            expected_buffer_rows.extend(inlay_snapshot.buffer_rows(prev_row));
-
-            assert_eq!(
-                expected_buffer_rows.len(),
-                expected_text.matches('\n').count() + 1,
-                "wrong expected buffer rows {:?}. text: {:?}",
-                expected_buffer_rows,
-                expected_text
-            );
-
-            for (output_row, line) in expected_text.lines().enumerate() {
-                let line_len = snapshot.line_len(output_row as u32);
-                assert_eq!(line_len, line.len() as u32);
-            }
-
-            let longest_row = snapshot.longest_row();
-            let longest_char_column = expected_text
-                .split('\n')
-                .nth(longest_row as usize)
-                .unwrap()
-                .chars()
-                .count();
-            let mut fold_point = FoldPoint::new(0, 0);
-            let mut fold_offset = FoldOffset(0);
-            let mut char_column = 0;
-            for c in expected_text.chars() {
-                let inlay_point = fold_point.to_inlay_point(&snapshot);
-                let inlay_offset = fold_offset.to_inlay_offset(&snapshot);
-                assert_eq!(
-                    snapshot.to_fold_point(inlay_point, Right),
-                    fold_point,
-                    "{:?} -> fold point",
-                    inlay_point,
-                );
-                assert_eq!(
-                    inlay_snapshot.to_offset(inlay_point),
-                    inlay_offset,
-                    "inlay_snapshot.to_offset({:?})",
-                    inlay_point,
-                );
-                assert_eq!(
-                    fold_point.to_offset(&snapshot),
-                    fold_offset,
-                    "fold_point.to_offset({:?})",
-                    fold_point,
-                );
-
-                if c == '\n' {
-                    *fold_point.row_mut() += 1;
-                    *fold_point.column_mut() = 0;
-                    char_column = 0;
-                } else {
-                    *fold_point.column_mut() += c.len_utf8() as u32;
-                    char_column += 1;
-                }
-                fold_offset.0 += c.len_utf8();
-                if char_column > longest_char_column {
-                    panic!(
-                        "invalid longest row {:?} (chars {}), found row {:?} (chars: {})",
-                        longest_row,
-                        longest_char_column,
-                        fold_point.row(),
-                        char_column
-                    );
-                }
-            }
-
-            for _ in 0..5 {
-                let mut start = snapshot
-                    .clip_offset(FoldOffset(rng.gen_range(0..=snapshot.len().0)), Bias::Left);
-                let mut end = snapshot
-                    .clip_offset(FoldOffset(rng.gen_range(0..=snapshot.len().0)), Bias::Right);
-                if start > end {
-                    mem::swap(&mut start, &mut end);
-                }
-
-                let text = &expected_text[start.0..end.0];
-                assert_eq!(
-                    snapshot
-                        .chunks(start..end, false, Highlights::default())
-                        .map(|c| c.text)
-                        .collect::<String>(),
-                    text,
-                );
-            }
-
-            let mut fold_row = 0;
-            while fold_row < expected_buffer_rows.len() as u32 {
-                assert_eq!(
-                    snapshot.buffer_rows(fold_row).collect::<Vec<_>>(),
-                    expected_buffer_rows[(fold_row as usize)..],
-                    "wrong buffer rows starting at fold row {}",
-                    fold_row,
-                );
-                fold_row += 1;
-            }
-
-            let folded_buffer_rows = map
-                .merged_fold_ranges()
-                .iter()
-                .flat_map(|range| {
-                    let start_row = range.start.to_point(&buffer_snapshot).row;
-                    let end = range.end.to_point(&buffer_snapshot);
-                    if end.column == 0 {
-                        start_row..end.row
-                    } else {
-                        start_row..end.row + 1
-                    }
-                })
-                .collect::<HashSet<_>>();
-            for row in 0..=buffer_snapshot.max_point().row {
-                assert_eq!(
-                    snapshot.is_line_folded(row),
-                    folded_buffer_rows.contains(&row),
-                    "expected buffer row {}{} to be folded",
-                    row,
-                    if folded_buffer_rows.contains(&row) {
-                        ""
-                    } else {
-                        " not"
-                    }
-                );
-            }
-
-            for _ in 0..5 {
-                let end =
-                    buffer_snapshot.clip_offset(rng.gen_range(0..=buffer_snapshot.len()), Right);
-                let start = buffer_snapshot.clip_offset(rng.gen_range(0..=end), Left);
-                let expected_folds = map
-                    .snapshot
-                    .folds
-                    .items(&buffer_snapshot)
-                    .into_iter()
-                    .filter(|fold| {
-                        let start = buffer_snapshot.anchor_before(start);
-                        let end = buffer_snapshot.anchor_after(end);
-                        start.cmp(&fold.range.end, &buffer_snapshot) == Ordering::Less
-                            && end.cmp(&fold.range.start, &buffer_snapshot) == Ordering::Greater
-                    })
-                    .collect::<Vec<_>>();
-
-                assert_eq!(
-                    snapshot
-                        .folds_in_range(start..end)
-                        .cloned()
-                        .collect::<Vec<_>>(),
-                    expected_folds
-                );
-            }
-
-            let text = snapshot.text();
-            for _ in 0..5 {
-                let start_row = rng.gen_range(0..=snapshot.max_point().row());
-                let start_column = rng.gen_range(0..=snapshot.line_len(start_row));
-                let end_row = rng.gen_range(0..=snapshot.max_point().row());
-                let end_column = rng.gen_range(0..=snapshot.line_len(end_row));
-                let mut start =
-                    snapshot.clip_point(FoldPoint::new(start_row, start_column), Bias::Left);
-                let mut end = snapshot.clip_point(FoldPoint::new(end_row, end_column), Bias::Right);
-                if start > end {
-                    mem::swap(&mut start, &mut end);
-                }
-
-                let lines = start..end;
-                let bytes = start.to_offset(&snapshot)..end.to_offset(&snapshot);
-                assert_eq!(
-                    snapshot.text_summary_for_range(lines),
-                    TextSummary::from(&text[bytes.start.0..bytes.end.0])
-                )
-            }
-
-            let mut text = initial_snapshot.text();
-            for (snapshot, edits) in snapshot_edits.drain(..) {
-                let new_text = snapshot.text();
-                for edit in edits {
-                    let old_bytes = edit.new.start.0..edit.new.start.0 + edit.old_len().0;
-                    let new_bytes = edit.new.start.0..edit.new.end.0;
-                    text.replace_range(old_bytes, &new_text[new_bytes]);
-                }
-
-                assert_eq!(text, new_text);
-                initial_snapshot = snapshot;
-            }
-        }
-    }
-
-    #[gpui::test]
-    fn test_buffer_rows(cx: &mut gpui::AppContext) {
-        let text = sample_text(6, 6, 'a') + "\n";
-        let buffer = MultiBuffer::build_simple(&text, cx);
-
-        let buffer_snapshot = buffer.read(cx).snapshot(cx);
-        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
-        let mut map = FoldMap::new(inlay_snapshot.clone()).0;
-
-        let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
-        writer.fold(vec![
-            Point::new(0, 2)..Point::new(2, 2),
-            Point::new(3, 1)..Point::new(4, 1),
-        ]);
-
-        let (snapshot, _) = map.read(inlay_snapshot, vec![]);
-        assert_eq!(snapshot.text(), "aaโ‹ฏcccc\ndโ‹ฏeeeee\nffffff\n");
-        assert_eq!(
-            snapshot.buffer_rows(0).collect::<Vec<_>>(),
-            [Some(0), Some(3), Some(5), Some(6)]
-        );
-        assert_eq!(snapshot.buffer_rows(3).collect::<Vec<_>>(), [Some(6)]);
-    }
-
-    fn init_test(cx: &mut gpui::AppContext) {
-        let store = SettingsStore::test(cx);
-        cx.set_global(store);
-    }
-
-    impl FoldMap {
-        fn merged_fold_ranges(&self) -> Vec<Range<usize>> {
-            let inlay_snapshot = self.snapshot.inlay_snapshot.clone();
-            let buffer = &inlay_snapshot.buffer;
-            let mut folds = self.snapshot.folds.items(buffer);
-            // Ensure sorting doesn't change how folds get merged and displayed.
-            folds.sort_by(|a, b| a.range.cmp(&b.range, buffer));
-            let mut fold_ranges = folds
-                .iter()
-                .map(|fold| fold.range.start.to_offset(buffer)..fold.range.end.to_offset(buffer))
-                .peekable();
-
-            let mut merged_ranges = Vec::new();
-            while let Some(mut fold_range) = fold_ranges.next() {
-                while let Some(next_range) = fold_ranges.peek() {
-                    if fold_range.end >= next_range.start {
-                        if next_range.end > fold_range.end {
-                            fold_range.end = next_range.end;
-                        }
-                        fold_ranges.next();
-                    } else {
-                        break;
-                    }
-                }
-                if fold_range.end > fold_range.start {
-                    merged_ranges.push(fold_range);
-                }
-            }
-            merged_ranges
-        }
-
-        pub fn randomly_mutate(
-            &mut self,
-            rng: &mut impl Rng,
-        ) -> Vec<(FoldSnapshot, Vec<FoldEdit>)> {
-            let mut snapshot_edits = Vec::new();
-            match rng.gen_range(0..=100) {
-                0..=39 if !self.snapshot.folds.is_empty() => {
-                    let inlay_snapshot = self.snapshot.inlay_snapshot.clone();
-                    let buffer = &inlay_snapshot.buffer;
-                    let mut to_unfold = Vec::new();
-                    for _ in 0..rng.gen_range(1..=3) {
-                        let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
-                        let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
-                        to_unfold.push(start..end);
-                    }
-                    log::info!("unfolding {:?}", to_unfold);
-                    let (mut writer, snapshot, edits) = self.write(inlay_snapshot, vec![]);
-                    snapshot_edits.push((snapshot, edits));
-                    let (snapshot, edits) = writer.fold(to_unfold);
-                    snapshot_edits.push((snapshot, edits));
-                }
-                _ => {
-                    let inlay_snapshot = self.snapshot.inlay_snapshot.clone();
-                    let buffer = &inlay_snapshot.buffer;
-                    let mut to_fold = Vec::new();
-                    for _ in 0..rng.gen_range(1..=2) {
-                        let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
-                        let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
-                        to_fold.push(start..end);
-                    }
-                    log::info!("folding {:?}", to_fold);
-                    let (mut writer, snapshot, edits) = self.write(inlay_snapshot, vec![]);
-                    snapshot_edits.push((snapshot, edits));
-                    let (snapshot, edits) = writer.fold(to_fold);
-                    snapshot_edits.push((snapshot, edits));
-                }
-            }
-            snapshot_edits
-        }
-    }
-}

crates/editor2/src/display_map/inlay_map.rs ๐Ÿ”—

@@ -1,1896 +0,0 @@
-use crate::{Anchor, InlayId, MultiBufferSnapshot, ToOffset};
-use collections::{BTreeMap, BTreeSet};
-use gpui::HighlightStyle;
-use language::{Chunk, Edit, Point, TextSummary};
-use multi_buffer::{MultiBufferChunks, MultiBufferRows};
-use std::{
-    any::TypeId,
-    cmp,
-    iter::Peekable,
-    ops::{Add, AddAssign, Range, Sub, SubAssign},
-    sync::Arc,
-    vec,
-};
-use sum_tree::{Bias, Cursor, SumTree, TreeMap};
-use text::{Patch, Rope};
-
-use super::Highlights;
-
-pub struct InlayMap {
-    snapshot: InlaySnapshot,
-    inlays: Vec<Inlay>,
-}
-
-#[derive(Clone)]
-pub struct InlaySnapshot {
-    pub buffer: MultiBufferSnapshot,
-    transforms: SumTree<Transform>,
-    pub version: usize,
-}
-
-#[derive(Clone, Debug)]
-enum Transform {
-    Isomorphic(TextSummary),
-    Inlay(Inlay),
-}
-
-#[derive(Debug, Clone)]
-pub struct Inlay {
-    pub id: InlayId,
-    pub position: Anchor,
-    pub text: text::Rope,
-}
-
-impl Inlay {
-    pub fn hint(id: usize, position: Anchor, hint: &project::InlayHint) -> Self {
-        let mut text = hint.text();
-        if hint.padding_right && !text.ends_with(' ') {
-            text.push(' ');
-        }
-        if hint.padding_left && !text.starts_with(' ') {
-            text.insert(0, ' ');
-        }
-        Self {
-            id: InlayId::Hint(id),
-            position,
-            text: text.into(),
-        }
-    }
-
-    pub fn suggestion<T: Into<Rope>>(id: usize, position: Anchor, text: T) -> Self {
-        Self {
-            id: InlayId::Suggestion(id),
-            position,
-            text: text.into(),
-        }
-    }
-}
-
-impl sum_tree::Item for Transform {
-    type Summary = TransformSummary;
-
-    fn summary(&self) -> Self::Summary {
-        match self {
-            Transform::Isomorphic(summary) => TransformSummary {
-                input: summary.clone(),
-                output: summary.clone(),
-            },
-            Transform::Inlay(inlay) => TransformSummary {
-                input: TextSummary::default(),
-                output: inlay.text.summary(),
-            },
-        }
-    }
-}
-
-#[derive(Clone, Debug, Default)]
-struct TransformSummary {
-    input: TextSummary,
-    output: TextSummary,
-}
-
-impl sum_tree::Summary for TransformSummary {
-    type Context = ();
-
-    fn add_summary(&mut self, other: &Self, _: &()) {
-        self.input += &other.input;
-        self.output += &other.output;
-    }
-}
-
-pub type InlayEdit = Edit<InlayOffset>;
-
-#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
-pub struct InlayOffset(pub usize);
-
-impl Add for InlayOffset {
-    type Output = Self;
-
-    fn add(self, rhs: Self) -> Self::Output {
-        Self(self.0 + rhs.0)
-    }
-}
-
-impl Sub for InlayOffset {
-    type Output = Self;
-
-    fn sub(self, rhs: Self) -> Self::Output {
-        Self(self.0 - rhs.0)
-    }
-}
-
-impl AddAssign for InlayOffset {
-    fn add_assign(&mut self, rhs: Self) {
-        self.0 += rhs.0;
-    }
-}
-
-impl SubAssign for InlayOffset {
-    fn sub_assign(&mut self, rhs: Self) {
-        self.0 -= rhs.0;
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayOffset {
-    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
-        self.0 += &summary.output.len;
-    }
-}
-
-#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
-pub struct InlayPoint(pub Point);
-
-impl Add for InlayPoint {
-    type Output = Self;
-
-    fn add(self, rhs: Self) -> Self::Output {
-        Self(self.0 + rhs.0)
-    }
-}
-
-impl Sub for InlayPoint {
-    type Output = Self;
-
-    fn sub(self, rhs: Self) -> Self::Output {
-        Self(self.0 - rhs.0)
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayPoint {
-    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
-        self.0 += &summary.output.lines;
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, TransformSummary> for usize {
-    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
-        *self += &summary.input.len;
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point {
-    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
-        *self += &summary.input.lines;
-    }
-}
-
-#[derive(Clone)]
-pub struct InlayBufferRows<'a> {
-    transforms: Cursor<'a, Transform, (InlayPoint, Point)>,
-    buffer_rows: MultiBufferRows<'a>,
-    inlay_row: u32,
-    max_buffer_row: u32,
-}
-
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-struct HighlightEndpoint {
-    offset: InlayOffset,
-    is_start: bool,
-    tag: Option<TypeId>,
-    style: HighlightStyle,
-}
-
-impl PartialOrd for HighlightEndpoint {
-    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl Ord for HighlightEndpoint {
-    fn cmp(&self, other: &Self) -> cmp::Ordering {
-        self.offset
-            .cmp(&other.offset)
-            .then_with(|| other.is_start.cmp(&self.is_start))
-    }
-}
-
-pub struct InlayChunks<'a> {
-    transforms: Cursor<'a, Transform, (InlayOffset, usize)>,
-    buffer_chunks: MultiBufferChunks<'a>,
-    buffer_chunk: Option<Chunk<'a>>,
-    inlay_chunks: Option<text::Chunks<'a>>,
-    inlay_chunk: Option<&'a str>,
-    output_offset: InlayOffset,
-    max_output_offset: InlayOffset,
-    inlay_highlight_style: Option<HighlightStyle>,
-    suggestion_highlight_style: Option<HighlightStyle>,
-    highlight_endpoints: Peekable<vec::IntoIter<HighlightEndpoint>>,
-    active_highlights: BTreeMap<Option<TypeId>, HighlightStyle>,
-    highlights: Highlights<'a>,
-    snapshot: &'a InlaySnapshot,
-}
-
-impl<'a> InlayChunks<'a> {
-    pub fn seek(&mut self, offset: InlayOffset) {
-        self.transforms.seek(&offset, Bias::Right, &());
-
-        let buffer_offset = self.snapshot.to_buffer_offset(offset);
-        self.buffer_chunks.seek(buffer_offset);
-        self.inlay_chunks = None;
-        self.buffer_chunk = None;
-        self.output_offset = offset;
-    }
-
-    pub fn offset(&self) -> InlayOffset {
-        self.output_offset
-    }
-}
-
-impl<'a> Iterator for InlayChunks<'a> {
-    type Item = Chunk<'a>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.output_offset == self.max_output_offset {
-            return None;
-        }
-
-        let mut next_highlight_endpoint = InlayOffset(usize::MAX);
-        while let Some(endpoint) = self.highlight_endpoints.peek().copied() {
-            if endpoint.offset <= self.output_offset {
-                if endpoint.is_start {
-                    self.active_highlights.insert(endpoint.tag, endpoint.style);
-                } else {
-                    self.active_highlights.remove(&endpoint.tag);
-                }
-                self.highlight_endpoints.next();
-            } else {
-                next_highlight_endpoint = endpoint.offset;
-                break;
-            }
-        }
-
-        let chunk = match self.transforms.item()? {
-            Transform::Isomorphic(_) => {
-                let chunk = self
-                    .buffer_chunk
-                    .get_or_insert_with(|| self.buffer_chunks.next().unwrap());
-                if chunk.text.is_empty() {
-                    *chunk = self.buffer_chunks.next().unwrap();
-                }
-
-                let (prefix, suffix) = chunk.text.split_at(
-                    chunk
-                        .text
-                        .len()
-                        .min(self.transforms.end(&()).0 .0 - self.output_offset.0)
-                        .min(next_highlight_endpoint.0 - self.output_offset.0),
-                );
-
-                chunk.text = suffix;
-                self.output_offset.0 += prefix.len();
-                let mut prefix = Chunk {
-                    text: prefix,
-                    ..chunk.clone()
-                };
-                if !self.active_highlights.is_empty() {
-                    let mut highlight_style = HighlightStyle::default();
-                    for active_highlight in self.active_highlights.values() {
-                        highlight_style.highlight(*active_highlight);
-                    }
-                    prefix.highlight_style = Some(highlight_style);
-                }
-                prefix
-            }
-            Transform::Inlay(inlay) => {
-                let mut inlay_style_and_highlight = None;
-                if let Some(inlay_highlights) = self.highlights.inlay_highlights {
-                    for (_, inlay_id_to_data) in inlay_highlights.iter() {
-                        let style_and_highlight = inlay_id_to_data.get(&inlay.id);
-                        if style_and_highlight.is_some() {
-                            inlay_style_and_highlight = style_and_highlight;
-                            break;
-                        }
-                    }
-                }
-
-                let mut highlight_style = match inlay.id {
-                    InlayId::Suggestion(_) => self.suggestion_highlight_style,
-                    InlayId::Hint(_) => self.inlay_highlight_style,
-                };
-                let next_inlay_highlight_endpoint;
-                let offset_in_inlay = self.output_offset - self.transforms.start().0;
-                if let Some((style, highlight)) = inlay_style_and_highlight {
-                    let range = &highlight.range;
-                    if offset_in_inlay.0 < range.start {
-                        next_inlay_highlight_endpoint = range.start - offset_in_inlay.0;
-                    } else if offset_in_inlay.0 >= range.end {
-                        next_inlay_highlight_endpoint = usize::MAX;
-                    } else {
-                        next_inlay_highlight_endpoint = range.end - offset_in_inlay.0;
-                        highlight_style
-                            .get_or_insert_with(|| Default::default())
-                            .highlight(style.clone());
-                    }
-                } else {
-                    next_inlay_highlight_endpoint = usize::MAX;
-                }
-
-                let inlay_chunks = self.inlay_chunks.get_or_insert_with(|| {
-                    let start = offset_in_inlay;
-                    let end = cmp::min(self.max_output_offset, self.transforms.end(&()).0)
-                        - self.transforms.start().0;
-                    inlay.text.chunks_in_range(start.0..end.0)
-                });
-                let inlay_chunk = self
-                    .inlay_chunk
-                    .get_or_insert_with(|| inlay_chunks.next().unwrap());
-                let (chunk, remainder) =
-                    inlay_chunk.split_at(inlay_chunk.len().min(next_inlay_highlight_endpoint));
-                *inlay_chunk = remainder;
-                if inlay_chunk.is_empty() {
-                    self.inlay_chunk = None;
-                }
-
-                self.output_offset.0 += chunk.len();
-
-                if !self.active_highlights.is_empty() {
-                    for active_highlight in self.active_highlights.values() {
-                        highlight_style
-                            .get_or_insert(Default::default())
-                            .highlight(*active_highlight);
-                    }
-                }
-                Chunk {
-                    text: chunk,
-                    highlight_style,
-                    ..Default::default()
-                }
-            }
-        };
-
-        if self.output_offset == self.transforms.end(&()).0 {
-            self.inlay_chunks = None;
-            self.transforms.next(&());
-        }
-
-        Some(chunk)
-    }
-}
-
-impl<'a> InlayBufferRows<'a> {
-    pub fn seek(&mut self, row: u32) {
-        let inlay_point = InlayPoint::new(row, 0);
-        self.transforms.seek(&inlay_point, Bias::Left, &());
-
-        let mut buffer_point = self.transforms.start().1;
-        let buffer_row = if row == 0 {
-            0
-        } else {
-            match self.transforms.item() {
-                Some(Transform::Isomorphic(_)) => {
-                    buffer_point += inlay_point.0 - self.transforms.start().0 .0;
-                    buffer_point.row
-                }
-                _ => cmp::min(buffer_point.row + 1, self.max_buffer_row),
-            }
-        };
-        self.inlay_row = inlay_point.row();
-        self.buffer_rows.seek(buffer_row);
-    }
-}
-
-impl<'a> Iterator for InlayBufferRows<'a> {
-    type Item = Option<u32>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let buffer_row = if self.inlay_row == 0 {
-            self.buffer_rows.next().unwrap()
-        } else {
-            match self.transforms.item()? {
-                Transform::Inlay(_) => None,
-                Transform::Isomorphic(_) => self.buffer_rows.next().unwrap(),
-            }
-        };
-
-        self.inlay_row += 1;
-        self.transforms
-            .seek_forward(&InlayPoint::new(self.inlay_row, 0), Bias::Left, &());
-
-        Some(buffer_row)
-    }
-}
-
-impl InlayPoint {
-    pub fn new(row: u32, column: u32) -> Self {
-        Self(Point::new(row, column))
-    }
-
-    pub fn row(self) -> u32 {
-        self.0.row
-    }
-}
-
-impl InlayMap {
-    pub fn new(buffer: MultiBufferSnapshot) -> (Self, InlaySnapshot) {
-        let version = 0;
-        let snapshot = InlaySnapshot {
-            buffer: buffer.clone(),
-            transforms: SumTree::from_iter(Some(Transform::Isomorphic(buffer.text_summary())), &()),
-            version,
-        };
-
-        (
-            Self {
-                snapshot: snapshot.clone(),
-                inlays: Vec::new(),
-            },
-            snapshot,
-        )
-    }
-
-    pub fn sync(
-        &mut self,
-        buffer_snapshot: MultiBufferSnapshot,
-        mut buffer_edits: Vec<text::Edit<usize>>,
-    ) -> (InlaySnapshot, Vec<InlayEdit>) {
-        let snapshot = &mut self.snapshot;
-
-        if buffer_edits.is_empty() {
-            if snapshot.buffer.trailing_excerpt_update_count()
-                != buffer_snapshot.trailing_excerpt_update_count()
-            {
-                buffer_edits.push(Edit {
-                    old: snapshot.buffer.len()..snapshot.buffer.len(),
-                    new: buffer_snapshot.len()..buffer_snapshot.len(),
-                });
-            }
-        }
-
-        if buffer_edits.is_empty() {
-            if snapshot.buffer.edit_count() != buffer_snapshot.edit_count()
-                || snapshot.buffer.parse_count() != buffer_snapshot.parse_count()
-                || snapshot.buffer.diagnostics_update_count()
-                    != buffer_snapshot.diagnostics_update_count()
-                || snapshot.buffer.git_diff_update_count()
-                    != buffer_snapshot.git_diff_update_count()
-                || snapshot.buffer.trailing_excerpt_update_count()
-                    != buffer_snapshot.trailing_excerpt_update_count()
-            {
-                snapshot.version += 1;
-            }
-
-            snapshot.buffer = buffer_snapshot;
-            (snapshot.clone(), Vec::new())
-        } else {
-            let mut inlay_edits = Patch::default();
-            let mut new_transforms = SumTree::new();
-            let mut cursor = snapshot.transforms.cursor::<(usize, InlayOffset)>();
-            let mut buffer_edits_iter = buffer_edits.iter().peekable();
-            while let Some(buffer_edit) = buffer_edits_iter.next() {
-                new_transforms.append(cursor.slice(&buffer_edit.old.start, Bias::Left, &()), &());
-                if let Some(Transform::Isomorphic(transform)) = cursor.item() {
-                    if cursor.end(&()).0 == buffer_edit.old.start {
-                        push_isomorphic(&mut new_transforms, transform.clone());
-                        cursor.next(&());
-                    }
-                }
-
-                // Remove all the inlays and transforms contained by the edit.
-                let old_start =
-                    cursor.start().1 + InlayOffset(buffer_edit.old.start - cursor.start().0);
-                cursor.seek(&buffer_edit.old.end, Bias::Right, &());
-                let old_end =
-                    cursor.start().1 + InlayOffset(buffer_edit.old.end - cursor.start().0);
-
-                // Push the unchanged prefix.
-                let prefix_start = new_transforms.summary().input.len;
-                let prefix_end = buffer_edit.new.start;
-                push_isomorphic(
-                    &mut new_transforms,
-                    buffer_snapshot.text_summary_for_range(prefix_start..prefix_end),
-                );
-                let new_start = InlayOffset(new_transforms.summary().output.len);
-
-                let start_ix = match self.inlays.binary_search_by(|probe| {
-                    probe
-                        .position
-                        .to_offset(&buffer_snapshot)
-                        .cmp(&buffer_edit.new.start)
-                        .then(std::cmp::Ordering::Greater)
-                }) {
-                    Ok(ix) | Err(ix) => ix,
-                };
-
-                for inlay in &self.inlays[start_ix..] {
-                    let buffer_offset = inlay.position.to_offset(&buffer_snapshot);
-                    if buffer_offset > buffer_edit.new.end {
-                        break;
-                    }
-
-                    let prefix_start = new_transforms.summary().input.len;
-                    let prefix_end = buffer_offset;
-                    push_isomorphic(
-                        &mut new_transforms,
-                        buffer_snapshot.text_summary_for_range(prefix_start..prefix_end),
-                    );
-
-                    if inlay.position.is_valid(&buffer_snapshot) {
-                        new_transforms.push(Transform::Inlay(inlay.clone()), &());
-                    }
-                }
-
-                // Apply the rest of the edit.
-                let transform_start = new_transforms.summary().input.len;
-                push_isomorphic(
-                    &mut new_transforms,
-                    buffer_snapshot.text_summary_for_range(transform_start..buffer_edit.new.end),
-                );
-                let new_end = InlayOffset(new_transforms.summary().output.len);
-                inlay_edits.push(Edit {
-                    old: old_start..old_end,
-                    new: new_start..new_end,
-                });
-
-                // If the next edit doesn't intersect the current isomorphic transform, then
-                // we can push its remainder.
-                if buffer_edits_iter
-                    .peek()
-                    .map_or(true, |edit| edit.old.start >= cursor.end(&()).0)
-                {
-                    let transform_start = new_transforms.summary().input.len;
-                    let transform_end =
-                        buffer_edit.new.end + (cursor.end(&()).0 - buffer_edit.old.end);
-                    push_isomorphic(
-                        &mut new_transforms,
-                        buffer_snapshot.text_summary_for_range(transform_start..transform_end),
-                    );
-                    cursor.next(&());
-                }
-            }
-
-            new_transforms.append(cursor.suffix(&()), &());
-            if new_transforms.is_empty() {
-                new_transforms.push(Transform::Isomorphic(Default::default()), &());
-            }
-
-            drop(cursor);
-            snapshot.transforms = new_transforms;
-            snapshot.version += 1;
-            snapshot.buffer = buffer_snapshot;
-            snapshot.check_invariants();
-
-            (snapshot.clone(), inlay_edits.into_inner())
-        }
-    }
-
-    pub fn splice(
-        &mut self,
-        to_remove: Vec<InlayId>,
-        to_insert: Vec<Inlay>,
-    ) -> (InlaySnapshot, Vec<InlayEdit>) {
-        let snapshot = &mut self.snapshot;
-        let mut edits = BTreeSet::new();
-
-        self.inlays.retain(|inlay| {
-            let retain = !to_remove.contains(&inlay.id);
-            if !retain {
-                let offset = inlay.position.to_offset(&snapshot.buffer);
-                edits.insert(offset);
-            }
-            retain
-        });
-
-        for inlay_to_insert in to_insert {
-            // Avoid inserting empty inlays.
-            if inlay_to_insert.text.is_empty() {
-                continue;
-            }
-
-            let offset = inlay_to_insert.position.to_offset(&snapshot.buffer);
-            match self.inlays.binary_search_by(|probe| {
-                probe
-                    .position
-                    .cmp(&inlay_to_insert.position, &snapshot.buffer)
-            }) {
-                Ok(ix) | Err(ix) => {
-                    self.inlays.insert(ix, inlay_to_insert);
-                }
-            }
-
-            edits.insert(offset);
-        }
-
-        let buffer_edits = edits
-            .into_iter()
-            .map(|offset| Edit {
-                old: offset..offset,
-                new: offset..offset,
-            })
-            .collect();
-        let buffer_snapshot = snapshot.buffer.clone();
-        let (snapshot, edits) = self.sync(buffer_snapshot, buffer_edits);
-        (snapshot, edits)
-    }
-
-    pub fn current_inlays(&self) -> impl Iterator<Item = &Inlay> {
-        self.inlays.iter()
-    }
-
-    #[cfg(test)]
-    pub(crate) fn randomly_mutate(
-        &mut self,
-        next_inlay_id: &mut usize,
-        rng: &mut rand::rngs::StdRng,
-    ) -> (InlaySnapshot, Vec<InlayEdit>) {
-        use rand::prelude::*;
-        use util::post_inc;
-
-        let mut to_remove = Vec::new();
-        let mut to_insert = Vec::new();
-        let snapshot = &mut self.snapshot;
-        for i in 0..rng.gen_range(1..=5) {
-            if self.inlays.is_empty() || rng.gen() {
-                let position = snapshot.buffer.random_byte_range(0, rng).start;
-                let bias = if rng.gen() { Bias::Left } else { Bias::Right };
-                let len = if rng.gen_bool(0.01) {
-                    0
-                } else {
-                    rng.gen_range(1..=5)
-                };
-                let text = util::RandomCharIter::new(&mut *rng)
-                    .filter(|ch| *ch != '\r')
-                    .take(len)
-                    .collect::<String>();
-
-                let inlay_id = if i % 2 == 0 {
-                    InlayId::Hint(post_inc(next_inlay_id))
-                } else {
-                    InlayId::Suggestion(post_inc(next_inlay_id))
-                };
-                log::info!(
-                    "creating inlay {:?} at buffer offset {} with bias {:?} and text {:?}",
-                    inlay_id,
-                    position,
-                    bias,
-                    text
-                );
-
-                to_insert.push(Inlay {
-                    id: inlay_id,
-                    position: snapshot.buffer.anchor_at(position, bias),
-                    text: text.into(),
-                });
-            } else {
-                to_remove.push(
-                    self.inlays
-                        .iter()
-                        .choose(rng)
-                        .map(|inlay| inlay.id)
-                        .unwrap(),
-                );
-            }
-        }
-        log::info!("removing inlays: {:?}", to_remove);
-
-        let (snapshot, edits) = self.splice(to_remove, to_insert);
-        (snapshot, edits)
-    }
-}
-
-impl InlaySnapshot {
-    pub fn to_point(&self, offset: InlayOffset) -> InlayPoint {
-        let mut cursor = self
-            .transforms
-            .cursor::<(InlayOffset, (InlayPoint, usize))>();
-        cursor.seek(&offset, Bias::Right, &());
-        let overshoot = offset.0 - cursor.start().0 .0;
-        match cursor.item() {
-            Some(Transform::Isomorphic(_)) => {
-                let buffer_offset_start = cursor.start().1 .1;
-                let buffer_offset_end = buffer_offset_start + overshoot;
-                let buffer_start = self.buffer.offset_to_point(buffer_offset_start);
-                let buffer_end = self.buffer.offset_to_point(buffer_offset_end);
-                InlayPoint(cursor.start().1 .0 .0 + (buffer_end - buffer_start))
-            }
-            Some(Transform::Inlay(inlay)) => {
-                let overshoot = inlay.text.offset_to_point(overshoot);
-                InlayPoint(cursor.start().1 .0 .0 + overshoot)
-            }
-            None => self.max_point(),
-        }
-    }
-
-    pub fn len(&self) -> InlayOffset {
-        InlayOffset(self.transforms.summary().output.len)
-    }
-
-    pub fn max_point(&self) -> InlayPoint {
-        InlayPoint(self.transforms.summary().output.lines)
-    }
-
-    pub fn to_offset(&self, point: InlayPoint) -> InlayOffset {
-        let mut cursor = self
-            .transforms
-            .cursor::<(InlayPoint, (InlayOffset, Point))>();
-        cursor.seek(&point, Bias::Right, &());
-        let overshoot = point.0 - cursor.start().0 .0;
-        match cursor.item() {
-            Some(Transform::Isomorphic(_)) => {
-                let buffer_point_start = cursor.start().1 .1;
-                let buffer_point_end = buffer_point_start + overshoot;
-                let buffer_offset_start = self.buffer.point_to_offset(buffer_point_start);
-                let buffer_offset_end = self.buffer.point_to_offset(buffer_point_end);
-                InlayOffset(cursor.start().1 .0 .0 + (buffer_offset_end - buffer_offset_start))
-            }
-            Some(Transform::Inlay(inlay)) => {
-                let overshoot = inlay.text.point_to_offset(overshoot);
-                InlayOffset(cursor.start().1 .0 .0 + overshoot)
-            }
-            None => self.len(),
-        }
-    }
-
-    pub fn to_buffer_point(&self, point: InlayPoint) -> Point {
-        let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>();
-        cursor.seek(&point, Bias::Right, &());
-        match cursor.item() {
-            Some(Transform::Isomorphic(_)) => {
-                let overshoot = point.0 - cursor.start().0 .0;
-                cursor.start().1 + overshoot
-            }
-            Some(Transform::Inlay(_)) => cursor.start().1,
-            None => self.buffer.max_point(),
-        }
-    }
-
-    pub fn to_buffer_offset(&self, offset: InlayOffset) -> usize {
-        let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>();
-        cursor.seek(&offset, Bias::Right, &());
-        match cursor.item() {
-            Some(Transform::Isomorphic(_)) => {
-                let overshoot = offset - cursor.start().0;
-                cursor.start().1 + overshoot.0
-            }
-            Some(Transform::Inlay(_)) => cursor.start().1,
-            None => self.buffer.len(),
-        }
-    }
-
-    pub fn to_inlay_offset(&self, offset: usize) -> InlayOffset {
-        let mut cursor = self.transforms.cursor::<(usize, InlayOffset)>();
-        cursor.seek(&offset, Bias::Left, &());
-        loop {
-            match cursor.item() {
-                Some(Transform::Isomorphic(_)) => {
-                    if offset == cursor.end(&()).0 {
-                        while let Some(Transform::Inlay(inlay)) = cursor.next_item() {
-                            if inlay.position.bias() == Bias::Right {
-                                break;
-                            } else {
-                                cursor.next(&());
-                            }
-                        }
-                        return cursor.end(&()).1;
-                    } else {
-                        let overshoot = offset - cursor.start().0;
-                        return InlayOffset(cursor.start().1 .0 + overshoot);
-                    }
-                }
-                Some(Transform::Inlay(inlay)) => {
-                    if inlay.position.bias() == Bias::Left {
-                        cursor.next(&());
-                    } else {
-                        return cursor.start().1;
-                    }
-                }
-                None => {
-                    return self.len();
-                }
-            }
-        }
-    }
-
-    pub fn to_inlay_point(&self, point: Point) -> InlayPoint {
-        let mut cursor = self.transforms.cursor::<(Point, InlayPoint)>();
-        cursor.seek(&point, Bias::Left, &());
-        loop {
-            match cursor.item() {
-                Some(Transform::Isomorphic(_)) => {
-                    if point == cursor.end(&()).0 {
-                        while let Some(Transform::Inlay(inlay)) = cursor.next_item() {
-                            if inlay.position.bias() == Bias::Right {
-                                break;
-                            } else {
-                                cursor.next(&());
-                            }
-                        }
-                        return cursor.end(&()).1;
-                    } else {
-                        let overshoot = point - cursor.start().0;
-                        return InlayPoint(cursor.start().1 .0 + overshoot);
-                    }
-                }
-                Some(Transform::Inlay(inlay)) => {
-                    if inlay.position.bias() == Bias::Left {
-                        cursor.next(&());
-                    } else {
-                        return cursor.start().1;
-                    }
-                }
-                None => {
-                    return self.max_point();
-                }
-            }
-        }
-    }
-
-    pub fn clip_point(&self, mut point: InlayPoint, mut bias: Bias) -> InlayPoint {
-        let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>();
-        cursor.seek(&point, Bias::Left, &());
-        loop {
-            match cursor.item() {
-                Some(Transform::Isomorphic(transform)) => {
-                    if cursor.start().0 == point {
-                        if let Some(Transform::Inlay(inlay)) = cursor.prev_item() {
-                            if inlay.position.bias() == Bias::Left {
-                                return point;
-                            } else if bias == Bias::Left {
-                                cursor.prev(&());
-                            } else if transform.first_line_chars == 0 {
-                                point.0 += Point::new(1, 0);
-                            } else {
-                                point.0 += Point::new(0, 1);
-                            }
-                        } else {
-                            return point;
-                        }
-                    } else if cursor.end(&()).0 == point {
-                        if let Some(Transform::Inlay(inlay)) = cursor.next_item() {
-                            if inlay.position.bias() == Bias::Right {
-                                return point;
-                            } else if bias == Bias::Right {
-                                cursor.next(&());
-                            } else if point.0.column == 0 {
-                                point.0.row -= 1;
-                                point.0.column = self.line_len(point.0.row);
-                            } else {
-                                point.0.column -= 1;
-                            }
-                        } else {
-                            return point;
-                        }
-                    } else {
-                        let overshoot = point.0 - cursor.start().0 .0;
-                        let buffer_point = cursor.start().1 + overshoot;
-                        let clipped_buffer_point = self.buffer.clip_point(buffer_point, bias);
-                        let clipped_overshoot = clipped_buffer_point - cursor.start().1;
-                        let clipped_point = InlayPoint(cursor.start().0 .0 + clipped_overshoot);
-                        if clipped_point == point {
-                            return clipped_point;
-                        } else {
-                            point = clipped_point;
-                        }
-                    }
-                }
-                Some(Transform::Inlay(inlay)) => {
-                    if point == cursor.start().0 && inlay.position.bias() == Bias::Right {
-                        match cursor.prev_item() {
-                            Some(Transform::Inlay(inlay)) => {
-                                if inlay.position.bias() == Bias::Left {
-                                    return point;
-                                }
-                            }
-                            _ => return point,
-                        }
-                    } else if point == cursor.end(&()).0 && inlay.position.bias() == Bias::Left {
-                        match cursor.next_item() {
-                            Some(Transform::Inlay(inlay)) => {
-                                if inlay.position.bias() == Bias::Right {
-                                    return point;
-                                }
-                            }
-                            _ => return point,
-                        }
-                    }
-
-                    if bias == Bias::Left {
-                        point = cursor.start().0;
-                        cursor.prev(&());
-                    } else {
-                        cursor.next(&());
-                        point = cursor.start().0;
-                    }
-                }
-                None => {
-                    bias = bias.invert();
-                    if bias == Bias::Left {
-                        point = cursor.start().0;
-                        cursor.prev(&());
-                    } else {
-                        cursor.next(&());
-                        point = cursor.start().0;
-                    }
-                }
-            }
-        }
-    }
-
-    pub fn text_summary(&self) -> TextSummary {
-        self.transforms.summary().output.clone()
-    }
-
-    pub fn text_summary_for_range(&self, range: Range<InlayOffset>) -> TextSummary {
-        let mut summary = TextSummary::default();
-
-        let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>();
-        cursor.seek(&range.start, Bias::Right, &());
-
-        let overshoot = range.start.0 - cursor.start().0 .0;
-        match cursor.item() {
-            Some(Transform::Isomorphic(_)) => {
-                let buffer_start = cursor.start().1;
-                let suffix_start = buffer_start + overshoot;
-                let suffix_end =
-                    buffer_start + (cmp::min(cursor.end(&()).0, range.end).0 - cursor.start().0 .0);
-                summary = self.buffer.text_summary_for_range(suffix_start..suffix_end);
-                cursor.next(&());
-            }
-            Some(Transform::Inlay(inlay)) => {
-                let suffix_start = overshoot;
-                let suffix_end = cmp::min(cursor.end(&()).0, range.end).0 - cursor.start().0 .0;
-                summary = inlay.text.cursor(suffix_start).summary(suffix_end);
-                cursor.next(&());
-            }
-            None => {}
-        }
-
-        if range.end > cursor.start().0 {
-            summary += cursor
-                .summary::<_, TransformSummary>(&range.end, Bias::Right, &())
-                .output;
-
-            let overshoot = range.end.0 - cursor.start().0 .0;
-            match cursor.item() {
-                Some(Transform::Isomorphic(_)) => {
-                    let prefix_start = cursor.start().1;
-                    let prefix_end = prefix_start + overshoot;
-                    summary += self
-                        .buffer
-                        .text_summary_for_range::<TextSummary, _>(prefix_start..prefix_end);
-                }
-                Some(Transform::Inlay(inlay)) => {
-                    let prefix_end = overshoot;
-                    summary += inlay.text.cursor(0).summary::<TextSummary>(prefix_end);
-                }
-                None => {}
-            }
-        }
-
-        summary
-    }
-
-    pub fn buffer_rows<'a>(&'a self, row: u32) -> InlayBufferRows<'a> {
-        let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>();
-        let inlay_point = InlayPoint::new(row, 0);
-        cursor.seek(&inlay_point, Bias::Left, &());
-
-        let max_buffer_row = self.buffer.max_point().row;
-        let mut buffer_point = cursor.start().1;
-        let buffer_row = if row == 0 {
-            0
-        } else {
-            match cursor.item() {
-                Some(Transform::Isomorphic(_)) => {
-                    buffer_point += inlay_point.0 - cursor.start().0 .0;
-                    buffer_point.row
-                }
-                _ => cmp::min(buffer_point.row + 1, max_buffer_row),
-            }
-        };
-
-        InlayBufferRows {
-            transforms: cursor,
-            inlay_row: inlay_point.row(),
-            buffer_rows: self.buffer.buffer_rows(buffer_row),
-            max_buffer_row,
-        }
-    }
-
-    pub fn line_len(&self, row: u32) -> u32 {
-        let line_start = self.to_offset(InlayPoint::new(row, 0)).0;
-        let line_end = if row >= self.max_point().row() {
-            self.len().0
-        } else {
-            self.to_offset(InlayPoint::new(row + 1, 0)).0 - 1
-        };
-        (line_end - line_start) as u32
-    }
-
-    pub fn chunks<'a>(
-        &'a self,
-        range: Range<InlayOffset>,
-        language_aware: bool,
-        highlights: Highlights<'a>,
-    ) -> InlayChunks<'a> {
-        let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>();
-        cursor.seek(&range.start, Bias::Right, &());
-
-        let mut highlight_endpoints = Vec::new();
-        if let Some(text_highlights) = highlights.text_highlights {
-            if !text_highlights.is_empty() {
-                self.apply_text_highlights(
-                    &mut cursor,
-                    &range,
-                    text_highlights,
-                    &mut highlight_endpoints,
-                );
-                cursor.seek(&range.start, Bias::Right, &());
-            }
-        }
-        highlight_endpoints.sort();
-        let buffer_range = self.to_buffer_offset(range.start)..self.to_buffer_offset(range.end);
-        let buffer_chunks = self.buffer.chunks(buffer_range, language_aware);
-
-        InlayChunks {
-            transforms: cursor,
-            buffer_chunks,
-            inlay_chunks: None,
-            inlay_chunk: None,
-            buffer_chunk: None,
-            output_offset: range.start,
-            max_output_offset: range.end,
-            inlay_highlight_style: highlights.inlay_highlight_style,
-            suggestion_highlight_style: highlights.suggestion_highlight_style,
-            highlight_endpoints: highlight_endpoints.into_iter().peekable(),
-            active_highlights: Default::default(),
-            highlights,
-            snapshot: self,
-        }
-    }
-
-    fn apply_text_highlights(
-        &self,
-        cursor: &mut Cursor<'_, Transform, (InlayOffset, usize)>,
-        range: &Range<InlayOffset>,
-        text_highlights: &TreeMap<Option<TypeId>, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>,
-        highlight_endpoints: &mut Vec<HighlightEndpoint>,
-    ) {
-        while cursor.start().0 < range.end {
-            let transform_start = self
-                .buffer
-                .anchor_after(self.to_buffer_offset(cmp::max(range.start, cursor.start().0)));
-            let transform_end =
-                {
-                    let overshoot = InlayOffset(range.end.0 - cursor.start().0 .0);
-                    self.buffer.anchor_before(self.to_buffer_offset(cmp::min(
-                        cursor.end(&()).0,
-                        cursor.start().0 + overshoot,
-                    )))
-                };
-
-            for (tag, text_highlights) in text_highlights.iter() {
-                let style = text_highlights.0;
-                let ranges = &text_highlights.1;
-
-                let start_ix = match ranges.binary_search_by(|probe| {
-                    let cmp = probe.end.cmp(&transform_start, &self.buffer);
-                    if cmp.is_gt() {
-                        cmp::Ordering::Greater
-                    } else {
-                        cmp::Ordering::Less
-                    }
-                }) {
-                    Ok(i) | Err(i) => i,
-                };
-                for range in &ranges[start_ix..] {
-                    if range.start.cmp(&transform_end, &self.buffer).is_ge() {
-                        break;
-                    }
-
-                    highlight_endpoints.push(HighlightEndpoint {
-                        offset: self.to_inlay_offset(range.start.to_offset(&self.buffer)),
-                        is_start: true,
-                        tag: *tag,
-                        style,
-                    });
-                    highlight_endpoints.push(HighlightEndpoint {
-                        offset: self.to_inlay_offset(range.end.to_offset(&self.buffer)),
-                        is_start: false,
-                        tag: *tag,
-                        style,
-                    });
-                }
-            }
-
-            cursor.next(&());
-        }
-    }
-
-    #[cfg(test)]
-    pub fn text(&self) -> String {
-        self.chunks(Default::default()..self.len(), false, Highlights::default())
-            .map(|chunk| chunk.text)
-            .collect()
-    }
-
-    fn check_invariants(&self) {
-        #[cfg(any(debug_assertions, feature = "test-support"))]
-        {
-            assert_eq!(self.transforms.summary().input, self.buffer.text_summary());
-            let mut transforms = self.transforms.iter().peekable();
-            while let Some(transform) = transforms.next() {
-                let transform_is_isomorphic = matches!(transform, Transform::Isomorphic(_));
-                if let Some(next_transform) = transforms.peek() {
-                    let next_transform_is_isomorphic =
-                        matches!(next_transform, Transform::Isomorphic(_));
-                    assert!(
-                        !transform_is_isomorphic || !next_transform_is_isomorphic,
-                        "two adjacent isomorphic transforms"
-                    );
-                }
-            }
-        }
-    }
-}
-
-fn push_isomorphic(sum_tree: &mut SumTree<Transform>, summary: TextSummary) {
-    if summary.len == 0 {
-        return;
-    }
-
-    let mut summary = Some(summary);
-    sum_tree.update_last(
-        |transform| {
-            if let Transform::Isomorphic(transform) = transform {
-                *transform += summary.take().unwrap();
-            }
-        },
-        &(),
-    );
-
-    if let Some(summary) = summary {
-        sum_tree.push(Transform::Isomorphic(summary), &());
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{
-        display_map::{InlayHighlights, TextHighlights},
-        link_go_to_definition::InlayHighlight,
-        InlayId, MultiBuffer,
-    };
-    use gpui::AppContext;
-    use project::{InlayHint, InlayHintLabel, ResolveState};
-    use rand::prelude::*;
-    use settings::SettingsStore;
-    use std::{cmp::Reverse, env, sync::Arc};
-    use text::Patch;
-    use util::post_inc;
-
-    #[test]
-    fn test_inlay_properties_label_padding() {
-        assert_eq!(
-            Inlay::hint(
-                0,
-                Anchor::min(),
-                &InlayHint {
-                    label: InlayHintLabel::String("a".to_string()),
-                    position: text::Anchor::default(),
-                    padding_left: false,
-                    padding_right: false,
-                    tooltip: None,
-                    kind: None,
-                    resolve_state: ResolveState::Resolved,
-                },
-            )
-            .text
-            .to_string(),
-            "a",
-            "Should not pad label if not requested"
-        );
-
-        assert_eq!(
-            Inlay::hint(
-                0,
-                Anchor::min(),
-                &InlayHint {
-                    label: InlayHintLabel::String("a".to_string()),
-                    position: text::Anchor::default(),
-                    padding_left: true,
-                    padding_right: true,
-                    tooltip: None,
-                    kind: None,
-                    resolve_state: ResolveState::Resolved,
-                },
-            )
-            .text
-            .to_string(),
-            " a ",
-            "Should pad label for every side requested"
-        );
-
-        assert_eq!(
-            Inlay::hint(
-                0,
-                Anchor::min(),
-                &InlayHint {
-                    label: InlayHintLabel::String(" a ".to_string()),
-                    position: text::Anchor::default(),
-                    padding_left: false,
-                    padding_right: false,
-                    tooltip: None,
-                    kind: None,
-                    resolve_state: ResolveState::Resolved,
-                },
-            )
-            .text
-            .to_string(),
-            " a ",
-            "Should not change already padded label"
-        );
-
-        assert_eq!(
-            Inlay::hint(
-                0,
-                Anchor::min(),
-                &InlayHint {
-                    label: InlayHintLabel::String(" a ".to_string()),
-                    position: text::Anchor::default(),
-                    padding_left: true,
-                    padding_right: true,
-                    tooltip: None,
-                    kind: None,
-                    resolve_state: ResolveState::Resolved,
-                },
-            )
-            .text
-            .to_string(),
-            " a ",
-            "Should not change already padded label"
-        );
-    }
-
-    #[gpui::test]
-    fn test_basic_inlays(cx: &mut AppContext) {
-        let buffer = MultiBuffer::build_simple("abcdefghi", cx);
-        let buffer_edits = buffer.update(cx, |buffer, _| buffer.subscribe());
-        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));
-        assert_eq!(inlay_snapshot.text(), "abcdefghi");
-        let mut next_inlay_id = 0;
-
-        let (inlay_snapshot, _) = inlay_map.splice(
-            Vec::new(),
-            vec![Inlay {
-                id: InlayId::Hint(post_inc(&mut next_inlay_id)),
-                position: buffer.read(cx).snapshot(cx).anchor_after(3),
-                text: "|123|".into(),
-            }],
-        );
-        assert_eq!(inlay_snapshot.text(), "abc|123|defghi");
-        assert_eq!(
-            inlay_snapshot.to_inlay_point(Point::new(0, 0)),
-            InlayPoint::new(0, 0)
-        );
-        assert_eq!(
-            inlay_snapshot.to_inlay_point(Point::new(0, 1)),
-            InlayPoint::new(0, 1)
-        );
-        assert_eq!(
-            inlay_snapshot.to_inlay_point(Point::new(0, 2)),
-            InlayPoint::new(0, 2)
-        );
-        assert_eq!(
-            inlay_snapshot.to_inlay_point(Point::new(0, 3)),
-            InlayPoint::new(0, 3)
-        );
-        assert_eq!(
-            inlay_snapshot.to_inlay_point(Point::new(0, 4)),
-            InlayPoint::new(0, 9)
-        );
-        assert_eq!(
-            inlay_snapshot.to_inlay_point(Point::new(0, 5)),
-            InlayPoint::new(0, 10)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 0), Bias::Left),
-            InlayPoint::new(0, 0)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 0), Bias::Right),
-            InlayPoint::new(0, 0)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 3), Bias::Left),
-            InlayPoint::new(0, 3)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 3), Bias::Right),
-            InlayPoint::new(0, 3)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 4), Bias::Left),
-            InlayPoint::new(0, 3)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 4), Bias::Right),
-            InlayPoint::new(0, 9)
-        );
-
-        // Edits before or after the inlay should not affect it.
-        buffer.update(cx, |buffer, cx| {
-            buffer.edit([(2..3, "x"), (3..3, "y"), (4..4, "z")], None, cx)
-        });
-        let (inlay_snapshot, _) = inlay_map.sync(
-            buffer.read(cx).snapshot(cx),
-            buffer_edits.consume().into_inner(),
-        );
-        assert_eq!(inlay_snapshot.text(), "abxy|123|dzefghi");
-
-        // An edit surrounding the inlay should invalidate it.
-        buffer.update(cx, |buffer, cx| buffer.edit([(4..5, "D")], None, cx));
-        let (inlay_snapshot, _) = inlay_map.sync(
-            buffer.read(cx).snapshot(cx),
-            buffer_edits.consume().into_inner(),
-        );
-        assert_eq!(inlay_snapshot.text(), "abxyDzefghi");
-
-        let (inlay_snapshot, _) = inlay_map.splice(
-            Vec::new(),
-            vec![
-                Inlay {
-                    id: InlayId::Hint(post_inc(&mut next_inlay_id)),
-                    position: buffer.read(cx).snapshot(cx).anchor_before(3),
-                    text: "|123|".into(),
-                },
-                Inlay {
-                    id: InlayId::Suggestion(post_inc(&mut next_inlay_id)),
-                    position: buffer.read(cx).snapshot(cx).anchor_after(3),
-                    text: "|456|".into(),
-                },
-            ],
-        );
-        assert_eq!(inlay_snapshot.text(), "abx|123||456|yDzefghi");
-
-        // Edits ending where the inlay starts should not move it if it has a left bias.
-        buffer.update(cx, |buffer, cx| buffer.edit([(3..3, "JKL")], None, cx));
-        let (inlay_snapshot, _) = inlay_map.sync(
-            buffer.read(cx).snapshot(cx),
-            buffer_edits.consume().into_inner(),
-        );
-        assert_eq!(inlay_snapshot.text(), "abx|123|JKL|456|yDzefghi");
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 0), Bias::Left),
-            InlayPoint::new(0, 0)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 0), Bias::Right),
-            InlayPoint::new(0, 0)
-        );
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 1), Bias::Left),
-            InlayPoint::new(0, 1)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 1), Bias::Right),
-            InlayPoint::new(0, 1)
-        );
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 2), Bias::Left),
-            InlayPoint::new(0, 2)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 2), Bias::Right),
-            InlayPoint::new(0, 2)
-        );
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 3), Bias::Left),
-            InlayPoint::new(0, 2)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 3), Bias::Right),
-            InlayPoint::new(0, 8)
-        );
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 4), Bias::Left),
-            InlayPoint::new(0, 2)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 4), Bias::Right),
-            InlayPoint::new(0, 8)
-        );
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 5), Bias::Left),
-            InlayPoint::new(0, 2)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 5), Bias::Right),
-            InlayPoint::new(0, 8)
-        );
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 6), Bias::Left),
-            InlayPoint::new(0, 2)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 6), Bias::Right),
-            InlayPoint::new(0, 8)
-        );
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 7), Bias::Left),
-            InlayPoint::new(0, 2)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 7), Bias::Right),
-            InlayPoint::new(0, 8)
-        );
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 8), Bias::Left),
-            InlayPoint::new(0, 8)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 8), Bias::Right),
-            InlayPoint::new(0, 8)
-        );
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 9), Bias::Left),
-            InlayPoint::new(0, 9)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 9), Bias::Right),
-            InlayPoint::new(0, 9)
-        );
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 10), Bias::Left),
-            InlayPoint::new(0, 10)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 10), Bias::Right),
-            InlayPoint::new(0, 10)
-        );
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 11), Bias::Left),
-            InlayPoint::new(0, 11)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 11), Bias::Right),
-            InlayPoint::new(0, 11)
-        );
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 12), Bias::Left),
-            InlayPoint::new(0, 11)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 12), Bias::Right),
-            InlayPoint::new(0, 17)
-        );
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 13), Bias::Left),
-            InlayPoint::new(0, 11)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 13), Bias::Right),
-            InlayPoint::new(0, 17)
-        );
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 14), Bias::Left),
-            InlayPoint::new(0, 11)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 14), Bias::Right),
-            InlayPoint::new(0, 17)
-        );
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 15), Bias::Left),
-            InlayPoint::new(0, 11)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 15), Bias::Right),
-            InlayPoint::new(0, 17)
-        );
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 16), Bias::Left),
-            InlayPoint::new(0, 11)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 16), Bias::Right),
-            InlayPoint::new(0, 17)
-        );
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 17), Bias::Left),
-            InlayPoint::new(0, 17)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 17), Bias::Right),
-            InlayPoint::new(0, 17)
-        );
-
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 18), Bias::Left),
-            InlayPoint::new(0, 18)
-        );
-        assert_eq!(
-            inlay_snapshot.clip_point(InlayPoint::new(0, 18), Bias::Right),
-            InlayPoint::new(0, 18)
-        );
-
-        // The inlays can be manually removed.
-        let (inlay_snapshot, _) = inlay_map.splice(
-            inlay_map.inlays.iter().map(|inlay| inlay.id).collect(),
-            Vec::new(),
-        );
-        assert_eq!(inlay_snapshot.text(), "abxJKLyDzefghi");
-    }
-
-    #[gpui::test]
-    fn test_inlay_buffer_rows(cx: &mut AppContext) {
-        let buffer = MultiBuffer::build_simple("abc\ndef\nghi", cx);
-        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));
-        assert_eq!(inlay_snapshot.text(), "abc\ndef\nghi");
-        let mut next_inlay_id = 0;
-
-        let (inlay_snapshot, _) = inlay_map.splice(
-            Vec::new(),
-            vec![
-                Inlay {
-                    id: InlayId::Hint(post_inc(&mut next_inlay_id)),
-                    position: buffer.read(cx).snapshot(cx).anchor_before(0),
-                    text: "|123|\n".into(),
-                },
-                Inlay {
-                    id: InlayId::Hint(post_inc(&mut next_inlay_id)),
-                    position: buffer.read(cx).snapshot(cx).anchor_before(4),
-                    text: "|456|".into(),
-                },
-                Inlay {
-                    id: InlayId::Suggestion(post_inc(&mut next_inlay_id)),
-                    position: buffer.read(cx).snapshot(cx).anchor_before(7),
-                    text: "\n|567|\n".into(),
-                },
-            ],
-        );
-        assert_eq!(inlay_snapshot.text(), "|123|\nabc\n|456|def\n|567|\n\nghi");
-        assert_eq!(
-            inlay_snapshot.buffer_rows(0).collect::<Vec<_>>(),
-            vec![Some(0), None, Some(1), None, None, Some(2)]
-        );
-    }
-
-    #[gpui::test(iterations = 100)]
-    fn test_random_inlays(cx: &mut AppContext, mut rng: StdRng) {
-        init_test(cx);
-
-        let operations = env::var("OPERATIONS")
-            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
-            .unwrap_or(10);
-
-        let len = rng.gen_range(0..30);
-        let buffer = if rng.gen() {
-            let text = util::RandomCharIter::new(&mut rng)
-                .take(len)
-                .collect::<String>();
-            MultiBuffer::build_simple(&text, cx)
-        } else {
-            MultiBuffer::build_random(&mut rng, cx)
-        };
-        let mut buffer_snapshot = buffer.read(cx).snapshot(cx);
-        let mut next_inlay_id = 0;
-        log::info!("buffer text: {:?}", buffer_snapshot.text());
-        let (mut inlay_map, mut inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
-        for _ in 0..operations {
-            let mut inlay_edits = Patch::default();
-
-            let mut prev_inlay_text = inlay_snapshot.text();
-            let mut buffer_edits = Vec::new();
-            match rng.gen_range(0..=100) {
-                0..=50 => {
-                    let (snapshot, edits) = inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng);
-                    log::info!("mutated text: {:?}", snapshot.text());
-                    inlay_edits = Patch::new(edits);
-                }
-                _ => buffer.update(cx, |buffer, cx| {
-                    let subscription = buffer.subscribe();
-                    let edit_count = rng.gen_range(1..=5);
-                    buffer.randomly_mutate(&mut rng, edit_count, cx);
-                    buffer_snapshot = buffer.snapshot(cx);
-                    let edits = subscription.consume().into_inner();
-                    log::info!("editing {:?}", edits);
-                    buffer_edits.extend(edits);
-                }),
-            };
-
-            let (new_inlay_snapshot, new_inlay_edits) =
-                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
-            inlay_snapshot = new_inlay_snapshot;
-            inlay_edits = inlay_edits.compose(new_inlay_edits);
-
-            log::info!("buffer text: {:?}", buffer_snapshot.text());
-            log::info!("inlay text: {:?}", inlay_snapshot.text());
-
-            let inlays = inlay_map
-                .inlays
-                .iter()
-                .filter(|inlay| inlay.position.is_valid(&buffer_snapshot))
-                .map(|inlay| {
-                    let offset = inlay.position.to_offset(&buffer_snapshot);
-                    (offset, inlay.clone())
-                })
-                .collect::<Vec<_>>();
-            let mut expected_text = Rope::from(buffer_snapshot.text());
-            for (offset, inlay) in inlays.iter().rev() {
-                expected_text.replace(*offset..*offset, &inlay.text.to_string());
-            }
-            assert_eq!(inlay_snapshot.text(), expected_text.to_string());
-
-            let expected_buffer_rows = inlay_snapshot.buffer_rows(0).collect::<Vec<_>>();
-            assert_eq!(
-                expected_buffer_rows.len() as u32,
-                expected_text.max_point().row + 1
-            );
-            for row_start in 0..expected_buffer_rows.len() {
-                assert_eq!(
-                    inlay_snapshot
-                        .buffer_rows(row_start as u32)
-                        .collect::<Vec<_>>(),
-                    &expected_buffer_rows[row_start..],
-                    "incorrect buffer rows starting at {}",
-                    row_start
-                );
-            }
-
-            let mut text_highlights = TextHighlights::default();
-            let text_highlight_count = rng.gen_range(0_usize..10);
-            let mut text_highlight_ranges = (0..text_highlight_count)
-                .map(|_| buffer_snapshot.random_byte_range(0, &mut rng))
-                .collect::<Vec<_>>();
-            text_highlight_ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
-            log::info!("highlighting text ranges {text_highlight_ranges:?}");
-            text_highlights.insert(
-                Some(TypeId::of::<()>()),
-                Arc::new((
-                    HighlightStyle::default(),
-                    text_highlight_ranges
-                        .into_iter()
-                        .map(|range| {
-                            buffer_snapshot.anchor_before(range.start)
-                                ..buffer_snapshot.anchor_after(range.end)
-                        })
-                        .collect(),
-                )),
-            );
-
-            let mut inlay_highlights = InlayHighlights::default();
-            if !inlays.is_empty() {
-                let inlay_highlight_count = rng.gen_range(0..inlays.len());
-                let mut inlay_indices = BTreeSet::default();
-                while inlay_indices.len() < inlay_highlight_count {
-                    inlay_indices.insert(rng.gen_range(0..inlays.len()));
-                }
-                let new_highlights = inlay_indices
-                    .into_iter()
-                    .filter_map(|i| {
-                        let (_, inlay) = &inlays[i];
-                        let inlay_text_len = inlay.text.len();
-                        match inlay_text_len {
-                            0 => None,
-                            1 => Some(InlayHighlight {
-                                inlay: inlay.id,
-                                inlay_position: inlay.position,
-                                range: 0..1,
-                            }),
-                            n => {
-                                let inlay_text = inlay.text.to_string();
-                                let mut highlight_end = rng.gen_range(1..n);
-                                let mut highlight_start = rng.gen_range(0..highlight_end);
-                                while !inlay_text.is_char_boundary(highlight_end) {
-                                    highlight_end += 1;
-                                }
-                                while !inlay_text.is_char_boundary(highlight_start) {
-                                    highlight_start -= 1;
-                                }
-                                Some(InlayHighlight {
-                                    inlay: inlay.id,
-                                    inlay_position: inlay.position,
-                                    range: highlight_start..highlight_end,
-                                })
-                            }
-                        }
-                    })
-                    .map(|highlight| (highlight.inlay, (HighlightStyle::default(), highlight)))
-                    .collect();
-                log::info!("highlighting inlay ranges {new_highlights:?}");
-                inlay_highlights.insert(TypeId::of::<()>(), new_highlights);
-            }
-
-            for _ in 0..5 {
-                let mut end = rng.gen_range(0..=inlay_snapshot.len().0);
-                end = expected_text.clip_offset(end, Bias::Right);
-                let mut start = rng.gen_range(0..=end);
-                start = expected_text.clip_offset(start, Bias::Right);
-
-                let range = InlayOffset(start)..InlayOffset(end);
-                log::info!("calling inlay_snapshot.chunks({range:?})");
-                let actual_text = inlay_snapshot
-                    .chunks(
-                        range,
-                        false,
-                        Highlights {
-                            text_highlights: Some(&text_highlights),
-                            inlay_highlights: Some(&inlay_highlights),
-                            ..Highlights::default()
-                        },
-                    )
-                    .map(|chunk| chunk.text)
-                    .collect::<String>();
-                assert_eq!(
-                    actual_text,
-                    expected_text.slice(start..end).to_string(),
-                    "incorrect text in range {:?}",
-                    start..end
-                );
-
-                assert_eq!(
-                    inlay_snapshot.text_summary_for_range(InlayOffset(start)..InlayOffset(end)),
-                    expected_text.slice(start..end).summary()
-                );
-            }
-
-            for edit in inlay_edits {
-                prev_inlay_text.replace_range(
-                    edit.new.start.0..edit.new.start.0 + edit.old_len().0,
-                    &inlay_snapshot.text()[edit.new.start.0..edit.new.end.0],
-                );
-            }
-            assert_eq!(prev_inlay_text, inlay_snapshot.text());
-
-            assert_eq!(expected_text.max_point(), inlay_snapshot.max_point().0);
-            assert_eq!(expected_text.len(), inlay_snapshot.len().0);
-
-            let mut buffer_point = Point::default();
-            let mut inlay_point = inlay_snapshot.to_inlay_point(buffer_point);
-            let mut buffer_chars = buffer_snapshot.chars_at(0);
-            loop {
-                // Ensure conversion from buffer coordinates to inlay coordinates
-                // is consistent.
-                let buffer_offset = buffer_snapshot.point_to_offset(buffer_point);
-                assert_eq!(
-                    inlay_snapshot.to_point(inlay_snapshot.to_inlay_offset(buffer_offset)),
-                    inlay_point
-                );
-
-                // No matter which bias we clip an inlay point with, it doesn't move
-                // because it was constructed from a buffer point.
-                assert_eq!(
-                    inlay_snapshot.clip_point(inlay_point, Bias::Left),
-                    inlay_point,
-                    "invalid inlay point for buffer point {:?} when clipped left",
-                    buffer_point
-                );
-                assert_eq!(
-                    inlay_snapshot.clip_point(inlay_point, Bias::Right),
-                    inlay_point,
-                    "invalid inlay point for buffer point {:?} when clipped right",
-                    buffer_point
-                );
-
-                if let Some(ch) = buffer_chars.next() {
-                    if ch == '\n' {
-                        buffer_point += Point::new(1, 0);
-                    } else {
-                        buffer_point += Point::new(0, ch.len_utf8() as u32);
-                    }
-
-                    // Ensure that moving forward in the buffer always moves the inlay point forward as well.
-                    let new_inlay_point = inlay_snapshot.to_inlay_point(buffer_point);
-                    assert!(new_inlay_point > inlay_point);
-                    inlay_point = new_inlay_point;
-                } else {
-                    break;
-                }
-            }
-
-            let mut inlay_point = InlayPoint::default();
-            let mut inlay_offset = InlayOffset::default();
-            for ch in expected_text.chars() {
-                assert_eq!(
-                    inlay_snapshot.to_offset(inlay_point),
-                    inlay_offset,
-                    "invalid to_offset({:?})",
-                    inlay_point
-                );
-                assert_eq!(
-                    inlay_snapshot.to_point(inlay_offset),
-                    inlay_point,
-                    "invalid to_point({:?})",
-                    inlay_offset
-                );
-
-                let mut bytes = [0; 4];
-                for byte in ch.encode_utf8(&mut bytes).as_bytes() {
-                    inlay_offset.0 += 1;
-                    if *byte == b'\n' {
-                        inlay_point.0 += Point::new(1, 0);
-                    } else {
-                        inlay_point.0 += Point::new(0, 1);
-                    }
-
-                    let clipped_left_point = inlay_snapshot.clip_point(inlay_point, Bias::Left);
-                    let clipped_right_point = inlay_snapshot.clip_point(inlay_point, Bias::Right);
-                    assert!(
-                        clipped_left_point <= clipped_right_point,
-                        "inlay point {:?} when clipped left is greater than when clipped right ({:?} > {:?})",
-                        inlay_point,
-                        clipped_left_point,
-                        clipped_right_point
-                    );
-
-                    // Ensure the clipped points are at valid text locations.
-                    assert_eq!(
-                        clipped_left_point.0,
-                        expected_text.clip_point(clipped_left_point.0, Bias::Left)
-                    );
-                    assert_eq!(
-                        clipped_right_point.0,
-                        expected_text.clip_point(clipped_right_point.0, Bias::Right)
-                    );
-
-                    // Ensure the clipped points never overshoot the end of the map.
-                    assert!(clipped_left_point <= inlay_snapshot.max_point());
-                    assert!(clipped_right_point <= inlay_snapshot.max_point());
-
-                    // Ensure the clipped points are at valid buffer locations.
-                    assert_eq!(
-                        inlay_snapshot
-                            .to_inlay_point(inlay_snapshot.to_buffer_point(clipped_left_point)),
-                        clipped_left_point,
-                        "to_buffer_point({:?}) = {:?}",
-                        clipped_left_point,
-                        inlay_snapshot.to_buffer_point(clipped_left_point),
-                    );
-                    assert_eq!(
-                        inlay_snapshot
-                            .to_inlay_point(inlay_snapshot.to_buffer_point(clipped_right_point)),
-                        clipped_right_point,
-                        "to_buffer_point({:?}) = {:?}",
-                        clipped_right_point,
-                        inlay_snapshot.to_buffer_point(clipped_right_point),
-                    );
-                }
-            }
-        }
-    }
-
-    fn init_test(cx: &mut AppContext) {
-        let store = SettingsStore::test(cx);
-        cx.set_global(store);
-        theme::init(theme::LoadThemes::JustBase, cx);
-    }
-}

crates/editor2/src/display_map/tab_map.rs ๐Ÿ”—

@@ -1,765 +0,0 @@
-use super::{
-    fold_map::{self, FoldChunks, FoldEdit, FoldPoint, FoldSnapshot},
-    Highlights,
-};
-use crate::MultiBufferSnapshot;
-use language::{Chunk, Point};
-use std::{cmp, mem, num::NonZeroU32, ops::Range};
-use sum_tree::Bias;
-
-const MAX_EXPANSION_COLUMN: u32 = 256;
-
-pub struct TabMap(TabSnapshot);
-
-impl TabMap {
-    pub fn new(fold_snapshot: FoldSnapshot, tab_size: NonZeroU32) -> (Self, TabSnapshot) {
-        let snapshot = TabSnapshot {
-            fold_snapshot,
-            tab_size,
-            max_expansion_column: MAX_EXPANSION_COLUMN,
-            version: 0,
-        };
-        (Self(snapshot.clone()), snapshot)
-    }
-
-    #[cfg(test)]
-    pub fn set_max_expansion_column(&mut self, column: u32) -> TabSnapshot {
-        self.0.max_expansion_column = column;
-        self.0.clone()
-    }
-
-    pub fn sync(
-        &mut self,
-        fold_snapshot: FoldSnapshot,
-        mut fold_edits: Vec<FoldEdit>,
-        tab_size: NonZeroU32,
-    ) -> (TabSnapshot, Vec<TabEdit>) {
-        let old_snapshot = &mut self.0;
-        let mut new_snapshot = TabSnapshot {
-            fold_snapshot,
-            tab_size,
-            max_expansion_column: old_snapshot.max_expansion_column,
-            version: old_snapshot.version,
-        };
-
-        if old_snapshot.fold_snapshot.version != new_snapshot.fold_snapshot.version {
-            new_snapshot.version += 1;
-        }
-
-        let mut tab_edits = Vec::with_capacity(fold_edits.len());
-
-        if old_snapshot.tab_size == new_snapshot.tab_size {
-            // Expand each edit to include the next tab on the same line as the edit,
-            // and any subsequent tabs on that line that moved across the tab expansion
-            // boundary.
-            for fold_edit in &mut fold_edits {
-                let old_end = fold_edit.old.end.to_point(&old_snapshot.fold_snapshot);
-                let old_end_row_successor_offset = cmp::min(
-                    FoldPoint::new(old_end.row() + 1, 0),
-                    old_snapshot.fold_snapshot.max_point(),
-                )
-                .to_offset(&old_snapshot.fold_snapshot);
-                let new_end = fold_edit.new.end.to_point(&new_snapshot.fold_snapshot);
-
-                let mut offset_from_edit = 0;
-                let mut first_tab_offset = None;
-                let mut last_tab_with_changed_expansion_offset = None;
-                'outer: for chunk in old_snapshot.fold_snapshot.chunks(
-                    fold_edit.old.end..old_end_row_successor_offset,
-                    false,
-                    Highlights::default(),
-                ) {
-                    for (ix, _) in chunk.text.match_indices('\t') {
-                        let offset_from_edit = offset_from_edit + (ix as u32);
-                        if first_tab_offset.is_none() {
-                            first_tab_offset = Some(offset_from_edit);
-                        }
-
-                        let old_column = old_end.column() + offset_from_edit;
-                        let new_column = new_end.column() + offset_from_edit;
-                        let was_expanded = old_column < old_snapshot.max_expansion_column;
-                        let is_expanded = new_column < new_snapshot.max_expansion_column;
-                        if was_expanded != is_expanded {
-                            last_tab_with_changed_expansion_offset = Some(offset_from_edit);
-                        } else if !was_expanded && !is_expanded {
-                            break 'outer;
-                        }
-                    }
-
-                    offset_from_edit += chunk.text.len() as u32;
-                    if old_end.column() + offset_from_edit >= old_snapshot.max_expansion_column
-                        && new_end.column() + offset_from_edit >= new_snapshot.max_expansion_column
-                    {
-                        break;
-                    }
-                }
-
-                if let Some(offset) = last_tab_with_changed_expansion_offset.or(first_tab_offset) {
-                    fold_edit.old.end.0 += offset as usize + 1;
-                    fold_edit.new.end.0 += offset as usize + 1;
-                }
-            }
-
-            // Combine any edits that overlap due to the expansion.
-            let mut ix = 1;
-            while ix < fold_edits.len() {
-                let (prev_edits, next_edits) = fold_edits.split_at_mut(ix);
-                let prev_edit = prev_edits.last_mut().unwrap();
-                let edit = &next_edits[0];
-                if prev_edit.old.end >= edit.old.start {
-                    prev_edit.old.end = edit.old.end;
-                    prev_edit.new.end = edit.new.end;
-                    fold_edits.remove(ix);
-                } else {
-                    ix += 1;
-                }
-            }
-
-            for fold_edit in fold_edits {
-                let old_start = fold_edit.old.start.to_point(&old_snapshot.fold_snapshot);
-                let old_end = fold_edit.old.end.to_point(&old_snapshot.fold_snapshot);
-                let new_start = fold_edit.new.start.to_point(&new_snapshot.fold_snapshot);
-                let new_end = fold_edit.new.end.to_point(&new_snapshot.fold_snapshot);
-                tab_edits.push(TabEdit {
-                    old: old_snapshot.to_tab_point(old_start)..old_snapshot.to_tab_point(old_end),
-                    new: new_snapshot.to_tab_point(new_start)..new_snapshot.to_tab_point(new_end),
-                });
-            }
-        } else {
-            new_snapshot.version += 1;
-            tab_edits.push(TabEdit {
-                old: TabPoint::zero()..old_snapshot.max_point(),
-                new: TabPoint::zero()..new_snapshot.max_point(),
-            });
-        }
-
-        *old_snapshot = new_snapshot;
-        (old_snapshot.clone(), tab_edits)
-    }
-}
-
-#[derive(Clone)]
-pub struct TabSnapshot {
-    pub fold_snapshot: FoldSnapshot,
-    pub tab_size: NonZeroU32,
-    pub max_expansion_column: u32,
-    pub version: usize,
-}
-
-impl TabSnapshot {
-    pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot {
-        &self.fold_snapshot.inlay_snapshot.buffer
-    }
-
-    pub fn line_len(&self, row: u32) -> u32 {
-        let max_point = self.max_point();
-        if row < max_point.row() {
-            self.to_tab_point(FoldPoint::new(row, self.fold_snapshot.line_len(row)))
-                .0
-                .column
-        } else {
-            max_point.column()
-        }
-    }
-
-    pub fn text_summary(&self) -> TextSummary {
-        self.text_summary_for_range(TabPoint::zero()..self.max_point())
-    }
-
-    pub fn text_summary_for_range(&self, range: Range<TabPoint>) -> TextSummary {
-        let input_start = self.to_fold_point(range.start, Bias::Left).0;
-        let input_end = self.to_fold_point(range.end, Bias::Right).0;
-        let input_summary = self
-            .fold_snapshot
-            .text_summary_for_range(input_start..input_end);
-
-        let mut first_line_chars = 0;
-        let line_end = if range.start.row() == range.end.row() {
-            range.end
-        } else {
-            self.max_point()
-        };
-        for c in self
-            .chunks(range.start..line_end, false, Highlights::default())
-            .flat_map(|chunk| chunk.text.chars())
-        {
-            if c == '\n' {
-                break;
-            }
-            first_line_chars += 1;
-        }
-
-        let mut last_line_chars = 0;
-        if range.start.row() == range.end.row() {
-            last_line_chars = first_line_chars;
-        } else {
-            for _ in self
-                .chunks(
-                    TabPoint::new(range.end.row(), 0)..range.end,
-                    false,
-                    Highlights::default(),
-                )
-                .flat_map(|chunk| chunk.text.chars())
-            {
-                last_line_chars += 1;
-            }
-        }
-
-        TextSummary {
-            lines: range.end.0 - range.start.0,
-            first_line_chars,
-            last_line_chars,
-            longest_row: input_summary.longest_row,
-            longest_row_chars: input_summary.longest_row_chars,
-        }
-    }
-
-    pub fn chunks<'a>(
-        &'a self,
-        range: Range<TabPoint>,
-        language_aware: bool,
-        highlights: Highlights<'a>,
-    ) -> TabChunks<'a> {
-        let (input_start, expanded_char_column, to_next_stop) =
-            self.to_fold_point(range.start, Bias::Left);
-        let input_column = input_start.column();
-        let input_start = input_start.to_offset(&self.fold_snapshot);
-        let input_end = self
-            .to_fold_point(range.end, Bias::Right)
-            .0
-            .to_offset(&self.fold_snapshot);
-        let to_next_stop = if range.start.0 + Point::new(0, to_next_stop) > range.end.0 {
-            range.end.column() - range.start.column()
-        } else {
-            to_next_stop
-        };
-
-        TabChunks {
-            fold_chunks: self.fold_snapshot.chunks(
-                input_start..input_end,
-                language_aware,
-                highlights,
-            ),
-            input_column,
-            column: expanded_char_column,
-            max_expansion_column: self.max_expansion_column,
-            output_position: range.start.0,
-            max_output_position: range.end.0,
-            tab_size: self.tab_size,
-            chunk: Chunk {
-                text: &SPACES[0..(to_next_stop as usize)],
-                is_tab: true,
-                ..Default::default()
-            },
-            inside_leading_tab: to_next_stop > 0,
-        }
-    }
-
-    pub fn buffer_rows(&self, row: u32) -> fold_map::FoldBufferRows<'_> {
-        self.fold_snapshot.buffer_rows(row)
-    }
-
-    #[cfg(test)]
-    pub fn text(&self) -> String {
-        self.chunks(
-            TabPoint::zero()..self.max_point(),
-            false,
-            Highlights::default(),
-        )
-        .map(|chunk| chunk.text)
-        .collect()
-    }
-
-    pub fn max_point(&self) -> TabPoint {
-        self.to_tab_point(self.fold_snapshot.max_point())
-    }
-
-    pub fn clip_point(&self, point: TabPoint, bias: Bias) -> TabPoint {
-        self.to_tab_point(
-            self.fold_snapshot
-                .clip_point(self.to_fold_point(point, bias).0, bias),
-        )
-    }
-
-    pub fn to_tab_point(&self, input: FoldPoint) -> TabPoint {
-        let chars = self.fold_snapshot.chars_at(FoldPoint::new(input.row(), 0));
-        let expanded = self.expand_tabs(chars, input.column());
-        TabPoint::new(input.row(), expanded)
-    }
-
-    pub fn to_fold_point(&self, output: TabPoint, bias: Bias) -> (FoldPoint, u32, u32) {
-        let chars = self.fold_snapshot.chars_at(FoldPoint::new(output.row(), 0));
-        let expanded = output.column();
-        let (collapsed, expanded_char_column, to_next_stop) =
-            self.collapse_tabs(chars, expanded, bias);
-        (
-            FoldPoint::new(output.row(), collapsed as u32),
-            expanded_char_column,
-            to_next_stop,
-        )
-    }
-
-    pub fn make_tab_point(&self, point: Point, bias: Bias) -> TabPoint {
-        let inlay_point = self.fold_snapshot.inlay_snapshot.to_inlay_point(point);
-        let fold_point = self.fold_snapshot.to_fold_point(inlay_point, bias);
-        self.to_tab_point(fold_point)
-    }
-
-    pub fn to_point(&self, point: TabPoint, bias: Bias) -> Point {
-        let fold_point = self.to_fold_point(point, bias).0;
-        let inlay_point = fold_point.to_inlay_point(&self.fold_snapshot);
-        self.fold_snapshot
-            .inlay_snapshot
-            .to_buffer_point(inlay_point)
-    }
-
-    fn expand_tabs(&self, chars: impl Iterator<Item = char>, column: u32) -> u32 {
-        let tab_size = self.tab_size.get();
-
-        let mut expanded_chars = 0;
-        let mut expanded_bytes = 0;
-        let mut collapsed_bytes = 0;
-        let end_column = column.min(self.max_expansion_column);
-        for c in chars {
-            if collapsed_bytes >= end_column {
-                break;
-            }
-            if c == '\t' {
-                let tab_len = tab_size - expanded_chars % tab_size;
-                expanded_bytes += tab_len;
-                expanded_chars += tab_len;
-            } else {
-                expanded_bytes += c.len_utf8() as u32;
-                expanded_chars += 1;
-            }
-            collapsed_bytes += c.len_utf8() as u32;
-        }
-        expanded_bytes + column.saturating_sub(collapsed_bytes)
-    }
-
-    fn collapse_tabs(
-        &self,
-        chars: impl Iterator<Item = char>,
-        column: u32,
-        bias: Bias,
-    ) -> (u32, u32, u32) {
-        let tab_size = self.tab_size.get();
-
-        let mut expanded_bytes = 0;
-        let mut expanded_chars = 0;
-        let mut collapsed_bytes = 0;
-        for c in chars {
-            if expanded_bytes >= column {
-                break;
-            }
-            if collapsed_bytes >= self.max_expansion_column {
-                break;
-            }
-
-            if c == '\t' {
-                let tab_len = tab_size - (expanded_chars % tab_size);
-                expanded_chars += tab_len;
-                expanded_bytes += tab_len;
-                if expanded_bytes > column {
-                    expanded_chars -= expanded_bytes - column;
-                    return match bias {
-                        Bias::Left => (collapsed_bytes, expanded_chars, expanded_bytes - column),
-                        Bias::Right => (collapsed_bytes + 1, expanded_chars, 0),
-                    };
-                }
-            } else {
-                expanded_chars += 1;
-                expanded_bytes += c.len_utf8() as u32;
-            }
-
-            if expanded_bytes > column && matches!(bias, Bias::Left) {
-                expanded_chars -= 1;
-                break;
-            }
-
-            collapsed_bytes += c.len_utf8() as u32;
-        }
-        (
-            collapsed_bytes + column.saturating_sub(expanded_bytes),
-            expanded_chars,
-            0,
-        )
-    }
-}
-
-#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
-pub struct TabPoint(pub Point);
-
-impl TabPoint {
-    pub fn new(row: u32, column: u32) -> Self {
-        Self(Point::new(row, column))
-    }
-
-    pub fn zero() -> Self {
-        Self::new(0, 0)
-    }
-
-    pub fn row(self) -> u32 {
-        self.0.row
-    }
-
-    pub fn column(self) -> u32 {
-        self.0.column
-    }
-}
-
-impl From<Point> for TabPoint {
-    fn from(point: Point) -> Self {
-        Self(point)
-    }
-}
-
-pub type TabEdit = text::Edit<TabPoint>;
-
-#[derive(Clone, Debug, Default, Eq, PartialEq)]
-pub struct TextSummary {
-    pub lines: Point,
-    pub first_line_chars: u32,
-    pub last_line_chars: u32,
-    pub longest_row: u32,
-    pub longest_row_chars: u32,
-}
-
-impl<'a> From<&'a str> for TextSummary {
-    fn from(text: &'a str) -> Self {
-        let sum = text::TextSummary::from(text);
-
-        TextSummary {
-            lines: sum.lines,
-            first_line_chars: sum.first_line_chars,
-            last_line_chars: sum.last_line_chars,
-            longest_row: sum.longest_row,
-            longest_row_chars: sum.longest_row_chars,
-        }
-    }
-}
-
-impl<'a> std::ops::AddAssign<&'a Self> for TextSummary {
-    fn add_assign(&mut self, other: &'a Self) {
-        let joined_chars = self.last_line_chars + other.first_line_chars;
-        if joined_chars > self.longest_row_chars {
-            self.longest_row = self.lines.row;
-            self.longest_row_chars = joined_chars;
-        }
-        if other.longest_row_chars > self.longest_row_chars {
-            self.longest_row = self.lines.row + other.longest_row;
-            self.longest_row_chars = other.longest_row_chars;
-        }
-
-        if self.lines.row == 0 {
-            self.first_line_chars += other.first_line_chars;
-        }
-
-        if other.lines.row == 0 {
-            self.last_line_chars += other.first_line_chars;
-        } else {
-            self.last_line_chars = other.last_line_chars;
-        }
-
-        self.lines += &other.lines;
-    }
-}
-
-// Handles a tab width <= 16
-const SPACES: &str = "                ";
-
-pub struct TabChunks<'a> {
-    fold_chunks: FoldChunks<'a>,
-    chunk: Chunk<'a>,
-    column: u32,
-    max_expansion_column: u32,
-    output_position: Point,
-    input_column: u32,
-    max_output_position: Point,
-    tab_size: NonZeroU32,
-    inside_leading_tab: bool,
-}
-
-impl<'a> Iterator for TabChunks<'a> {
-    type Item = Chunk<'a>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.chunk.text.is_empty() {
-            if let Some(chunk) = self.fold_chunks.next() {
-                self.chunk = chunk;
-                if self.inside_leading_tab {
-                    self.chunk.text = &self.chunk.text[1..];
-                    self.inside_leading_tab = false;
-                    self.input_column += 1;
-                }
-            } else {
-                return None;
-            }
-        }
-
-        for (ix, c) in self.chunk.text.char_indices() {
-            match c {
-                '\t' => {
-                    if ix > 0 {
-                        let (prefix, suffix) = self.chunk.text.split_at(ix);
-                        self.chunk.text = suffix;
-                        return Some(Chunk {
-                            text: prefix,
-                            ..self.chunk
-                        });
-                    } else {
-                        self.chunk.text = &self.chunk.text[1..];
-                        let tab_size = if self.input_column < self.max_expansion_column {
-                            self.tab_size.get() as u32
-                        } else {
-                            1
-                        };
-                        let mut len = tab_size - self.column % tab_size;
-                        let next_output_position = cmp::min(
-                            self.output_position + Point::new(0, len),
-                            self.max_output_position,
-                        );
-                        len = next_output_position.column - self.output_position.column;
-                        self.column += len;
-                        self.input_column += 1;
-                        self.output_position = next_output_position;
-                        return Some(Chunk {
-                            text: &SPACES[..len as usize],
-                            is_tab: true,
-                            ..self.chunk
-                        });
-                    }
-                }
-                '\n' => {
-                    self.column = 0;
-                    self.input_column = 0;
-                    self.output_position += Point::new(1, 0);
-                }
-                _ => {
-                    self.column += 1;
-                    if !self.inside_leading_tab {
-                        self.input_column += c.len_utf8() as u32;
-                    }
-                    self.output_position.column += c.len_utf8() as u32;
-                }
-            }
-        }
-
-        Some(mem::take(&mut self.chunk))
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{
-        display_map::{fold_map::FoldMap, inlay_map::InlayMap},
-        MultiBuffer,
-    };
-    use rand::{prelude::StdRng, Rng};
-
-    #[gpui::test]
-    fn test_expand_tabs(cx: &mut gpui::AppContext) {
-        let buffer = MultiBuffer::build_simple("", cx);
-        let buffer_snapshot = buffer.read(cx).snapshot(cx);
-        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
-        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
-        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
-
-        assert_eq!(tab_snapshot.expand_tabs("\t".chars(), 0), 0);
-        assert_eq!(tab_snapshot.expand_tabs("\t".chars(), 1), 4);
-        assert_eq!(tab_snapshot.expand_tabs("\ta".chars(), 2), 5);
-    }
-
-    #[gpui::test]
-    fn test_long_lines(cx: &mut gpui::AppContext) {
-        let max_expansion_column = 12;
-        let input = "A\tBC\tDEF\tG\tHI\tJ\tK\tL\tM";
-        let output = "A   BC  DEF G   HI J K L M";
-
-        let buffer = MultiBuffer::build_simple(input, cx);
-        let buffer_snapshot = buffer.read(cx).snapshot(cx);
-        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
-        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
-        let (_, mut tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
-
-        tab_snapshot.max_expansion_column = max_expansion_column;
-        assert_eq!(tab_snapshot.text(), output);
-
-        for (ix, c) in input.char_indices() {
-            assert_eq!(
-                tab_snapshot
-                    .chunks(
-                        TabPoint::new(0, ix as u32)..tab_snapshot.max_point(),
-                        false,
-                        Highlights::default(),
-                    )
-                    .map(|c| c.text)
-                    .collect::<String>(),
-                &output[ix..],
-                "text from index {ix}"
-            );
-
-            if c != '\t' {
-                let input_point = Point::new(0, ix as u32);
-                let output_point = Point::new(0, output.find(c).unwrap() as u32);
-                assert_eq!(
-                    tab_snapshot.to_tab_point(FoldPoint(input_point)),
-                    TabPoint(output_point),
-                    "to_tab_point({input_point:?})"
-                );
-                assert_eq!(
-                    tab_snapshot
-                        .to_fold_point(TabPoint(output_point), Bias::Left)
-                        .0,
-                    FoldPoint(input_point),
-                    "to_fold_point({output_point:?})"
-                );
-            }
-        }
-    }
-
-    #[gpui::test]
-    fn test_long_lines_with_character_spanning_max_expansion_column(cx: &mut gpui::AppContext) {
-        let max_expansion_column = 8;
-        let input = "abcdefgโ‹ฏhij";
-
-        let buffer = MultiBuffer::build_simple(input, cx);
-        let buffer_snapshot = buffer.read(cx).snapshot(cx);
-        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
-        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
-        let (_, mut tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
-
-        tab_snapshot.max_expansion_column = max_expansion_column;
-        assert_eq!(tab_snapshot.text(), input);
-    }
-
-    #[gpui::test]
-    fn test_marking_tabs(cx: &mut gpui::AppContext) {
-        let input = "\t \thello";
-
-        let buffer = MultiBuffer::build_simple(&input, cx);
-        let buffer_snapshot = buffer.read(cx).snapshot(cx);
-        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
-        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
-        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
-
-        assert_eq!(
-            chunks(&tab_snapshot, TabPoint::zero()),
-            vec![
-                ("    ".to_string(), true),
-                (" ".to_string(), false),
-                ("   ".to_string(), true),
-                ("hello".to_string(), false),
-            ]
-        );
-        assert_eq!(
-            chunks(&tab_snapshot, TabPoint::new(0, 2)),
-            vec![
-                ("  ".to_string(), true),
-                (" ".to_string(), false),
-                ("   ".to_string(), true),
-                ("hello".to_string(), false),
-            ]
-        );
-
-        fn chunks(snapshot: &TabSnapshot, start: TabPoint) -> Vec<(String, bool)> {
-            let mut chunks = Vec::new();
-            let mut was_tab = false;
-            let mut text = String::new();
-            for chunk in snapshot.chunks(start..snapshot.max_point(), false, Highlights::default())
-            {
-                if chunk.is_tab != was_tab {
-                    if !text.is_empty() {
-                        chunks.push((mem::take(&mut text), was_tab));
-                    }
-                    was_tab = chunk.is_tab;
-                }
-                text.push_str(chunk.text);
-            }
-
-            if !text.is_empty() {
-                chunks.push((text, was_tab));
-            }
-            chunks
-        }
-    }
-
-    #[gpui::test(iterations = 100)]
-    fn test_random_tabs(cx: &mut gpui::AppContext, mut rng: StdRng) {
-        let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap();
-        let len = rng.gen_range(0..30);
-        let buffer = if rng.gen() {
-            let text = util::RandomCharIter::new(&mut rng)
-                .take(len)
-                .collect::<String>();
-            MultiBuffer::build_simple(&text, cx)
-        } else {
-            MultiBuffer::build_random(&mut rng, cx)
-        };
-        let buffer_snapshot = buffer.read(cx).snapshot(cx);
-        log::info!("Buffer text: {:?}", buffer_snapshot.text());
-
-        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
-        log::info!("InlayMap text: {:?}", inlay_snapshot.text());
-        let (mut fold_map, _) = FoldMap::new(inlay_snapshot.clone());
-        fold_map.randomly_mutate(&mut rng);
-        let (fold_snapshot, _) = fold_map.read(inlay_snapshot, vec![]);
-        log::info!("FoldMap text: {:?}", fold_snapshot.text());
-        let (inlay_snapshot, _) = inlay_map.randomly_mutate(&mut 0, &mut rng);
-        log::info!("InlayMap text: {:?}", inlay_snapshot.text());
-
-        let (mut tab_map, _) = TabMap::new(fold_snapshot.clone(), tab_size);
-        let tabs_snapshot = tab_map.set_max_expansion_column(32);
-
-        let text = text::Rope::from(tabs_snapshot.text().as_str());
-        log::info!(
-            "TabMap text (tab size: {}): {:?}",
-            tab_size,
-            tabs_snapshot.text(),
-        );
-
-        for _ in 0..5 {
-            let end_row = rng.gen_range(0..=text.max_point().row);
-            let end_column = rng.gen_range(0..=text.line_len(end_row));
-            let mut end = TabPoint(text.clip_point(Point::new(end_row, end_column), Bias::Right));
-            let start_row = rng.gen_range(0..=text.max_point().row);
-            let start_column = rng.gen_range(0..=text.line_len(start_row));
-            let mut start =
-                TabPoint(text.clip_point(Point::new(start_row, start_column), Bias::Left));
-            if start > end {
-                mem::swap(&mut start, &mut end);
-            }
-
-            let expected_text = text
-                .chunks_in_range(text.point_to_offset(start.0)..text.point_to_offset(end.0))
-                .collect::<String>();
-            let expected_summary = TextSummary::from(expected_text.as_str());
-            assert_eq!(
-                tabs_snapshot
-                    .chunks(start..end, false, Highlights::default())
-                    .map(|c| c.text)
-                    .collect::<String>(),
-                expected_text,
-                "chunks({:?}..{:?})",
-                start,
-                end
-            );
-
-            let mut actual_summary = tabs_snapshot.text_summary_for_range(start..end);
-            if tab_size.get() > 1 && inlay_snapshot.text().contains('\t') {
-                actual_summary.longest_row = expected_summary.longest_row;
-                actual_summary.longest_row_chars = expected_summary.longest_row_chars;
-            }
-            assert_eq!(actual_summary, expected_summary);
-        }
-
-        for row in 0..=text.max_point().row {
-            assert_eq!(
-                tabs_snapshot.line_len(row),
-                text.line_len(row),
-                "line_len({row})"
-            );
-        }
-    }
-}

crates/editor2/src/display_map/wrap_map.rs ๐Ÿ”—

@@ -1,1359 +0,0 @@
-use super::{
-    fold_map::FoldBufferRows,
-    tab_map::{self, TabEdit, TabPoint, TabSnapshot},
-    Highlights,
-};
-use crate::MultiBufferSnapshot;
-use gpui::{AppContext, Context, Font, LineWrapper, Model, ModelContext, Pixels, Task};
-use language::{Chunk, Point};
-use lazy_static::lazy_static;
-use smol::future::yield_now;
-use std::{cmp, collections::VecDeque, mem, ops::Range, time::Duration};
-use sum_tree::{Bias, Cursor, SumTree};
-use text::Patch;
-use util::ResultExt;
-
-pub use super::tab_map::TextSummary;
-pub type WrapEdit = text::Edit<u32>;
-
-pub struct WrapMap {
-    snapshot: WrapSnapshot,
-    pending_edits: VecDeque<(TabSnapshot, Vec<TabEdit>)>,
-    interpolated_edits: Patch<u32>,
-    edits_since_sync: Patch<u32>,
-    wrap_width: Option<Pixels>,
-    background_task: Option<Task<()>>,
-    font_with_size: (Font, Pixels),
-}
-
-#[derive(Clone)]
-pub struct WrapSnapshot {
-    tab_snapshot: TabSnapshot,
-    transforms: SumTree<Transform>,
-    interpolated: bool,
-}
-
-#[derive(Clone, Debug, Default, Eq, PartialEq)]
-struct Transform {
-    summary: TransformSummary,
-    display_text: Option<&'static str>,
-}
-
-#[derive(Clone, Debug, Default, Eq, PartialEq)]
-struct TransformSummary {
-    input: TextSummary,
-    output: TextSummary,
-}
-
-#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
-pub struct WrapPoint(pub Point);
-
-pub struct WrapChunks<'a> {
-    input_chunks: tab_map::TabChunks<'a>,
-    input_chunk: Chunk<'a>,
-    output_position: WrapPoint,
-    max_output_row: u32,
-    transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>,
-}
-
-#[derive(Clone)]
-pub struct WrapBufferRows<'a> {
-    input_buffer_rows: FoldBufferRows<'a>,
-    input_buffer_row: Option<u32>,
-    output_row: u32,
-    soft_wrapped: bool,
-    max_output_row: u32,
-    transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>,
-}
-
-impl WrapMap {
-    pub fn new(
-        tab_snapshot: TabSnapshot,
-        font: Font,
-        font_size: Pixels,
-        wrap_width: Option<Pixels>,
-        cx: &mut AppContext,
-    ) -> (Model<Self>, WrapSnapshot) {
-        let handle = cx.new_model(|cx| {
-            let mut this = Self {
-                font_with_size: (font, font_size),
-                wrap_width: None,
-                pending_edits: Default::default(),
-                interpolated_edits: Default::default(),
-                edits_since_sync: Default::default(),
-                snapshot: WrapSnapshot::new(tab_snapshot),
-                background_task: None,
-            };
-            this.set_wrap_width(wrap_width, cx);
-            mem::take(&mut this.edits_since_sync);
-            this
-        });
-        let snapshot = handle.read(cx).snapshot.clone();
-        (handle, snapshot)
-    }
-
-    #[cfg(test)]
-    pub fn is_rewrapping(&self) -> bool {
-        self.background_task.is_some()
-    }
-
-    pub fn sync(
-        &mut self,
-        tab_snapshot: TabSnapshot,
-        edits: Vec<TabEdit>,
-        cx: &mut ModelContext<Self>,
-    ) -> (WrapSnapshot, Patch<u32>) {
-        if self.wrap_width.is_some() {
-            self.pending_edits.push_back((tab_snapshot, edits));
-            self.flush_edits(cx);
-        } else {
-            self.edits_since_sync = self
-                .edits_since_sync
-                .compose(&self.snapshot.interpolate(tab_snapshot, &edits));
-            self.snapshot.interpolated = false;
-        }
-
-        (self.snapshot.clone(), mem::take(&mut self.edits_since_sync))
-    }
-
-    pub fn set_font_with_size(
-        &mut self,
-        font: Font,
-        font_size: Pixels,
-        cx: &mut ModelContext<Self>,
-    ) -> bool {
-        let font_with_size = (font, font_size);
-
-        if font_with_size != self.font_with_size {
-            self.font_with_size = font_with_size;
-            self.rewrap(cx);
-            true
-        } else {
-            false
-        }
-    }
-
-    pub fn set_wrap_width(
-        &mut self,
-        wrap_width: Option<Pixels>,
-        cx: &mut ModelContext<Self>,
-    ) -> bool {
-        if wrap_width == self.wrap_width {
-            return false;
-        }
-
-        self.wrap_width = wrap_width;
-        self.rewrap(cx);
-        true
-    }
-
-    fn rewrap(&mut self, cx: &mut ModelContext<Self>) {
-        self.background_task.take();
-        self.interpolated_edits.clear();
-        self.pending_edits.clear();
-
-        if let Some(wrap_width) = self.wrap_width {
-            let mut new_snapshot = self.snapshot.clone();
-            let mut edits = Patch::default();
-            let text_system = cx.text_system().clone();
-            let (font, font_size) = self.font_with_size.clone();
-            let task = cx.background_executor().spawn(async move {
-                if let Some(mut line_wrapper) = text_system.line_wrapper(font, font_size).log_err()
-                {
-                    let tab_snapshot = new_snapshot.tab_snapshot.clone();
-                    let range = TabPoint::zero()..tab_snapshot.max_point();
-                    edits = new_snapshot
-                        .update(
-                            tab_snapshot,
-                            &[TabEdit {
-                                old: range.clone(),
-                                new: range.clone(),
-                            }],
-                            wrap_width,
-                            &mut line_wrapper,
-                        )
-                        .await;
-                }
-                (new_snapshot, edits)
-            });
-
-            match cx
-                .background_executor()
-                .block_with_timeout(Duration::from_millis(5), task)
-            {
-                Ok((snapshot, edits)) => {
-                    self.snapshot = snapshot;
-                    self.edits_since_sync = self.edits_since_sync.compose(&edits);
-                }
-                Err(wrap_task) => {
-                    self.background_task = Some(cx.spawn(|this, mut cx| async move {
-                        let (snapshot, edits) = wrap_task.await;
-                        this.update(&mut cx, |this, cx| {
-                            this.snapshot = snapshot;
-                            this.edits_since_sync = this
-                                .edits_since_sync
-                                .compose(mem::take(&mut this.interpolated_edits).invert())
-                                .compose(&edits);
-                            this.background_task = None;
-                            this.flush_edits(cx);
-                            cx.notify();
-                        })
-                        .ok();
-                    }));
-                }
-            }
-        } else {
-            let old_rows = self.snapshot.transforms.summary().output.lines.row + 1;
-            self.snapshot.transforms = SumTree::new();
-            let summary = self.snapshot.tab_snapshot.text_summary();
-            if !summary.lines.is_zero() {
-                self.snapshot
-                    .transforms
-                    .push(Transform::isomorphic(summary), &());
-            }
-            let new_rows = self.snapshot.transforms.summary().output.lines.row + 1;
-            self.snapshot.interpolated = false;
-            self.edits_since_sync = self.edits_since_sync.compose(&Patch::new(vec![WrapEdit {
-                old: 0..old_rows,
-                new: 0..new_rows,
-            }]));
-        }
-    }
-
-    fn flush_edits(&mut self, cx: &mut ModelContext<Self>) {
-        if !self.snapshot.interpolated {
-            let mut to_remove_len = 0;
-            for (tab_snapshot, _) in &self.pending_edits {
-                if tab_snapshot.version <= self.snapshot.tab_snapshot.version {
-                    to_remove_len += 1;
-                } else {
-                    break;
-                }
-            }
-            self.pending_edits.drain(..to_remove_len);
-        }
-
-        if self.pending_edits.is_empty() {
-            return;
-        }
-
-        if let Some(wrap_width) = self.wrap_width {
-            if self.background_task.is_none() {
-                let pending_edits = self.pending_edits.clone();
-                let mut snapshot = self.snapshot.clone();
-                let text_system = cx.text_system().clone();
-                let (font, font_size) = self.font_with_size.clone();
-                let update_task = cx.background_executor().spawn(async move {
-                    let mut edits = Patch::default();
-                    if let Some(mut line_wrapper) =
-                        text_system.line_wrapper(font, font_size).log_err()
-                    {
-                        for (tab_snapshot, tab_edits) in pending_edits {
-                            let wrap_edits = snapshot
-                                .update(tab_snapshot, &tab_edits, wrap_width, &mut line_wrapper)
-                                .await;
-                            edits = edits.compose(&wrap_edits);
-                        }
-                    }
-                    (snapshot, edits)
-                });
-
-                match cx
-                    .background_executor()
-                    .block_with_timeout(Duration::from_millis(1), update_task)
-                {
-                    Ok((snapshot, output_edits)) => {
-                        self.snapshot = snapshot;
-                        self.edits_since_sync = self.edits_since_sync.compose(&output_edits);
-                    }
-                    Err(update_task) => {
-                        self.background_task = Some(cx.spawn(|this, mut cx| async move {
-                            let (snapshot, edits) = update_task.await;
-                            this.update(&mut cx, |this, cx| {
-                                this.snapshot = snapshot;
-                                this.edits_since_sync = this
-                                    .edits_since_sync
-                                    .compose(mem::take(&mut this.interpolated_edits).invert())
-                                    .compose(&edits);
-                                this.background_task = None;
-                                this.flush_edits(cx);
-                                cx.notify();
-                            })
-                            .ok();
-                        }));
-                    }
-                }
-            }
-        }
-
-        let was_interpolated = self.snapshot.interpolated;
-        let mut to_remove_len = 0;
-        for (tab_snapshot, edits) in &self.pending_edits {
-            if tab_snapshot.version <= self.snapshot.tab_snapshot.version {
-                to_remove_len += 1;
-            } else {
-                let interpolated_edits = self.snapshot.interpolate(tab_snapshot.clone(), edits);
-                self.edits_since_sync = self.edits_since_sync.compose(&interpolated_edits);
-                self.interpolated_edits = self.interpolated_edits.compose(&interpolated_edits);
-            }
-        }
-
-        if !was_interpolated {
-            self.pending_edits.drain(..to_remove_len);
-        }
-    }
-}
-
-impl WrapSnapshot {
-    fn new(tab_snapshot: TabSnapshot) -> Self {
-        let mut transforms = SumTree::new();
-        let extent = tab_snapshot.text_summary();
-        if !extent.lines.is_zero() {
-            transforms.push(Transform::isomorphic(extent), &());
-        }
-        Self {
-            transforms,
-            tab_snapshot,
-            interpolated: true,
-        }
-    }
-
-    pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot {
-        self.tab_snapshot.buffer_snapshot()
-    }
-
-    fn interpolate(&mut self, new_tab_snapshot: TabSnapshot, tab_edits: &[TabEdit]) -> Patch<u32> {
-        let mut new_transforms;
-        if tab_edits.is_empty() {
-            new_transforms = self.transforms.clone();
-        } else {
-            let mut old_cursor = self.transforms.cursor::<TabPoint>();
-
-            let mut tab_edits_iter = tab_edits.iter().peekable();
-            new_transforms =
-                old_cursor.slice(&tab_edits_iter.peek().unwrap().old.start, Bias::Right, &());
-
-            while let Some(edit) = tab_edits_iter.next() {
-                if edit.new.start > TabPoint::from(new_transforms.summary().input.lines) {
-                    let summary = new_tab_snapshot.text_summary_for_range(
-                        TabPoint::from(new_transforms.summary().input.lines)..edit.new.start,
-                    );
-                    new_transforms.push_or_extend(Transform::isomorphic(summary));
-                }
-
-                if !edit.new.is_empty() {
-                    new_transforms.push_or_extend(Transform::isomorphic(
-                        new_tab_snapshot.text_summary_for_range(edit.new.clone()),
-                    ));
-                }
-
-                old_cursor.seek_forward(&edit.old.end, Bias::Right, &());
-                if let Some(next_edit) = tab_edits_iter.peek() {
-                    if next_edit.old.start > old_cursor.end(&()) {
-                        if old_cursor.end(&()) > edit.old.end {
-                            let summary = self
-                                .tab_snapshot
-                                .text_summary_for_range(edit.old.end..old_cursor.end(&()));
-                            new_transforms.push_or_extend(Transform::isomorphic(summary));
-                        }
-
-                        old_cursor.next(&());
-                        new_transforms.append(
-                            old_cursor.slice(&next_edit.old.start, Bias::Right, &()),
-                            &(),
-                        );
-                    }
-                } else {
-                    if old_cursor.end(&()) > edit.old.end {
-                        let summary = self
-                            .tab_snapshot
-                            .text_summary_for_range(edit.old.end..old_cursor.end(&()));
-                        new_transforms.push_or_extend(Transform::isomorphic(summary));
-                    }
-                    old_cursor.next(&());
-                    new_transforms.append(old_cursor.suffix(&()), &());
-                }
-            }
-        }
-
-        let old_snapshot = mem::replace(
-            self,
-            WrapSnapshot {
-                tab_snapshot: new_tab_snapshot,
-                transforms: new_transforms,
-                interpolated: true,
-            },
-        );
-        self.check_invariants();
-        old_snapshot.compute_edits(tab_edits, self)
-    }
-
-    async fn update(
-        &mut self,
-        new_tab_snapshot: TabSnapshot,
-        tab_edits: &[TabEdit],
-        wrap_width: Pixels,
-        line_wrapper: &mut LineWrapper,
-    ) -> Patch<u32> {
-        #[derive(Debug)]
-        struct RowEdit {
-            old_rows: Range<u32>,
-            new_rows: Range<u32>,
-        }
-
-        let mut tab_edits_iter = tab_edits.iter().peekable();
-        let mut row_edits = Vec::new();
-        while let Some(edit) = tab_edits_iter.next() {
-            let mut row_edit = RowEdit {
-                old_rows: edit.old.start.row()..edit.old.end.row() + 1,
-                new_rows: edit.new.start.row()..edit.new.end.row() + 1,
-            };
-
-            while let Some(next_edit) = tab_edits_iter.peek() {
-                if next_edit.old.start.row() <= row_edit.old_rows.end {
-                    row_edit.old_rows.end = next_edit.old.end.row() + 1;
-                    row_edit.new_rows.end = next_edit.new.end.row() + 1;
-                    tab_edits_iter.next();
-                } else {
-                    break;
-                }
-            }
-
-            row_edits.push(row_edit);
-        }
-
-        let mut new_transforms;
-        if row_edits.is_empty() {
-            new_transforms = self.transforms.clone();
-        } else {
-            let mut row_edits = row_edits.into_iter().peekable();
-            let mut old_cursor = self.transforms.cursor::<TabPoint>();
-
-            new_transforms = old_cursor.slice(
-                &TabPoint::new(row_edits.peek().unwrap().old_rows.start, 0),
-                Bias::Right,
-                &(),
-            );
-
-            while let Some(edit) = row_edits.next() {
-                if edit.new_rows.start > new_transforms.summary().input.lines.row {
-                    let summary = new_tab_snapshot.text_summary_for_range(
-                        TabPoint(new_transforms.summary().input.lines)
-                            ..TabPoint::new(edit.new_rows.start, 0),
-                    );
-                    new_transforms.push_or_extend(Transform::isomorphic(summary));
-                }
-
-                let mut line = String::new();
-                let mut remaining = None;
-                let mut chunks = new_tab_snapshot.chunks(
-                    TabPoint::new(edit.new_rows.start, 0)..new_tab_snapshot.max_point(),
-                    false,
-                    Highlights::default(),
-                );
-                let mut edit_transforms = Vec::<Transform>::new();
-                for _ in edit.new_rows.start..edit.new_rows.end {
-                    while let Some(chunk) =
-                        remaining.take().or_else(|| chunks.next().map(|c| c.text))
-                    {
-                        if let Some(ix) = chunk.find('\n') {
-                            line.push_str(&chunk[..ix + 1]);
-                            remaining = Some(&chunk[ix + 1..]);
-                            break;
-                        } else {
-                            line.push_str(chunk)
-                        }
-                    }
-
-                    if line.is_empty() {
-                        break;
-                    }
-
-                    let mut prev_boundary_ix = 0;
-                    for boundary in line_wrapper.wrap_line(&line, wrap_width) {
-                        let wrapped = &line[prev_boundary_ix..boundary.ix];
-                        push_isomorphic(&mut edit_transforms, TextSummary::from(wrapped));
-                        edit_transforms.push(Transform::wrap(boundary.next_indent));
-                        prev_boundary_ix = boundary.ix;
-                    }
-
-                    if prev_boundary_ix < line.len() {
-                        push_isomorphic(
-                            &mut edit_transforms,
-                            TextSummary::from(&line[prev_boundary_ix..]),
-                        );
-                    }
-
-                    line.clear();
-                    yield_now().await;
-                }
-
-                let mut edit_transforms = edit_transforms.into_iter();
-                if let Some(transform) = edit_transforms.next() {
-                    new_transforms.push_or_extend(transform);
-                }
-                new_transforms.extend(edit_transforms, &());
-
-                old_cursor.seek_forward(&TabPoint::new(edit.old_rows.end, 0), Bias::Right, &());
-                if let Some(next_edit) = row_edits.peek() {
-                    if next_edit.old_rows.start > old_cursor.end(&()).row() {
-                        if old_cursor.end(&()) > TabPoint::new(edit.old_rows.end, 0) {
-                            let summary = self.tab_snapshot.text_summary_for_range(
-                                TabPoint::new(edit.old_rows.end, 0)..old_cursor.end(&()),
-                            );
-                            new_transforms.push_or_extend(Transform::isomorphic(summary));
-                        }
-                        old_cursor.next(&());
-                        new_transforms.append(
-                            old_cursor.slice(
-                                &TabPoint::new(next_edit.old_rows.start, 0),
-                                Bias::Right,
-                                &(),
-                            ),
-                            &(),
-                        );
-                    }
-                } else {
-                    if old_cursor.end(&()) > TabPoint::new(edit.old_rows.end, 0) {
-                        let summary = self.tab_snapshot.text_summary_for_range(
-                            TabPoint::new(edit.old_rows.end, 0)..old_cursor.end(&()),
-                        );
-                        new_transforms.push_or_extend(Transform::isomorphic(summary));
-                    }
-                    old_cursor.next(&());
-                    new_transforms.append(old_cursor.suffix(&()), &());
-                }
-            }
-        }
-
-        let old_snapshot = mem::replace(
-            self,
-            WrapSnapshot {
-                tab_snapshot: new_tab_snapshot,
-                transforms: new_transforms,
-                interpolated: false,
-            },
-        );
-        self.check_invariants();
-        old_snapshot.compute_edits(tab_edits, self)
-    }
-
-    fn compute_edits(&self, tab_edits: &[TabEdit], new_snapshot: &WrapSnapshot) -> Patch<u32> {
-        let mut wrap_edits = Vec::new();
-        let mut old_cursor = self.transforms.cursor::<TransformSummary>();
-        let mut new_cursor = new_snapshot.transforms.cursor::<TransformSummary>();
-        for mut tab_edit in tab_edits.iter().cloned() {
-            tab_edit.old.start.0.column = 0;
-            tab_edit.old.end.0 += Point::new(1, 0);
-            tab_edit.new.start.0.column = 0;
-            tab_edit.new.end.0 += Point::new(1, 0);
-
-            old_cursor.seek(&tab_edit.old.start, Bias::Right, &());
-            let mut old_start = old_cursor.start().output.lines;
-            old_start += tab_edit.old.start.0 - old_cursor.start().input.lines;
-
-            old_cursor.seek(&tab_edit.old.end, Bias::Right, &());
-            let mut old_end = old_cursor.start().output.lines;
-            old_end += tab_edit.old.end.0 - old_cursor.start().input.lines;
-
-            new_cursor.seek(&tab_edit.new.start, Bias::Right, &());
-            let mut new_start = new_cursor.start().output.lines;
-            new_start += tab_edit.new.start.0 - new_cursor.start().input.lines;
-
-            new_cursor.seek(&tab_edit.new.end, Bias::Right, &());
-            let mut new_end = new_cursor.start().output.lines;
-            new_end += tab_edit.new.end.0 - new_cursor.start().input.lines;
-
-            wrap_edits.push(WrapEdit {
-                old: old_start.row..old_end.row,
-                new: new_start.row..new_end.row,
-            });
-        }
-
-        consolidate_wrap_edits(&mut wrap_edits);
-        Patch::new(wrap_edits)
-    }
-
-    pub fn chunks<'a>(
-        &'a self,
-        rows: Range<u32>,
-        language_aware: bool,
-        highlights: Highlights<'a>,
-    ) -> WrapChunks<'a> {
-        let output_start = WrapPoint::new(rows.start, 0);
-        let output_end = WrapPoint::new(rows.end, 0);
-        let mut transforms = self.transforms.cursor::<(WrapPoint, TabPoint)>();
-        transforms.seek(&output_start, Bias::Right, &());
-        let mut input_start = TabPoint(transforms.start().1 .0);
-        if transforms.item().map_or(false, |t| t.is_isomorphic()) {
-            input_start.0 += output_start.0 - transforms.start().0 .0;
-        }
-        let input_end = self
-            .to_tab_point(output_end)
-            .min(self.tab_snapshot.max_point());
-        WrapChunks {
-            input_chunks: self.tab_snapshot.chunks(
-                input_start..input_end,
-                language_aware,
-                highlights,
-            ),
-            input_chunk: Default::default(),
-            output_position: output_start,
-            max_output_row: rows.end,
-            transforms,
-        }
-    }
-
-    pub fn max_point(&self) -> WrapPoint {
-        WrapPoint(self.transforms.summary().output.lines)
-    }
-
-    pub fn line_len(&self, row: u32) -> u32 {
-        let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>();
-        cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Left, &());
-        if cursor
-            .item()
-            .map_or(false, |transform| transform.is_isomorphic())
-        {
-            let overshoot = row - cursor.start().0.row();
-            let tab_row = cursor.start().1.row() + overshoot;
-            let tab_line_len = self.tab_snapshot.line_len(tab_row);
-            if overshoot == 0 {
-                cursor.start().0.column() + (tab_line_len - cursor.start().1.column())
-            } else {
-                tab_line_len
-            }
-        } else {
-            cursor.start().0.column()
-        }
-    }
-
-    pub fn soft_wrap_indent(&self, row: u32) -> Option<u32> {
-        let mut cursor = self.transforms.cursor::<WrapPoint>();
-        cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Right, &());
-        cursor.item().and_then(|transform| {
-            if transform.is_isomorphic() {
-                None
-            } else {
-                Some(transform.summary.output.lines.column)
-            }
-        })
-    }
-
-    pub fn longest_row(&self) -> u32 {
-        self.transforms.summary().output.longest_row
-    }
-
-    pub fn buffer_rows(&self, start_row: u32) -> WrapBufferRows {
-        let mut transforms = self.transforms.cursor::<(WrapPoint, TabPoint)>();
-        transforms.seek(&WrapPoint::new(start_row, 0), Bias::Left, &());
-        let mut input_row = transforms.start().1.row();
-        if transforms.item().map_or(false, |t| t.is_isomorphic()) {
-            input_row += start_row - transforms.start().0.row();
-        }
-        let soft_wrapped = transforms.item().map_or(false, |t| !t.is_isomorphic());
-        let mut input_buffer_rows = self.tab_snapshot.buffer_rows(input_row);
-        let input_buffer_row = input_buffer_rows.next().unwrap();
-        WrapBufferRows {
-            transforms,
-            input_buffer_row,
-            input_buffer_rows,
-            output_row: start_row,
-            soft_wrapped,
-            max_output_row: self.max_point().row(),
-        }
-    }
-
-    pub fn to_tab_point(&self, point: WrapPoint) -> TabPoint {
-        let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>();
-        cursor.seek(&point, Bias::Right, &());
-        let mut tab_point = cursor.start().1 .0;
-        if cursor.item().map_or(false, |t| t.is_isomorphic()) {
-            tab_point += point.0 - cursor.start().0 .0;
-        }
-        TabPoint(tab_point)
-    }
-
-    pub fn to_point(&self, point: WrapPoint, bias: Bias) -> Point {
-        self.tab_snapshot.to_point(self.to_tab_point(point), bias)
-    }
-
-    pub fn make_wrap_point(&self, point: Point, bias: Bias) -> WrapPoint {
-        self.tab_point_to_wrap_point(self.tab_snapshot.make_tab_point(point, bias))
-    }
-
-    pub fn tab_point_to_wrap_point(&self, point: TabPoint) -> WrapPoint {
-        let mut cursor = self.transforms.cursor::<(TabPoint, WrapPoint)>();
-        cursor.seek(&point, Bias::Right, &());
-        WrapPoint(cursor.start().1 .0 + (point.0 - cursor.start().0 .0))
-    }
-
-    pub fn clip_point(&self, mut point: WrapPoint, bias: Bias) -> WrapPoint {
-        if bias == Bias::Left {
-            let mut cursor = self.transforms.cursor::<WrapPoint>();
-            cursor.seek(&point, Bias::Right, &());
-            if cursor.item().map_or(false, |t| !t.is_isomorphic()) {
-                point = *cursor.start();
-                *point.column_mut() -= 1;
-            }
-        }
-
-        self.tab_point_to_wrap_point(self.tab_snapshot.clip_point(self.to_tab_point(point), bias))
-    }
-
-    pub fn prev_row_boundary(&self, mut point: WrapPoint) -> u32 {
-        if self.transforms.is_empty() {
-            return 0;
-        }
-
-        *point.column_mut() = 0;
-
-        let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>();
-        cursor.seek(&point, Bias::Right, &());
-        if cursor.item().is_none() {
-            cursor.prev(&());
-        }
-
-        while let Some(transform) = cursor.item() {
-            if transform.is_isomorphic() && cursor.start().1.column() == 0 {
-                return cmp::min(cursor.end(&()).0.row(), point.row());
-            } else {
-                cursor.prev(&());
-            }
-        }
-
-        unreachable!()
-    }
-
-    pub fn next_row_boundary(&self, mut point: WrapPoint) -> Option<u32> {
-        point.0 += Point::new(1, 0);
-
-        let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>();
-        cursor.seek(&point, Bias::Right, &());
-        while let Some(transform) = cursor.item() {
-            if transform.is_isomorphic() && cursor.start().1.column() == 0 {
-                return Some(cmp::max(cursor.start().0.row(), point.row()));
-            } else {
-                cursor.next(&());
-            }
-        }
-
-        None
-    }
-
-    fn check_invariants(&self) {
-        #[cfg(test)]
-        {
-            assert_eq!(
-                TabPoint::from(self.transforms.summary().input.lines),
-                self.tab_snapshot.max_point()
-            );
-
-            {
-                let mut transforms = self.transforms.cursor::<()>().peekable();
-                while let Some(transform) = transforms.next() {
-                    if let Some(next_transform) = transforms.peek() {
-                        assert!(transform.is_isomorphic() != next_transform.is_isomorphic());
-                    }
-                }
-            }
-
-            let text = language::Rope::from(self.text().as_str());
-            let mut input_buffer_rows = self.tab_snapshot.buffer_rows(0);
-            let mut expected_buffer_rows = Vec::new();
-            let mut prev_tab_row = 0;
-            for display_row in 0..=self.max_point().row() {
-                let tab_point = self.to_tab_point(WrapPoint::new(display_row, 0));
-                if tab_point.row() == prev_tab_row && display_row != 0 {
-                    expected_buffer_rows.push(None);
-                } else {
-                    expected_buffer_rows.push(input_buffer_rows.next().unwrap());
-                }
-
-                prev_tab_row = tab_point.row();
-                assert_eq!(self.line_len(display_row), text.line_len(display_row));
-            }
-
-            for start_display_row in 0..expected_buffer_rows.len() {
-                assert_eq!(
-                    self.buffer_rows(start_display_row as u32)
-                        .collect::<Vec<_>>(),
-                    &expected_buffer_rows[start_display_row..],
-                    "invalid buffer_rows({}..)",
-                    start_display_row
-                );
-            }
-        }
-    }
-}
-
-impl<'a> Iterator for WrapChunks<'a> {
-    type Item = Chunk<'a>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.output_position.row() >= self.max_output_row {
-            return None;
-        }
-
-        let transform = self.transforms.item()?;
-        if let Some(display_text) = transform.display_text {
-            let mut start_ix = 0;
-            let mut end_ix = display_text.len();
-            let mut summary = transform.summary.output.lines;
-
-            if self.output_position > self.transforms.start().0 {
-                // Exclude newline starting prior to the desired row.
-                start_ix = 1;
-                summary.row = 0;
-            } else if self.output_position.row() + 1 >= self.max_output_row {
-                // Exclude soft indentation ending after the desired row.
-                end_ix = 1;
-                summary.column = 0;
-            }
-
-            self.output_position.0 += summary;
-            self.transforms.next(&());
-            return Some(Chunk {
-                text: &display_text[start_ix..end_ix],
-                ..self.input_chunk
-            });
-        }
-
-        if self.input_chunk.text.is_empty() {
-            self.input_chunk = self.input_chunks.next().unwrap();
-        }
-
-        let mut input_len = 0;
-        let transform_end = self.transforms.end(&()).0;
-        for c in self.input_chunk.text.chars() {
-            let char_len = c.len_utf8();
-            input_len += char_len;
-            if c == '\n' {
-                *self.output_position.row_mut() += 1;
-                *self.output_position.column_mut() = 0;
-            } else {
-                *self.output_position.column_mut() += char_len as u32;
-            }
-
-            if self.output_position >= transform_end {
-                self.transforms.next(&());
-                break;
-            }
-        }
-
-        let (prefix, suffix) = self.input_chunk.text.split_at(input_len);
-        self.input_chunk.text = suffix;
-        Some(Chunk {
-            text: prefix,
-            ..self.input_chunk
-        })
-    }
-}
-
-impl<'a> Iterator for WrapBufferRows<'a> {
-    type Item = Option<u32>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.output_row > self.max_output_row {
-            return None;
-        }
-
-        let buffer_row = self.input_buffer_row;
-        let soft_wrapped = self.soft_wrapped;
-
-        self.output_row += 1;
-        self.transforms
-            .seek_forward(&WrapPoint::new(self.output_row, 0), Bias::Left, &());
-        if self.transforms.item().map_or(false, |t| t.is_isomorphic()) {
-            self.input_buffer_row = self.input_buffer_rows.next().unwrap();
-            self.soft_wrapped = false;
-        } else {
-            self.soft_wrapped = true;
-        }
-
-        Some(if soft_wrapped { None } else { buffer_row })
-    }
-}
-
-impl Transform {
-    fn isomorphic(summary: TextSummary) -> Self {
-        #[cfg(test)]
-        assert!(!summary.lines.is_zero());
-
-        Self {
-            summary: TransformSummary {
-                input: summary.clone(),
-                output: summary,
-            },
-            display_text: None,
-        }
-    }
-
-    fn wrap(indent: u32) -> Self {
-        lazy_static! {
-            static ref WRAP_TEXT: String = {
-                let mut wrap_text = String::new();
-                wrap_text.push('\n');
-                wrap_text.extend((0..LineWrapper::MAX_INDENT as usize).map(|_| ' '));
-                wrap_text
-            };
-        }
-
-        Self {
-            summary: TransformSummary {
-                input: TextSummary::default(),
-                output: TextSummary {
-                    lines: Point::new(1, indent),
-                    first_line_chars: 0,
-                    last_line_chars: indent,
-                    longest_row: 1,
-                    longest_row_chars: indent,
-                },
-            },
-            display_text: Some(&WRAP_TEXT[..1 + indent as usize]),
-        }
-    }
-
-    fn is_isomorphic(&self) -> bool {
-        self.display_text.is_none()
-    }
-}
-
-impl sum_tree::Item for Transform {
-    type Summary = TransformSummary;
-
-    fn summary(&self) -> Self::Summary {
-        self.summary.clone()
-    }
-}
-
-fn push_isomorphic(transforms: &mut Vec<Transform>, summary: TextSummary) {
-    if let Some(last_transform) = transforms.last_mut() {
-        if last_transform.is_isomorphic() {
-            last_transform.summary.input += &summary;
-            last_transform.summary.output += &summary;
-            return;
-        }
-    }
-    transforms.push(Transform::isomorphic(summary));
-}
-
-trait SumTreeExt {
-    fn push_or_extend(&mut self, transform: Transform);
-}
-
-impl SumTreeExt for SumTree<Transform> {
-    fn push_or_extend(&mut self, transform: Transform) {
-        let mut transform = Some(transform);
-        self.update_last(
-            |last_transform| {
-                if last_transform.is_isomorphic() && transform.as_ref().unwrap().is_isomorphic() {
-                    let transform = transform.take().unwrap();
-                    last_transform.summary.input += &transform.summary.input;
-                    last_transform.summary.output += &transform.summary.output;
-                }
-            },
-            &(),
-        );
-
-        if let Some(transform) = transform {
-            self.push(transform, &());
-        }
-    }
-}
-
-impl WrapPoint {
-    pub fn new(row: u32, column: u32) -> Self {
-        Self(Point::new(row, column))
-    }
-
-    pub fn row(self) -> u32 {
-        self.0.row
-    }
-
-    pub fn row_mut(&mut self) -> &mut u32 {
-        &mut self.0.row
-    }
-
-    pub fn column(self) -> u32 {
-        self.0.column
-    }
-
-    pub fn column_mut(&mut self) -> &mut u32 {
-        &mut self.0.column
-    }
-}
-
-impl sum_tree::Summary for TransformSummary {
-    type Context = ();
-
-    fn add_summary(&mut self, other: &Self, _: &()) {
-        self.input += &other.input;
-        self.output += &other.output;
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, TransformSummary> for TabPoint {
-    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
-        self.0 += summary.input.lines;
-    }
-}
-
-impl<'a> sum_tree::SeekTarget<'a, TransformSummary, TransformSummary> for TabPoint {
-    fn cmp(&self, cursor_location: &TransformSummary, _: &()) -> std::cmp::Ordering {
-        Ord::cmp(&self.0, &cursor_location.input.lines)
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapPoint {
-    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
-        self.0 += summary.output.lines;
-    }
-}
-
-fn consolidate_wrap_edits(edits: &mut Vec<WrapEdit>) {
-    let mut i = 1;
-    while i < edits.len() {
-        let edit = edits[i].clone();
-        let prev_edit = &mut edits[i - 1];
-        if prev_edit.old.end >= edit.old.start {
-            prev_edit.old.end = edit.old.end;
-            prev_edit.new.end = edit.new.end;
-            edits.remove(i);
-            continue;
-        }
-        i += 1;
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{
-        display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap},
-        MultiBuffer,
-    };
-    use gpui::{font, px, test::observe};
-    use rand::prelude::*;
-    use settings::SettingsStore;
-    use smol::stream::StreamExt;
-    use std::{cmp, env, num::NonZeroU32};
-    use text::Rope;
-    use theme::LoadThemes;
-
-    #[gpui::test(iterations = 100)]
-    async fn test_random_wraps(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
-        // todo!() this test is flaky
-        init_test(cx);
-
-        cx.background_executor.set_block_on_ticks(0..=50);
-        let operations = env::var("OPERATIONS")
-            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
-            .unwrap_or(10);
-
-        let text_system = cx.read(|cx| cx.text_system().clone());
-        let mut wrap_width = if rng.gen_bool(0.1) {
-            None
-        } else {
-            Some(px(rng.gen_range(0.0..=1000.0)))
-        };
-        let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap();
-        let font = font("Helvetica");
-        let _font_id = text_system.font_id(&font).unwrap();
-        let font_size = px(14.0);
-
-        log::info!("Tab size: {}", tab_size);
-        log::info!("Wrap width: {:?}", wrap_width);
-
-        let buffer = cx.update(|cx| {
-            if rng.gen() {
-                MultiBuffer::build_random(&mut rng, cx)
-            } else {
-                let len = rng.gen_range(0..10);
-                let text = util::RandomCharIter::new(&mut rng)
-                    .take(len)
-                    .collect::<String>();
-                MultiBuffer::build_simple(&text, cx)
-            }
-        });
-        let mut buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
-        log::info!("Buffer text: {:?}", buffer_snapshot.text());
-        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
-        log::info!("InlayMap text: {:?}", inlay_snapshot.text());
-        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot.clone());
-        log::info!("FoldMap text: {:?}", fold_snapshot.text());
-        let (mut tab_map, _) = TabMap::new(fold_snapshot.clone(), tab_size);
-        let tabs_snapshot = tab_map.set_max_expansion_column(32);
-        log::info!("TabMap text: {:?}", tabs_snapshot.text());
-
-        let mut line_wrapper = text_system.line_wrapper(font.clone(), font_size).unwrap();
-        let unwrapped_text = tabs_snapshot.text();
-        let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
-
-        let (wrap_map, _) =
-            cx.update(|cx| WrapMap::new(tabs_snapshot.clone(), font, font_size, wrap_width, cx));
-        let mut notifications = observe(&wrap_map, cx);
-
-        if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
-            notifications.next().await.unwrap();
-        }
-
-        let (initial_snapshot, _) = wrap_map.update(cx, |map, cx| {
-            assert!(!map.is_rewrapping());
-            map.sync(tabs_snapshot.clone(), Vec::new(), cx)
-        });
-
-        let actual_text = initial_snapshot.text();
-        assert_eq!(
-            actual_text, expected_text,
-            "unwrapped text is: {:?}",
-            unwrapped_text
-        );
-        log::info!("Wrapped text: {:?}", actual_text);
-
-        let mut next_inlay_id = 0;
-        let mut edits = Vec::new();
-        for _i in 0..operations {
-            log::info!("{} ==============================================", _i);
-
-            let mut buffer_edits = Vec::new();
-            match rng.gen_range(0..=100) {
-                0..=19 => {
-                    wrap_width = if rng.gen_bool(0.2) {
-                        None
-                    } else {
-                        Some(px(rng.gen_range(0.0..=1000.0)))
-                    };
-                    log::info!("Setting wrap width to {:?}", wrap_width);
-                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
-                }
-                20..=39 => {
-                    for (fold_snapshot, fold_edits) in fold_map.randomly_mutate(&mut rng) {
-                        let (tabs_snapshot, tab_edits) =
-                            tab_map.sync(fold_snapshot, fold_edits, tab_size);
-                        let (mut snapshot, wrap_edits) =
-                            wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx));
-                        snapshot.check_invariants();
-                        snapshot.verify_chunks(&mut rng);
-                        edits.push((snapshot, wrap_edits));
-                    }
-                }
-                40..=59 => {
-                    let (inlay_snapshot, inlay_edits) =
-                        inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng);
-                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
-                    let (tabs_snapshot, tab_edits) =
-                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
-                    let (mut snapshot, wrap_edits) =
-                        wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx));
-                    snapshot.check_invariants();
-                    snapshot.verify_chunks(&mut rng);
-                    edits.push((snapshot, wrap_edits));
-                }
-                _ => {
-                    buffer.update(cx, |buffer, cx| {
-                        let subscription = buffer.subscribe();
-                        let edit_count = rng.gen_range(1..=5);
-                        buffer.randomly_mutate(&mut rng, edit_count, cx);
-                        buffer_snapshot = buffer.snapshot(cx);
-                        buffer_edits.extend(subscription.consume());
-                    });
-                }
-            }
-
-            log::info!("Buffer text: {:?}", buffer_snapshot.text());
-            let (inlay_snapshot, inlay_edits) =
-                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
-            log::info!("InlayMap text: {:?}", inlay_snapshot.text());
-            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
-            log::info!("FoldMap text: {:?}", fold_snapshot.text());
-            let (tabs_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
-            log::info!("TabMap text: {:?}", tabs_snapshot.text());
-
-            let unwrapped_text = tabs_snapshot.text();
-            let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
-            let (mut snapshot, wrap_edits) =
-                wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot.clone(), tab_edits, cx));
-            snapshot.check_invariants();
-            snapshot.verify_chunks(&mut rng);
-            edits.push((snapshot, wrap_edits));
-
-            if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) && rng.gen_bool(0.4) {
-                log::info!("Waiting for wrapping to finish");
-                while wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
-                    notifications.next().await.unwrap();
-                }
-                wrap_map.read_with(cx, |map, _| assert!(map.pending_edits.is_empty()));
-            }
-
-            if !wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
-                let (mut wrapped_snapshot, wrap_edits) =
-                    wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, Vec::new(), cx));
-                let actual_text = wrapped_snapshot.text();
-                let actual_longest_row = wrapped_snapshot.longest_row();
-                log::info!("Wrapping finished: {:?}", actual_text);
-                wrapped_snapshot.check_invariants();
-                wrapped_snapshot.verify_chunks(&mut rng);
-                edits.push((wrapped_snapshot.clone(), wrap_edits));
-                assert_eq!(
-                    actual_text, expected_text,
-                    "unwrapped text is: {:?}",
-                    unwrapped_text
-                );
-
-                let mut summary = TextSummary::default();
-                for (ix, item) in wrapped_snapshot
-                    .transforms
-                    .items(&())
-                    .into_iter()
-                    .enumerate()
-                {
-                    summary += &item.summary.output;
-                    log::info!("{} summary: {:?}", ix, item.summary.output,);
-                }
-
-                if tab_size.get() == 1
-                    || !wrapped_snapshot
-                        .tab_snapshot
-                        .fold_snapshot
-                        .text()
-                        .contains('\t')
-                {
-                    let mut expected_longest_rows = Vec::new();
-                    let mut longest_line_len = -1;
-                    for (row, line) in expected_text.split('\n').enumerate() {
-                        let line_char_count = line.chars().count() as isize;
-                        if line_char_count > longest_line_len {
-                            expected_longest_rows.clear();
-                            longest_line_len = line_char_count;
-                        }
-                        if line_char_count >= longest_line_len {
-                            expected_longest_rows.push(row as u32);
-                        }
-                    }
-
-                    assert!(
-                        expected_longest_rows.contains(&actual_longest_row),
-                        "incorrect longest row {}. expected {:?} with length {}",
-                        actual_longest_row,
-                        expected_longest_rows,
-                        longest_line_len,
-                    )
-                }
-            }
-        }
-
-        let mut initial_text = Rope::from(initial_snapshot.text().as_str());
-        for (snapshot, patch) in edits {
-            let snapshot_text = Rope::from(snapshot.text().as_str());
-            for edit in &patch {
-                let old_start = initial_text.point_to_offset(Point::new(edit.new.start, 0));
-                let old_end = initial_text.point_to_offset(cmp::min(
-                    Point::new(edit.new.start + edit.old.len() as u32, 0),
-                    initial_text.max_point(),
-                ));
-                let new_start = snapshot_text.point_to_offset(Point::new(edit.new.start, 0));
-                let new_end = snapshot_text.point_to_offset(cmp::min(
-                    Point::new(edit.new.end, 0),
-                    snapshot_text.max_point(),
-                ));
-                let new_text = snapshot_text
-                    .chunks_in_range(new_start..new_end)
-                    .collect::<String>();
-
-                initial_text.replace(old_start..old_end, &new_text);
-            }
-            assert_eq!(initial_text.to_string(), snapshot_text.to_string());
-        }
-
-        if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
-            log::info!("Waiting for wrapping to finish");
-            while wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
-                notifications.next().await.unwrap();
-            }
-        }
-        wrap_map.read_with(cx, |map, _| assert!(map.pending_edits.is_empty()));
-    }
-
-    fn init_test(cx: &mut gpui::TestAppContext) {
-        cx.update(|cx| {
-            let settings = SettingsStore::test(cx);
-            cx.set_global(settings);
-            theme::init(LoadThemes::JustBase, cx);
-        });
-    }
-
-    fn wrap_text(
-        unwrapped_text: &str,
-        wrap_width: Option<Pixels>,
-        line_wrapper: &mut LineWrapper,
-    ) -> String {
-        if let Some(wrap_width) = wrap_width {
-            let mut wrapped_text = String::new();
-            for (row, line) in unwrapped_text.split('\n').enumerate() {
-                if row > 0 {
-                    wrapped_text.push('\n')
-                }
-
-                let mut prev_ix = 0;
-                for boundary in line_wrapper.wrap_line(line, wrap_width) {
-                    wrapped_text.push_str(&line[prev_ix..boundary.ix]);
-                    wrapped_text.push('\n');
-                    wrapped_text.push_str(&" ".repeat(boundary.next_indent as usize));
-                    prev_ix = boundary.ix;
-                }
-                wrapped_text.push_str(&line[prev_ix..]);
-            }
-            wrapped_text
-        } else {
-            unwrapped_text.to_string()
-        }
-    }
-
-    impl WrapSnapshot {
-        pub fn text(&self) -> String {
-            self.text_chunks(0).collect()
-        }
-
-        pub fn text_chunks(&self, wrap_row: u32) -> impl Iterator<Item = &str> {
-            self.chunks(
-                wrap_row..self.max_point().row() + 1,
-                false,
-                Highlights::default(),
-            )
-            .map(|h| h.text)
-        }
-
-        fn verify_chunks(&mut self, rng: &mut impl Rng) {
-            for _ in 0..5 {
-                let mut end_row = rng.gen_range(0..=self.max_point().row());
-                let start_row = rng.gen_range(0..=end_row);
-                end_row += 1;
-
-                let mut expected_text = self.text_chunks(start_row).collect::<String>();
-                if expected_text.ends_with('\n') {
-                    expected_text.push('\n');
-                }
-                let mut expected_text = expected_text
-                    .lines()
-                    .take((end_row - start_row) as usize)
-                    .collect::<Vec<_>>()
-                    .join("\n");
-                if end_row <= self.max_point().row() {
-                    expected_text.push('\n');
-                }
-
-                let actual_text = self
-                    .chunks(start_row..end_row, true, Highlights::default())
-                    .map(|c| c.text)
-                    .collect::<String>();
-                assert_eq!(
-                    expected_text,
-                    actual_text,
-                    "chunks != highlighted_chunks for rows {:?}",
-                    start_row..end_row
-                );
-            }
-        }
-    }
-}

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

@@ -1,9884 +0,0 @@
-mod blink_manager;
-pub mod display_map;
-mod editor_settings;
-mod element;
-mod inlay_hint_cache;
-
-mod git;
-mod highlight_matching_bracket;
-mod hover_popover;
-pub mod items;
-mod link_go_to_definition;
-mod mouse_context_menu;
-pub mod movement;
-mod persistence;
-mod rust_analyzer_ext;
-pub mod scroll;
-pub mod selections_collection;
-
-#[cfg(test)]
-mod editor_tests;
-#[cfg(any(test, feature = "test-support"))]
-pub mod test;
-use ::git::diff::DiffHunk;
-use aho_corasick::AhoCorasick;
-use anyhow::{anyhow, Context as _, Result};
-use blink_manager::BlinkManager;
-use client::{Client, Collaborator, ParticipantIndex, TelemetrySettings};
-use clock::ReplicaId;
-use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
-use convert_case::{Case, Casing};
-use copilot::Copilot;
-pub use display_map::DisplayPoint;
-use display_map::*;
-pub use editor_settings::EditorSettings;
-pub use element::{
-    Cursor, EditorElement, HighlightedRange, HighlightedRangeLine, LineWithInvisibles,
-};
-use futures::FutureExt;
-use fuzzy::{StringMatch, StringMatchCandidate};
-use git::diff_hunk_to_display;
-use gpui::{
-    actions, 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,
-    ParentElement, Pixels, Render, SharedString, Styled, StyledText, Subscription, Task, TextStyle,
-    UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WhiteSpace, WindowContext,
-};
-use highlight_matching_bracket::refresh_matching_bracket_highlights;
-use hover_popover::{hide_hover, HoverState};
-use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
-pub use items::MAX_TAB_TITLE_LEN;
-use itertools::Itertools;
-pub use language::{char_kind, CharKind};
-use language::{
-    language_settings::{self, all_language_settings, InlayHintSettings},
-    markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel,
-    Completion, CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language,
-    LanguageRegistry, LanguageServerName, OffsetRangeExt, Point, Selection, SelectionGoal,
-    TransactionId,
-};
-
-use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState};
-use lsp::{DiagnosticSeverity, LanguageServerId};
-use mouse_context_menu::MouseContextMenu;
-use movement::TextLayoutDetails;
-use multi_buffer::ToOffsetUtf16;
-pub use multi_buffer::{
-    Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
-    ToPoint,
-};
-use ordered_float::OrderedFloat;
-use parking_lot::RwLock;
-use project::{FormatTrigger, Location, Project, ProjectPath, ProjectTransaction};
-use rand::prelude::*;
-use rpc::proto::{self, *};
-use scroll::{
-    autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
-};
-use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
-use serde::{Deserialize, Serialize};
-use settings::{Settings, SettingsStore};
-use smallvec::SmallVec;
-use snippet::Snippet;
-use std::{
-    any::TypeId,
-    borrow::Cow,
-    cmp::{self, Ordering, Reverse},
-    mem,
-    num::NonZeroU32,
-    ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive},
-    path::Path,
-    sync::Arc,
-    sync::Weak,
-    time::{Duration, Instant},
-};
-pub use sum_tree::Bias;
-use sum_tree::TreeMap;
-use text::{OffsetUtf16, Rope};
-use theme::{ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, ThemeColors, ThemeSettings};
-use ui::{
-    h_stack, ButtonSize, ButtonStyle, Icon, IconButton, ListItem, ListItemSpacing, Popover, Tooltip,
-};
-use ui::{prelude::*, IconSize};
-use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
-use workspace::{searchable::SearchEvent, ItemNavHistory, Pane, SplitDirection, ViewId, Workspace};
-
-const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
-const MAX_LINE_LEN: usize = 1024;
-const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
-const MAX_SELECTION_HISTORY_LEN: usize = 1024;
-const COPILOT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
-pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
-pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
-
-pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
-
-pub fn render_parsed_markdown(
-    element_id: impl Into<ElementId>,
-    parsed: &language::ParsedMarkdown,
-    editor_style: &EditorStyle,
-    workspace: Option<WeakView<Workspace>>,
-    cx: &mut ViewContext<Editor>,
-) -> InteractiveText {
-    let code_span_background_color = cx
-        .theme()
-        .colors()
-        .editor_document_highlight_read_background;
-
-    let highlights = gpui::combine_highlights(
-        parsed.highlights.iter().filter_map(|(range, highlight)| {
-            let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
-            Some((range.clone(), highlight))
-        }),
-        parsed
-            .regions
-            .iter()
-            .zip(&parsed.region_ranges)
-            .filter_map(|(region, range)| {
-                if region.code {
-                    Some((
-                        range.clone(),
-                        HighlightStyle {
-                            background_color: Some(code_span_background_color),
-                            ..Default::default()
-                        },
-                    ))
-                } else {
-                    None
-                }
-            }),
-    );
-
-    let mut links = Vec::new();
-    let mut link_ranges = Vec::new();
-    for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
-        if let Some(link) = region.link.clone() {
-            links.push(link);
-            link_ranges.push(range.clone());
-        }
-    }
-
-    InteractiveText::new(
-        element_id,
-        StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
-    )
-    .on_click(link_ranges, move |clicked_range_ix, cx| {
-        match &links[clicked_range_ix] {
-            markdown::Link::Web { url } => cx.open_url(url),
-            markdown::Link::Path { path } => {
-                if let Some(workspace) = &workspace {
-                    _ = workspace.update(cx, |workspace, cx| {
-                        workspace.open_abs_path(path.clone(), false, cx).detach();
-                    });
-                }
-            }
-        }
-    })
-}
-
-#[derive(PartialEq, Clone, Deserialize, Default)]
-pub struct SelectNext {
-    #[serde(default)]
-    pub replace_newest: bool,
-}
-
-#[derive(PartialEq, Clone, Deserialize, Default)]
-pub struct SelectPrevious {
-    #[serde(default)]
-    pub replace_newest: bool,
-}
-
-#[derive(PartialEq, Clone, Deserialize, Default)]
-pub struct SelectAllMatches {
-    #[serde(default)]
-    pub replace_newest: bool,
-}
-
-#[derive(PartialEq, Clone, Deserialize, Default)]
-pub struct SelectToBeginningOfLine {
-    #[serde(default)]
-    stop_at_soft_wraps: bool,
-}
-
-#[derive(PartialEq, Clone, Deserialize, Default)]
-pub struct MovePageUp {
-    #[serde(default)]
-    center_cursor: bool,
-}
-
-#[derive(PartialEq, Clone, Deserialize, Default)]
-pub struct MovePageDown {
-    #[serde(default)]
-    center_cursor: bool,
-}
-
-#[derive(PartialEq, Clone, Deserialize, Default)]
-pub struct SelectToEndOfLine {
-    #[serde(default)]
-    stop_at_soft_wraps: bool,
-}
-
-#[derive(PartialEq, Clone, Deserialize, Default)]
-pub struct ToggleCodeActions {
-    #[serde(default)]
-    pub deployed_from_indicator: bool,
-}
-
-#[derive(PartialEq, Clone, Deserialize, Default)]
-pub struct ConfirmCompletion {
-    #[serde(default)]
-    pub item_ix: Option<usize>,
-}
-
-#[derive(PartialEq, Clone, Deserialize, Default)]
-pub struct ConfirmCodeAction {
-    #[serde(default)]
-    pub item_ix: Option<usize>,
-}
-
-#[derive(PartialEq, Clone, Deserialize, Default)]
-pub struct ToggleComments {
-    #[serde(default)]
-    pub advance_downwards: bool,
-}
-
-#[derive(PartialEq, Clone, Deserialize, Default)]
-pub struct FoldAt {
-    pub buffer_row: u32,
-}
-
-#[derive(PartialEq, Clone, Deserialize, Default)]
-pub struct UnfoldAt {
-    pub buffer_row: u32,
-}
-
-impl_actions!(
-    editor,
-    [
-        SelectNext,
-        SelectPrevious,
-        SelectAllMatches,
-        SelectToBeginningOfLine,
-        MovePageUp,
-        MovePageDown,
-        SelectToEndOfLine,
-        ToggleCodeActions,
-        ConfirmCompletion,
-        ConfirmCodeAction,
-        ToggleComments,
-        FoldAt,
-        UnfoldAt
-    ]
-);
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum InlayId {
-    Suggestion(usize),
-    Hint(usize),
-}
-
-impl InlayId {
-    fn id(&self) -> usize {
-        match self {
-            Self::Suggestion(id) => *id,
-            Self::Hint(id) => *id,
-        }
-    }
-}
-
-actions!(
-    editor,
-    [
-        AddSelectionAbove,
-        AddSelectionBelow,
-        Backspace,
-        Cancel,
-        ConfirmRename,
-        ContextMenuFirst,
-        ContextMenuLast,
-        ContextMenuNext,
-        ContextMenuPrev,
-        ConvertToKebabCase,
-        ConvertToLowerCamelCase,
-        ConvertToLowerCase,
-        ConvertToSnakeCase,
-        ConvertToTitleCase,
-        ConvertToUpperCamelCase,
-        ConvertToUpperCase,
-        Copy,
-        CopyHighlightJson,
-        CopyPath,
-        CopyRelativePath,
-        Cut,
-        CutToEndOfLine,
-        Delete,
-        DeleteLine,
-        DeleteToBeginningOfLine,
-        DeleteToEndOfLine,
-        DeleteToNextSubwordEnd,
-        DeleteToNextWordEnd,
-        DeleteToPreviousSubwordStart,
-        DeleteToPreviousWordStart,
-        DuplicateLine,
-        ExpandMacroRecursively,
-        FindAllReferences,
-        Fold,
-        FoldSelectedRanges,
-        Format,
-        GoToDefinition,
-        GoToDefinitionSplit,
-        GoToDiagnostic,
-        GoToHunk,
-        GoToPrevDiagnostic,
-        GoToPrevHunk,
-        GoToTypeDefinition,
-        GoToTypeDefinitionSplit,
-        HalfPageDown,
-        HalfPageUp,
-        Hover,
-        Indent,
-        JoinLines,
-        LineDown,
-        LineUp,
-        MoveDown,
-        MoveLeft,
-        MoveLineDown,
-        MoveLineUp,
-        MoveRight,
-        MoveToBeginning,
-        MoveToBeginningOfLine,
-        MoveToEnclosingBracket,
-        MoveToEnd,
-        MoveToEndOfLine,
-        MoveToEndOfParagraph,
-        MoveToNextSubwordEnd,
-        MoveToNextWordEnd,
-        MoveToPreviousSubwordStart,
-        MoveToPreviousWordStart,
-        MoveToStartOfParagraph,
-        MoveUp,
-        Newline,
-        NewlineAbove,
-        NewlineBelow,
-        NextScreen,
-        OpenExcerpts,
-        Outdent,
-        PageDown,
-        PageUp,
-        Paste,
-        Redo,
-        RedoSelection,
-        Rename,
-        RestartLanguageServer,
-        RevealInFinder,
-        ReverseLines,
-        ScrollCursorBottom,
-        ScrollCursorCenter,
-        ScrollCursorTop,
-        SelectAll,
-        SelectDown,
-        SelectLargerSyntaxNode,
-        SelectLeft,
-        SelectLine,
-        SelectRight,
-        SelectSmallerSyntaxNode,
-        SelectToBeginning,
-        SelectToEnd,
-        SelectToEndOfParagraph,
-        SelectToNextSubwordEnd,
-        SelectToNextWordEnd,
-        SelectToPreviousSubwordStart,
-        SelectToPreviousWordStart,
-        SelectToStartOfParagraph,
-        SelectUp,
-        ShowCharacterPalette,
-        ShowCompletions,
-        ShuffleLines,
-        SortLinesCaseInsensitive,
-        SortLinesCaseSensitive,
-        SplitSelectionIntoLines,
-        Tab,
-        TabPrev,
-        ToggleInlayHints,
-        ToggleSoftWrap,
-        Transpose,
-        Undo,
-        UndoSelection,
-        UnfoldLines,
-    ]
-);
-
-enum DocumentHighlightRead {}
-enum DocumentHighlightWrite {}
-enum InputComposition {}
-
-#[derive(Copy, Clone, PartialEq, Eq)]
-pub enum Direction {
-    Prev,
-    Next,
-}
-
-pub fn init_settings(cx: &mut AppContext) {
-    EditorSettings::register(cx);
-}
-
-pub fn init(cx: &mut AppContext) {
-    init_settings(cx);
-
-    workspace::register_project_item::<Editor>(cx);
-    workspace::register_followable_item::<Editor>(cx);
-    workspace::register_deserializable_item::<Editor>(cx);
-    cx.observe_new_views(
-        |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
-            workspace.register_action(Editor::new_file);
-            workspace.register_action(Editor::new_file_in_direction);
-        },
-    )
-    .detach();
-
-    cx.on_action(move |_: &workspace::NewFile, cx| {
-        let app_state = cx.global::<Weak<workspace::AppState>>();
-        if let Some(app_state) = app_state.upgrade() {
-            workspace::open_new(&app_state, cx, |workspace, cx| {
-                Editor::new_file(workspace, &Default::default(), cx)
-            })
-            .detach();
-        }
-    });
-    cx.on_action(move |_: &workspace::NewWindow, cx| {
-        let app_state = cx.global::<Weak<workspace::AppState>>();
-        if let Some(app_state) = app_state.upgrade() {
-            workspace::open_new(&app_state, cx, |workspace, cx| {
-                Editor::new_file(workspace, &Default::default(), cx)
-            })
-            .detach();
-        }
-    });
-}
-
-trait InvalidationRegion {
-    fn ranges(&self) -> &[Range<Anchor>];
-}
-
-#[derive(Clone, Debug, PartialEq)]
-pub enum SelectPhase {
-    Begin {
-        position: DisplayPoint,
-        add: bool,
-        click_count: usize,
-    },
-    BeginColumnar {
-        position: DisplayPoint,
-        goal_column: u32,
-    },
-    Extend {
-        position: DisplayPoint,
-        click_count: usize,
-    },
-    Update {
-        position: DisplayPoint,
-        goal_column: u32,
-        scroll_position: gpui::Point<f32>,
-    },
-    End,
-}
-
-#[derive(Clone, Debug)]
-pub enum SelectMode {
-    Character,
-    Word(Range<Anchor>),
-    Line(Range<Anchor>),
-    All,
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub enum EditorMode {
-    SingleLine,
-    AutoHeight { max_lines: usize },
-    Full,
-}
-
-#[derive(Clone, Debug)]
-pub enum SoftWrap {
-    None,
-    EditorWidth,
-    Column(u32),
-}
-
-#[derive(Clone, Default)]
-pub struct EditorStyle {
-    pub background: Hsla,
-    pub local_player: PlayerColor,
-    pub text: TextStyle,
-    pub scrollbar_width: Pixels,
-    pub syntax: Arc<SyntaxTheme>,
-    pub status: StatusColors,
-    pub inlays_style: HighlightStyle,
-    pub suggestions_style: HighlightStyle,
-}
-
-type CompletionId = usize;
-
-// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
-// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
-
-type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec<Range<Anchor>>);
-type InlayBackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec<InlayHighlight>);
-
-pub struct Editor {
-    handle: WeakView<Self>,
-    focus_handle: FocusHandle,
-    buffer: Model<MultiBuffer>,
-    display_map: Model<DisplayMap>,
-    pub selections: SelectionsCollection,
-    pub scroll_manager: ScrollManager,
-    columnar_selection_tail: Option<Anchor>,
-    add_selections_state: Option<AddSelectionsState>,
-    select_next_state: Option<SelectNextState>,
-    select_prev_state: Option<SelectNextState>,
-    selection_history: SelectionHistory,
-    autoclose_regions: Vec<AutocloseRegion>,
-    snippet_stack: InvalidationStack<SnippetState>,
-    select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
-    ime_transaction: Option<TransactionId>,
-    active_diagnostics: Option<ActiveDiagnosticGroup>,
-    soft_wrap_mode_override: Option<language_settings::SoftWrap>,
-    project: Option<Model<Project>>,
-    collaboration_hub: Option<Box<dyn CollaborationHub>>,
-    blink_manager: Model<BlinkManager>,
-    pub show_local_selections: bool,
-    mode: EditorMode,
-    show_gutter: bool,
-    show_wrap_guides: Option<bool>,
-    placeholder_text: Option<Arc<str>>,
-    highlighted_rows: Option<Range<u32>>,
-    background_highlights: BTreeMap<TypeId, BackgroundHighlight>,
-    inlay_background_highlights: TreeMap<Option<TypeId>, InlayBackgroundHighlight>,
-    nav_history: Option<ItemNavHistory>,
-    context_menu: RwLock<Option<ContextMenu>>,
-    mouse_context_menu: Option<MouseContextMenu>,
-    completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
-    next_completion_id: CompletionId,
-    available_code_actions: Option<(Model<Buffer>, Arc<[CodeAction]>)>,
-    code_actions_task: Option<Task<()>>,
-    document_highlights_task: Option<Task<()>>,
-    pending_rename: Option<RenameState>,
-    searchable: bool,
-    cursor_shape: CursorShape,
-    collapse_matches: bool,
-    autoindent_mode: Option<AutoindentMode>,
-    workspace: Option<(WeakView<Workspace>, i64)>,
-    keymap_context_layers: BTreeMap<TypeId, KeyContext>,
-    input_enabled: bool,
-    read_only: bool,
-    leader_peer_id: Option<PeerId>,
-    remote_id: Option<ViewId>,
-    hover_state: HoverState,
-    gutter_hovered: bool,
-    link_go_to_definition_state: LinkGoToDefinitionState,
-    copilot_state: CopilotState,
-    inlay_hint_cache: InlayHintCache,
-    next_inlay_id: usize,
-    _subscriptions: Vec<Subscription>,
-    pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
-    gutter_width: Pixels,
-    style: Option<EditorStyle>,
-    editor_actions: Vec<Box<dyn Fn(&mut ViewContext<Self>)>>,
-}
-
-pub struct EditorSnapshot {
-    pub mode: EditorMode,
-    pub show_gutter: bool,
-    pub display_snapshot: DisplaySnapshot,
-    pub placeholder_text: Option<Arc<str>>,
-    is_focused: bool,
-    scroll_anchor: ScrollAnchor,
-    ongoing_scroll: OngoingScroll,
-}
-
-pub struct RemoteSelection {
-    pub replica_id: ReplicaId,
-    pub selection: Selection<Anchor>,
-    pub cursor_shape: CursorShape,
-    pub peer_id: PeerId,
-    pub line_mode: bool,
-    pub participant_index: Option<ParticipantIndex>,
-}
-
-#[derive(Clone, Debug)]
-struct SelectionHistoryEntry {
-    selections: Arc<[Selection<Anchor>]>,
-    select_next_state: Option<SelectNextState>,
-    select_prev_state: Option<SelectNextState>,
-    add_selections_state: Option<AddSelectionsState>,
-}
-
-enum SelectionHistoryMode {
-    Normal,
-    Undoing,
-    Redoing,
-}
-
-impl Default for SelectionHistoryMode {
-    fn default() -> Self {
-        Self::Normal
-    }
-}
-
-#[derive(Default)]
-struct SelectionHistory {
-    #[allow(clippy::type_complexity)]
-    selections_by_transaction:
-        HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
-    mode: SelectionHistoryMode,
-    undo_stack: VecDeque<SelectionHistoryEntry>,
-    redo_stack: VecDeque<SelectionHistoryEntry>,
-}
-
-impl SelectionHistory {
-    fn insert_transaction(
-        &mut self,
-        transaction_id: TransactionId,
-        selections: Arc<[Selection<Anchor>]>,
-    ) {
-        self.selections_by_transaction
-            .insert(transaction_id, (selections, None));
-    }
-
-    #[allow(clippy::type_complexity)]
-    fn transaction(
-        &self,
-        transaction_id: TransactionId,
-    ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
-        self.selections_by_transaction.get(&transaction_id)
-    }
-
-    #[allow(clippy::type_complexity)]
-    fn transaction_mut(
-        &mut self,
-        transaction_id: TransactionId,
-    ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
-        self.selections_by_transaction.get_mut(&transaction_id)
-    }
-
-    fn push(&mut self, entry: SelectionHistoryEntry) {
-        if !entry.selections.is_empty() {
-            match self.mode {
-                SelectionHistoryMode::Normal => {
-                    self.push_undo(entry);
-                    self.redo_stack.clear();
-                }
-                SelectionHistoryMode::Undoing => self.push_redo(entry),
-                SelectionHistoryMode::Redoing => self.push_undo(entry),
-            }
-        }
-    }
-
-    fn push_undo(&mut self, entry: SelectionHistoryEntry) {
-        if self
-            .undo_stack
-            .back()
-            .map_or(true, |e| e.selections != entry.selections)
-        {
-            self.undo_stack.push_back(entry);
-            if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
-                self.undo_stack.pop_front();
-            }
-        }
-    }
-
-    fn push_redo(&mut self, entry: SelectionHistoryEntry) {
-        if self
-            .redo_stack
-            .back()
-            .map_or(true, |e| e.selections != entry.selections)
-        {
-            self.redo_stack.push_back(entry);
-            if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
-                self.redo_stack.pop_front();
-            }
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-struct AddSelectionsState {
-    above: bool,
-    stack: Vec<usize>,
-}
-
-#[derive(Clone)]
-struct SelectNextState {
-    query: AhoCorasick,
-    wordwise: bool,
-    done: bool,
-}
-
-impl std::fmt::Debug for SelectNextState {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct(std::any::type_name::<Self>())
-            .field("wordwise", &self.wordwise)
-            .field("done", &self.done)
-            .finish()
-    }
-}
-
-#[derive(Debug)]
-struct AutocloseRegion {
-    selection_id: usize,
-    range: Range<Anchor>,
-    pair: BracketPair,
-}
-
-#[derive(Debug)]
-struct SnippetState {
-    ranges: Vec<Vec<Range<Anchor>>>,
-    active_index: usize,
-}
-
-pub struct RenameState {
-    pub range: Range<Anchor>,
-    pub old_name: Arc<str>,
-    pub editor: View<Editor>,
-    block_id: BlockId,
-}
-
-struct InvalidationStack<T>(Vec<T>);
-
-enum ContextMenu {
-    Completions(CompletionsMenu),
-    CodeActions(CodeActionsMenu),
-}
-
-impl ContextMenu {
-    fn select_first(
-        &mut self,
-        project: Option<&Model<Project>>,
-        cx: &mut ViewContext<Editor>,
-    ) -> bool {
-        if self.visible() {
-            match self {
-                ContextMenu::Completions(menu) => menu.select_first(project, cx),
-                ContextMenu::CodeActions(menu) => menu.select_first(cx),
-            }
-            true
-        } else {
-            false
-        }
-    }
-
-    fn select_prev(
-        &mut self,
-        project: Option<&Model<Project>>,
-        cx: &mut ViewContext<Editor>,
-    ) -> bool {
-        if self.visible() {
-            match self {
-                ContextMenu::Completions(menu) => menu.select_prev(project, cx),
-                ContextMenu::CodeActions(menu) => menu.select_prev(cx),
-            }
-            true
-        } else {
-            false
-        }
-    }
-
-    fn select_next(
-        &mut self,
-        project: Option<&Model<Project>>,
-        cx: &mut ViewContext<Editor>,
-    ) -> bool {
-        if self.visible() {
-            match self {
-                ContextMenu::Completions(menu) => menu.select_next(project, cx),
-                ContextMenu::CodeActions(menu) => menu.select_next(cx),
-            }
-            true
-        } else {
-            false
-        }
-    }
-
-    fn select_last(
-        &mut self,
-        project: Option<&Model<Project>>,
-        cx: &mut ViewContext<Editor>,
-    ) -> bool {
-        if self.visible() {
-            match self {
-                ContextMenu::Completions(menu) => menu.select_last(project, cx),
-                ContextMenu::CodeActions(menu) => menu.select_last(cx),
-            }
-            true
-        } else {
-            false
-        }
-    }
-
-    fn visible(&self) -> bool {
-        match self {
-            ContextMenu::Completions(menu) => menu.visible(),
-            ContextMenu::CodeActions(menu) => menu.visible(),
-        }
-    }
-
-    fn render(
-        &self,
-        cursor_position: DisplayPoint,
-        style: &EditorStyle,
-        max_height: Pixels,
-        workspace: Option<WeakView<Workspace>>,
-        cx: &mut ViewContext<Editor>,
-    ) -> (DisplayPoint, AnyElement) {
-        match self {
-            ContextMenu::Completions(menu) => (
-                cursor_position,
-                menu.render(style, max_height, workspace, cx),
-            ),
-            ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, max_height, cx),
-        }
-    }
-}
-
-#[derive(Clone)]
-struct CompletionsMenu {
-    id: CompletionId,
-    initial_position: Anchor,
-    buffer: Model<Buffer>,
-    completions: Arc<RwLock<Box<[Completion]>>>,
-    match_candidates: Arc<[StringMatchCandidate]>,
-    matches: Arc<[StringMatch]>,
-    selected_item: usize,
-    scroll_handle: UniformListScrollHandle,
-}
-
-impl CompletionsMenu {
-    fn select_first(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
-        self.selected_item = 0;
-        self.scroll_handle.scroll_to_item(self.selected_item);
-        self.attempt_resolve_selected_completion_documentation(project, cx);
-        cx.notify();
-    }
-
-    fn select_prev(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
-        if self.selected_item > 0 {
-            self.selected_item -= 1;
-        } else {
-            self.selected_item = self.matches.len() - 1;
-        }
-        self.scroll_handle.scroll_to_item(self.selected_item);
-        self.attempt_resolve_selected_completion_documentation(project, cx);
-        cx.notify();
-    }
-
-    fn select_next(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
-        if self.selected_item + 1 < self.matches.len() {
-            self.selected_item += 1;
-        } else {
-            self.selected_item = 0;
-        }
-        self.scroll_handle.scroll_to_item(self.selected_item);
-        self.attempt_resolve_selected_completion_documentation(project, cx);
-        cx.notify();
-    }
-
-    fn select_last(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
-        self.selected_item = self.matches.len() - 1;
-        self.scroll_handle.scroll_to_item(self.selected_item);
-        self.attempt_resolve_selected_completion_documentation(project, cx);
-        cx.notify();
-    }
-
-    fn pre_resolve_completion_documentation(
-        &self,
-        editor: &Editor,
-        cx: &mut ViewContext<Editor>,
-    ) -> Option<Task<()>> {
-        let settings = EditorSettings::get_global(cx);
-        if !settings.show_completion_documentation {
-            return None;
-        }
-
-        let Some(project) = editor.project.clone() 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;
-
-                    _ = this.update(&mut cx, |_, cx| cx.notify());
-                }
-            }
-        }))
-    }
-
-    fn attempt_resolve_selected_completion_documentation(
-        &mut self,
-        project: Option<&Model<Project>>,
-        cx: &mut ViewContext<Editor>,
-    ) {
-        let settings = EditorSettings::get_global(cx);
-        if !settings.show_completion_documentation {
-            return;
-        }
-
-        let completion_index = self.matches[self.selected_item].candidate_id;
-        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);
-        }
-    }
-
-    fn visible(&self) -> bool {
-        !self.matches.is_empty()
-    }
-
-    fn render(
-        &self,
-        style: &EditorStyle,
-        max_height: Pixels,
-        workspace: Option<WeakView<Workspace>>,
-        cx: &mut ViewContext<Editor>,
-    ) -> AnyElement {
-        let settings = EditorSettings::get_global(cx);
-        let show_completion_documentation = settings.show_completion_documentation;
-
-        let widest_completion_ix = self
-            .matches
-            .iter()
-            .enumerate()
-            .max_by_key(|(_, mat)| {
-                let completions = self.completions.read();
-                let completion = &completions[mat.candidate_id];
-                let documentation = &completion.documentation;
-
-                let mut len = completion.label.text.chars().count();
-                if let Some(Documentation::SingleLine(text)) = documentation {
-                    if show_completion_documentation {
-                        len += text.chars().count();
-                    }
-                }
-
-                len
-            })
-            .map(|(ix, _)| ix);
-
-        let completions = self.completions.clone();
-        let matches = self.matches.clone();
-        let selected_item = self.selected_item;
-        let style = style.clone();
-
-        let multiline_docs = {
-            let mat = &self.matches[selected_item];
-            let multiline_docs = match &self.completions.read()[mat.candidate_id].documentation {
-                Some(Documentation::MultiLinePlainText(text)) => {
-                    Some(div().child(SharedString::from(text.clone())))
-                }
-                Some(Documentation::MultiLineMarkdown(parsed)) => Some(div().child(
-                    render_parsed_markdown("completions_markdown", parsed, &style, workspace, cx),
-                )),
-                _ => None,
-            };
-            multiline_docs.map(|div| {
-                div.id("multiline_docs")
-                    .max_h(max_height)
-                    .flex_1()
-                    .px_1p5()
-                    .py_1()
-                    .min_w(px(260.))
-                    .max_w(px(640.))
-                    .w(px(500.))
-                    .overflow_y_scroll()
-                    // Prevent a mouse down on documentation from being propagated to the editor,
-                    // because that would move the cursor.
-                    .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
-            })
-        };
-
-        let list = uniform_list(
-            cx.view().clone(),
-            "completions",
-            matches.len(),
-            move |_editor, range, cx| {
-                let start_ix = range.start;
-                let completions_guard = completions.read();
-
-                matches[range]
-                    .iter()
-                    .enumerate()
-                    .map(|(ix, mat)| {
-                        let item_ix = start_ix + ix;
-                        let candidate_id = mat.candidate_id;
-                        let completion = &completions_guard[candidate_id];
-
-                        let documentation = if show_completion_documentation {
-                            &completion.documentation
-                        } else {
-                            &None
-                        };
-
-                        let highlights = gpui::combine_highlights(
-                            mat.ranges().map(|range| (range, FontWeight::BOLD.into())),
-                            styled_runs_for_code_label(&completion.label, &style.syntax).map(
-                                |(range, mut highlight)| {
-                                    // Ignore font weight for syntax highlighting, as we'll use it
-                                    // for fuzzy matches.
-                                    highlight.font_weight = None;
-                                    (range, highlight)
-                                },
-                            ),
-                        );
-                        let completion_label = StyledText::new(completion.label.text.clone())
-                            .with_highlights(&style.text, highlights);
-                        let documentation_label =
-                            if let Some(Documentation::SingleLine(text)) = documentation {
-                                if text.trim().is_empty() {
-                                    None
-                                } else {
-                                    Some(
-                                        h_stack().ml_4().child(
-                                            Label::new(text.clone())
-                                                .size(LabelSize::Small)
-                                                .color(Color::Muted),
-                                        ),
-                                    )
-                                }
-                            } else {
-                                None
-                            };
-
-                        div().min_w(px(220.)).max_w(px(540.)).child(
-                            ListItem::new(mat.candidate_id)
-                                .inset(true)
-                                .spacing(ListItemSpacing::Sparse)
-                                .selected(item_ix == selected_item)
-                                .on_click(cx.listener(move |editor, _event, cx| {
-                                    cx.stop_propagation();
-                                    editor
-                                        .confirm_completion(
-                                            &ConfirmCompletion {
-                                                item_ix: Some(item_ix),
-                                            },
-                                            cx,
-                                        )
-                                        .map(|task| task.detach_and_log_err(cx));
-                                }))
-                                .child(h_stack().overflow_hidden().child(completion_label))
-                                .end_slot::<Div>(documentation_label),
-                        )
-                    })
-                    .collect()
-            },
-        )
-        .max_h(max_height)
-        .track_scroll(self.scroll_handle.clone())
-        .with_width_from_item(widest_completion_ix);
-
-        Popover::new()
-            .child(list)
-            .when_some(multiline_docs, |popover, multiline_docs| {
-                popover.aside(multiline_docs)
-            })
-            .into_any_element()
-    }
-
-    pub async fn filter(&mut self, query: Option<&str>, executor: BackgroundExecutor) {
-        let mut matches = if let Some(query) = query {
-            fuzzy::match_strings(
-                &self.match_candidates,
-                query,
-                query.chars().any(|c| c.is_uppercase()),
-                100,
-                &Default::default(),
-                executor,
-            )
-            .await
-        } else {
-            self.match_candidates
-                .iter()
-                .enumerate()
-                .map(|(candidate_id, candidate)| StringMatch {
-                    candidate_id,
-                    score: Default::default(),
-                    positions: Default::default(),
-                    string: candidate.string.clone(),
-                })
-                .collect()
-        };
-
-        // Remove all candidates where the query's start does not match the start of any word in the candidate
-        if let Some(query) = query {
-            if let Some(query_start) = query.chars().next() {
-                matches.retain(|string_match| {
-                    split_words(&string_match.string).any(|word| {
-                        // Check that the first codepoint of the word as lowercase matches the first
-                        // codepoint of the query as lowercase
-                        word.chars()
-                            .flat_map(|codepoint| codepoint.to_lowercase())
-                            .zip(query_start.to_lowercase())
-                            .all(|(word_cp, query_cp)| word_cp == query_cp)
-                    })
-                });
-            }
-        }
-
-        let completions = self.completions.read();
-        matches.sort_unstable_by_key(|mat| {
-            let completion = &completions[mat.candidate_id];
-            (
-                completion.lsp_completion.sort_text.as_ref(),
-                Reverse(OrderedFloat(mat.score)),
-                completion.sort_key(),
-            )
-        });
-
-        for mat in &mut matches {
-            let completion = &completions[mat.candidate_id];
-            mat.string = completion.label.text.clone();
-            for position in &mut mat.positions {
-                *position += completion.label.filter_range.start;
-            }
-        }
-        drop(completions);
-
-        self.matches = matches.into();
-        self.selected_item = 0;
-    }
-}
-
-#[derive(Clone)]
-struct CodeActionsMenu {
-    actions: Arc<[CodeAction]>,
-    buffer: Model<Buffer>,
-    selected_item: usize,
-    scroll_handle: UniformListScrollHandle,
-    deployed_from_indicator: bool,
-}
-
-impl CodeActionsMenu {
-    fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
-        self.selected_item = 0;
-        self.scroll_handle.scroll_to_item(self.selected_item);
-        cx.notify()
-    }
-
-    fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
-        if self.selected_item > 0 {
-            self.selected_item -= 1;
-        } else {
-            self.selected_item = self.actions.len() - 1;
-        }
-        self.scroll_handle.scroll_to_item(self.selected_item);
-        cx.notify();
-    }
-
-    fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
-        if self.selected_item + 1 < self.actions.len() {
-            self.selected_item += 1;
-        } else {
-            self.selected_item = 0;
-        }
-        self.scroll_handle.scroll_to_item(self.selected_item);
-        cx.notify();
-    }
-
-    fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
-        self.selected_item = self.actions.len() - 1;
-        self.scroll_handle.scroll_to_item(self.selected_item);
-        cx.notify()
-    }
-
-    fn visible(&self) -> bool {
-        !self.actions.is_empty()
-    }
-
-    fn render(
-        &self,
-        mut cursor_position: DisplayPoint,
-        _style: &EditorStyle,
-        max_height: Pixels,
-        cx: &mut ViewContext<Editor>,
-    ) -> (DisplayPoint, AnyElement) {
-        let actions = self.actions.clone();
-        let selected_item = self.selected_item;
-
-        let element = uniform_list(
-            cx.view().clone(),
-            "code_actions_menu",
-            self.actions.len(),
-            move |_this, range, cx| {
-                actions[range.clone()]
-                    .iter()
-                    .enumerate()
-                    .map(|(ix, action)| {
-                        let item_ix = range.start + ix;
-                        let selected = selected_item == item_ix;
-                        let colors = cx.theme().colors();
-                        div()
-                            .px_2()
-                            .text_color(colors.text)
-                            .when(selected, |style| {
-                                style
-                                    .bg(colors.element_active)
-                                    .text_color(colors.text_accent)
-                            })
-                            .hover(|style| {
-                                style
-                                    .bg(colors.element_hover)
-                                    .text_color(colors.text_accent)
-                            })
-                            .on_mouse_down(
-                                MouseButton::Left,
-                                cx.listener(move |editor, _, cx| {
-                                    cx.stop_propagation();
-                                    editor
-                                        .confirm_code_action(
-                                            &ConfirmCodeAction {
-                                                item_ix: Some(item_ix),
-                                            },
-                                            cx,
-                                        )
-                                        .map(|task| task.detach_and_log_err(cx));
-                                }),
-                            )
-                            // TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here.
-                            .child(SharedString::from(action.lsp_action.title.clone()))
-                    })
-                    .collect()
-            },
-        )
-        .elevation_1(cx)
-        .px_2()
-        .py_1()
-        .max_h(max_height)
-        .track_scroll(self.scroll_handle.clone())
-        .with_width_from_item(
-            self.actions
-                .iter()
-                .enumerate()
-                .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
-                .map(|(ix, _)| ix),
-        )
-        .into_any_element();
-
-        if self.deployed_from_indicator {
-            *cursor_position.column_mut() = 0;
-        }
-
-        (cursor_position, element)
-    }
-}
-
-pub struct CopilotState {
-    excerpt_id: Option<ExcerptId>,
-    pending_refresh: Task<Option<()>>,
-    pending_cycling_refresh: Task<Option<()>>,
-    cycled: bool,
-    completions: Vec<copilot::Completion>,
-    active_completion_index: usize,
-    suggestion: Option<Inlay>,
-}
-
-impl Default for CopilotState {
-    fn default() -> Self {
-        Self {
-            excerpt_id: None,
-            pending_cycling_refresh: Task::ready(Some(())),
-            pending_refresh: Task::ready(Some(())),
-            completions: Default::default(),
-            active_completion_index: 0,
-            cycled: false,
-            suggestion: None,
-        }
-    }
-}
-
-impl CopilotState {
-    fn active_completion(&self) -> Option<&copilot::Completion> {
-        self.completions.get(self.active_completion_index)
-    }
-
-    fn text_for_active_completion(
-        &self,
-        cursor: Anchor,
-        buffer: &MultiBufferSnapshot,
-    ) -> Option<&str> {
-        use language::ToOffset as _;
-
-        let completion = self.active_completion()?;
-        let excerpt_id = self.excerpt_id?;
-        let completion_buffer = buffer.buffer_for_excerpt(excerpt_id)?;
-        if excerpt_id != cursor.excerpt_id
-            || !completion.range.start.is_valid(completion_buffer)
-            || !completion.range.end.is_valid(completion_buffer)
-        {
-            return None;
-        }
-
-        let mut completion_range = completion.range.to_offset(&completion_buffer);
-        let prefix_len = Self::common_prefix(
-            completion_buffer.chars_for_range(completion_range.clone()),
-            completion.text.chars(),
-        );
-        completion_range.start += prefix_len;
-        let suffix_len = Self::common_prefix(
-            completion_buffer.reversed_chars_for_range(completion_range.clone()),
-            completion.text[prefix_len..].chars().rev(),
-        );
-        completion_range.end = completion_range.end.saturating_sub(suffix_len);
-
-        if completion_range.is_empty()
-            && completion_range.start == cursor.text_anchor.to_offset(&completion_buffer)
-        {
-            Some(&completion.text[prefix_len..completion.text.len() - suffix_len])
-        } else {
-            None
-        }
-    }
-
-    fn cycle_completions(&mut self, direction: Direction) {
-        match direction {
-            Direction::Prev => {
-                self.active_completion_index = if self.active_completion_index == 0 {
-                    self.completions.len().saturating_sub(1)
-                } else {
-                    self.active_completion_index - 1
-                };
-            }
-            Direction::Next => {
-                if self.completions.len() == 0 {
-                    self.active_completion_index = 0
-                } else {
-                    self.active_completion_index =
-                        (self.active_completion_index + 1) % self.completions.len();
-                }
-            }
-        }
-    }
-
-    fn push_completion(&mut self, new_completion: copilot::Completion) {
-        for completion in &self.completions {
-            if completion.text == new_completion.text && completion.range == new_completion.range {
-                return;
-            }
-        }
-        self.completions.push(new_completion);
-    }
-
-    fn common_prefix<T1: Iterator<Item = char>, T2: Iterator<Item = char>>(a: T1, b: T2) -> usize {
-        a.zip(b)
-            .take_while(|(a, b)| a == b)
-            .map(|(a, _)| a.len_utf8())
-            .sum()
-    }
-}
-
-#[derive(Debug)]
-struct ActiveDiagnosticGroup {
-    primary_range: Range<Anchor>,
-    primary_message: String,
-    blocks: HashMap<BlockId, Diagnostic>,
-    is_valid: bool,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct ClipboardSelection {
-    pub len: usize,
-    pub is_entire_line: bool,
-    pub first_line_indent: u32,
-}
-
-#[derive(Debug)]
-pub struct NavigationData {
-    cursor_anchor: Anchor,
-    cursor_position: Point,
-    scroll_anchor: ScrollAnchor,
-    scroll_top_row: u32,
-}
-
-pub struct EditorCreated(pub View<Editor>);
-
-enum GotoDefinitionKind {
-    Symbol,
-    Type,
-}
-
-#[derive(Debug, Clone)]
-enum InlayHintRefreshReason {
-    Toggle(bool),
-    SettingsChange(InlayHintSettings),
-    NewLinesShown,
-    BufferEdited(HashSet<Arc<Language>>),
-    RefreshRequested,
-    ExcerptsRemoved(Vec<ExcerptId>),
-}
-impl InlayHintRefreshReason {
-    fn description(&self) -> &'static str {
-        match self {
-            Self::Toggle(_) => "toggle",
-            Self::SettingsChange(_) => "settings change",
-            Self::NewLinesShown => "new lines shown",
-            Self::BufferEdited(_) => "buffer edited",
-            Self::RefreshRequested => "refresh requested",
-            Self::ExcerptsRemoved(_) => "excerpts removed",
-        }
-    }
-}
-
-impl Editor {
-    pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
-        let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new()));
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-        Self::new(EditorMode::SingleLine, buffer, None, cx)
-    }
-
-    pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
-        let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new()));
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-        Self::new(EditorMode::Full, buffer, None, cx)
-    }
-
-    pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
-        let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new()));
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-        Self::new(EditorMode::AutoHeight { max_lines }, buffer, None, cx)
-    }
-
-    pub fn for_buffer(
-        buffer: Model<Buffer>,
-        project: Option<Model<Project>>,
-        cx: &mut ViewContext<Self>,
-    ) -> Self {
-        let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-        Self::new(EditorMode::Full, buffer, project, cx)
-    }
-
-    pub fn for_multibuffer(
-        buffer: Model<MultiBuffer>,
-        project: Option<Model<Project>>,
-        cx: &mut ViewContext<Self>,
-    ) -> Self {
-        Self::new(EditorMode::Full, buffer, project, cx)
-    }
-
-    pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
-        let mut clone = Self::new(self.mode, self.buffer.clone(), self.project.clone(), cx);
-        self.display_map.update(cx, |display_map, cx| {
-            let snapshot = display_map.snapshot(cx);
-            clone.display_map.update(cx, |display_map, cx| {
-                display_map.set_state(&snapshot, cx);
-            });
-        });
-        clone.selections.clone_state(&self.selections);
-        clone.scroll_manager.clone_state(&self.scroll_manager);
-        clone.searchable = self.searchable;
-        clone
-    }
-
-    fn new(
-        mode: EditorMode,
-        buffer: Model<MultiBuffer>,
-        project: Option<Model<Project>>,
-        cx: &mut ViewContext<Self>,
-    ) -> Self {
-        let style = cx.text_style();
-        let font_size = style.font_size.to_pixels(cx.rem_size());
-        let display_map = cx.new_model(|cx| {
-            DisplayMap::new(buffer.clone(), style.font(), font_size, None, 2, 1, cx)
-        });
-
-        let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
-
-        let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
-
-        let soft_wrap_mode_override =
-            (mode == EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
-
-        let mut project_subscriptions = Vec::new();
-        if mode == EditorMode::Full {
-            if let Some(project) = project.as_ref() {
-                if buffer.read(cx).is_singleton() {
-                    project_subscriptions.push(cx.observe(project, |_, _, cx| {
-                        cx.emit(EditorEvent::TitleChanged);
-                    }));
-                }
-                project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
-                    if let project::Event::RefreshInlayHints = event {
-                        editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
-                    };
-                }));
-            }
-        }
-
-        let inlay_hint_settings = inlay_hint_settings(
-            selections.newest_anchor().head(),
-            &buffer.read(cx).snapshot(cx),
-            cx,
-        );
-
-        let focus_handle = cx.focus_handle();
-        cx.on_focus(&focus_handle, Self::handle_focus).detach();
-        cx.on_blur(&focus_handle, Self::handle_blur).detach();
-
-        let mut this = Self {
-            handle: cx.view().downgrade(),
-            focus_handle,
-            buffer: buffer.clone(),
-            display_map: display_map.clone(),
-            selections,
-            scroll_manager: ScrollManager::new(),
-            columnar_selection_tail: None,
-            add_selections_state: None,
-            select_next_state: None,
-            select_prev_state: None,
-            selection_history: Default::default(),
-            autoclose_regions: Default::default(),
-            snippet_stack: Default::default(),
-            select_larger_syntax_node_stack: Vec::new(),
-            ime_transaction: Default::default(),
-            active_diagnostics: None,
-            soft_wrap_mode_override,
-            collaboration_hub: project.clone().map(|project| Box::new(project) as _),
-            project,
-            blink_manager: blink_manager.clone(),
-            show_local_selections: true,
-            mode,
-            show_gutter: mode == EditorMode::Full,
-            show_wrap_guides: None,
-            placeholder_text: None,
-            highlighted_rows: None,
-            background_highlights: Default::default(),
-            inlay_background_highlights: Default::default(),
-            nav_history: None,
-            context_menu: RwLock::new(None),
-            mouse_context_menu: None,
-            completion_tasks: Default::default(),
-            next_completion_id: 0,
-            next_inlay_id: 0,
-            available_code_actions: Default::default(),
-            code_actions_task: Default::default(),
-            document_highlights_task: Default::default(),
-            pending_rename: Default::default(),
-            searchable: true,
-            cursor_shape: Default::default(),
-            autoindent_mode: Some(AutoindentMode::EachLine),
-            collapse_matches: false,
-            workspace: None,
-            keymap_context_layers: Default::default(),
-            input_enabled: true,
-            read_only: false,
-            leader_peer_id: None,
-            remote_id: None,
-            hover_state: Default::default(),
-            link_go_to_definition_state: Default::default(),
-            copilot_state: Default::default(),
-            inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
-            gutter_hovered: false,
-            pixel_position_of_newest_cursor: None,
-            gutter_width: Default::default(),
-            style: None,
-            editor_actions: Default::default(),
-            _subscriptions: vec![
-                cx.observe(&buffer, Self::on_buffer_changed),
-                cx.subscribe(&buffer, Self::on_buffer_event),
-                cx.observe(&display_map, Self::on_display_map_changed),
-                cx.observe(&blink_manager, |_, _, cx| cx.notify()),
-                cx.observe_global::<SettingsStore>(Self::settings_changed),
-                cx.observe_window_activation(|editor, cx| {
-                    let active = cx.is_window_active();
-                    editor.blink_manager.update(cx, |blink_manager, cx| {
-                        if active {
-                            blink_manager.enable(cx);
-                        } else {
-                            blink_manager.show_cursor(cx);
-                            blink_manager.disable(cx);
-                        }
-                    });
-                }),
-            ],
-        };
-
-        this._subscriptions.extend(project_subscriptions);
-
-        this.end_selection(cx);
-        this.scroll_manager.show_scrollbar(cx);
-
-        // todo!("use a different mechanism")
-        // let editor_created_event = EditorCreated(cx.handle());
-        // cx.emit_global(editor_created_event);
-
-        if mode == EditorMode::Full {
-            let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
-            cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
-        }
-
-        this.report_editor_event("open", None, cx);
-        this
-    }
-
-    fn key_context(&self, cx: &AppContext) -> KeyContext {
-        let mut key_context = KeyContext::default();
-        key_context.add("Editor");
-        let mode = match self.mode {
-            EditorMode::SingleLine => "single_line",
-            EditorMode::AutoHeight { .. } => "auto_height",
-            EditorMode::Full => "full",
-        };
-        key_context.set("mode", mode);
-        if self.pending_rename.is_some() {
-            key_context.add("renaming");
-        }
-        if self.context_menu_visible() {
-            match self.context_menu.read().as_ref() {
-                Some(ContextMenu::Completions(_)) => {
-                    key_context.add("menu");
-                    key_context.add("showing_completions")
-                }
-                Some(ContextMenu::CodeActions(_)) => {
-                    key_context.add("menu");
-                    key_context.add("showing_code_actions")
-                }
-                None => {}
-            }
-        }
-
-        for layer in self.keymap_context_layers.values() {
-            key_context.extend(layer);
-        }
-
-        if let Some(extension) = self
-            .buffer
-            .read(cx)
-            .as_singleton()
-            .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
-        {
-            key_context.set("extension", extension.to_string());
-        }
-
-        key_context
-    }
-
-    pub fn new_file(
-        workspace: &mut Workspace,
-        _: &workspace::NewFile,
-        cx: &mut ViewContext<Workspace>,
-    ) {
-        let project = workspace.project().clone();
-        if project.read(cx).is_remote() {
-            cx.propagate();
-        } else if let Some(buffer) = project
-            .update(cx, |project, cx| project.create_buffer("", None, cx))
-            .log_err()
-        {
-            workspace.add_item(
-                Box::new(cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
-                cx,
-            );
-        }
-    }
-
-    pub fn new_file_in_direction(
-        workspace: &mut Workspace,
-        action: &workspace::NewFileInDirection,
-        cx: &mut ViewContext<Workspace>,
-    ) {
-        let project = workspace.project().clone();
-        if project.read(cx).is_remote() {
-            cx.propagate();
-        } else if let Some(buffer) = project
-            .update(cx, |project, cx| project.create_buffer("", None, cx))
-            .log_err()
-        {
-            workspace.split_item(
-                action.0,
-                Box::new(cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
-                cx,
-            );
-        }
-    }
-
-    pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
-        self.buffer.read(cx).replica_id()
-    }
-
-    pub fn leader_peer_id(&self) -> Option<PeerId> {
-        self.leader_peer_id
-    }
-
-    pub fn buffer(&self) -> &Model<MultiBuffer> {
-        &self.buffer
-    }
-
-    pub fn workspace(&self) -> Option<View<Workspace>> {
-        self.workspace.as_ref()?.0.upgrade()
-    }
-
-    pub fn pane(&self, cx: &AppContext) -> Option<View<Pane>> {
-        self.workspace()?.read(cx).pane_for(&self.handle.upgrade()?)
-    }
-
-    pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
-        self.buffer().read(cx).title(cx)
-    }
-
-    pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
-        EditorSnapshot {
-            mode: self.mode,
-            show_gutter: self.show_gutter,
-            display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
-            scroll_anchor: self.scroll_manager.anchor(),
-            ongoing_scroll: self.scroll_manager.ongoing_scroll(),
-            placeholder_text: self.placeholder_text.clone(),
-            is_focused: self.focus_handle.is_focused(cx),
-        }
-    }
-
-    //     pub fn language_at<'a, T: ToOffset>(
-    //         &self,
-    //         point: T,
-    //         cx: &'a AppContext,
-    //     ) -> Option<Arc<Language>> {
-    //         self.buffer.read(cx).language_at(point, cx)
-    //     }
-
-    //     pub fn file_at<'a, T: ToOffset>(&self, point: T, cx: &'a AppContext) -> Option<Arc<dyn File>> {
-    //         self.buffer.read(cx).read(cx).file_at(point).cloned()
-    //     }
-
-    pub fn active_excerpt(
-        &self,
-        cx: &AppContext,
-    ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
-        self.buffer
-            .read(cx)
-            .excerpt_containing(self.selections.newest_anchor().head(), cx)
-    }
-
-    //     pub fn style(&self, cx: &AppContext) -> EditorStyle {
-    //         build_style(
-    //             settings::get::<ThemeSettings>(cx),
-    //             self.get_field_editor_theme.as_deref(),
-    //             self.override_text_style.as_deref(),
-    //             cx,
-    //         )
-    //     }
-
-    pub fn mode(&self) -> EditorMode {
-        self.mode
-    }
-
-    pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
-        self.collaboration_hub.as_deref()
-    }
-
-    pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
-        self.collaboration_hub = Some(hub);
-    }
-
-    pub fn placeholder_text(&self) -> Option<&str> {
-        self.placeholder_text.as_deref()
-    }
-
-    pub fn set_placeholder_text(
-        &mut self,
-        placeholder_text: impl Into<Arc<str>>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        let placeholder_text = Some(placeholder_text.into());
-        if self.placeholder_text != placeholder_text {
-            self.placeholder_text = placeholder_text;
-            cx.notify();
-        }
-    }
-
-    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
-        self.cursor_shape = cursor_shape;
-        cx.notify();
-    }
-
-    pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
-        self.collapse_matches = collapse_matches;
-    }
-
-    pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
-        if self.collapse_matches {
-            return range.start..range.start;
-        }
-        range.clone()
-    }
-
-    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
-        if self.display_map.read(cx).clip_at_line_ends != clip {
-            self.display_map
-                .update(cx, |map, _| map.clip_at_line_ends = clip);
-        }
-    }
-
-    pub fn set_keymap_context_layer<Tag: 'static>(
-        &mut self,
-        context: KeyContext,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.keymap_context_layers
-            .insert(TypeId::of::<Tag>(), context);
-        cx.notify();
-    }
-
-    pub fn remove_keymap_context_layer<Tag: 'static>(&mut self, cx: &mut ViewContext<Self>) {
-        self.keymap_context_layers.remove(&TypeId::of::<Tag>());
-        cx.notify();
-    }
-
-    pub fn set_input_enabled(&mut self, input_enabled: bool) {
-        self.input_enabled = input_enabled;
-    }
-
-    pub fn set_autoindent(&mut self, autoindent: bool) {
-        if autoindent {
-            self.autoindent_mode = Some(AutoindentMode::EachLine);
-        } else {
-            self.autoindent_mode = None;
-        }
-    }
-
-    pub fn read_only(&self) -> bool {
-        self.read_only
-    }
-
-    pub fn set_read_only(&mut self, read_only: bool) {
-        self.read_only = read_only;
-    }
-
-    fn selections_did_change(
-        &mut self,
-        local: bool,
-        old_cursor_position: &Anchor,
-        cx: &mut ViewContext<Self>,
-    ) {
-        if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
-            self.buffer.update(cx, |buffer, cx| {
-                buffer.set_active_selections(
-                    &self.selections.disjoint_anchors(),
-                    self.selections.line_mode,
-                    self.cursor_shape,
-                    cx,
-                )
-            });
-        }
-
-        let display_map = self
-            .display_map
-            .update(cx, |display_map, cx| display_map.snapshot(cx));
-        let buffer = &display_map.buffer_snapshot;
-        self.add_selections_state = None;
-        self.select_next_state = None;
-        self.select_prev_state = None;
-        self.select_larger_syntax_node_stack.clear();
-        self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
-        self.snippet_stack
-            .invalidate(&self.selections.disjoint_anchors(), buffer);
-        self.take_rename(false, cx);
-
-        let new_cursor_position = self.selections.newest_anchor().head();
-
-        self.push_to_nav_history(
-            old_cursor_position.clone(),
-            Some(new_cursor_position.to_point(buffer)),
-            cx,
-        );
-
-        if local {
-            let new_cursor_position = self.selections.newest_anchor().head();
-            let mut context_menu = self.context_menu.write();
-            let completion_menu = match context_menu.as_ref() {
-                Some(ContextMenu::Completions(menu)) => Some(menu),
-
-                _ => {
-                    *context_menu = None;
-                    None
-                }
-            };
-
-            if let Some(completion_menu) = completion_menu {
-                let cursor_position = new_cursor_position.to_offset(buffer);
-                let (word_range, kind) =
-                    buffer.surrounding_word(completion_menu.initial_position.clone());
-                if kind == Some(CharKind::Word)
-                    && word_range.to_inclusive().contains(&cursor_position)
-                {
-                    let mut completion_menu = completion_menu.clone();
-                    drop(context_menu);
-
-                    let query = Self::completion_query(buffer, cursor_position);
-                    cx.spawn(move |this, mut cx| async move {
-                        completion_menu
-                            .filter(query.as_deref(), cx.background_executor().clone())
-                            .await;
-
-                        this.update(&mut cx, |this, cx| {
-                            let mut context_menu = this.context_menu.write();
-                            let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else {
-                                return;
-                            };
-
-                            if menu.id > completion_menu.id {
-                                return;
-                            }
-
-                            *context_menu = Some(ContextMenu::Completions(completion_menu));
-                            drop(context_menu);
-                            cx.notify();
-                        })
-                    })
-                    .detach();
-
-                    self.show_completions(&ShowCompletions, cx);
-                } else {
-                    drop(context_menu);
-                    self.hide_context_menu(cx);
-                }
-            } else {
-                drop(context_menu);
-            }
-
-            hide_hover(self, cx);
-
-            if old_cursor_position.to_display_point(&display_map).row()
-                != new_cursor_position.to_display_point(&display_map).row()
-            {
-                self.available_code_actions.take();
-            }
-            self.refresh_code_actions(cx);
-            self.refresh_document_highlights(cx);
-            refresh_matching_bracket_highlights(self, cx);
-            self.discard_copilot_suggestion(cx);
-        }
-
-        self.blink_manager.update(cx, BlinkManager::pause_blinking);
-        cx.emit(EditorEvent::SelectionsChanged { local });
-
-        if self.selections.disjoint_anchors().len() == 1 {
-            cx.emit(SearchEvent::ActiveMatchChanged)
-        }
-
-        cx.notify();
-    }
-
-    pub fn change_selections<R>(
-        &mut self,
-        autoscroll: Option<Autoscroll>,
-        cx: &mut ViewContext<Self>,
-        change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
-    ) -> R {
-        let old_cursor_position = self.selections.newest_anchor().head();
-        self.push_to_selection_history();
-
-        let (changed, result) = self.selections.change_with(cx, change);
-
-        if changed {
-            if let Some(autoscroll) = autoscroll {
-                self.request_autoscroll(autoscroll, cx);
-            }
-            self.selections_did_change(true, &old_cursor_position, cx);
-        }
-
-        result
-    }
-
-    pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
-    where
-        I: IntoIterator<Item = (Range<S>, T)>,
-        S: ToOffset,
-        T: Into<Arc<str>>,
-    {
-        if self.read_only {
-            return;
-        }
-
-        self.buffer
-            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
-    }
-
-    pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
-    where
-        I: IntoIterator<Item = (Range<S>, T)>,
-        S: ToOffset,
-        T: Into<Arc<str>>,
-    {
-        if self.read_only {
-            return;
-        }
-
-        self.buffer.update(cx, |buffer, cx| {
-            buffer.edit(edits, self.autoindent_mode.clone(), cx)
-        });
-    }
-
-    pub fn edit_with_block_indent<I, S, T>(
-        &mut self,
-        edits: I,
-        original_indent_columns: Vec<u32>,
-        cx: &mut ViewContext<Self>,
-    ) where
-        I: IntoIterator<Item = (Range<S>, T)>,
-        S: ToOffset,
-        T: Into<Arc<str>>,
-    {
-        if self.read_only {
-            return;
-        }
-
-        self.buffer.update(cx, |buffer, cx| {
-            buffer.edit(
-                edits,
-                Some(AutoindentMode::Block {
-                    original_indent_columns,
-                }),
-                cx,
-            )
-        });
-    }
-
-    fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
-        self.hide_context_menu(cx);
-
-        match phase {
-            SelectPhase::Begin {
-                position,
-                add,
-                click_count,
-            } => self.begin_selection(position, add, click_count, cx),
-            SelectPhase::BeginColumnar {
-                position,
-                goal_column,
-            } => self.begin_columnar_selection(position, goal_column, cx),
-            SelectPhase::Extend {
-                position,
-                click_count,
-            } => self.extend_selection(position, click_count, cx),
-            SelectPhase::Update {
-                position,
-                goal_column,
-                scroll_position,
-            } => self.update_selection(position, goal_column, scroll_position, cx),
-            SelectPhase::End => self.end_selection(cx),
-        }
-    }
-
-    fn extend_selection(
-        &mut self,
-        position: DisplayPoint,
-        click_count: usize,
-        cx: &mut ViewContext<Self>,
-    ) {
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let tail = self.selections.newest::<usize>(cx).tail();
-        self.begin_selection(position, false, click_count, cx);
-
-        let position = position.to_offset(&display_map, Bias::Left);
-        let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
-
-        let mut pending_selection = self
-            .selections
-            .pending_anchor()
-            .expect("extend_selection not called with pending selection");
-        if position >= tail {
-            pending_selection.start = tail_anchor;
-        } else {
-            pending_selection.end = tail_anchor;
-            pending_selection.reversed = true;
-        }
-
-        let mut pending_mode = self.selections.pending_mode().unwrap();
-        match &mut pending_mode {
-            SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
-            _ => {}
-        }
-
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.set_pending(pending_selection, pending_mode)
-        });
-    }
-
-    fn begin_selection(
-        &mut self,
-        position: DisplayPoint,
-        add: bool,
-        click_count: usize,
-        cx: &mut ViewContext<Self>,
-    ) {
-        if !self.focus_handle.is_focused(cx) {
-            cx.focus(&self.focus_handle);
-        }
-
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let buffer = &display_map.buffer_snapshot;
-        let newest_selection = self.selections.newest_anchor().clone();
-        let position = display_map.clip_point(position, Bias::Left);
-
-        let start;
-        let end;
-        let mode;
-        let auto_scroll;
-        match click_count {
-            1 => {
-                start = buffer.anchor_before(position.to_point(&display_map));
-                end = start.clone();
-                mode = SelectMode::Character;
-                auto_scroll = true;
-            }
-            2 => {
-                let range = movement::surrounding_word(&display_map, position);
-                start = buffer.anchor_before(range.start.to_point(&display_map));
-                end = buffer.anchor_before(range.end.to_point(&display_map));
-                mode = SelectMode::Word(start.clone()..end.clone());
-                auto_scroll = true;
-            }
-            3 => {
-                let position = display_map
-                    .clip_point(position, Bias::Left)
-                    .to_point(&display_map);
-                let line_start = display_map.prev_line_boundary(position).0;
-                let next_line_start = buffer.clip_point(
-                    display_map.next_line_boundary(position).0 + Point::new(1, 0),
-                    Bias::Left,
-                );
-                start = buffer.anchor_before(line_start);
-                end = buffer.anchor_before(next_line_start);
-                mode = SelectMode::Line(start.clone()..end.clone());
-                auto_scroll = true;
-            }
-            _ => {
-                start = buffer.anchor_before(0);
-                end = buffer.anchor_before(buffer.len());
-                mode = SelectMode::All;
-                auto_scroll = false;
-            }
-        }
-
-        self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
-            if !add {
-                s.clear_disjoint();
-            } else if click_count > 1 {
-                s.delete(newest_selection.id)
-            }
-
-            s.set_pending_anchor_range(start..end, mode);
-        });
-    }
-
-    fn begin_columnar_selection(
-        &mut self,
-        position: DisplayPoint,
-        goal_column: u32,
-        cx: &mut ViewContext<Self>,
-    ) {
-        if !self.focus_handle.is_focused(cx) {
-            cx.focus(&self.focus_handle);
-        }
-
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let tail = self.selections.newest::<Point>(cx).tail();
-        self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
-
-        self.select_columns(
-            tail.to_display_point(&display_map),
-            position,
-            goal_column,
-            &display_map,
-            cx,
-        );
-    }
-
-    fn update_selection(
-        &mut self,
-        position: DisplayPoint,
-        goal_column: u32,
-        scroll_position: gpui::Point<f32>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-
-        if let Some(tail) = self.columnar_selection_tail.as_ref() {
-            let tail = tail.to_display_point(&display_map);
-            self.select_columns(tail, position, goal_column, &display_map, cx);
-        } else if let Some(mut pending) = self.selections.pending_anchor() {
-            let buffer = self.buffer.read(cx).snapshot(cx);
-            let head;
-            let tail;
-            let mode = self.selections.pending_mode().unwrap();
-            match &mode {
-                SelectMode::Character => {
-                    head = position.to_point(&display_map);
-                    tail = pending.tail().to_point(&buffer);
-                }
-                SelectMode::Word(original_range) => {
-                    let original_display_range = original_range.start.to_display_point(&display_map)
-                        ..original_range.end.to_display_point(&display_map);
-                    let original_buffer_range = original_display_range.start.to_point(&display_map)
-                        ..original_display_range.end.to_point(&display_map);
-                    if movement::is_inside_word(&display_map, position)
-                        || original_display_range.contains(&position)
-                    {
-                        let word_range = movement::surrounding_word(&display_map, position);
-                        if word_range.start < original_display_range.start {
-                            head = word_range.start.to_point(&display_map);
-                        } else {
-                            head = word_range.end.to_point(&display_map);
-                        }
-                    } else {
-                        head = position.to_point(&display_map);
-                    }
-
-                    if head <= original_buffer_range.start {
-                        tail = original_buffer_range.end;
-                    } else {
-                        tail = original_buffer_range.start;
-                    }
-                }
-                SelectMode::Line(original_range) => {
-                    let original_range = original_range.to_point(&display_map.buffer_snapshot);
-
-                    let position = display_map
-                        .clip_point(position, Bias::Left)
-                        .to_point(&display_map);
-                    let line_start = display_map.prev_line_boundary(position).0;
-                    let next_line_start = buffer.clip_point(
-                        display_map.next_line_boundary(position).0 + Point::new(1, 0),
-                        Bias::Left,
-                    );
-
-                    if line_start < original_range.start {
-                        head = line_start
-                    } else {
-                        head = next_line_start
-                    }
-
-                    if head <= original_range.start {
-                        tail = original_range.end;
-                    } else {
-                        tail = original_range.start;
-                    }
-                }
-                SelectMode::All => {
-                    return;
-                }
-            };
-
-            if head < tail {
-                pending.start = buffer.anchor_before(head);
-                pending.end = buffer.anchor_before(tail);
-                pending.reversed = true;
-            } else {
-                pending.start = buffer.anchor_before(tail);
-                pending.end = buffer.anchor_before(head);
-                pending.reversed = false;
-            }
-
-            self.change_selections(None, cx, |s| {
-                s.set_pending(pending, mode);
-            });
-        } else {
-            log::error!("update_selection dispatched with no pending selection");
-            return;
-        }
-
-        self.set_scroll_position(scroll_position, cx);
-        cx.notify();
-    }
-
-    fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
-        self.columnar_selection_tail.take();
-        if self.selections.pending_anchor().is_some() {
-            let selections = self.selections.all::<usize>(cx);
-            self.change_selections(None, cx, |s| {
-                s.select(selections);
-                s.clear_pending();
-            });
-        }
-    }
-
-    fn select_columns(
-        &mut self,
-        tail: DisplayPoint,
-        head: DisplayPoint,
-        goal_column: u32,
-        display_map: &DisplaySnapshot,
-        cx: &mut ViewContext<Self>,
-    ) {
-        let start_row = cmp::min(tail.row(), head.row());
-        let end_row = cmp::max(tail.row(), head.row());
-        let start_column = cmp::min(tail.column(), goal_column);
-        let end_column = cmp::max(tail.column(), goal_column);
-        let reversed = start_column < tail.column();
-
-        let selection_ranges = (start_row..=end_row)
-            .filter_map(|row| {
-                if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
-                    let start = display_map
-                        .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
-                        .to_point(display_map);
-                    let end = display_map
-                        .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
-                        .to_point(display_map);
-                    if reversed {
-                        Some(end..start)
-                    } else {
-                        Some(start..end)
-                    }
-                } else {
-                    None
-                }
-            })
-            .collect::<Vec<_>>();
-
-        self.change_selections(None, cx, |s| {
-            s.select_ranges(selection_ranges);
-        });
-        cx.notify();
-    }
-
-    pub fn has_pending_nonempty_selection(&self) -> bool {
-        let pending_nonempty_selection = match self.selections.pending_anchor() {
-            Some(Selection { start, end, .. }) => start != end,
-            None => false,
-        };
-        pending_nonempty_selection || self.columnar_selection_tail.is_some()
-    }
-
-    pub fn has_pending_selection(&self) -> bool {
-        self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
-    }
-
-    pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
-        if self.take_rename(false, cx).is_some() {
-            return;
-        }
-
-        if hide_hover(self, cx) {
-            return;
-        }
-
-        if self.hide_context_menu(cx).is_some() {
-            return;
-        }
-
-        if self.discard_copilot_suggestion(cx) {
-            return;
-        }
-
-        if self.snippet_stack.pop().is_some() {
-            return;
-        }
-
-        if self.mode == EditorMode::Full {
-            if self.active_diagnostics.is_some() {
-                self.dismiss_diagnostics(cx);
-                return;
-            }
-
-            if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
-                return;
-            }
-        }
-
-        cx.propagate();
-    }
-
-    pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
-        let text: Arc<str> = text.into();
-
-        if self.read_only {
-            return;
-        }
-
-        let selections = self.selections.all_adjusted(cx);
-        let mut brace_inserted = false;
-        let mut edits = Vec::new();
-        let mut new_selections = Vec::with_capacity(selections.len());
-        let mut new_autoclose_regions = Vec::new();
-        let snapshot = self.buffer.read(cx).read(cx);
-
-        for (selection, autoclose_region) in
-            self.selections_with_autoclose_regions(selections, &snapshot)
-        {
-            if let Some(scope) = snapshot.language_scope_at(selection.head()) {
-                // Determine if the inserted text matches the opening or closing
-                // bracket of any of this language's bracket pairs.
-                let mut bracket_pair = None;
-                let mut is_bracket_pair_start = false;
-                if !text.is_empty() {
-                    // `text` can be empty when an user is using IME (e.g. Chinese Wubi Simplified)
-                    //  and they are removing the character that triggered IME popup.
-                    for (pair, enabled) in scope.brackets() {
-                        if enabled && pair.close && pair.start.ends_with(text.as_ref()) {
-                            bracket_pair = Some(pair.clone());
-                            is_bracket_pair_start = true;
-                            break;
-                        } else if pair.end.as_str() == text.as_ref() {
-                            bracket_pair = Some(pair.clone());
-                            break;
-                        }
-                    }
-                }
-
-                if let Some(bracket_pair) = bracket_pair {
-                    if selection.is_empty() {
-                        if is_bracket_pair_start {
-                            let prefix_len = bracket_pair.start.len() - text.len();
-
-                            // If the inserted text is a suffix of an opening bracket and the
-                            // selection is preceded by the rest of the opening bracket, then
-                            // insert the closing bracket.
-                            let following_text_allows_autoclose = snapshot
-                                .chars_at(selection.start)
-                                .next()
-                                .map_or(true, |c| scope.should_autoclose_before(c));
-                            let preceding_text_matches_prefix = prefix_len == 0
-                                || (selection.start.column >= (prefix_len as u32)
-                                    && snapshot.contains_str_at(
-                                        Point::new(
-                                            selection.start.row,
-                                            selection.start.column - (prefix_len as u32),
-                                        ),
-                                        &bracket_pair.start[..prefix_len],
-                                    ));
-                            if 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((
-                                    anchor,
-                                    text.len(),
-                                    selection.id,
-                                    bracket_pair.clone(),
-                                ));
-                                edits.push((
-                                    selection.range(),
-                                    format!("{}{}", text, bracket_pair.end).into(),
-                                ));
-                                brace_inserted = true;
-                                continue;
-                            }
-                        }
-
-                        if let Some(region) = autoclose_region {
-                            // If the selection is followed by an auto-inserted closing bracket,
-                            // then don't insert that closing bracket again; just move the selection
-                            // past the closing bracket.
-                            let should_skip = selection.end == region.range.end.to_point(&snapshot)
-                                && text.as_ref() == region.pair.end.as_str();
-                            if should_skip {
-                                let anchor = snapshot.anchor_after(selection.end);
-                                new_selections
-                                    .push((selection.map(|_| anchor), region.pair.end.len()));
-                                continue;
-                            }
-                        }
-                    }
-                    // If an opening bracket is 1 character long and is typed while
-                    // text is selected, then surround that text with the bracket pair.
-                    else if is_bracket_pair_start && bracket_pair.start.chars().count() == 1 {
-                        edits.push((selection.start..selection.start, text.clone()));
-                        edits.push((
-                            selection.end..selection.end,
-                            bracket_pair.end.as_str().into(),
-                        ));
-                        brace_inserted = true;
-                        new_selections.push((
-                            Selection {
-                                id: selection.id,
-                                start: snapshot.anchor_after(selection.start),
-                                end: snapshot.anchor_before(selection.end),
-                                reversed: selection.reversed,
-                                goal: selection.goal,
-                            },
-                            0,
-                        ));
-                        continue;
-                    }
-                }
-            }
-
-            // If not handling any auto-close operation, then just replace the selected
-            // text with the given input and move the selection to the end of the
-            // newly inserted text.
-            let anchor = snapshot.anchor_after(selection.end);
-            new_selections.push((selection.map(|_| anchor), 0));
-            edits.push((selection.start..selection.end, text.clone()));
-        }
-
-        drop(snapshot);
-        self.transact(cx, |this, cx| {
-            this.buffer.update(cx, |buffer, cx| {
-                buffer.edit(edits, this.autoindent_mode.clone(), cx);
-            });
-
-            let new_anchor_selections = new_selections.iter().map(|e| &e.0);
-            let new_selection_deltas = new_selections.iter().map(|e| e.1);
-            let snapshot = this.buffer.read(cx).read(cx);
-            let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
-                .zip(new_selection_deltas)
-                .map(|(selection, delta)| Selection {
-                    id: selection.id,
-                    start: selection.start + delta,
-                    end: selection.end + delta,
-                    reversed: selection.reversed,
-                    goal: SelectionGoal::None,
-                })
-                .collect::<Vec<_>>();
-
-            let mut i = 0;
-            for (position, delta, selection_id, pair) in new_autoclose_regions {
-                let position = position.to_offset(&snapshot) + delta;
-                let start = snapshot.anchor_before(position);
-                let end = snapshot.anchor_after(position);
-                while let Some(existing_state) = this.autoclose_regions.get(i) {
-                    match existing_state.range.start.cmp(&start, &snapshot) {
-                        Ordering::Less => i += 1,
-                        Ordering::Greater => break,
-                        Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
-                            Ordering::Less => i += 1,
-                            Ordering::Equal => break,
-                            Ordering::Greater => break,
-                        },
-                    }
-                }
-                this.autoclose_regions.insert(
-                    i,
-                    AutocloseRegion {
-                        selection_id,
-                        range: start..end,
-                        pair,
-                    },
-                );
-            }
-
-            drop(snapshot);
-            let had_active_copilot_suggestion = this.has_active_copilot_suggestion(cx);
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
-
-            if !brace_inserted && EditorSettings::get_global(cx).use_on_type_format {
-                if let Some(on_type_format_task) =
-                    this.trigger_on_type_formatting(text.to_string(), cx)
-                {
-                    on_type_format_task.detach_and_log_err(cx);
-                }
-            }
-
-            if had_active_copilot_suggestion {
-                this.refresh_copilot_suggestions(true, cx);
-                if !this.has_active_copilot_suggestion(cx) {
-                    this.trigger_completion_on_input(&text, cx);
-                }
-            } else {
-                this.trigger_completion_on_input(&text, cx);
-                this.refresh_copilot_suggestions(true, cx);
-            }
-        });
-    }
-
-    pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
-        self.transact(cx, |this, cx| {
-            let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
-                let selections = this.selections.all::<usize>(cx);
-                let multi_buffer = this.buffer.read(cx);
-                let buffer = multi_buffer.snapshot(cx);
-                selections
-                    .iter()
-                    .map(|selection| {
-                        let start_point = selection.start.to_point(&buffer);
-                        let mut indent = buffer.indent_size_for_line(start_point.row);
-                        indent.len = cmp::min(indent.len, start_point.column);
-                        let start = selection.start;
-                        let end = selection.end;
-                        let is_cursor = start == end;
-                        let language_scope = buffer.language_scope_at(start);
-                        let (comment_delimiter, insert_extra_newline) = if let Some(language) =
-                            &language_scope
-                        {
-                            let leading_whitespace_len = buffer
-                                .reversed_chars_at(start)
-                                .take_while(|c| c.is_whitespace() && *c != '\n')
-                                .map(|c| c.len_utf8())
-                                .sum::<usize>();
-
-                            let trailing_whitespace_len = buffer
-                                .chars_at(end)
-                                .take_while(|c| c.is_whitespace() && *c != '\n')
-                                .map(|c| c.len_utf8())
-                                .sum::<usize>();
-
-                            let insert_extra_newline =
-                                language.brackets().any(|(pair, enabled)| {
-                                    let pair_start = pair.start.trim_end();
-                                    let pair_end = pair.end.trim_start();
-
-                                    enabled
-                                        && pair.newline
-                                        && buffer.contains_str_at(
-                                            end + trailing_whitespace_len,
-                                            pair_end,
-                                        )
-                                        && buffer.contains_str_at(
-                                            (start - leading_whitespace_len)
-                                                .saturating_sub(pair_start.len()),
-                                            pair_start,
-                                        )
-                                });
-                            // Comment extension on newline is allowed only for cursor selections
-                            let comment_delimiter = language.line_comment_prefix().filter(|_| {
-                                let is_comment_extension_enabled =
-                                    multi_buffer.settings_at(0, cx).extend_comment_on_newline;
-                                is_cursor && is_comment_extension_enabled
-                            });
-                            let comment_delimiter = if let Some(delimiter) = comment_delimiter {
-                                buffer
-                                    .buffer_line_for_row(start_point.row)
-                                    .is_some_and(|(snapshot, range)| {
-                                        let mut index_of_first_non_whitespace = 0;
-                                        let line_starts_with_comment = snapshot
-                                            .chars_for_range(range)
-                                            .skip_while(|c| {
-                                                let should_skip = c.is_whitespace();
-                                                if should_skip {
-                                                    index_of_first_non_whitespace += 1;
-                                                }
-                                                should_skip
-                                            })
-                                            .take(delimiter.len())
-                                            .eq(delimiter.chars());
-                                        let cursor_is_placed_after_comment_marker =
-                                            index_of_first_non_whitespace + delimiter.len()
-                                                <= start_point.column as usize;
-                                        line_starts_with_comment
-                                            && cursor_is_placed_after_comment_marker
-                                    })
-                                    .then(|| delimiter.clone())
-                            } else {
-                                None
-                            };
-                            (comment_delimiter, insert_extra_newline)
-                        } else {
-                            (None, false)
-                        };
-
-                        let capacity_for_delimiter = comment_delimiter
-                            .as_deref()
-                            .map(str::len)
-                            .unwrap_or_default();
-                        let mut new_text =
-                            String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
-                        new_text.push_str("\n");
-                        new_text.extend(indent.chars());
-                        if let Some(delimiter) = &comment_delimiter {
-                            new_text.push_str(&delimiter);
-                        }
-                        if insert_extra_newline {
-                            new_text = new_text.repeat(2);
-                        }
-
-                        let anchor = buffer.anchor_after(end);
-                        let new_selection = selection.map(|_| anchor);
-                        (
-                            (start..end, new_text),
-                            (insert_extra_newline, new_selection),
-                        )
-                    })
-                    .unzip()
-            };
-
-            this.edit_with_autoindent(edits, cx);
-            let buffer = this.buffer.read(cx).snapshot(cx);
-            let new_selections = selection_fixup_info
-                .into_iter()
-                .map(|(extra_newline_inserted, new_selection)| {
-                    let mut cursor = new_selection.end.to_point(&buffer);
-                    if extra_newline_inserted {
-                        cursor.row -= 1;
-                        cursor.column = buffer.line_len(cursor.row);
-                    }
-                    new_selection.map(|_| cursor)
-                })
-                .collect();
-
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
-            this.refresh_copilot_suggestions(true, cx);
-        });
-    }
-
-    pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
-        let buffer = self.buffer.read(cx);
-        let snapshot = buffer.snapshot(cx);
-
-        let mut edits = Vec::new();
-        let mut rows = Vec::new();
-        let mut rows_inserted = 0;
-
-        for selection in self.selections.all_adjusted(cx) {
-            let cursor = selection.head();
-            let row = cursor.row;
-
-            let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
-
-            let newline = "\n".to_string();
-            edits.push((start_of_line..start_of_line, newline));
-
-            rows.push(row + rows_inserted);
-            rows_inserted += 1;
-        }
-
-        self.transact(cx, |editor, cx| {
-            editor.edit(edits, cx);
-
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                let mut index = 0;
-                s.move_cursors_with(|map, _, _| {
-                    let row = rows[index];
-                    index += 1;
-
-                    let point = Point::new(row, 0);
-                    let boundary = map.next_line_boundary(point).1;
-                    let clipped = map.clip_point(boundary, Bias::Left);
-
-                    (clipped, SelectionGoal::None)
-                });
-            });
-
-            let mut indent_edits = Vec::new();
-            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
-            for row in rows {
-                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
-                for (row, indent) in indents {
-                    if indent.len == 0 {
-                        continue;
-                    }
-
-                    let text = match indent.kind {
-                        IndentKind::Space => " ".repeat(indent.len as usize),
-                        IndentKind::Tab => "\t".repeat(indent.len as usize),
-                    };
-                    let point = Point::new(row, 0);
-                    indent_edits.push((point..point, text));
-                }
-            }
-            editor.edit(indent_edits, cx);
-        });
-    }
-
-    pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
-        let buffer = self.buffer.read(cx);
-        let snapshot = buffer.snapshot(cx);
-
-        let mut edits = Vec::new();
-        let mut rows = Vec::new();
-        let mut rows_inserted = 0;
-
-        for selection in self.selections.all_adjusted(cx) {
-            let cursor = selection.head();
-            let row = cursor.row;
-
-            let point = Point::new(row + 1, 0);
-            let start_of_line = snapshot.clip_point(point, Bias::Left);
-
-            let newline = "\n".to_string();
-            edits.push((start_of_line..start_of_line, newline));
-
-            rows_inserted += 1;
-            rows.push(row + rows_inserted);
-        }
-
-        self.transact(cx, |editor, cx| {
-            editor.edit(edits, cx);
-
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                let mut index = 0;
-                s.move_cursors_with(|map, _, _| {
-                    let row = rows[index];
-                    index += 1;
-
-                    let point = Point::new(row, 0);
-                    let boundary = map.next_line_boundary(point).1;
-                    let clipped = map.clip_point(boundary, Bias::Left);
-
-                    (clipped, SelectionGoal::None)
-                });
-            });
-
-            let mut indent_edits = Vec::new();
-            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
-            for row in rows {
-                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
-                for (row, indent) in indents {
-                    if indent.len == 0 {
-                        continue;
-                    }
-
-                    let text = match indent.kind {
-                        IndentKind::Space => " ".repeat(indent.len as usize),
-                        IndentKind::Tab => "\t".repeat(indent.len as usize),
-                    };
-                    let point = Point::new(row, 0);
-                    indent_edits.push((point..point, text));
-                }
-            }
-            editor.edit(indent_edits, cx);
-        });
-    }
-
-    pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
-        self.insert_with_autoindent_mode(
-            text,
-            Some(AutoindentMode::Block {
-                original_indent_columns: Vec::new(),
-            }),
-            cx,
-        );
-    }
-
-    fn insert_with_autoindent_mode(
-        &mut self,
-        text: &str,
-        autoindent_mode: Option<AutoindentMode>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        if self.read_only {
-            return;
-        }
-
-        let text: Arc<str> = text.into();
-        self.transact(cx, |this, cx| {
-            let old_selections = this.selections.all_adjusted(cx);
-            let selection_anchors = this.buffer.update(cx, |buffer, cx| {
-                let anchors = {
-                    let snapshot = buffer.read(cx);
-                    old_selections
-                        .iter()
-                        .map(|s| {
-                            let anchor = snapshot.anchor_after(s.head());
-                            s.map(|_| anchor)
-                        })
-                        .collect::<Vec<_>>()
-                };
-                buffer.edit(
-                    old_selections
-                        .iter()
-                        .map(|s| (s.start..s.end, text.clone())),
-                    autoindent_mode,
-                    cx,
-                );
-                anchors
-            });
-
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.select_anchors(selection_anchors);
-            })
-        });
-    }
-
-    fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
-        if !EditorSettings::get_global(cx).show_completions_on_input {
-            return;
-        }
-
-        let selection = self.selections.newest_anchor();
-        if self
-            .buffer
-            .read(cx)
-            .is_completion_trigger(selection.head(), text, cx)
-        {
-            self.show_completions(&ShowCompletions, cx);
-        } else {
-            self.hide_context_menu(cx);
-        }
-    }
-
-    /// If any empty selections is touching the start of its innermost containing autoclose
-    /// region, expand it to select the brackets.
-    fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
-        let selections = self.selections.all::<usize>(cx);
-        let buffer = self.buffer.read(cx).read(cx);
-        let mut new_selections = Vec::new();
-        for (mut selection, region) in self.selections_with_autoclose_regions(selections, &buffer) {
-            if let (Some(region), true) = (region, selection.is_empty()) {
-                let mut range = region.range.to_offset(&buffer);
-                if selection.start == range.start {
-                    if range.start >= region.pair.start.len() {
-                        range.start -= region.pair.start.len();
-                        if buffer.contains_str_at(range.start, &region.pair.start) {
-                            if buffer.contains_str_at(range.end, &region.pair.end) {
-                                range.end += region.pair.end.len();
-                                selection.start = range.start;
-                                selection.end = range.end;
-                            }
-                        }
-                    }
-                }
-            }
-            new_selections.push(selection);
-        }
-
-        drop(buffer);
-        self.change_selections(None, cx, |selections| selections.select(new_selections));
-    }
-
-    /// Iterate the given selections, and for each one, find the smallest surrounding
-    /// autoclose region. This uses the ordering of the selections and the autoclose
-    /// regions to avoid repeated comparisons.
-    fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
-        &'a self,
-        selections: impl IntoIterator<Item = Selection<D>>,
-        buffer: &'a MultiBufferSnapshot,
-    ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
-        let mut i = 0;
-        let mut regions = self.autoclose_regions.as_slice();
-        selections.into_iter().map(move |selection| {
-            let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
-
-            let mut enclosing = None;
-            while let Some(pair_state) = regions.get(i) {
-                if pair_state.range.end.to_offset(buffer) < range.start {
-                    regions = &regions[i + 1..];
-                    i = 0;
-                } else if pair_state.range.start.to_offset(buffer) > range.end {
-                    break;
-                } else {
-                    if pair_state.selection_id == selection.id {
-                        enclosing = Some(pair_state);
-                    }
-                    i += 1;
-                }
-            }
-
-            (selection.clone(), enclosing)
-        })
-    }
-
-    /// Remove any autoclose regions that no longer contain their selection.
-    fn invalidate_autoclose_regions(
-        &mut self,
-        mut selections: &[Selection<Anchor>],
-        buffer: &MultiBufferSnapshot,
-    ) {
-        self.autoclose_regions.retain(|state| {
-            let mut i = 0;
-            while let Some(selection) = selections.get(i) {
-                if selection.end.cmp(&state.range.start, buffer).is_lt() {
-                    selections = &selections[1..];
-                    continue;
-                }
-                if selection.start.cmp(&state.range.end, buffer).is_gt() {
-                    break;
-                }
-                if selection.id == state.selection_id {
-                    return true;
-                } else {
-                    i += 1;
-                }
-            }
-            false
-        });
-    }
-
-    fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
-        let offset = position.to_offset(buffer);
-        let (word_range, kind) = buffer.surrounding_word(offset);
-        if offset > word_range.start && kind == Some(CharKind::Word) {
-            Some(
-                buffer
-                    .text_for_range(word_range.start..offset)
-                    .collect::<String>(),
-            )
-        } else {
-            None
-        }
-    }
-
-    pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
-        self.refresh_inlay_hints(
-            InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
-            cx,
-        );
-    }
-
-    pub fn inlay_hints_enabled(&self) -> bool {
-        self.inlay_hint_cache.enabled
-    }
-
-    fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
-        if self.project.is_none() || self.mode != EditorMode::Full {
-            return;
-        }
-
-        let reason_description = reason.description();
-        let (invalidate_cache, required_languages) = match reason {
-            InlayHintRefreshReason::Toggle(enabled) => {
-                self.inlay_hint_cache.enabled = enabled;
-                if enabled {
-                    (InvalidationStrategy::RefreshRequested, None)
-                } else {
-                    self.inlay_hint_cache.clear();
-                    self.splice_inlay_hints(
-                        self.visible_inlay_hints(cx)
-                            .iter()
-                            .map(|inlay| inlay.id)
-                            .collect(),
-                        Vec::new(),
-                        cx,
-                    );
-                    return;
-                }
-            }
-            InlayHintRefreshReason::SettingsChange(new_settings) => {
-                match self.inlay_hint_cache.update_settings(
-                    &self.buffer,
-                    new_settings,
-                    self.visible_inlay_hints(cx),
-                    cx,
-                ) {
-                    ControlFlow::Break(Some(InlaySplice {
-                        to_remove,
-                        to_insert,
-                    })) => {
-                        self.splice_inlay_hints(to_remove, to_insert, cx);
-                        return;
-                    }
-                    ControlFlow::Break(None) => return,
-                    ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
-                }
-            }
-            InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
-                if let Some(InlaySplice {
-                    to_remove,
-                    to_insert,
-                }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
-                {
-                    self.splice_inlay_hints(to_remove, to_insert, cx);
-                }
-                return;
-            }
-            InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
-            InlayHintRefreshReason::BufferEdited(buffer_languages) => {
-                (InvalidationStrategy::BufferEdited, Some(buffer_languages))
-            }
-            InlayHintRefreshReason::RefreshRequested => {
-                (InvalidationStrategy::RefreshRequested, None)
-            }
-        };
-
-        if let Some(InlaySplice {
-            to_remove,
-            to_insert,
-        }) = self.inlay_hint_cache.spawn_hint_refresh(
-            reason_description,
-            self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
-            invalidate_cache,
-            cx,
-        ) {
-            self.splice_inlay_hints(to_remove, to_insert, cx);
-        }
-    }
-
-    fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
-        self.display_map
-            .read(cx)
-            .current_inlays()
-            .filter(move |inlay| {
-                Some(inlay.id) != self.copilot_state.suggestion.as_ref().map(|h| h.id)
-            })
-            .cloned()
-            .collect()
-    }
-
-    pub fn excerpts_for_inlay_hints_query(
-        &self,
-        restrict_to_languages: Option<&HashSet<Arc<Language>>>,
-        cx: &mut ViewContext<Editor>,
-    ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
-        let Some(project) = self.project.as_ref() else {
-            return HashMap::default();
-        };
-        let project = project.read(cx);
-        let multi_buffer = self.buffer().read(cx);
-        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
-        let multi_buffer_visible_start = self
-            .scroll_manager
-            .anchor()
-            .anchor
-            .to_point(&multi_buffer_snapshot);
-        let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
-            multi_buffer_visible_start
-                + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
-            Bias::Left,
-        );
-        let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
-        multi_buffer
-            .range_to_buffer_ranges(multi_buffer_visible_range, cx)
-            .into_iter()
-            .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
-            .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
-                let buffer = buffer_handle.read(cx);
-                let buffer_file = project::worktree::File::from_dyn(buffer.file())?;
-                let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
-                let worktree_entry = buffer_worktree
-                    .read(cx)
-                    .entry_for_id(buffer_file.project_entry_id(cx)?)?;
-                if worktree_entry.is_ignored {
-                    return None;
-                }
-
-                let language = buffer.language()?;
-                if let Some(restrict_to_languages) = restrict_to_languages {
-                    if !restrict_to_languages.contains(language) {
-                        return None;
-                    }
-                }
-                Some((
-                    excerpt_id,
-                    (
-                        buffer_handle,
-                        buffer.version().clone(),
-                        excerpt_visible_range,
-                    ),
-                ))
-            })
-            .collect()
-    }
-
-    pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
-        TextLayoutDetails {
-            text_system: cx.text_system().clone(),
-            editor_style: self.style.clone().unwrap(),
-            rem_size: cx.rem_size(),
-        }
-    }
-
-    fn splice_inlay_hints(
-        &self,
-        to_remove: Vec<InlayId>,
-        to_insert: Vec<Inlay>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.display_map.update(cx, |display_map, cx| {
-            display_map.splice_inlays(to_remove, to_insert, cx);
-        });
-        cx.notify();
-    }
-
-    fn trigger_on_type_formatting(
-        &self,
-        input: String,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<Task<Result<()>>> {
-        if input.len() != 1 {
-            return None;
-        }
-
-        let project = self.project.as_ref()?;
-        let position = self.selections.newest_anchor().head();
-        let (buffer, buffer_position) = self
-            .buffer
-            .read(cx)
-            .text_anchor_for_position(position.clone(), cx)?;
-
-        // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
-        // hence we do LSP request & edit on host side only โ€”ย add formats to host's history.
-        let push_to_lsp_host_history = true;
-        // If this is not the host, append its history with new edits.
-        let push_to_client_history = project.read(cx).is_remote();
-
-        let on_type_formatting = project.update(cx, |project, cx| {
-            project.on_type_format(
-                buffer.clone(),
-                buffer_position,
-                input,
-                push_to_lsp_host_history,
-                cx,
-            )
-        });
-        Some(cx.spawn(|editor, mut cx| async move {
-            if let Some(transaction) = on_type_formatting.await? {
-                if push_to_client_history {
-                    buffer
-                        .update(&mut cx, |buffer, _| {
-                            buffer.push_transaction(transaction, Instant::now());
-                        })
-                        .ok();
-                }
-                editor.update(&mut cx, |editor, cx| {
-                    editor.refresh_document_highlights(cx);
-                })?;
-            }
-            Ok(())
-        }))
-    }
-
-    fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
-        if self.pending_rename.is_some() {
-            return;
-        }
-
-        let project = if let Some(project) = self.project.clone() {
-            project
-        } else {
-            return;
-        };
-
-        let position = self.selections.newest_anchor().head();
-        let (buffer, buffer_position) = if let Some(output) = self
-            .buffer
-            .read(cx)
-            .text_anchor_for_position(position.clone(), cx)
-        {
-            output
-        } else {
-            return;
-        };
-
-        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 id = post_inc(&mut self.next_completion_id);
-        let task = cx.spawn(|this, mut cx| {
-            async move {
-                let completions = completions.await.log_err();
-                let (menu, pre_resolve_task) = if let Some(completions) = completions {
-                    let mut menu = CompletionsMenu {
-                        id,
-                        initial_position: position,
-                        match_candidates: completions
-                            .iter()
-                            .enumerate()
-                            .map(|(id, completion)| {
-                                StringMatchCandidate::new(
-                                    id,
-                                    completion.label.text[completion.label.filter_range.clone()]
-                                        .into(),
-                                )
-                            })
-                            .collect(),
-                        buffer,
-                        completions: Arc::new(RwLock::new(completions.into())),
-                        matches: Vec::new().into(),
-                        selected_item: 0,
-                        scroll_handle: UniformListScrollHandle::new(),
-                    };
-                    menu.filter(query.as_deref(), cx.background_executor().clone())
-                        .await;
-
-                    if menu.matches.is_empty() {
-                        (None, None)
-                    } else {
-                        let pre_resolve_task = this
-                            .update(&mut cx, |editor, cx| {
-                                menu.pre_resolve_completion_documentation(editor, cx)
-                            })
-                            .ok()
-                            .flatten();
-                        (Some(menu), pre_resolve_task)
-                    }
-                } else {
-                    (None, None)
-                };
-
-                this.update(&mut cx, |this, cx| {
-                    this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
-
-                    let mut context_menu = this.context_menu.write();
-                    match context_menu.as_ref() {
-                        None => {}
-
-                        Some(ContextMenu::Completions(prev_menu)) => {
-                            if prev_menu.id > id {
-                                return;
-                            }
-                        }
-
-                        _ => return,
-                    }
-
-                    if this.focus_handle.is_focused(cx) && menu.is_some() {
-                        let menu = menu.unwrap();
-                        *context_menu = Some(ContextMenu::Completions(menu));
-                        drop(context_menu);
-                        this.discard_copilot_suggestion(cx);
-                        cx.notify();
-                    } else if this.completion_tasks.len() <= 1 {
-                        // If there are no more completion tasks and the last menu was
-                        // empty, we should hide it. If it was already hidden, we should
-                        // also show the copilot suggestion when available.
-                        drop(context_menu);
-                        if this.hide_context_menu(cx).is_none() {
-                            this.update_visible_copilot_suggestion(cx);
-                        }
-                    }
-                })?;
-
-                if let Some(pre_resolve_task) = pre_resolve_task {
-                    pre_resolve_task.await;
-                }
-
-                Ok::<_, anyhow::Error>(())
-            }
-            .log_err()
-        });
-
-        self.completion_tasks.push((id, task));
-    }
-
-    pub fn confirm_completion(
-        &mut self,
-        action: &ConfirmCompletion,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<Task<Result<()>>> {
-        use language::ToOffset as _;
-
-        let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
-            menu
-        } else {
-            return None;
-        };
-
-        let mat = completions_menu
-            .matches
-            .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
-        let buffer_handle = completions_menu.buffer;
-        let completions = completions_menu.completions.read();
-        let completion = completions.get(mat.candidate_id)?;
-
-        let snippet;
-        let text;
-        if completion.is_snippet() {
-            snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
-            text = snippet.as_ref().unwrap().text.clone();
-        } else {
-            snippet = None;
-            text = completion.new_text.clone();
-        };
-        let selections = self.selections.all::<usize>(cx);
-        let buffer = buffer_handle.read(cx);
-        let old_range = completion.old_range.to_offset(buffer);
-        let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
-
-        let newest_selection = self.selections.newest_anchor();
-        if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
-            return None;
-        }
-
-        let lookbehind = newest_selection
-            .start
-            .text_anchor
-            .to_offset(buffer)
-            .saturating_sub(old_range.start);
-        let lookahead = old_range
-            .end
-            .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
-        let mut common_prefix_len = old_text
-            .bytes()
-            .zip(text.bytes())
-            .take_while(|(a, b)| a == b)
-            .count();
-
-        let snapshot = self.buffer.read(cx).snapshot(cx);
-        let mut range_to_replace: Option<Range<isize>> = None;
-        let mut ranges = Vec::new();
-        for selection in &selections {
-            if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
-                let start = selection.start.saturating_sub(lookbehind);
-                let end = selection.end + lookahead;
-                if selection.id == newest_selection.id {
-                    range_to_replace = Some(
-                        ((start + common_prefix_len) as isize - selection.start as isize)
-                            ..(end as isize - selection.start as isize),
-                    );
-                }
-                ranges.push(start + common_prefix_len..end);
-            } else {
-                common_prefix_len = 0;
-                ranges.clear();
-                ranges.extend(selections.iter().map(|s| {
-                    if s.id == newest_selection.id {
-                        range_to_replace = Some(
-                            old_range.start.to_offset_utf16(&snapshot).0 as isize
-                                - selection.start as isize
-                                ..old_range.end.to_offset_utf16(&snapshot).0 as isize
-                                    - selection.start as isize,
-                        );
-                        old_range.clone()
-                    } else {
-                        s.start..s.end
-                    }
-                }));
-                break;
-            }
-        }
-        let text = &text[common_prefix_len..];
-
-        cx.emit(EditorEvent::InputHandled {
-            utf16_range_to_replace: range_to_replace,
-            text: text.into(),
-        });
-
-        self.transact(cx, |this, cx| {
-            if let Some(mut snippet) = snippet {
-                snippet.text = text.to_string();
-                for tabstop in snippet.tabstops.iter_mut().flatten() {
-                    tabstop.start -= common_prefix_len as isize;
-                    tabstop.end -= common_prefix_len as isize;
-                }
-
-                this.insert_snippet(&ranges, snippet, cx).log_err();
-            } else {
-                this.buffer.update(cx, |buffer, cx| {
-                    buffer.edit(
-                        ranges.iter().map(|range| (range.clone(), text)),
-                        this.autoindent_mode.clone(),
-                        cx,
-                    );
-                });
-            }
-
-            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,
-            )
-        });
-        Some(cx.foreground_executor().spawn(async move {
-            apply_edits.await?;
-            Ok(())
-        }))
-    }
-
-    pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
-        let mut context_menu = self.context_menu.write();
-        if matches!(context_menu.as_ref(), Some(ContextMenu::CodeActions(_))) {
-            *context_menu = None;
-            cx.notify();
-            return;
-        }
-        drop(context_menu);
-
-        let deployed_from_indicator = action.deployed_from_indicator;
-        let mut task = self.code_actions_task.take();
-        cx.spawn(|this, mut cx| async move {
-            while let Some(prev_task) = task {
-                prev_task.await;
-                task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
-            }
-
-            this.update(&mut cx, |this, cx| {
-                if this.focus_handle.is_focused(cx) {
-                    if let Some((buffer, actions)) = this.available_code_actions.clone() {
-                        this.completion_tasks.clear();
-                        this.discard_copilot_suggestion(cx);
-                        *this.context_menu.write() =
-                            Some(ContextMenu::CodeActions(CodeActionsMenu {
-                                buffer,
-                                actions,
-                                selected_item: Default::default(),
-                                scroll_handle: UniformListScrollHandle::default(),
-                                deployed_from_indicator,
-                            }));
-                        cx.notify();
-                    }
-                }
-            })?;
-
-            Ok::<_, anyhow::Error>(())
-        })
-        .detach_and_log_err(cx);
-    }
-
-    pub fn confirm_code_action(
-        &mut self,
-        action: &ConfirmCodeAction,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<Task<Result<()>>> {
-        let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
-            menu
-        } else {
-            return None;
-        };
-        let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
-        let action = actions_menu.actions.get(action_ix)?.clone();
-        let title = action.lsp_action.title.clone();
-        let buffer = actions_menu.buffer;
-        let workspace = self.workspace()?;
-
-        let apply_code_actions = workspace
-            .read(cx)
-            .project()
-            .clone()
-            .update(cx, |project, cx| {
-                project.apply_code_action(buffer, action, true, cx)
-            });
-        let workspace = workspace.downgrade();
-        Some(cx.spawn(|editor, cx| async move {
-            let project_transaction = apply_code_actions.await?;
-            Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
-        }))
-    }
-
-    async fn open_project_transaction(
-        this: &WeakView<Editor>,
-        workspace: WeakView<Workspace>,
-        transaction: ProjectTransaction,
-        title: String,
-        mut cx: AsyncWindowContext,
-    ) -> Result<()> {
-        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| {
-            entries.sort_unstable_by_key(|(buffer, _)| {
-                buffer.read(cx).file().map(|f| f.path().clone())
-            });
-        })?;
-
-        // If the project transaction's edits are all contained within this editor, then
-        // avoid opening a new editor to display them.
-
-        if let Some((buffer, transaction)) = entries.first() {
-            if entries.len() == 1 {
-                let excerpt = this.update(&mut cx, |editor, cx| {
-                    editor
-                        .buffer()
-                        .read(cx)
-                        .excerpt_containing(editor.selections.newest_anchor().head(), cx)
-                })?;
-                if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
-                    if excerpted_buffer == *buffer {
-                        let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
-                            let excerpt_range = excerpt_range.to_offset(buffer);
-                            buffer
-                                .edited_ranges_for_transaction::<usize>(transaction)
-                                .all(|range| {
-                                    excerpt_range.start <= range.start
-                                        && excerpt_range.end >= range.end
-                                })
-                        })?;
-
-                        if all_edits_within_excerpt {
-                            return Ok(());
-                        }
-                    }
-                }
-            }
-        } else {
-            return Ok(());
-        }
-
-        let mut ranges_to_highlight = Vec::new();
-        let excerpt_buffer = cx.new_model(|cx| {
-            let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
-            for (buffer_handle, transaction) in &entries {
-                let buffer = buffer_handle.read(cx);
-                ranges_to_highlight.extend(
-                    multibuffer.push_excerpts_with_context_lines(
-                        buffer_handle.clone(),
-                        buffer
-                            .edited_ranges_for_transaction::<usize>(transaction)
-                            .collect(),
-                        1,
-                        cx,
-                    ),
-                );
-            }
-            multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
-            multibuffer
-        })?;
-
-        workspace.update(&mut cx, |workspace, cx| {
-            let project = workspace.project().clone();
-            let editor =
-                cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
-            workspace.add_item(Box::new(editor.clone()), cx);
-            editor.update(cx, |editor, cx| {
-                editor.highlight_background::<Self>(
-                    ranges_to_highlight,
-                    |theme| theme.editor_highlighted_line_background,
-                    cx,
-                );
-            });
-        })?;
-
-        Ok(())
-    }
-
-    fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
-        let project = self.project.clone()?;
-        let buffer = self.buffer.read(cx);
-        let newest_selection = self.selections.newest_anchor().clone();
-        let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
-        let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
-        if start_buffer != end_buffer {
-            return None;
-        }
-
-        self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
-            cx.background_executor()
-                .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
-                .await;
-
-            let actions = if let Ok(code_actions) = project.update(&mut cx, |project, cx| {
-                project.code_actions(&start_buffer, start..end, cx)
-            }) {
-                code_actions.await.log_err()
-            } else {
-                None
-            };
-
-            this.update(&mut cx, |this, cx| {
-                this.available_code_actions = actions.and_then(|actions| {
-                    if actions.is_empty() {
-                        None
-                    } else {
-                        Some((start_buffer, actions.into()))
-                    }
-                });
-                cx.notify();
-            })
-            .log_err();
-        }));
-        None
-    }
-
-    fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
-        if self.pending_rename.is_some() {
-            return None;
-        }
-
-        let project = self.project.clone()?;
-        let buffer = self.buffer.read(cx);
-        let newest_selection = self.selections.newest_anchor().clone();
-        let cursor_position = newest_selection.head();
-        let (cursor_buffer, cursor_buffer_position) =
-            buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
-        let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
-        if cursor_buffer != tail_buffer {
-            return None;
-        }
-
-        self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
-            cx.background_executor()
-                .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
-                .await;
-
-            let highlights = if let Some(highlights) = project
-                .update(&mut cx, |project, cx| {
-                    project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
-                })
-                .log_err()
-            {
-                highlights.await.log_err()
-            } else {
-                None
-            };
-
-            if let Some(highlights) = highlights {
-                this.update(&mut cx, |this, cx| {
-                    if this.pending_rename.is_some() {
-                        return;
-                    }
-
-                    let buffer_id = cursor_position.buffer_id;
-                    let buffer = this.buffer.read(cx);
-                    if !buffer
-                        .text_anchor_for_position(cursor_position, cx)
-                        .map_or(false, |(buffer, _)| buffer == cursor_buffer)
-                    {
-                        return;
-                    }
-
-                    let cursor_buffer_snapshot = cursor_buffer.read(cx);
-                    let mut write_ranges = Vec::new();
-                    let mut read_ranges = Vec::new();
-                    for highlight in highlights {
-                        for (excerpt_id, excerpt_range) in
-                            buffer.excerpts_for_buffer(&cursor_buffer, cx)
-                        {
-                            let start = highlight
-                                .range
-                                .start
-                                .max(&excerpt_range.context.start, cursor_buffer_snapshot);
-                            let end = highlight
-                                .range
-                                .end
-                                .min(&excerpt_range.context.end, cursor_buffer_snapshot);
-                            if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
-                                continue;
-                            }
-
-                            let range = Anchor {
-                                buffer_id,
-                                excerpt_id: excerpt_id.clone(),
-                                text_anchor: start,
-                            }..Anchor {
-                                buffer_id,
-                                excerpt_id,
-                                text_anchor: end,
-                            };
-                            if highlight.kind == lsp::DocumentHighlightKind::WRITE {
-                                write_ranges.push(range);
-                            } else {
-                                read_ranges.push(range);
-                            }
-                        }
-                    }
-
-                    this.highlight_background::<DocumentHighlightRead>(
-                        read_ranges,
-                        |theme| theme.editor_document_highlight_read_background,
-                        cx,
-                    );
-                    this.highlight_background::<DocumentHighlightWrite>(
-                        write_ranges,
-                        |theme| theme.editor_document_highlight_write_background,
-                        cx,
-                    );
-                    cx.notify();
-                })
-                .log_err();
-            }
-        }));
-        None
-    }
-
-    fn refresh_copilot_suggestions(
-        &mut self,
-        debounce: bool,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<()> {
-        let copilot = Copilot::global(cx)?;
-        if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
-            self.clear_copilot_suggestions(cx);
-            return None;
-        }
-        self.update_visible_copilot_suggestion(cx);
-
-        let snapshot = self.buffer.read(cx).snapshot(cx);
-        let cursor = self.selections.newest_anchor().head();
-        if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
-            self.clear_copilot_suggestions(cx);
-            return None;
-        }
-
-        let (buffer, buffer_position) =
-            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
-        self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
-            if debounce {
-                cx.background_executor()
-                    .timer(COPILOT_DEBOUNCE_TIMEOUT)
-                    .await;
-            }
-
-            let completions = copilot
-                .update(&mut cx, |copilot, cx| {
-                    copilot.completions(&buffer, buffer_position, cx)
-                })
-                .log_err()
-                .unwrap_or(Task::ready(Ok(Vec::new())))
-                .await
-                .log_err()
-                .into_iter()
-                .flatten()
-                .collect_vec();
-
-            this.update(&mut cx, |this, cx| {
-                if !completions.is_empty() {
-                    this.copilot_state.cycled = false;
-                    this.copilot_state.pending_cycling_refresh = Task::ready(None);
-                    this.copilot_state.completions.clear();
-                    this.copilot_state.active_completion_index = 0;
-                    this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
-                    for completion in completions {
-                        this.copilot_state.push_completion(completion);
-                    }
-                    this.update_visible_copilot_suggestion(cx);
-                }
-            })
-            .log_err()?;
-            Some(())
-        });
-
-        Some(())
-    }
-
-    fn cycle_copilot_suggestions(
-        &mut self,
-        direction: Direction,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<()> {
-        let copilot = Copilot::global(cx)?;
-        if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
-            return None;
-        }
-
-        if self.copilot_state.cycled {
-            self.copilot_state.cycle_completions(direction);
-            self.update_visible_copilot_suggestion(cx);
-        } else {
-            let cursor = self.selections.newest_anchor().head();
-            let (buffer, buffer_position) =
-                self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
-            self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
-                let completions = copilot
-                    .update(&mut cx, |copilot, cx| {
-                        copilot.completions_cycling(&buffer, buffer_position, cx)
-                    })
-                    .log_err()?
-                    .await;
-
-                this.update(&mut cx, |this, cx| {
-                    this.copilot_state.cycled = true;
-                    for completion in completions.log_err().into_iter().flatten() {
-                        this.copilot_state.push_completion(completion);
-                    }
-                    this.copilot_state.cycle_completions(direction);
-                    this.update_visible_copilot_suggestion(cx);
-                })
-                .log_err()?;
-
-                Some(())
-            });
-        }
-
-        Some(())
-    }
-
-    fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
-        if !self.has_active_copilot_suggestion(cx) {
-            self.refresh_copilot_suggestions(false, cx);
-            return;
-        }
-
-        self.update_visible_copilot_suggestion(cx);
-    }
-
-    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);
-        } else {
-            let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none();
-            if is_copilot_disabled {
-                cx.propagate();
-            }
-        }
-    }
-
-    fn previous_copilot_suggestion(
-        &mut self,
-        _: &copilot::PreviousSuggestion,
-        cx: &mut ViewContext<Self>,
-    ) {
-        if self.has_active_copilot_suggestion(cx) {
-            self.cycle_copilot_suggestions(Direction::Prev, cx);
-        } else {
-            let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none();
-            if is_copilot_disabled {
-                cx.propagate();
-            }
-        }
-    }
-
-    fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
-        if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
-            if let Some((copilot, completion)) =
-                Copilot::global(cx).zip(self.copilot_state.active_completion())
-            {
-                copilot
-                    .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
-                    .detach_and_log_err(cx);
-
-                self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
-            }
-            cx.emit(EditorEvent::InputHandled {
-                utf16_range_to_replace: None,
-                text: suggestion.text.to_string().into(),
-            });
-            self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
-            cx.notify();
-            true
-        } else {
-            false
-        }
-    }
-
-    fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
-        if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
-            if let Some(copilot) = Copilot::global(cx) {
-                copilot
-                    .update(cx, |copilot, cx| {
-                        copilot.discard_completions(&self.copilot_state.completions, cx)
-                    })
-                    .detach_and_log_err(cx);
-
-                self.report_copilot_event(None, false, cx)
-            }
-
-            self.display_map.update(cx, |map, cx| {
-                map.splice_inlays(vec![suggestion.id], Vec::new(), cx)
-            });
-            cx.notify();
-            true
-        } else {
-            false
-        }
-    }
-
-    fn is_copilot_enabled_at(
-        &self,
-        location: Anchor,
-        snapshot: &MultiBufferSnapshot,
-        cx: &mut ViewContext<Self>,
-    ) -> bool {
-        let file = snapshot.file_at(location);
-        let language = snapshot.language_at(location);
-        let settings = all_language_settings(file, cx);
-        settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
-    }
-
-    fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
-        if let Some(suggestion) = self.copilot_state.suggestion.as_ref() {
-            let buffer = self.buffer.read(cx).read(cx);
-            suggestion.position.is_valid(&buffer)
-        } else {
-            false
-        }
-    }
-
-    fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
-        let suggestion = self.copilot_state.suggestion.take()?;
-        self.display_map.update(cx, |map, cx| {
-            map.splice_inlays(vec![suggestion.id], Default::default(), cx);
-        });
-        let buffer = self.buffer.read(cx).read(cx);
-
-        if suggestion.position.is_valid(&buffer) {
-            Some(suggestion)
-        } else {
-            None
-        }
-    }
-
-    fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
-        let snapshot = self.buffer.read(cx).snapshot(cx);
-        let selection = self.selections.newest_anchor();
-        let cursor = selection.head();
-
-        if self.context_menu.read().is_some()
-            || !self.completion_tasks.is_empty()
-            || selection.start != selection.end
-        {
-            self.discard_copilot_suggestion(cx);
-        } else if let Some(text) = self
-            .copilot_state
-            .text_for_active_completion(cursor, &snapshot)
-        {
-            let text = Rope::from(text);
-            let mut to_remove = Vec::new();
-            if let Some(suggestion) = self.copilot_state.suggestion.take() {
-                to_remove.push(suggestion.id);
-            }
-
-            let suggestion_inlay =
-                Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
-            self.copilot_state.suggestion = Some(suggestion_inlay.clone());
-            self.display_map.update(cx, move |map, cx| {
-                map.splice_inlays(to_remove, vec![suggestion_inlay], cx)
-            });
-            cx.notify();
-        } else {
-            self.discard_copilot_suggestion(cx);
-        }
-    }
-
-    fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
-        self.copilot_state = Default::default();
-        self.discard_copilot_suggestion(cx);
-    }
-
-    pub fn render_code_actions_indicator(
-        &self,
-        _style: &EditorStyle,
-        is_active: bool,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<IconButton> {
-        if self.available_code_actions.is_some() {
-            Some(
-                IconButton::new("code_actions_indicator", ui::Icon::Bolt)
-                    .icon_size(IconSize::Small)
-                    .icon_color(Color::Muted)
-                    .selected(is_active)
-                    .on_click(cx.listener(|editor, _e, cx| {
-                        editor.toggle_code_actions(
-                            &ToggleCodeActions {
-                                deployed_from_indicator: true,
-                            },
-                            cx,
-                        );
-                    })),
-            )
-        } else {
-            None
-        }
-    }
-
-    pub fn render_fold_indicators(
-        &self,
-        fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
-        _style: &EditorStyle,
-        gutter_hovered: bool,
-        _line_height: Pixels,
-        _gutter_margin: Pixels,
-        cx: &mut ViewContext<Self>,
-    ) -> Vec<Option<IconButton>> {
-        fold_data
-            .iter()
-            .enumerate()
-            .map(|(ix, fold_data)| {
-                fold_data
-                    .map(|(fold_status, buffer_row, active)| {
-                        (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
-                            IconButton::new(ix as usize, ui::Icon::ChevronDown)
-                                .on_click(cx.listener(move |editor, _e, cx| match fold_status {
-                                    FoldStatus::Folded => {
-                                        editor.unfold_at(&UnfoldAt { 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)
-                                .selected_icon(ui::Icon::ChevronRight)
-                                .size(ui::ButtonSize::None)
-                        })
-                    })
-                    .flatten()
-            })
-            .collect()
-    }
-
-    pub fn context_menu_visible(&self) -> bool {
-        self.context_menu
-            .read()
-            .as_ref()
-            .map_or(false, |menu| menu.visible())
-    }
-
-    pub fn render_context_menu(
-        &self,
-        cursor_position: DisplayPoint,
-        style: &EditorStyle,
-        max_height: Pixels,
-        cx: &mut ViewContext<Editor>,
-    ) -> Option<(DisplayPoint, AnyElement)> {
-        self.context_menu.read().as_ref().map(|menu| {
-            menu.render(
-                cursor_position,
-                style,
-                max_height,
-                self.workspace.as_ref().map(|(w, _)| w.clone()),
-                cx,
-            )
-        })
-    }
-
-    fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
-        cx.notify();
-        self.completion_tasks.clear();
-        let context_menu = self.context_menu.write().take();
-        if context_menu.is_some() {
-            self.update_visible_copilot_suggestion(cx);
-        }
-        context_menu
-    }
-
-    pub fn insert_snippet(
-        &mut self,
-        insertion_ranges: &[Range<usize>],
-        snippet: Snippet,
-        cx: &mut ViewContext<Self>,
-    ) -> Result<()> {
-        let tabstops = self.buffer.update(cx, |buffer, cx| {
-            let snippet_text: Arc<str> = snippet.text.clone().into();
-            buffer.edit(
-                insertion_ranges
-                    .iter()
-                    .cloned()
-                    .map(|range| (range, snippet_text.clone())),
-                Some(AutoindentMode::EachLine),
-                cx,
-            );
-
-            let snapshot = &*buffer.read(cx);
-            let snippet = &snippet;
-            snippet
-                .tabstops
-                .iter()
-                .map(|tabstop| {
-                    let mut tabstop_ranges = tabstop
-                        .iter()
-                        .flat_map(|tabstop_range| {
-                            let mut delta = 0_isize;
-                            insertion_ranges.iter().map(move |insertion_range| {
-                                let insertion_start = insertion_range.start as isize + delta;
-                                delta +=
-                                    snippet.text.len() as isize - insertion_range.len() as isize;
-
-                                let start = snapshot.anchor_before(
-                                    (insertion_start + tabstop_range.start) as usize,
-                                );
-                                let end = snapshot
-                                    .anchor_after((insertion_start + tabstop_range.end) as usize);
-                                start..end
-                            })
-                        })
-                        .collect::<Vec<_>>();
-                    tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
-                    tabstop_ranges
-                })
-                .collect::<Vec<_>>()
-        });
-
-        if let Some(tabstop) = tabstops.first() {
-            self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.select_ranges(tabstop.iter().cloned());
-            });
-            self.snippet_stack.push(SnippetState {
-                active_index: 0,
-                ranges: tabstops,
-            });
-        }
-
-        Ok(())
-    }
-
-    pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
-        self.move_to_snippet_tabstop(Bias::Right, cx)
-    }
-
-    pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
-        self.move_to_snippet_tabstop(Bias::Left, cx)
-    }
-
-    pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
-        if let Some(mut snippet) = self.snippet_stack.pop() {
-            match bias {
-                Bias::Left => {
-                    if snippet.active_index > 0 {
-                        snippet.active_index -= 1;
-                    } else {
-                        self.snippet_stack.push(snippet);
-                        return false;
-                    }
-                }
-                Bias::Right => {
-                    if snippet.active_index + 1 < snippet.ranges.len() {
-                        snippet.active_index += 1;
-                    } else {
-                        self.snippet_stack.push(snippet);
-                        return false;
-                    }
-                }
-            }
-            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
-                self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                    s.select_anchor_ranges(current_ranges.iter().cloned())
-                });
-                // If snippet state is not at the last tabstop, push it back on the stack
-                if snippet.active_index + 1 < snippet.ranges.len() {
-                    self.snippet_stack.push(snippet);
-                }
-                return true;
-            }
-        }
-
-        false
-    }
-
-    pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
-        self.transact(cx, |this, cx| {
-            this.select_all(&SelectAll, cx);
-            this.insert("", cx);
-        });
-    }
-
-    pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
-        self.transact(cx, |this, cx| {
-            this.select_autoclose_pair(cx);
-            let mut selections = this.selections.all::<Point>(cx);
-            if !this.selections.line_mode {
-                let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
-                for selection in &mut selections {
-                    if selection.is_empty() {
-                        let old_head = selection.head();
-                        let mut new_head =
-                            movement::left(&display_map, old_head.to_display_point(&display_map))
-                                .to_point(&display_map);
-                        if let Some((buffer, line_buffer_range)) = display_map
-                            .buffer_snapshot
-                            .buffer_line_for_row(old_head.row)
-                        {
-                            let indent_size =
-                                buffer.indent_size_for_line(line_buffer_range.start.row);
-                            let indent_len = match indent_size.kind {
-                                IndentKind::Space => {
-                                    buffer.settings_at(line_buffer_range.start, cx).tab_size
-                                }
-                                IndentKind::Tab => NonZeroU32::new(1).unwrap(),
-                            };
-                            if old_head.column <= indent_size.len && old_head.column > 0 {
-                                let indent_len = indent_len.get();
-                                new_head = cmp::min(
-                                    new_head,
-                                    Point::new(
-                                        old_head.row,
-                                        ((old_head.column - 1) / indent_len) * indent_len,
-                                    ),
-                                );
-                            }
-                        }
-
-                        selection.set_head(new_head, SelectionGoal::None);
-                    }
-                }
-            }
-
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
-            this.insert("", cx);
-            this.refresh_copilot_suggestions(true, cx);
-        });
-    }
-
-    pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
-        self.transact(cx, |this, cx| {
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                let line_mode = s.line_mode;
-                s.move_with(|map, selection| {
-                    if selection.is_empty() && !line_mode {
-                        let cursor = movement::right(map, selection.head());
-                        selection.end = cursor;
-                        selection.reversed = true;
-                        selection.goal = SelectionGoal::None;
-                    }
-                })
-            });
-            this.insert("", cx);
-            this.refresh_copilot_suggestions(true, cx);
-        });
-    }
-
-    pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
-        if self.move_to_prev_snippet_tabstop(cx) {
-            return;
-        }
-
-        self.outdent(&Outdent, cx);
-    }
-
-    pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
-        if self.move_to_next_snippet_tabstop(cx) {
-            return;
-        }
-
-        let mut selections = self.selections.all_adjusted(cx);
-        let buffer = self.buffer.read(cx);
-        let snapshot = buffer.snapshot(cx);
-        let rows_iter = selections.iter().map(|s| s.head().row);
-        let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
-
-        let mut edits = Vec::new();
-        let mut prev_edited_row = 0;
-        let mut row_delta = 0;
-        for selection in &mut selections {
-            if selection.start.row != prev_edited_row {
-                row_delta = 0;
-            }
-            prev_edited_row = selection.end.row;
-
-            // If the selection is non-empty, then increase the indentation of the selected lines.
-            if !selection.is_empty() {
-                row_delta =
-                    Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
-                continue;
-            }
-
-            // If the selection is empty and the cursor is in the leading whitespace before the
-            // suggested indentation, then auto-indent the line.
-            let cursor = selection.head();
-            let current_indent = snapshot.indent_size_for_line(cursor.row);
-            if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
-                if cursor.column < suggested_indent.len
-                    && cursor.column <= current_indent.len
-                    && current_indent.len <= suggested_indent.len
-                {
-                    selection.start = Point::new(cursor.row, suggested_indent.len);
-                    selection.end = selection.start;
-                    if row_delta == 0 {
-                        edits.extend(Buffer::edit_for_indent_size_adjustment(
-                            cursor.row,
-                            current_indent,
-                            suggested_indent,
-                        ));
-                        row_delta = suggested_indent.len - current_indent.len;
-                    }
-                    continue;
-                }
-            }
-
-            // Accept copilot suggestion if there is only one selection and the cursor is not
-            // in the leading whitespace.
-            if self.selections.count() == 1
-                && cursor.column >= current_indent.len
-                && self.has_active_copilot_suggestion(cx)
-            {
-                self.accept_copilot_suggestion(cx);
-                return;
-            }
-
-            // Otherwise, insert a hard or soft tab.
-            let settings = buffer.settings_at(cursor, cx);
-            let tab_size = if settings.hard_tabs {
-                IndentSize::tab()
-            } else {
-                let tab_size = settings.tab_size.get();
-                let char_column = snapshot
-                    .text_for_range(Point::new(cursor.row, 0)..cursor)
-                    .flat_map(str::chars)
-                    .count()
-                    + row_delta as usize;
-                let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
-                IndentSize::spaces(chars_to_next_tab_stop)
-            };
-            selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
-            selection.end = selection.start;
-            edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
-            row_delta += tab_size.len;
-        }
-
-        self.transact(cx, |this, cx| {
-            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
-            this.refresh_copilot_suggestions(true, cx);
-        });
-    }
-
-    pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
-        let mut selections = self.selections.all::<Point>(cx);
-        let mut prev_edited_row = 0;
-        let mut row_delta = 0;
-        let mut edits = Vec::new();
-        let buffer = self.buffer.read(cx);
-        let snapshot = buffer.snapshot(cx);
-        for selection in &mut selections {
-            if selection.start.row != prev_edited_row {
-                row_delta = 0;
-            }
-            prev_edited_row = selection.end.row;
-
-            row_delta =
-                Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
-        }
-
-        self.transact(cx, |this, cx| {
-            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
-        });
-    }
-
-    fn indent_selection(
-        buffer: &MultiBuffer,
-        snapshot: &MultiBufferSnapshot,
-        selection: &mut Selection<Point>,
-        edits: &mut Vec<(Range<Point>, String)>,
-        delta_for_start_row: u32,
-        cx: &AppContext,
-    ) -> u32 {
-        let settings = buffer.settings_at(selection.start, cx);
-        let tab_size = settings.tab_size.get();
-        let indent_kind = if settings.hard_tabs {
-            IndentKind::Tab
-        } else {
-            IndentKind::Space
-        };
-        let mut start_row = selection.start.row;
-        let mut end_row = selection.end.row + 1;
-
-        // If a selection ends at the beginning of a line, don't indent
-        // that last line.
-        if selection.end.column == 0 {
-            end_row -= 1;
-        }
-
-        // Avoid re-indenting a row that has already been indented by a
-        // previous selection, but still update this selection's column
-        // to reflect that indentation.
-        if delta_for_start_row > 0 {
-            start_row += 1;
-            selection.start.column += delta_for_start_row;
-            if selection.end.row == selection.start.row {
-                selection.end.column += delta_for_start_row;
-            }
-        }
-
-        let mut delta_for_end_row = 0;
-        for row in start_row..end_row {
-            let current_indent = snapshot.indent_size_for_line(row);
-            let indent_delta = match (current_indent.kind, indent_kind) {
-                (IndentKind::Space, IndentKind::Space) => {
-                    let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
-                    IndentSize::spaces(columns_to_next_tab_stop)
-                }
-                (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
-                (_, IndentKind::Tab) => IndentSize::tab(),
-            };
-
-            let row_start = Point::new(row, 0);
-            edits.push((
-                row_start..row_start,
-                indent_delta.chars().collect::<String>(),
-            ));
-
-            // Update this selection's endpoints to reflect the indentation.
-            if row == selection.start.row {
-                selection.start.column += indent_delta.len;
-            }
-            if row == selection.end.row {
-                selection.end.column += indent_delta.len;
-                delta_for_end_row = indent_delta.len;
-            }
-        }
-
-        if selection.start.row == selection.end.row {
-            delta_for_start_row + delta_for_end_row
-        } else {
-            delta_for_end_row
-        }
-    }
-
-    pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let selections = self.selections.all::<Point>(cx);
-        let mut deletion_ranges = Vec::new();
-        let mut last_outdent = None;
-        {
-            let buffer = self.buffer.read(cx);
-            let snapshot = buffer.snapshot(cx);
-            for selection in &selections {
-                let settings = buffer.settings_at(selection.start, cx);
-                let tab_size = settings.tab_size.get();
-                let mut rows = selection.spanned_rows(false, &display_map);
-
-                // Avoid re-outdenting a row that has already been outdented by a
-                // previous selection.
-                if let Some(last_row) = last_outdent {
-                    if last_row == rows.start {
-                        rows.start += 1;
-                    }
-                }
-
-                for row in rows {
-                    let indent_size = snapshot.indent_size_for_line(row);
-                    if indent_size.len > 0 {
-                        let deletion_len = match indent_size.kind {
-                            IndentKind::Space => {
-                                let columns_to_prev_tab_stop = indent_size.len % tab_size;
-                                if columns_to_prev_tab_stop == 0 {
-                                    tab_size
-                                } else {
-                                    columns_to_prev_tab_stop
-                                }
-                            }
-                            IndentKind::Tab => 1,
-                        };
-                        deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
-                        last_outdent = Some(row);
-                    }
-                }
-            }
-        }
-
-        self.transact(cx, |this, cx| {
-            this.buffer.update(cx, |buffer, cx| {
-                let empty_str: Arc<str> = "".into();
-                buffer.edit(
-                    deletion_ranges
-                        .into_iter()
-                        .map(|range| (range, empty_str.clone())),
-                    None,
-                    cx,
-                );
-            });
-            let selections = this.selections.all::<usize>(cx);
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
-        });
-    }
-
-    pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let selections = self.selections.all::<Point>(cx);
-
-        let mut new_cursors = Vec::new();
-        let mut edit_ranges = Vec::new();
-        let mut selections = selections.iter().peekable();
-        while let Some(selection) = selections.next() {
-            let mut rows = selection.spanned_rows(false, &display_map);
-            let goal_display_column = selection.head().to_display_point(&display_map).column();
-
-            // Accumulate contiguous regions of rows that we want to delete.
-            while let Some(next_selection) = selections.peek() {
-                let next_rows = next_selection.spanned_rows(false, &display_map);
-                if next_rows.start <= rows.end {
-                    rows.end = next_rows.end;
-                    selections.next().unwrap();
-                } else {
-                    break;
-                }
-            }
-
-            let buffer = &display_map.buffer_snapshot;
-            let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
-            let edit_end;
-            let cursor_buffer_row;
-            if buffer.max_point().row >= rows.end {
-                // If there's a line after the range, delete the \n from the end of the row range
-                // and position the cursor on the next line.
-                edit_end = Point::new(rows.end, 0).to_offset(buffer);
-                cursor_buffer_row = rows.end;
-            } else {
-                // If there isn't a line after the range, delete the \n from the line before the
-                // start of the row range and position the cursor there.
-                edit_start = edit_start.saturating_sub(1);
-                edit_end = buffer.len();
-                cursor_buffer_row = rows.start.saturating_sub(1);
-            }
-
-            let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
-            *cursor.column_mut() =
-                cmp::min(goal_display_column, display_map.line_len(cursor.row()));
-
-            new_cursors.push((
-                selection.id,
-                buffer.anchor_after(cursor.to_point(&display_map)),
-            ));
-            edit_ranges.push(edit_start..edit_end);
-        }
-
-        self.transact(cx, |this, cx| {
-            let buffer = this.buffer.update(cx, |buffer, cx| {
-                let empty_str: Arc<str> = "".into();
-                buffer.edit(
-                    edit_ranges
-                        .into_iter()
-                        .map(|range| (range, empty_str.clone())),
-                    None,
-                    cx,
-                );
-                buffer.snapshot(cx)
-            });
-            let new_selections = new_cursors
-                .into_iter()
-                .map(|(id, cursor)| {
-                    let cursor = cursor.to_point(&buffer);
-                    Selection {
-                        id,
-                        start: cursor,
-                        end: cursor,
-                        reversed: false,
-                        goal: SelectionGoal::None,
-                    }
-                })
-                .collect();
-
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.select(new_selections);
-            });
-        });
-    }
-
-    pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
-        let mut row_ranges = Vec::<Range<u32>>::new();
-        for selection in self.selections.all::<Point>(cx) {
-            let start = selection.start.row;
-            let end = if selection.start.row == selection.end.row {
-                selection.start.row + 1
-            } else {
-                selection.end.row
-            };
-
-            if let Some(last_row_range) = row_ranges.last_mut() {
-                if start <= last_row_range.end {
-                    last_row_range.end = end;
-                    continue;
-                }
-            }
-            row_ranges.push(start..end);
-        }
-
-        let snapshot = self.buffer.read(cx).snapshot(cx);
-        let mut cursor_positions = Vec::new();
-        for row_range in &row_ranges {
-            let anchor = snapshot.anchor_before(Point::new(
-                row_range.end - 1,
-                snapshot.line_len(row_range.end - 1),
-            ));
-            cursor_positions.push(anchor.clone()..anchor);
-        }
-
-        self.transact(cx, |this, cx| {
-            for row_range in row_ranges.into_iter().rev() {
-                for row in row_range.rev() {
-                    let end_of_line = Point::new(row, snapshot.line_len(row));
-                    let indent = snapshot.indent_size_for_line(row + 1);
-                    let start_of_next_line = Point::new(row + 1, indent.len);
-
-                    let replace = if snapshot.line_len(row + 1) > indent.len {
-                        " "
-                    } else {
-                        ""
-                    };
-
-                    this.buffer.update(cx, |buffer, cx| {
-                        buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
-                    });
-                }
-            }
-
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.select_anchor_ranges(cursor_positions)
-            });
-        });
-    }
-
-    pub fn sort_lines_case_sensitive(
-        &mut self,
-        _: &SortLinesCaseSensitive,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.manipulate_lines(cx, |lines| lines.sort())
-    }
-
-    pub fn sort_lines_case_insensitive(
-        &mut self,
-        _: &SortLinesCaseInsensitive,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
-    }
-
-    pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
-        self.manipulate_lines(cx, |lines| lines.reverse())
-    }
-
-    pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
-        self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
-    }
-
-    fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
-    where
-        Fn: FnMut(&mut [&str]),
-    {
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let buffer = self.buffer.read(cx).snapshot(cx);
-
-        let mut edits = Vec::new();
-
-        let selections = self.selections.all::<Point>(cx);
-        let mut selections = selections.iter().peekable();
-        let mut contiguous_row_selections = Vec::new();
-        let mut new_selections = Vec::new();
-
-        while let Some(selection) = selections.next() {
-            let (start_row, end_row) = consume_contiguous_rows(
-                &mut contiguous_row_selections,
-                selection,
-                &display_map,
-                &mut selections,
-            );
-
-            let start_point = Point::new(start_row, 0);
-            let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1));
-            let text = buffer
-                .text_for_range(start_point..end_point)
-                .collect::<String>();
-            let mut lines = text.split("\n").collect_vec();
-
-            let lines_len = lines.len();
-            callback(&mut lines);
-
-            // This is a current limitation with selections.
-            // If we wanted to support removing or adding lines, we'd need to fix the logic associated with selections.
-            debug_assert!(
-                lines.len() == lines_len,
-                "callback should not change the number of lines"
-            );
-
-            edits.push((start_point..end_point, lines.join("\n")));
-            let start_anchor = buffer.anchor_after(start_point);
-            let end_anchor = buffer.anchor_before(end_point);
-
-            // Make selection and push
-            new_selections.push(Selection {
-                id: selection.id,
-                start: start_anchor.to_offset(&buffer),
-                end: end_anchor.to_offset(&buffer),
-                goal: SelectionGoal::None,
-                reversed: selection.reversed,
-            });
-        }
-
-        self.transact(cx, |this, cx| {
-            this.buffer.update(cx, |buffer, cx| {
-                buffer.edit(edits, None, cx);
-            });
-
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.select(new_selections);
-            });
-
-            this.request_autoscroll(Autoscroll::fit(), cx);
-        });
-    }
-
-    pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
-        self.manipulate_text(cx, |text| text.to_uppercase())
-    }
-
-    pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
-        self.manipulate_text(cx, |text| text.to_lowercase())
-    }
-
-    pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
-        self.manipulate_text(cx, |text| {
-            // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
-            // https://github.com/rutrum/convert-case/issues/16
-            text.split("\n")
-                .map(|line| line.to_case(Case::Title))
-                .join("\n")
-        })
-    }
-
-    pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
-        self.manipulate_text(cx, |text| text.to_case(Case::Snake))
-    }
-
-    pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
-        self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
-    }
-
-    pub fn convert_to_upper_camel_case(
-        &mut self,
-        _: &ConvertToUpperCamelCase,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.manipulate_text(cx, |text| {
-            // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
-            // https://github.com/rutrum/convert-case/issues/16
-            text.split("\n")
-                .map(|line| line.to_case(Case::UpperCamel))
-                .join("\n")
-        })
-    }
-
-    pub fn convert_to_lower_camel_case(
-        &mut self,
-        _: &ConvertToLowerCamelCase,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.manipulate_text(cx, |text| text.to_case(Case::Camel))
-    }
-
-    fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
-    where
-        Fn: FnMut(&str) -> String,
-    {
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let buffer = self.buffer.read(cx).snapshot(cx);
-
-        let mut new_selections = Vec::new();
-        let mut edits = Vec::new();
-        let mut selection_adjustment = 0i32;
-
-        for selection in self.selections.all::<usize>(cx) {
-            let selection_is_empty = selection.is_empty();
-
-            let (start, end) = if selection_is_empty {
-                let word_range = movement::surrounding_word(
-                    &display_map,
-                    selection.start.to_display_point(&display_map),
-                );
-                let start = word_range.start.to_offset(&display_map, Bias::Left);
-                let end = word_range.end.to_offset(&display_map, Bias::Left);
-                (start, end)
-            } else {
-                (selection.start, selection.end)
-            };
-
-            let text = buffer.text_for_range(start..end).collect::<String>();
-            let old_length = text.len() as i32;
-            let text = callback(&text);
-
-            new_selections.push(Selection {
-                start: (start as i32 - selection_adjustment) as usize,
-                end: ((start + text.len()) as i32 - selection_adjustment) as usize,
-                goal: SelectionGoal::None,
-                ..selection
-            });
-
-            selection_adjustment += old_length - text.len() as i32;
-
-            edits.push((start..end, text));
-        }
-
-        self.transact(cx, |this, cx| {
-            this.buffer.update(cx, |buffer, cx| {
-                buffer.edit(edits, None, cx);
-            });
-
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.select(new_selections);
-            });
-
-            this.request_autoscroll(Autoscroll::fit(), cx);
-        });
-    }
-
-    pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let buffer = &display_map.buffer_snapshot;
-        let selections = self.selections.all::<Point>(cx);
-
-        let mut edits = Vec::new();
-        let mut selections_iter = selections.iter().peekable();
-        while let Some(selection) = selections_iter.next() {
-            // Avoid duplicating the same lines twice.
-            let mut rows = selection.spanned_rows(false, &display_map);
-
-            while let Some(next_selection) = selections_iter.peek() {
-                let next_rows = next_selection.spanned_rows(false, &display_map);
-                if next_rows.start < rows.end {
-                    rows.end = next_rows.end;
-                    selections_iter.next().unwrap();
-                } else {
-                    break;
-                }
-            }
-
-            // Copy the text from the selected row region and splice it at the start of the region.
-            let start = Point::new(rows.start, 0);
-            let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
-            let text = buffer
-                .text_for_range(start..end)
-                .chain(Some("\n"))
-                .collect::<String>();
-            edits.push((start..start, text));
-        }
-
-        self.transact(cx, |this, cx| {
-            this.buffer.update(cx, |buffer, cx| {
-                buffer.edit(edits, None, cx);
-            });
-
-            this.request_autoscroll(Autoscroll::fit(), cx);
-        });
-    }
-
-    pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let buffer = self.buffer.read(cx).snapshot(cx);
-
-        let mut edits = Vec::new();
-        let mut unfold_ranges = Vec::new();
-        let mut refold_ranges = Vec::new();
-
-        let selections = self.selections.all::<Point>(cx);
-        let mut selections = selections.iter().peekable();
-        let mut contiguous_row_selections = Vec::new();
-        let mut new_selections = Vec::new();
-
-        while let Some(selection) = selections.next() {
-            // Find all the selections that span a contiguous row range
-            let (start_row, end_row) = consume_contiguous_rows(
-                &mut contiguous_row_selections,
-                selection,
-                &display_map,
-                &mut selections,
-            );
-
-            // Move the text spanned by the row range to be before the line preceding the row range
-            if start_row > 0 {
-                let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
-                    ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
-                let insertion_point = display_map
-                    .prev_line_boundary(Point::new(start_row - 1, 0))
-                    .0;
-
-                // Don't move lines across excerpts
-                if buffer
-                    .excerpt_boundaries_in_range((
-                        Bound::Excluded(insertion_point),
-                        Bound::Included(range_to_move.end),
-                    ))
-                    .next()
-                    .is_none()
-                {
-                    let text = buffer
-                        .text_for_range(range_to_move.clone())
-                        .flat_map(|s| s.chars())
-                        .skip(1)
-                        .chain(['\n'])
-                        .collect::<String>();
-
-                    edits.push((
-                        buffer.anchor_after(range_to_move.start)
-                            ..buffer.anchor_before(range_to_move.end),
-                        String::new(),
-                    ));
-                    let insertion_anchor = buffer.anchor_after(insertion_point);
-                    edits.push((insertion_anchor..insertion_anchor, text));
-
-                    let row_delta = range_to_move.start.row - insertion_point.row + 1;
-
-                    // Move selections up
-                    new_selections.extend(contiguous_row_selections.drain(..).map(
-                        |mut selection| {
-                            selection.start.row -= row_delta;
-                            selection.end.row -= row_delta;
-                            selection
-                        },
-                    ));
-
-                    // Move folds up
-                    unfold_ranges.push(range_to_move.clone());
-                    for fold in display_map.folds_in_range(
-                        buffer.anchor_before(range_to_move.start)
-                            ..buffer.anchor_after(range_to_move.end),
-                    ) {
-                        let mut start = fold.range.start.to_point(&buffer);
-                        let mut end = fold.range.end.to_point(&buffer);
-                        start.row -= row_delta;
-                        end.row -= row_delta;
-                        refold_ranges.push(start..end);
-                    }
-                }
-            }
-
-            // If we didn't move line(s), preserve the existing selections
-            new_selections.append(&mut contiguous_row_selections);
-        }
-
-        self.transact(cx, |this, cx| {
-            this.unfold_ranges(unfold_ranges, true, true, cx);
-            this.buffer.update(cx, |buffer, cx| {
-                for (range, text) in edits {
-                    buffer.edit([(range, text)], None, cx);
-                }
-            });
-            this.fold_ranges(refold_ranges, true, cx);
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.select(new_selections);
-            })
-        });
-    }
-
-    pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let buffer = self.buffer.read(cx).snapshot(cx);
-
-        let mut edits = Vec::new();
-        let mut unfold_ranges = Vec::new();
-        let mut refold_ranges = Vec::new();
-
-        let selections = self.selections.all::<Point>(cx);
-        let mut selections = selections.iter().peekable();
-        let mut contiguous_row_selections = Vec::new();
-        let mut new_selections = Vec::new();
-
-        while let Some(selection) = selections.next() {
-            // Find all the selections that span a contiguous row range
-            let (start_row, end_row) = consume_contiguous_rows(
-                &mut contiguous_row_selections,
-                selection,
-                &display_map,
-                &mut selections,
-            );
-
-            // Move the text spanned by the row range to be after the last line of the row range
-            if end_row <= buffer.max_point().row {
-                let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
-                let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
-
-                // Don't move lines across excerpt boundaries
-                if buffer
-                    .excerpt_boundaries_in_range((
-                        Bound::Excluded(range_to_move.start),
-                        Bound::Included(insertion_point),
-                    ))
-                    .next()
-                    .is_none()
-                {
-                    let mut text = String::from("\n");
-                    text.extend(buffer.text_for_range(range_to_move.clone()));
-                    text.pop(); // Drop trailing newline
-                    edits.push((
-                        buffer.anchor_after(range_to_move.start)
-                            ..buffer.anchor_before(range_to_move.end),
-                        String::new(),
-                    ));
-                    let insertion_anchor = buffer.anchor_after(insertion_point);
-                    edits.push((insertion_anchor..insertion_anchor, text));
-
-                    let row_delta = insertion_point.row - range_to_move.end.row + 1;
-
-                    // Move selections down
-                    new_selections.extend(contiguous_row_selections.drain(..).map(
-                        |mut selection| {
-                            selection.start.row += row_delta;
-                            selection.end.row += row_delta;
-                            selection
-                        },
-                    ));
-
-                    // Move folds down
-                    unfold_ranges.push(range_to_move.clone());
-                    for fold in display_map.folds_in_range(
-                        buffer.anchor_before(range_to_move.start)
-                            ..buffer.anchor_after(range_to_move.end),
-                    ) {
-                        let mut start = fold.range.start.to_point(&buffer);
-                        let mut end = fold.range.end.to_point(&buffer);
-                        start.row += row_delta;
-                        end.row += row_delta;
-                        refold_ranges.push(start..end);
-                    }
-                }
-            }
-
-            // If we didn't move line(s), preserve the existing selections
-            new_selections.append(&mut contiguous_row_selections);
-        }
-
-        self.transact(cx, |this, cx| {
-            this.unfold_ranges(unfold_ranges, true, true, cx);
-            this.buffer.update(cx, |buffer, cx| {
-                for (range, text) in edits {
-                    buffer.edit([(range, text)], None, cx);
-                }
-            });
-            this.fold_ranges(refold_ranges, true, cx);
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
-        });
-    }
-
-    pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
-        let text_layout_details = &self.text_layout_details(cx);
-        self.transact(cx, |this, cx| {
-            let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                let mut edits: Vec<(Range<usize>, String)> = Default::default();
-                let line_mode = s.line_mode;
-                s.move_with(|display_map, selection| {
-                    if !selection.is_empty() || line_mode {
-                        return;
-                    }
-
-                    let mut head = selection.head();
-                    let mut transpose_offset = head.to_offset(display_map, Bias::Right);
-                    if head.column() == display_map.line_len(head.row()) {
-                        transpose_offset = display_map
-                            .buffer_snapshot
-                            .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
-                    }
-
-                    if transpose_offset == 0 {
-                        return;
-                    }
-
-                    *head.column_mut() += 1;
-                    head = display_map.clip_point(head, Bias::Right);
-                    let goal = SelectionGoal::HorizontalPosition(
-                        display_map
-                            .x_for_display_point(head, &text_layout_details)
-                            .into(),
-                    );
-                    selection.collapse_to(head, goal);
-
-                    let transpose_start = display_map
-                        .buffer_snapshot
-                        .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
-                    if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
-                        let transpose_end = display_map
-                            .buffer_snapshot
-                            .clip_offset(transpose_offset + 1, Bias::Right);
-                        if let Some(ch) =
-                            display_map.buffer_snapshot.chars_at(transpose_start).next()
-                        {
-                            edits.push((transpose_start..transpose_offset, String::new()));
-                            edits.push((transpose_end..transpose_end, ch.to_string()));
-                        }
-                    }
-                });
-                edits
-            });
-            this.buffer
-                .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
-            let selections = this.selections.all::<usize>(cx);
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.select(selections);
-            });
-        });
-    }
-
-    pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
-        let mut text = String::new();
-        let buffer = self.buffer.read(cx).snapshot(cx);
-        let mut selections = self.selections.all::<Point>(cx);
-        let mut clipboard_selections = Vec::with_capacity(selections.len());
-        {
-            let max_point = buffer.max_point();
-            let mut is_first = true;
-            for selection in &mut selections {
-                let is_entire_line = selection.is_empty() || self.selections.line_mode;
-                if is_entire_line {
-                    selection.start = Point::new(selection.start.row, 0);
-                    selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
-                    selection.goal = SelectionGoal::None;
-                }
-                if is_first {
-                    is_first = false;
-                } else {
-                    text += "\n";
-                }
-                let mut len = 0;
-                for chunk in buffer.text_for_range(selection.start..selection.end) {
-                    text.push_str(chunk);
-                    len += chunk.len();
-                }
-                clipboard_selections.push(ClipboardSelection {
-                    len,
-                    is_entire_line,
-                    first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
-                });
-            }
-        }
-
-        self.transact(cx, |this, cx| {
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.select(selections);
-            });
-            this.insert("", cx);
-            cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
-        });
-    }
-
-    pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
-        let selections = self.selections.all::<Point>(cx);
-        let buffer = self.buffer.read(cx).read(cx);
-        let mut text = String::new();
-
-        let mut clipboard_selections = Vec::with_capacity(selections.len());
-        {
-            let max_point = buffer.max_point();
-            let mut is_first = true;
-            for selection in selections.iter() {
-                let mut start = selection.start;
-                let mut end = selection.end;
-                let is_entire_line = selection.is_empty() || self.selections.line_mode;
-                if is_entire_line {
-                    start = Point::new(start.row, 0);
-                    end = cmp::min(max_point, Point::new(end.row + 1, 0));
-                }
-                if is_first {
-                    is_first = false;
-                } else {
-                    text += "\n";
-                }
-                let mut len = 0;
-                for chunk in buffer.text_for_range(start..end) {
-                    text.push_str(chunk);
-                    len += chunk.len();
-                }
-                clipboard_selections.push(ClipboardSelection {
-                    len,
-                    is_entire_line,
-                    first_line_indent: buffer.indent_size_for_line(start.row).len,
-                });
-            }
-        }
-
-        cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
-    }
-
-    pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
-        self.transact(cx, |this, cx| {
-            if let Some(item) = cx.read_from_clipboard() {
-                let clipboard_text = Cow::Borrowed(item.text());
-                if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
-                    let old_selections = this.selections.all::<usize>(cx);
-                    let all_selections_were_entire_line =
-                        clipboard_selections.iter().all(|s| s.is_entire_line);
-                    let first_selection_indent_column =
-                        clipboard_selections.first().map(|s| s.first_line_indent);
-                    if clipboard_selections.len() != old_selections.len() {
-                        clipboard_selections.drain(..);
-                    }
-
-                    this.buffer.update(cx, |buffer, cx| {
-                        let snapshot = buffer.read(cx);
-                        let mut start_offset = 0;
-                        let mut edits = Vec::new();
-                        let mut original_indent_columns = Vec::new();
-                        let line_mode = this.selections.line_mode;
-                        for (ix, selection) in old_selections.iter().enumerate() {
-                            let to_insert;
-                            let entire_line;
-                            let original_indent_column;
-                            if let Some(clipboard_selection) = clipboard_selections.get(ix) {
-                                let end_offset = start_offset + clipboard_selection.len;
-                                to_insert = &clipboard_text[start_offset..end_offset];
-                                entire_line = clipboard_selection.is_entire_line;
-                                start_offset = end_offset + 1;
-                                original_indent_column =
-                                    Some(clipboard_selection.first_line_indent);
-                            } else {
-                                to_insert = clipboard_text.as_str();
-                                entire_line = all_selections_were_entire_line;
-                                original_indent_column = first_selection_indent_column
-                            }
-
-                            // If the corresponding selection was empty when this slice of the
-                            // clipboard text was written, then the entire line containing the
-                            // selection was copied. If this selection is also currently empty,
-                            // then paste the line before the current line of the buffer.
-                            let range = if selection.is_empty() && !line_mode && entire_line {
-                                let column = selection.start.to_point(&snapshot).column as usize;
-                                let line_start = selection.start - column;
-                                line_start..line_start
-                            } else {
-                                selection.range()
-                            };
-
-                            edits.push((range, to_insert));
-                            original_indent_columns.extend(original_indent_column);
-                        }
-                        drop(snapshot);
-
-                        buffer.edit(
-                            edits,
-                            Some(AutoindentMode::Block {
-                                original_indent_columns,
-                            }),
-                            cx,
-                        );
-                    });
-
-                    let selections = this.selections.all::<usize>(cx);
-                    this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
-                } else {
-                    this.insert(&clipboard_text, cx);
-                }
-            }
-        });
-    }
-
-    pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
-        if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
-            if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
-                self.change_selections(None, cx, |s| {
-                    s.select_anchors(selections.to_vec());
-                });
-            }
-            self.request_autoscroll(Autoscroll::fit(), cx);
-            self.unmark_text(cx);
-            self.refresh_copilot_suggestions(true, cx);
-            cx.emit(EditorEvent::Edited);
-        }
-    }
-
-    pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
-        if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
-            if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
-            {
-                self.change_selections(None, cx, |s| {
-                    s.select_anchors(selections.to_vec());
-                });
-            }
-            self.request_autoscroll(Autoscroll::fit(), cx);
-            self.unmark_text(cx);
-            self.refresh_copilot_suggestions(true, cx);
-            cx.emit(EditorEvent::Edited);
-        }
-    }
-
-    pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
-        self.buffer
-            .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
-    }
-
-    pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            let line_mode = s.line_mode;
-            s.move_with(|map, selection| {
-                let cursor = if selection.is_empty() && !line_mode {
-                    movement::left(map, selection.start)
-                } else {
-                    selection.start
-                };
-                selection.collapse_to(cursor, SelectionGoal::None);
-            });
-        })
-    }
-
-    pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
-        })
-    }
-
-    pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            let line_mode = s.line_mode;
-            s.move_with(|map, selection| {
-                let cursor = if selection.is_empty() && !line_mode {
-                    movement::right(map, selection.end)
-                } else {
-                    selection.end
-                };
-                selection.collapse_to(cursor, SelectionGoal::None)
-            });
-        })
-    }
-
-    pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
-        })
-    }
-
-    pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
-        if self.take_rename(true, cx).is_some() {
-            return;
-        }
-
-        if matches!(self.mode, EditorMode::SingleLine) {
-            cx.propagate();
-            return;
-        }
-
-        let text_layout_details = &self.text_layout_details(cx);
-
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            let line_mode = s.line_mode;
-            s.move_with(|map, selection| {
-                if !selection.is_empty() && !line_mode {
-                    selection.goal = SelectionGoal::None;
-                }
-                let (cursor, goal) = movement::up(
-                    map,
-                    selection.start,
-                    selection.goal,
-                    false,
-                    &text_layout_details,
-                );
-                selection.collapse_to(cursor, goal);
-            });
-        })
-    }
-
-    pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
-        if self.take_rename(true, cx).is_some() {
-            return;
-        }
-
-        if matches!(self.mode, EditorMode::SingleLine) {
-            cx.propagate();
-            return;
-        }
-
-        let row_count = if let Some(row_count) = self.visible_line_count() {
-            row_count as u32 - 1
-        } else {
-            return;
-        };
-
-        let autoscroll = if action.center_cursor {
-            Autoscroll::center()
-        } else {
-            Autoscroll::fit()
-        };
-
-        let text_layout_details = &self.text_layout_details(cx);
-
-        self.change_selections(Some(autoscroll), cx, |s| {
-            let line_mode = s.line_mode;
-            s.move_with(|map, selection| {
-                if !selection.is_empty() && !line_mode {
-                    selection.goal = SelectionGoal::None;
-                }
-                let (cursor, goal) = movement::up_by_rows(
-                    map,
-                    selection.end,
-                    row_count,
-                    selection.goal,
-                    false,
-                    &text_layout_details,
-                );
-                selection.collapse_to(cursor, goal);
-            });
-        });
-    }
-
-    pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
-        let text_layout_details = &self.text_layout_details(cx);
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_heads_with(|map, head, goal| {
-                movement::up(map, head, goal, false, &text_layout_details)
-            })
-        })
-    }
-
-    pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
-        self.take_rename(true, cx);
-
-        if self.mode == EditorMode::SingleLine {
-            cx.propagate();
-            return;
-        }
-
-        let text_layout_details = &self.text_layout_details(cx);
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            let line_mode = s.line_mode;
-            s.move_with(|map, selection| {
-                if !selection.is_empty() && !line_mode {
-                    selection.goal = SelectionGoal::None;
-                }
-                let (cursor, goal) = movement::down(
-                    map,
-                    selection.end,
-                    selection.goal,
-                    false,
-                    &text_layout_details,
-                );
-                selection.collapse_to(cursor, goal);
-            });
-        });
-    }
-
-    pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
-        if self.take_rename(true, cx).is_some() {
-            return;
-        }
-
-        if self
-            .context_menu
-            .write()
-            .as_mut()
-            .map(|menu| menu.select_last(self.project.as_ref(), cx))
-            .unwrap_or(false)
-        {
-            return;
-        }
-
-        if matches!(self.mode, EditorMode::SingleLine) {
-            cx.propagate();
-            return;
-        }
-
-        let row_count = if let Some(row_count) = self.visible_line_count() {
-            row_count as u32 - 1
-        } else {
-            return;
-        };
-
-        let autoscroll = if action.center_cursor {
-            Autoscroll::center()
-        } else {
-            Autoscroll::fit()
-        };
-
-        let text_layout_details = &self.text_layout_details(cx);
-        self.change_selections(Some(autoscroll), cx, |s| {
-            let line_mode = s.line_mode;
-            s.move_with(|map, selection| {
-                if !selection.is_empty() && !line_mode {
-                    selection.goal = SelectionGoal::None;
-                }
-                let (cursor, goal) = movement::down_by_rows(
-                    map,
-                    selection.end,
-                    row_count,
-                    selection.goal,
-                    false,
-                    &text_layout_details,
-                );
-                selection.collapse_to(cursor, goal);
-            });
-        });
-    }
-
-    pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
-        let text_layout_details = &self.text_layout_details(cx);
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_heads_with(|map, head, goal| {
-                movement::down(map, head, goal, false, &text_layout_details)
-            })
-        });
-    }
-
-    pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
-        if let Some(context_menu) = self.context_menu.write().as_mut() {
-            context_menu.select_first(self.project.as_ref(), cx);
-        }
-    }
-
-    pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
-        if let Some(context_menu) = self.context_menu.write().as_mut() {
-            context_menu.select_prev(self.project.as_ref(), cx);
-        }
-    }
-
-    pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
-        if let Some(context_menu) = self.context_menu.write().as_mut() {
-            context_menu.select_next(self.project.as_ref(), cx);
-        }
-    }
-
-    pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
-        if let Some(context_menu) = self.context_menu.write().as_mut() {
-            context_menu.select_last(self.project.as_ref(), cx);
-        }
-    }
-
-    pub fn move_to_previous_word_start(
-        &mut self,
-        _: &MoveToPreviousWordStart,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_cursors_with(|map, head, _| {
-                (
-                    movement::previous_word_start(map, head),
-                    SelectionGoal::None,
-                )
-            });
-        })
-    }
-
-    pub fn move_to_previous_subword_start(
-        &mut self,
-        _: &MoveToPreviousSubwordStart,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_cursors_with(|map, head, _| {
-                (
-                    movement::previous_subword_start(map, head),
-                    SelectionGoal::None,
-                )
-            });
-        })
-    }
-
-    pub fn select_to_previous_word_start(
-        &mut self,
-        _: &SelectToPreviousWordStart,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_heads_with(|map, head, _| {
-                (
-                    movement::previous_word_start(map, head),
-                    SelectionGoal::None,
-                )
-            });
-        })
-    }
-
-    pub fn select_to_previous_subword_start(
-        &mut self,
-        _: &SelectToPreviousSubwordStart,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_heads_with(|map, head, _| {
-                (
-                    movement::previous_subword_start(map, head),
-                    SelectionGoal::None,
-                )
-            });
-        })
-    }
-
-    pub fn delete_to_previous_word_start(
-        &mut self,
-        _: &DeleteToPreviousWordStart,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.transact(cx, |this, cx| {
-            this.select_autoclose_pair(cx);
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                let line_mode = s.line_mode;
-                s.move_with(|map, selection| {
-                    if selection.is_empty() && !line_mode {
-                        let cursor = movement::previous_word_start(map, selection.head());
-                        selection.set_head(cursor, SelectionGoal::None);
-                    }
-                });
-            });
-            this.insert("", cx);
-        });
-    }
-
-    pub fn delete_to_previous_subword_start(
-        &mut self,
-        _: &DeleteToPreviousSubwordStart,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.transact(cx, |this, cx| {
-            this.select_autoclose_pair(cx);
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                let line_mode = s.line_mode;
-                s.move_with(|map, selection| {
-                    if selection.is_empty() && !line_mode {
-                        let cursor = movement::previous_subword_start(map, selection.head());
-                        selection.set_head(cursor, SelectionGoal::None);
-                    }
-                });
-            });
-            this.insert("", cx);
-        });
-    }
-
-    pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_cursors_with(|map, head, _| {
-                (movement::next_word_end(map, head), SelectionGoal::None)
-            });
-        })
-    }
-
-    pub fn move_to_next_subword_end(
-        &mut self,
-        _: &MoveToNextSubwordEnd,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_cursors_with(|map, head, _| {
-                (movement::next_subword_end(map, head), SelectionGoal::None)
-            });
-        })
-    }
-
-    pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_heads_with(|map, head, _| {
-                (movement::next_word_end(map, head), SelectionGoal::None)
-            });
-        })
-    }
-
-    pub fn select_to_next_subword_end(
-        &mut self,
-        _: &SelectToNextSubwordEnd,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_heads_with(|map, head, _| {
-                (movement::next_subword_end(map, head), SelectionGoal::None)
-            });
-        })
-    }
-
-    pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
-        self.transact(cx, |this, cx| {
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                let line_mode = s.line_mode;
-                s.move_with(|map, selection| {
-                    if selection.is_empty() && !line_mode {
-                        let cursor = movement::next_word_end(map, selection.head());
-                        selection.set_head(cursor, SelectionGoal::None);
-                    }
-                });
-            });
-            this.insert("", cx);
-        });
-    }
-
-    pub fn delete_to_next_subword_end(
-        &mut self,
-        _: &DeleteToNextSubwordEnd,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.transact(cx, |this, cx| {
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.move_with(|map, selection| {
-                    if selection.is_empty() {
-                        let cursor = movement::next_subword_end(map, selection.head());
-                        selection.set_head(cursor, SelectionGoal::None);
-                    }
-                });
-            });
-            this.insert("", cx);
-        });
-    }
-
-    pub fn move_to_beginning_of_line(
-        &mut self,
-        _: &MoveToBeginningOfLine,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_cursors_with(|map, head, _| {
-                (
-                    movement::indented_line_beginning(map, head, true),
-                    SelectionGoal::None,
-                )
-            });
-        })
-    }
-
-    pub fn select_to_beginning_of_line(
-        &mut self,
-        action: &SelectToBeginningOfLine,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_heads_with(|map, head, _| {
-                (
-                    movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
-                    SelectionGoal::None,
-                )
-            });
-        });
-    }
-
-    pub fn delete_to_beginning_of_line(
-        &mut self,
-        _: &DeleteToBeginningOfLine,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.transact(cx, |this, cx| {
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.move_with(|_, selection| {
-                    selection.reversed = true;
-                });
-            });
-
-            this.select_to_beginning_of_line(
-                &SelectToBeginningOfLine {
-                    stop_at_soft_wraps: false,
-                },
-                cx,
-            );
-            this.backspace(&Backspace, cx);
-        });
-    }
-
-    pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_cursors_with(|map, head, _| {
-                (movement::line_end(map, head, true), SelectionGoal::None)
-            });
-        })
-    }
-
-    pub fn select_to_end_of_line(
-        &mut self,
-        action: &SelectToEndOfLine,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_heads_with(|map, head, _| {
-                (
-                    movement::line_end(map, head, action.stop_at_soft_wraps),
-                    SelectionGoal::None,
-                )
-            });
-        })
-    }
-
-    pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
-        self.transact(cx, |this, cx| {
-            this.select_to_end_of_line(
-                &SelectToEndOfLine {
-                    stop_at_soft_wraps: false,
-                },
-                cx,
-            );
-            this.delete(&Delete, cx);
-        });
-    }
-
-    pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
-        self.transact(cx, |this, cx| {
-            this.select_to_end_of_line(
-                &SelectToEndOfLine {
-                    stop_at_soft_wraps: false,
-                },
-                cx,
-            );
-            this.cut(&Cut, cx);
-        });
-    }
-
-    pub fn move_to_start_of_paragraph(
-        &mut self,
-        _: &MoveToStartOfParagraph,
-        cx: &mut ViewContext<Self>,
-    ) {
-        if matches!(self.mode, EditorMode::SingleLine) {
-            cx.propagate();
-            return;
-        }
-
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_with(|map, selection| {
-                selection.collapse_to(
-                    movement::start_of_paragraph(map, selection.head(), 1),
-                    SelectionGoal::None,
-                )
-            });
-        })
-    }
-
-    pub fn move_to_end_of_paragraph(
-        &mut self,
-        _: &MoveToEndOfParagraph,
-        cx: &mut ViewContext<Self>,
-    ) {
-        if matches!(self.mode, EditorMode::SingleLine) {
-            cx.propagate();
-            return;
-        }
-
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_with(|map, selection| {
-                selection.collapse_to(
-                    movement::end_of_paragraph(map, selection.head(), 1),
-                    SelectionGoal::None,
-                )
-            });
-        })
-    }
-
-    pub fn select_to_start_of_paragraph(
-        &mut self,
-        _: &SelectToStartOfParagraph,
-        cx: &mut ViewContext<Self>,
-    ) {
-        if matches!(self.mode, EditorMode::SingleLine) {
-            cx.propagate();
-            return;
-        }
-
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_heads_with(|map, head, _| {
-                (
-                    movement::start_of_paragraph(map, head, 1),
-                    SelectionGoal::None,
-                )
-            });
-        })
-    }
-
-    pub fn select_to_end_of_paragraph(
-        &mut self,
-        _: &SelectToEndOfParagraph,
-        cx: &mut ViewContext<Self>,
-    ) {
-        if matches!(self.mode, EditorMode::SingleLine) {
-            cx.propagate();
-            return;
-        }
-
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_heads_with(|map, head, _| {
-                (
-                    movement::end_of_paragraph(map, head, 1),
-                    SelectionGoal::None,
-                )
-            });
-        })
-    }
-
-    pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
-        if matches!(self.mode, EditorMode::SingleLine) {
-            cx.propagate();
-            return;
-        }
-
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.select_ranges(vec![0..0]);
-        });
-    }
-
-    pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
-        let mut selection = self.selections.last::<Point>(cx);
-        selection.set_head(Point::zero(), SelectionGoal::None);
-
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.select(vec![selection]);
-        });
-    }
-
-    pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
-        if matches!(self.mode, EditorMode::SingleLine) {
-            cx.propagate();
-            return;
-        }
-
-        let cursor = self.buffer.read(cx).read(cx).len();
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.select_ranges(vec![cursor..cursor])
-        });
-    }
-
-    pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
-        self.nav_history = nav_history;
-    }
-
-    pub fn nav_history(&self) -> Option<&ItemNavHistory> {
-        self.nav_history.as_ref()
-    }
-
-    fn push_to_nav_history(
-        &mut self,
-        cursor_anchor: Anchor,
-        new_position: Option<Point>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        if let Some(nav_history) = self.nav_history.as_mut() {
-            let buffer = self.buffer.read(cx).read(cx);
-            let cursor_position = cursor_anchor.to_point(&buffer);
-            let scroll_state = self.scroll_manager.anchor();
-            let scroll_top_row = scroll_state.top_row(&buffer);
-            drop(buffer);
-
-            if let Some(new_position) = new_position {
-                let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
-                if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
-                    return;
-                }
-            }
-
-            nav_history.push(
-                Some(NavigationData {
-                    cursor_anchor,
-                    cursor_position,
-                    scroll_anchor: scroll_state,
-                    scroll_top_row,
-                }),
-                cx,
-            );
-        }
-    }
-
-    pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
-        let buffer = self.buffer.read(cx).snapshot(cx);
-        let mut selection = self.selections.first::<usize>(cx);
-        selection.set_head(buffer.len(), SelectionGoal::None);
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.select(vec![selection]);
-        });
-    }
-
-    pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
-        let end = self.buffer.read(cx).read(cx).len();
-        self.change_selections(None, cx, |s| {
-            s.select_ranges(vec![0..end]);
-        });
-    }
-
-    pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections.all::<Point>(cx);
-        let max_point = display_map.buffer_snapshot.max_point();
-        for selection in &mut selections {
-            let rows = selection.spanned_rows(true, &display_map);
-            selection.start = Point::new(rows.start, 0);
-            selection.end = cmp::min(max_point, Point::new(rows.end, 0));
-            selection.reversed = false;
-        }
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.select(selections);
-        });
-    }
-
-    pub fn split_selection_into_lines(
-        &mut self,
-        _: &SplitSelectionIntoLines,
-        cx: &mut ViewContext<Self>,
-    ) {
-        let mut to_unfold = Vec::new();
-        let mut new_selection_ranges = Vec::new();
-        {
-            let selections = self.selections.all::<Point>(cx);
-            let buffer = self.buffer.read(cx).read(cx);
-            for selection in selections {
-                for row in selection.start.row..selection.end.row {
-                    let cursor = Point::new(row, buffer.line_len(row));
-                    new_selection_ranges.push(cursor..cursor);
-                }
-                new_selection_ranges.push(selection.end..selection.end);
-                to_unfold.push(selection.start..selection.end);
-            }
-        }
-        self.unfold_ranges(to_unfold, true, true, cx);
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.select_ranges(new_selection_ranges);
-        });
-    }
-
-    pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
-        self.add_selection(true, cx);
-    }
-
-    pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
-        self.add_selection(false, cx);
-    }
-
-    fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections.all::<Point>(cx);
-        let text_layout_details = self.text_layout_details(cx);
-        let mut state = self.add_selections_state.take().unwrap_or_else(|| {
-            let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
-            let range = oldest_selection.display_range(&display_map).sorted();
-
-            let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
-            let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
-            let positions = start_x.min(end_x)..start_x.max(end_x);
-
-            selections.clear();
-            let mut stack = Vec::new();
-            for row in range.start.row()..=range.end.row() {
-                if let Some(selection) = self.selections.build_columnar_selection(
-                    &display_map,
-                    row,
-                    &positions,
-                    oldest_selection.reversed,
-                    &text_layout_details,
-                ) {
-                    stack.push(selection.id);
-                    selections.push(selection);
-                }
-            }
-
-            if above {
-                stack.reverse();
-            }
-
-            AddSelectionsState { above, stack }
-        });
-
-        let last_added_selection = *state.stack.last().unwrap();
-        let mut new_selections = Vec::new();
-        if above == state.above {
-            let end_row = if above {
-                0
-            } else {
-                display_map.max_point().row()
-            };
-
-            'outer: for selection in selections {
-                if selection.id == last_added_selection {
-                    let range = selection.display_range(&display_map).sorted();
-                    debug_assert_eq!(range.start.row(), range.end.row());
-                    let mut row = range.start.row();
-                    let positions =
-                        if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
-                            px(start)..px(end)
-                        } else {
-                            let start_x =
-                                display_map.x_for_display_point(range.start, &text_layout_details);
-                            let end_x =
-                                display_map.x_for_display_point(range.end, &text_layout_details);
-                            start_x.min(end_x)..start_x.max(end_x)
-                        };
-
-                    while row != end_row {
-                        if above {
-                            row -= 1;
-                        } else {
-                            row += 1;
-                        }
-
-                        if let Some(new_selection) = self.selections.build_columnar_selection(
-                            &display_map,
-                            row,
-                            &positions,
-                            selection.reversed,
-                            &text_layout_details,
-                        ) {
-                            state.stack.push(new_selection.id);
-                            if above {
-                                new_selections.push(new_selection);
-                                new_selections.push(selection);
-                            } else {
-                                new_selections.push(selection);
-                                new_selections.push(new_selection);
-                            }
-
-                            continue 'outer;
-                        }
-                    }
-                }
-
-                new_selections.push(selection);
-            }
-        } else {
-            new_selections = selections;
-            new_selections.retain(|s| s.id != last_added_selection);
-            state.stack.pop();
-        }
-
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.select(new_selections);
-        });
-        if state.stack.len() > 1 {
-            self.add_selections_state = Some(state);
-        }
-    }
-
-    pub fn select_next_match_internal(
-        &mut self,
-        display_map: &DisplaySnapshot,
-        replace_newest: bool,
-        autoscroll: Option<Autoscroll>,
-        cx: &mut ViewContext<Self>,
-    ) -> Result<()> {
-        fn select_next_match_ranges(
-            this: &mut Editor,
-            range: Range<usize>,
-            replace_newest: bool,
-            auto_scroll: Option<Autoscroll>,
-            cx: &mut ViewContext<Editor>,
-        ) {
-            this.unfold_ranges([range.clone()], false, true, cx);
-            this.change_selections(auto_scroll, cx, |s| {
-                if replace_newest {
-                    s.delete(s.newest_anchor().id);
-                }
-                s.insert_range(range.clone());
-            });
-        }
-
-        let buffer = &display_map.buffer_snapshot;
-        let mut selections = self.selections.all::<usize>(cx);
-        if let Some(mut select_next_state) = self.select_next_state.take() {
-            let query = &select_next_state.query;
-            if !select_next_state.done {
-                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
-                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
-                let mut next_selected_range = None;
-
-                let bytes_after_last_selection =
-                    buffer.bytes_in_range(last_selection.end..buffer.len());
-                let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
-                let query_matches = query
-                    .stream_find_iter(bytes_after_last_selection)
-                    .map(|result| (last_selection.end, result))
-                    .chain(
-                        query
-                            .stream_find_iter(bytes_before_first_selection)
-                            .map(|result| (0, result)),
-                    );
-
-                for (start_offset, query_match) in query_matches {
-                    let query_match = query_match.unwrap(); // can only fail due to I/O
-                    let offset_range =
-                        start_offset + query_match.start()..start_offset + query_match.end();
-                    let display_range = offset_range.start.to_display_point(&display_map)
-                        ..offset_range.end.to_display_point(&display_map);
-
-                    if !select_next_state.wordwise
-                        || (!movement::is_inside_word(&display_map, display_range.start)
-                            && !movement::is_inside_word(&display_map, display_range.end))
-                    {
-                        if selections
-                            .iter()
-                            .find(|selection| selection.range().overlaps(&offset_range))
-                            .is_none()
-                        {
-                            next_selected_range = Some(offset_range);
-                            break;
-                        }
-                    }
-                }
-
-                if let Some(next_selected_range) = next_selected_range {
-                    select_next_match_ranges(
-                        self,
-                        next_selected_range,
-                        replace_newest,
-                        autoscroll,
-                        cx,
-                    );
-                } else {
-                    select_next_state.done = true;
-                }
-            }
-
-            self.select_next_state = Some(select_next_state);
-        } else if selections.len() == 1 {
-            let selection = selections.last_mut().unwrap();
-            if selection.start == selection.end {
-                let word_range = movement::surrounding_word(
-                    &display_map,
-                    selection.start.to_display_point(&display_map),
-                );
-                selection.start = word_range.start.to_offset(&display_map, Bias::Left);
-                selection.end = word_range.end.to_offset(&display_map, Bias::Left);
-                selection.goal = SelectionGoal::None;
-                selection.reversed = false;
-
-                let query = buffer
-                    .text_for_range(selection.start..selection.end)
-                    .collect::<String>();
-
-                let is_empty = query.is_empty();
-                let select_state = SelectNextState {
-                    query: AhoCorasick::new(&[query])?,
-                    wordwise: true,
-                    done: is_empty,
-                };
-                select_next_match_ranges(
-                    self,
-                    selection.start..selection.end,
-                    replace_newest,
-                    autoscroll,
-                    cx,
-                );
-                self.select_next_state = Some(select_state);
-            } else {
-                let query = buffer
-                    .text_for_range(selection.start..selection.end)
-                    .collect::<String>();
-                self.select_next_state = Some(SelectNextState {
-                    query: AhoCorasick::new(&[query])?,
-                    wordwise: false,
-                    done: false,
-                });
-                self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
-            }
-        }
-        Ok(())
-    }
-
-    pub fn select_all_matches(
-        &mut self,
-        action: &SelectAllMatches,
-        cx: &mut ViewContext<Self>,
-    ) -> Result<()> {
-        self.push_to_selection_history();
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-
-        loop {
-            self.select_next_match_internal(&display_map, action.replace_newest, None, cx)?;
-
-            if self
-                .select_next_state
-                .as_ref()
-                .map(|selection_state| selection_state.done)
-                .unwrap_or(true)
-            {
-                break;
-            }
-        }
-
-        Ok(())
-    }
-
-    pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
-        self.push_to_selection_history();
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        self.select_next_match_internal(
-            &display_map,
-            action.replace_newest,
-            Some(Autoscroll::newest()),
-            cx,
-        )?;
-        Ok(())
-    }
-
-    pub fn select_previous(
-        &mut self,
-        action: &SelectPrevious,
-        cx: &mut ViewContext<Self>,
-    ) -> Result<()> {
-        self.push_to_selection_history();
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let buffer = &display_map.buffer_snapshot;
-        let mut selections = self.selections.all::<usize>(cx);
-        if let Some(mut select_prev_state) = self.select_prev_state.take() {
-            let query = &select_prev_state.query;
-            if !select_prev_state.done {
-                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
-                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
-                let mut next_selected_range = None;
-                // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
-                let bytes_before_last_selection =
-                    buffer.reversed_bytes_in_range(0..last_selection.start);
-                let bytes_after_first_selection =
-                    buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
-                let query_matches = query
-                    .stream_find_iter(bytes_before_last_selection)
-                    .map(|result| (last_selection.start, result))
-                    .chain(
-                        query
-                            .stream_find_iter(bytes_after_first_selection)
-                            .map(|result| (buffer.len(), result)),
-                    );
-                for (end_offset, query_match) in query_matches {
-                    let query_match = query_match.unwrap(); // can only fail due to I/O
-                    let offset_range =
-                        end_offset - query_match.end()..end_offset - query_match.start();
-                    let display_range = offset_range.start.to_display_point(&display_map)
-                        ..offset_range.end.to_display_point(&display_map);
-
-                    if !select_prev_state.wordwise
-                        || (!movement::is_inside_word(&display_map, display_range.start)
-                            && !movement::is_inside_word(&display_map, display_range.end))
-                    {
-                        next_selected_range = Some(offset_range);
-                        break;
-                    }
-                }
-
-                if let Some(next_selected_range) = next_selected_range {
-                    self.unfold_ranges([next_selected_range.clone()], false, true, cx);
-                    self.change_selections(Some(Autoscroll::newest()), cx, |s| {
-                        if action.replace_newest {
-                            s.delete(s.newest_anchor().id);
-                        }
-                        s.insert_range(next_selected_range);
-                    });
-                } else {
-                    select_prev_state.done = true;
-                }
-            }
-
-            self.select_prev_state = Some(select_prev_state);
-        } else if selections.len() == 1 {
-            let selection = selections.last_mut().unwrap();
-            if selection.start == selection.end {
-                let word_range = movement::surrounding_word(
-                    &display_map,
-                    selection.start.to_display_point(&display_map),
-                );
-                selection.start = word_range.start.to_offset(&display_map, Bias::Left);
-                selection.end = word_range.end.to_offset(&display_map, Bias::Left);
-                selection.goal = SelectionGoal::None;
-                selection.reversed = false;
-
-                let query = buffer
-                    .text_for_range(selection.start..selection.end)
-                    .collect::<String>();
-                let query = query.chars().rev().collect::<String>();
-                let select_state = SelectNextState {
-                    query: AhoCorasick::new(&[query])?,
-                    wordwise: true,
-                    done: false,
-                };
-                self.unfold_ranges([selection.start..selection.end], false, true, cx);
-                self.change_selections(Some(Autoscroll::newest()), cx, |s| {
-                    s.select(selections);
-                });
-                self.select_prev_state = Some(select_state);
-            } else {
-                let query = buffer
-                    .text_for_range(selection.start..selection.end)
-                    .collect::<String>();
-                let query = query.chars().rev().collect::<String>();
-                self.select_prev_state = Some(SelectNextState {
-                    query: AhoCorasick::new(&[query])?,
-                    wordwise: false,
-                    done: false,
-                });
-                self.select_previous(action, cx)?;
-            }
-        }
-        Ok(())
-    }
-
-    pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
-        let text_layout_details = &self.text_layout_details(cx);
-        self.transact(cx, |this, cx| {
-            let mut selections = this.selections.all::<Point>(cx);
-            let mut edits = Vec::new();
-            let mut selection_edit_ranges = Vec::new();
-            let mut last_toggled_row = None;
-            let snapshot = this.buffer.read(cx).read(cx);
-            let empty_str: Arc<str> = "".into();
-            let mut suffixes_inserted = Vec::new();
-
-            fn comment_prefix_range(
-                snapshot: &MultiBufferSnapshot,
-                row: u32,
-                comment_prefix: &str,
-                comment_prefix_whitespace: &str,
-            ) -> Range<Point> {
-                let start = Point::new(row, snapshot.indent_size_for_line(row).len);
-
-                let mut line_bytes = snapshot
-                    .bytes_in_range(start..snapshot.max_point())
-                    .flatten()
-                    .copied();
-
-                // If this line currently begins with the line comment prefix, then record
-                // the range containing the prefix.
-                if line_bytes
-                    .by_ref()
-                    .take(comment_prefix.len())
-                    .eq(comment_prefix.bytes())
-                {
-                    // Include any whitespace that matches the comment prefix.
-                    let matching_whitespace_len = line_bytes
-                        .zip(comment_prefix_whitespace.bytes())
-                        .take_while(|(a, b)| a == b)
-                        .count() as u32;
-                    let end = Point::new(
-                        start.row,
-                        start.column + comment_prefix.len() as u32 + matching_whitespace_len,
-                    );
-                    start..end
-                } else {
-                    start..start
-                }
-            }
-
-            fn comment_suffix_range(
-                snapshot: &MultiBufferSnapshot,
-                row: u32,
-                comment_suffix: &str,
-                comment_suffix_has_leading_space: bool,
-            ) -> Range<Point> {
-                let end = Point::new(row, snapshot.line_len(row));
-                let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
-
-                let mut line_end_bytes = snapshot
-                    .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
-                    .flatten()
-                    .copied();
-
-                let leading_space_len = if suffix_start_column > 0
-                    && line_end_bytes.next() == Some(b' ')
-                    && comment_suffix_has_leading_space
-                {
-                    1
-                } else {
-                    0
-                };
-
-                // If this line currently begins with the line comment prefix, then record
-                // the range containing the prefix.
-                if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
-                    let start = Point::new(end.row, suffix_start_column - leading_space_len);
-                    start..end
-                } else {
-                    end..end
-                }
-            }
-
-            // TODO: Handle selections that cross excerpts
-            for selection in &mut selections {
-                let start_column = snapshot.indent_size_for_line(selection.start.row).len;
-                let language = if let Some(language) =
-                    snapshot.language_scope_at(Point::new(selection.start.row, start_column))
-                {
-                    language
-                } else {
-                    continue;
-                };
-
-                selection_edit_ranges.clear();
-
-                // If multiple selections contain a given row, avoid processing that
-                // row more than once.
-                let mut start_row = selection.start.row;
-                if last_toggled_row == Some(start_row) {
-                    start_row += 1;
-                }
-                let end_row =
-                    if selection.end.row > selection.start.row && selection.end.column == 0 {
-                        selection.end.row - 1
-                    } else {
-                        selection.end.row
-                    };
-                last_toggled_row = Some(end_row);
-
-                if start_row > end_row {
-                    continue;
-                }
-
-                // If the language has line comments, toggle those.
-                if let Some(full_comment_prefix) = language.line_comment_prefix() {
-                    // Split the comment prefix's trailing whitespace into a separate string,
-                    // as that portion won't be used for detecting if a line is a comment.
-                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
-                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
-                    let mut all_selection_lines_are_comments = true;
-
-                    for row in start_row..=end_row {
-                        if snapshot.is_line_blank(row) && start_row < end_row {
-                            continue;
-                        }
-
-                        let prefix_range = comment_prefix_range(
-                            snapshot.deref(),
-                            row,
-                            comment_prefix,
-                            comment_prefix_whitespace,
-                        );
-                        if prefix_range.is_empty() {
-                            all_selection_lines_are_comments = false;
-                        }
-                        selection_edit_ranges.push(prefix_range);
-                    }
-
-                    if all_selection_lines_are_comments {
-                        edits.extend(
-                            selection_edit_ranges
-                                .iter()
-                                .cloned()
-                                .map(|range| (range, empty_str.clone())),
-                        );
-                    } else {
-                        let min_column = selection_edit_ranges
-                            .iter()
-                            .map(|r| r.start.column)
-                            .min()
-                            .unwrap_or(0);
-                        edits.extend(selection_edit_ranges.iter().map(|range| {
-                            let position = Point::new(range.start.row, min_column);
-                            (position..position, full_comment_prefix.clone())
-                        }));
-                    }
-                } else if let Some((full_comment_prefix, comment_suffix)) =
-                    language.block_comment_delimiters()
-                {
-                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
-                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
-                    let prefix_range = comment_prefix_range(
-                        snapshot.deref(),
-                        start_row,
-                        comment_prefix,
-                        comment_prefix_whitespace,
-                    );
-                    let suffix_range = comment_suffix_range(
-                        snapshot.deref(),
-                        end_row,
-                        comment_suffix.trim_start_matches(' '),
-                        comment_suffix.starts_with(' '),
-                    );
-
-                    if prefix_range.is_empty() || suffix_range.is_empty() {
-                        edits.push((
-                            prefix_range.start..prefix_range.start,
-                            full_comment_prefix.clone(),
-                        ));
-                        edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
-                        suffixes_inserted.push((end_row, comment_suffix.len()));
-                    } else {
-                        edits.push((prefix_range, empty_str.clone()));
-                        edits.push((suffix_range, empty_str.clone()));
-                    }
-                } else {
-                    continue;
-                }
-            }
-
-            drop(snapshot);
-            this.buffer.update(cx, |buffer, cx| {
-                buffer.edit(edits, None, cx);
-            });
-
-            // Adjust selections so that they end before any comment suffixes that
-            // were inserted.
-            let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
-            let mut selections = this.selections.all::<Point>(cx);
-            let snapshot = this.buffer.read(cx).read(cx);
-            for selection in &mut selections {
-                while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
-                    match row.cmp(&selection.end.row) {
-                        Ordering::Less => {
-                            suffixes_inserted.next();
-                            continue;
-                        }
-                        Ordering::Greater => break,
-                        Ordering::Equal => {
-                            if selection.end.column == snapshot.line_len(row) {
-                                if selection.is_empty() {
-                                    selection.start.column -= suffix_len as u32;
-                                }
-                                selection.end.column -= suffix_len as u32;
-                            }
-                            break;
-                        }
-                    }
-                }
-            }
-
-            drop(snapshot);
-            this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
-
-            let selections = this.selections.all::<Point>(cx);
-            let selections_on_single_row = selections.windows(2).all(|selections| {
-                selections[0].start.row == selections[1].start.row
-                    && selections[0].end.row == selections[1].end.row
-                    && selections[0].start.row == selections[0].end.row
-            });
-            let selections_selecting = selections
-                .iter()
-                .any(|selection| selection.start != selection.end);
-            let advance_downwards = action.advance_downwards
-                && selections_on_single_row
-                && !selections_selecting
-                && this.mode != EditorMode::SingleLine;
-
-            if advance_downwards {
-                let snapshot = this.buffer.read(cx).snapshot(cx);
-
-                this.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                    s.move_cursors_with(|display_snapshot, display_point, _| {
-                        let mut point = display_point.to_point(display_snapshot);
-                        point.row += 1;
-                        point = snapshot.clip_point(point, Bias::Left);
-                        let display_point = point.to_display_point(display_snapshot);
-                        let goal = SelectionGoal::HorizontalPosition(
-                            display_snapshot
-                                .x_for_display_point(display_point, &text_layout_details)
-                                .into(),
-                        );
-                        (display_point, goal)
-                    })
-                });
-            }
-        });
-    }
-
-    pub fn select_larger_syntax_node(
-        &mut self,
-        _: &SelectLargerSyntaxNode,
-        cx: &mut ViewContext<Self>,
-    ) {
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let buffer = self.buffer.read(cx).snapshot(cx);
-        let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
-
-        let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
-        let mut selected_larger_node = false;
-        let new_selections = old_selections
-            .iter()
-            .map(|selection| {
-                let old_range = selection.start..selection.end;
-                let mut new_range = old_range.clone();
-                while let Some(containing_range) =
-                    buffer.range_for_syntax_ancestor(new_range.clone())
-                {
-                    new_range = containing_range;
-                    if !display_map.intersects_fold(new_range.start)
-                        && !display_map.intersects_fold(new_range.end)
-                    {
-                        break;
-                    }
-                }
-
-                selected_larger_node |= new_range != old_range;
-                Selection {
-                    id: selection.id,
-                    start: new_range.start,
-                    end: new_range.end,
-                    goal: SelectionGoal::None,
-                    reversed: selection.reversed,
-                }
-            })
-            .collect::<Vec<_>>();
-
-        if selected_larger_node {
-            stack.push(old_selections);
-            self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.select(new_selections);
-            });
-        }
-        self.select_larger_syntax_node_stack = stack;
-    }
-
-    pub fn select_smaller_syntax_node(
-        &mut self,
-        _: &SelectSmallerSyntaxNode,
-        cx: &mut ViewContext<Self>,
-    ) {
-        let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
-        if let Some(selections) = stack.pop() {
-            self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.select(selections.to_vec());
-            });
-        }
-        self.select_larger_syntax_node_stack = stack;
-    }
-
-    pub fn move_to_enclosing_bracket(
-        &mut self,
-        _: &MoveToEnclosingBracket,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_offsets_with(|snapshot, selection| {
-                let Some(enclosing_bracket_ranges) =
-                    snapshot.enclosing_bracket_ranges(selection.start..selection.end)
-                else {
-                    return;
-                };
-
-                let mut best_length = usize::MAX;
-                let mut best_inside = false;
-                let mut best_in_bracket_range = false;
-                let mut best_destination = None;
-                for (open, close) in enclosing_bracket_ranges {
-                    let close = close.to_inclusive();
-                    let length = close.end() - open.start;
-                    let inside = selection.start >= open.end && selection.end <= *close.start();
-                    let in_bracket_range = open.to_inclusive().contains(&selection.head())
-                        || close.contains(&selection.head());
-
-                    // If best is next to a bracket and current isn't, skip
-                    if !in_bracket_range && best_in_bracket_range {
-                        continue;
-                    }
-
-                    // Prefer smaller lengths unless best is inside and current isn't
-                    if length > best_length && (best_inside || !inside) {
-                        continue;
-                    }
-
-                    best_length = length;
-                    best_inside = inside;
-                    best_in_bracket_range = in_bracket_range;
-                    best_destination = Some(
-                        if close.contains(&selection.start) && close.contains(&selection.end) {
-                            if inside {
-                                open.end
-                            } else {
-                                open.start
-                            }
-                        } else {
-                            if inside {
-                                *close.start()
-                            } else {
-                                *close.end()
-                            }
-                        },
-                    );
-                }
-
-                if let Some(destination) = best_destination {
-                    selection.collapse_to(destination, SelectionGoal::None);
-                }
-            })
-        });
-    }
-
-    pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
-        self.end_selection(cx);
-        self.selection_history.mode = SelectionHistoryMode::Undoing;
-        if let Some(entry) = self.selection_history.undo_stack.pop_back() {
-            self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
-            self.select_next_state = entry.select_next_state;
-            self.select_prev_state = entry.select_prev_state;
-            self.add_selections_state = entry.add_selections_state;
-            self.request_autoscroll(Autoscroll::newest(), cx);
-        }
-        self.selection_history.mode = SelectionHistoryMode::Normal;
-    }
-
-    pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
-        self.end_selection(cx);
-        self.selection_history.mode = SelectionHistoryMode::Redoing;
-        if let Some(entry) = self.selection_history.redo_stack.pop_back() {
-            self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
-            self.select_next_state = entry.select_next_state;
-            self.select_prev_state = entry.select_prev_state;
-            self.add_selections_state = entry.add_selections_state;
-            self.request_autoscroll(Autoscroll::newest(), cx);
-        }
-        self.selection_history.mode = SelectionHistoryMode::Normal;
-    }
-
-    fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
-        self.go_to_diagnostic_impl(Direction::Next, cx)
-    }
-
-    fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
-        self.go_to_diagnostic_impl(Direction::Prev, cx)
-    }
-
-    pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
-        let buffer = self.buffer.read(cx).snapshot(cx);
-        let selection = self.selections.newest::<usize>(cx);
-
-        // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
-        if direction == Direction::Next {
-            if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
-                let (group_id, jump_to) = popover.activation_info();
-                if self.activate_diagnostics(group_id, cx) {
-                    self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                        let mut new_selection = s.newest_anchor().clone();
-                        new_selection.collapse_to(jump_to, SelectionGoal::None);
-                        s.select_anchors(vec![new_selection.clone()]);
-                    });
-                }
-                return;
-            }
-        }
-
-        let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
-            active_diagnostics
-                .primary_range
-                .to_offset(&buffer)
-                .to_inclusive()
-        });
-        let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
-            if active_primary_range.contains(&selection.head()) {
-                *active_primary_range.end()
-            } else {
-                selection.head()
-            }
-        } else {
-            selection.head()
-        };
-
-        loop {
-            let mut diagnostics = if direction == Direction::Prev {
-                buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
-            } else {
-                buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
-            };
-            let group = diagnostics.find_map(|entry| {
-                if entry.diagnostic.is_primary
-                    && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
-                    && !entry.range.is_empty()
-                    && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
-                    && !entry.range.contains(&search_start)
-                {
-                    Some((entry.range, entry.diagnostic.group_id))
-                } else {
-                    None
-                }
-            });
-
-            if let Some((primary_range, group_id)) = group {
-                if self.activate_diagnostics(group_id, cx) {
-                    self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                        s.select(vec![Selection {
-                            id: selection.id,
-                            start: primary_range.start,
-                            end: primary_range.start,
-                            reversed: false,
-                            goal: SelectionGoal::None,
-                        }]);
-                    });
-                }
-                break;
-            } else {
-                // Cycle around to the start of the buffer, potentially moving back to the start of
-                // the currently active diagnostic.
-                active_primary_range.take();
-                if direction == Direction::Prev {
-                    if search_start == buffer.len() {
-                        break;
-                    } else {
-                        search_start = buffer.len();
-                    }
-                } else if search_start == 0 {
-                    break;
-                } else {
-                    search_start = 0;
-                }
-            }
-        }
-    }
-
-    fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
-        let snapshot = self
-            .display_map
-            .update(cx, |display_map, cx| display_map.snapshot(cx));
-        let selection = self.selections.newest::<Point>(cx);
-
-        if !self.seek_in_direction(
-            &snapshot,
-            selection.head(),
-            false,
-            snapshot
-                .buffer_snapshot
-                .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
-            cx,
-        ) {
-            let wrapped_point = Point::zero();
-            self.seek_in_direction(
-                &snapshot,
-                wrapped_point,
-                true,
-                snapshot
-                    .buffer_snapshot
-                    .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
-                cx,
-            );
-        }
-    }
-
-    fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
-        let snapshot = self
-            .display_map
-            .update(cx, |display_map, cx| display_map.snapshot(cx));
-        let selection = self.selections.newest::<Point>(cx);
-
-        if !self.seek_in_direction(
-            &snapshot,
-            selection.head(),
-            false,
-            snapshot
-                .buffer_snapshot
-                .git_diff_hunks_in_range_rev(0..selection.head().row),
-            cx,
-        ) {
-            let wrapped_point = snapshot.buffer_snapshot.max_point();
-            self.seek_in_direction(
-                &snapshot,
-                wrapped_point,
-                true,
-                snapshot
-                    .buffer_snapshot
-                    .git_diff_hunks_in_range_rev(0..wrapped_point.row),
-                cx,
-            );
-        }
-    }
-
-    fn seek_in_direction(
-        &mut self,
-        snapshot: &DisplaySnapshot,
-        initial_point: Point,
-        is_wrapped: bool,
-        hunks: impl Iterator<Item = DiffHunk<u32>>,
-        cx: &mut ViewContext<Editor>,
-    ) -> bool {
-        let display_point = initial_point.to_display_point(snapshot);
-        let mut hunks = hunks
-            .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
-            .filter(|hunk| {
-                if is_wrapped {
-                    true
-                } else {
-                    !hunk.contains_display_row(display_point.row())
-                }
-            })
-            .dedup();
-
-        if let Some(hunk) = hunks.next() {
-            self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                let row = hunk.start_display_row();
-                let point = DisplayPoint::new(row, 0);
-                s.select_display_ranges([point..point]);
-            });
-
-            true
-        } else {
-            false
-        }
-    }
-
-    pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
-        self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
-    }
-
-    pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
-        self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
-    }
-
-    pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
-        self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
-    }
-
-    pub fn go_to_type_definition_split(
-        &mut self,
-        _: &GoToTypeDefinitionSplit,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
-    }
-
-    fn go_to_definition_of_kind(
-        &mut self,
-        kind: GotoDefinitionKind,
-        split: bool,
-        cx: &mut ViewContext<Self>,
-    ) {
-        let Some(workspace) = self.workspace() else {
-            return;
-        };
-        let buffer = self.buffer.read(cx);
-        let head = self.selections.newest::<usize>(cx).head();
-        let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
-            text_anchor
-        } else {
-            return;
-        };
-
-        let project = workspace.read(cx).project().clone();
-        let definitions = project.update(cx, |project, cx| match kind {
-            GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
-            GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
-        });
-
-        cx.spawn(|editor, mut cx| async move {
-            let definitions = definitions.await?;
-            editor.update(&mut cx, |editor, cx| {
-                editor.navigate_to_definitions(
-                    definitions
-                        .into_iter()
-                        .map(GoToDefinitionLink::Text)
-                        .collect(),
-                    split,
-                    cx,
-                );
-            })?;
-            Ok::<(), anyhow::Error>(())
-        })
-        .detach_and_log_err(cx);
-    }
-
-    pub fn navigate_to_definitions(
-        &mut self,
-        mut definitions: Vec<GoToDefinitionLink>,
-        split: bool,
-        cx: &mut ViewContext<Editor>,
-    ) {
-        let Some(workspace) = self.workspace() else {
-            return;
-        };
-        let pane = workspace.read(cx).active_pane().clone();
-        // If there is one definition, just open it directly
-        if definitions.len() == 1 {
-            let definition = definitions.pop().unwrap();
-            let target_task = match definition {
-                GoToDefinitionLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
-                GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
-                    self.compute_target_location(lsp_location, server_id, cx)
-                }
-            };
-            cx.spawn(|editor, mut cx| async move {
-                let target = target_task.await.context("target resolution task")?;
-                if let Some(target) = target {
-                    editor.update(&mut cx, |editor, cx| {
-                        let range = target.range.to_offset(target.buffer.read(cx));
-                        let range = editor.range_for_match(&range);
-                        if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
-                            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                                s.select_ranges([range]);
-                            });
-                        } else {
-                            cx.window_context().defer(move |cx| {
-                                let target_editor: View<Self> =
-                                    workspace.update(cx, |workspace, cx| {
-                                        if split {
-                                            workspace.split_project_item(target.buffer.clone(), cx)
-                                        } else {
-                                            workspace.open_project_item(target.buffer.clone(), cx)
-                                        }
-                                    });
-                                target_editor.update(cx, |target_editor, cx| {
-                                    // When selecting a definition in a different buffer, disable the nav history
-                                    // to avoid creating a history entry at the previous cursor location.
-                                    pane.update(cx, |pane, _| pane.disable_history());
-                                    target_editor.change_selections(
-                                        Some(Autoscroll::fit()),
-                                        cx,
-                                        |s| {
-                                            s.select_ranges([range]);
-                                        },
-                                    );
-                                    pane.update(cx, |pane, _| pane.enable_history());
-                                });
-                            });
-                        }
-                    })
-                } else {
-                    Ok(())
-                }
-            })
-            .detach_and_log_err(cx);
-        } else if !definitions.is_empty() {
-            let replica_id = self.replica_id(cx);
-            cx.spawn(|editor, mut cx| async move {
-                let (title, location_tasks) = editor
-                    .update(&mut cx, |editor, cx| {
-                        let title = definitions
-                            .iter()
-                            .find_map(|definition| match definition {
-                                GoToDefinitionLink::Text(link) => {
-                                    link.origin.as_ref().map(|origin| {
-                                        let buffer = origin.buffer.read(cx);
-                                        format!(
-                                            "Definitions for {}",
-                                            buffer
-                                                .text_for_range(origin.range.clone())
-                                                .collect::<String>()
-                                        )
-                                    })
-                                }
-                                GoToDefinitionLink::InlayHint(_, _) => None,
-                            })
-                            .unwrap_or("Definitions".to_string());
-                        let location_tasks = definitions
-                            .into_iter()
-                            .map(|definition| match definition {
-                                GoToDefinitionLink::Text(link) => {
-                                    Task::Ready(Some(Ok(Some(link.target))))
-                                }
-                                GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
-                                    editor.compute_target_location(lsp_location, server_id, cx)
-                                }
-                            })
-                            .collect::<Vec<_>>();
-                        (title, location_tasks)
-                    })
-                    .context("location tasks preparation")?;
-
-                let locations = futures::future::join_all(location_tasks)
-                    .await
-                    .into_iter()
-                    .filter_map(|location| location.transpose())
-                    .collect::<Result<_>>()
-                    .context("location tasks")?;
-                workspace
-                    .update(&mut cx, |workspace, cx| {
-                        Self::open_locations_in_multibuffer(
-                            workspace, locations, replica_id, title, split, cx,
-                        )
-                    })
-                    .ok();
-
-                anyhow::Ok(())
-            })
-            .detach_and_log_err(cx);
-        }
-    }
-
-    fn compute_target_location(
-        &self,
-        lsp_location: lsp::Location,
-        server_id: LanguageServerId,
-        cx: &mut ViewContext<Editor>,
-    ) -> Task<anyhow::Result<Option<Location>>> {
-        let Some(project) = self.project.clone() else {
-            return Task::Ready(Some(Ok(None)));
-        };
-
-        cx.spawn(move |editor, mut cx| async move {
-            let location_task = editor.update(&mut cx, |editor, cx| {
-                project.update(cx, |project, cx| {
-                    let language_server_name =
-                        editor.buffer.read(cx).as_singleton().and_then(|buffer| {
-                            project
-                                .language_server_for_buffer(buffer.read(cx), server_id, cx)
-                                .map(|(_, lsp_adapter)| {
-                                    LanguageServerName(Arc::from(lsp_adapter.name()))
-                                })
-                        });
-                    language_server_name.map(|language_server_name| {
-                        project.open_local_buffer_via_lsp(
-                            lsp_location.uri.clone(),
-                            server_id,
-                            language_server_name,
-                            cx,
-                        )
-                    })
-                })
-            })?;
-            let location = match location_task {
-                Some(task) => Some({
-                    let target_buffer_handle = task.await.context("open local buffer")?;
-                    let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
-                        let target_start = target_buffer
-                            .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
-                        let target_end = target_buffer
-                            .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
-                        target_buffer.anchor_after(target_start)
-                            ..target_buffer.anchor_before(target_end)
-                    })?;
-                    Location {
-                        buffer: target_buffer_handle,
-                        range,
-                    }
-                }),
-                None => None,
-            };
-            Ok(location)
-        })
-    }
-
-    pub fn find_all_references(
-        &mut self,
-        _: &FindAllReferences,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<Task<Result<()>>> {
-        let buffer = self.buffer.read(cx);
-        let head = self.selections.newest::<usize>(cx).head();
-        let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
-        let replica_id = self.replica_id(cx);
-
-        let workspace = self.workspace()?;
-        let project = workspace.read(cx).project().clone();
-        let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
-        Some(cx.spawn(|_, mut cx| async move {
-            let locations = references.await?;
-            if locations.is_empty() {
-                return Ok(());
-            }
-
-            workspace.update(&mut cx, |workspace, cx| {
-                let title = locations
-                    .first()
-                    .as_ref()
-                    .map(|location| {
-                        let buffer = location.buffer.read(cx);
-                        format!(
-                            "References to `{}`",
-                            buffer
-                                .text_for_range(location.range.clone())
-                                .collect::<String>()
-                        )
-                    })
-                    .unwrap();
-                Self::open_locations_in_multibuffer(
-                    workspace, locations, replica_id, title, false, cx,
-                );
-            })?;
-
-            Ok(())
-        }))
-    }
-
-    /// Opens a multibuffer with the given project locations in it
-    pub fn open_locations_in_multibuffer(
-        workspace: &mut Workspace,
-        mut locations: Vec<Location>,
-        replica_id: ReplicaId,
-        title: String,
-        split: bool,
-        cx: &mut ViewContext<Workspace>,
-    ) {
-        // If there are multiple definitions, open them in a multibuffer
-        locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
-        let mut locations = locations.into_iter().peekable();
-        let mut ranges_to_highlight = Vec::new();
-
-        let excerpt_buffer = cx.new_model(|cx| {
-            let mut multibuffer = MultiBuffer::new(replica_id);
-            while let Some(location) = locations.next() {
-                let buffer = location.buffer.read(cx);
-                let mut ranges_for_buffer = Vec::new();
-                let range = location.range.to_offset(buffer);
-                ranges_for_buffer.push(range.clone());
-
-                while let Some(next_location) = locations.peek() {
-                    if next_location.buffer == location.buffer {
-                        ranges_for_buffer.push(next_location.range.to_offset(buffer));
-                        locations.next();
-                    } else {
-                        break;
-                    }
-                }
-
-                ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
-                ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
-                    location.buffer.clone(),
-                    ranges_for_buffer,
-                    1,
-                    cx,
-                ))
-            }
-
-            multibuffer.with_title(title)
-        });
-
-        let editor = cx.new_view(|cx| {
-            Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
-        });
-        editor.update(cx, |editor, cx| {
-            editor.highlight_background::<Self>(
-                ranges_to_highlight,
-                |theme| theme.editor_highlighted_line_background,
-                cx,
-            );
-        });
-        if split {
-            workspace.split_item(SplitDirection::Right, Box::new(editor), cx);
-        } else {
-            workspace.add_item(Box::new(editor), cx);
-        }
-    }
-
-    pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
-        use language::ToOffset as _;
-
-        let project = self.project.clone()?;
-        let selection = self.selections.newest_anchor().clone();
-        let (cursor_buffer, cursor_buffer_position) = self
-            .buffer
-            .read(cx)
-            .text_anchor_for_position(selection.head(), cx)?;
-        let (tail_buffer, _) = self
-            .buffer
-            .read(cx)
-            .text_anchor_for_position(selection.tail(), cx)?;
-        if tail_buffer != cursor_buffer {
-            return None;
-        }
-
-        let snapshot = cursor_buffer.read(cx).snapshot();
-        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
-        let prepare_rename = project.update(cx, |project, cx| {
-            project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
-        });
-
-        Some(cx.spawn(|this, mut cx| async move {
-            let rename_range = if let Some(range) = prepare_rename.await? {
-                Some(range)
-            } else {
-                this.update(&mut cx, |this, cx| {
-                    let buffer = this.buffer.read(cx).snapshot(cx);
-                    let mut buffer_highlights = this
-                        .document_highlights_for_position(selection.head(), &buffer)
-                        .filter(|highlight| {
-                            highlight.start.excerpt_id == selection.head().excerpt_id
-                                && highlight.end.excerpt_id == selection.head().excerpt_id
-                        });
-                    buffer_highlights
-                        .next()
-                        .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
-                })?
-            };
-            if let Some(rename_range) = rename_range {
-                let rename_buffer_range = rename_range.to_offset(&snapshot);
-                let cursor_offset_in_rename_range =
-                    cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
-
-                this.update(&mut cx, |this, cx| {
-                    this.take_rename(false, cx);
-                    let buffer = this.buffer.read(cx).read(cx);
-                    let cursor_offset = selection.head().to_offset(&buffer);
-                    let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
-                    let rename_end = rename_start + rename_buffer_range.len();
-                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
-                    let mut old_highlight_id = None;
-                    let old_name: Arc<str> = buffer
-                        .chunks(rename_start..rename_end, true)
-                        .map(|chunk| {
-                            if old_highlight_id.is_none() {
-                                old_highlight_id = chunk.syntax_highlight_id;
-                            }
-                            chunk.text
-                        })
-                        .collect::<String>()
-                        .into();
-
-                    drop(buffer);
-
-                    // Position the selection in the rename editor so that it matches the current selection.
-                    this.show_local_selections = false;
-                    let rename_editor = cx.new_view(|cx| {
-                        let mut editor = Editor::single_line(cx);
-                        editor.buffer.update(cx, |buffer, cx| {
-                            buffer.edit([(0..0, old_name.clone())], None, cx)
-                        });
-                        editor.select_all(&SelectAll, cx);
-                        editor
-                    });
-
-                    let ranges = this
-                        .clear_background_highlights::<DocumentHighlightWrite>(cx)
-                        .into_iter()
-                        .flat_map(|(_, ranges)| ranges.into_iter())
-                        .chain(
-                            this.clear_background_highlights::<DocumentHighlightRead>(cx)
-                                .into_iter()
-                                .flat_map(|(_, ranges)| ranges.into_iter()),
-                        )
-                        .collect();
-
-                    this.highlight_text::<Rename>(
-                        ranges,
-                        HighlightStyle {
-                            fade_out: Some(0.6),
-                            ..Default::default()
-                        },
-                        cx,
-                    );
-                    let rename_focus_handle = rename_editor.focus_handle(cx);
-                    cx.focus(&rename_focus_handle);
-                    let block_id = this.insert_blocks(
-                        [BlockProperties {
-                            style: BlockStyle::Flex,
-                            position: range.start.clone(),
-                            height: 1,
-                            render: Arc::new({
-                                let rename_editor = rename_editor.clone();
-                                move |cx: &mut BlockContext| {
-                                    let mut text_style = cx.editor_style.text.clone();
-                                    if let Some(highlight_style) = old_highlight_id
-                                        .and_then(|h| h.style(&cx.editor_style.syntax))
-                                    {
-                                        text_style = text_style.highlight(highlight_style);
-                                    }
-                                    div()
-                                        .pl(cx.anchor_x)
-                                        .child(EditorElement::new(
-                                            &rename_editor,
-                                            EditorStyle {
-                                                background: cx.theme().system().transparent,
-                                                local_player: cx.editor_style.local_player,
-                                                text: text_style,
-                                                scrollbar_width: cx.editor_style.scrollbar_width,
-                                                syntax: cx.editor_style.syntax.clone(),
-                                                status: cx.editor_style.status.clone(),
-                                                // todo!("what about the rest of the highlight style parts for inlays and suggestions?")
-                                                inlays_style: HighlightStyle {
-                                                    color: Some(cx.theme().status().hint),
-                                                    font_weight: Some(FontWeight::BOLD),
-                                                    ..HighlightStyle::default()
-                                                },
-                                                suggestions_style: HighlightStyle {
-                                                    color: Some(cx.theme().status().predictive),
-                                                    ..HighlightStyle::default()
-                                                },
-                                            },
-                                        ))
-                                        .into_any_element()
-                                }
-                            }),
-                            disposition: BlockDisposition::Below,
-                        }],
-                        Some(Autoscroll::fit()),
-                        cx,
-                    )[0];
-                    this.pending_rename = Some(RenameState {
-                        range,
-                        old_name,
-                        editor: rename_editor,
-                        block_id,
-                    });
-                })?;
-            }
-
-            Ok(())
-        }))
-    }
-
-    pub fn confirm_rename(
-        &mut self,
-        _: &ConfirmRename,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<Task<Result<()>>> {
-        let rename = self.take_rename(false, cx)?;
-        let workspace = self.workspace()?;
-        let (start_buffer, start) = self
-            .buffer
-            .read(cx)
-            .text_anchor_for_position(rename.range.start.clone(), cx)?;
-        let (end_buffer, end) = self
-            .buffer
-            .read(cx)
-            .text_anchor_for_position(rename.range.end.clone(), cx)?;
-        if start_buffer != end_buffer {
-            return None;
-        }
-
-        let buffer = start_buffer;
-        let range = start..end;
-        let old_name = rename.old_name;
-        let new_name = rename.editor.read(cx).text(cx);
-
-        let rename = workspace
-            .read(cx)
-            .project()
-            .clone()
-            .update(cx, |project, cx| {
-                project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
-            });
-        let workspace = workspace.downgrade();
-
-        Some(cx.spawn(|editor, mut cx| async move {
-            let project_transaction = rename.await?;
-            Self::open_project_transaction(
-                &editor,
-                workspace,
-                project_transaction,
-                format!("Rename: {} โ†’ {}", old_name, new_name),
-                cx.clone(),
-            )
-            .await?;
-
-            editor.update(&mut cx, |editor, cx| {
-                editor.refresh_document_highlights(cx);
-            })?;
-            Ok(())
-        }))
-    }
-
-    fn take_rename(
-        &mut self,
-        moving_cursor: bool,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<RenameState> {
-        let rename = self.pending_rename.take()?;
-        if rename.editor.focus_handle(cx).is_focused(cx) {
-            cx.focus(&self.focus_handle);
-        }
-
-        self.remove_blocks(
-            [rename.block_id].into_iter().collect(),
-            Some(Autoscroll::fit()),
-            cx,
-        );
-        self.clear_highlights::<Rename>(cx);
-        self.show_local_selections = true;
-
-        if moving_cursor {
-            let rename_editor = rename.editor.read(cx);
-            let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
-
-            // Update the selection to match the position of the selection inside
-            // the rename editor.
-            let snapshot = self.buffer.read(cx).read(cx);
-            let rename_range = rename.range.to_offset(&snapshot);
-            let cursor_in_editor = snapshot
-                .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
-                .min(rename_range.end);
-            drop(snapshot);
-
-            self.change_selections(None, cx, |s| {
-                s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
-            });
-        } else {
-            self.refresh_document_highlights(cx);
-        }
-
-        Some(rename)
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn pending_rename(&self) -> Option<&RenameState> {
-        self.pending_rename.as_ref()
-    }
-
-    fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
-        let project = match &self.project {
-            Some(project) => project.clone(),
-            None => return None,
-        };
-
-        Some(self.perform_format(project, FormatTrigger::Manual, cx))
-    }
-
-    fn perform_format(
-        &mut self,
-        project: Model<Project>,
-        trigger: FormatTrigger,
-        cx: &mut ViewContext<Self>,
-    ) -> Task<Result<()>> {
-        let buffer = self.buffer().clone();
-        let buffers = buffer.read(cx).all_buffers();
-
-        let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
-        let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
-
-        cx.spawn(|_, mut cx| async move {
-            let transaction = futures::select_biased! {
-                _ = timeout => {
-                    log::warn!("timed out waiting for formatting");
-                    None
-                }
-                transaction = format.log_err().fuse() => transaction,
-            };
-
-            buffer
-                .update(&mut cx, |buffer, cx| {
-                    if let Some(transaction) = transaction {
-                        if !buffer.is_singleton() {
-                            buffer.push_transaction(&transaction.0, cx);
-                        }
-                    }
-
-                    cx.notify();
-                })
-                .ok();
-
-            Ok(())
-        })
-    }
-
-    fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
-        if let Some(project) = self.project.clone() {
-            self.buffer.update(cx, |multi_buffer, cx| {
-                project.update(cx, |project, cx| {
-                    project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
-                });
-            })
-        }
-    }
-
-    fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
-        cx.show_character_palette();
-    }
-
-    fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
-        if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
-            let buffer = self.buffer.read(cx).snapshot(cx);
-            let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
-            let is_valid = buffer
-                .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
-                .any(|entry| {
-                    entry.diagnostic.is_primary
-                        && !entry.range.is_empty()
-                        && entry.range.start == primary_range_start
-                        && entry.diagnostic.message == active_diagnostics.primary_message
-                });
-
-            if is_valid != active_diagnostics.is_valid {
-                active_diagnostics.is_valid = is_valid;
-                let mut new_styles = HashMap::default();
-                for (block_id, diagnostic) in &active_diagnostics.blocks {
-                    new_styles.insert(
-                        *block_id,
-                        diagnostic_block_renderer(diagnostic.clone(), is_valid),
-                    );
-                }
-                self.display_map
-                    .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
-            }
-        }
-    }
-
-    fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
-        self.dismiss_diagnostics(cx);
-        self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
-            let buffer = self.buffer.read(cx).snapshot(cx);
-
-            let mut primary_range = None;
-            let mut primary_message = None;
-            let mut group_end = Point::zero();
-            let diagnostic_group = buffer
-                .diagnostic_group::<Point>(group_id)
-                .map(|entry| {
-                    if entry.range.end > group_end {
-                        group_end = entry.range.end;
-                    }
-                    if entry.diagnostic.is_primary {
-                        primary_range = Some(entry.range.clone());
-                        primary_message = Some(entry.diagnostic.message.clone());
-                    }
-                    entry
-                })
-                .collect::<Vec<_>>();
-            let primary_range = primary_range?;
-            let primary_message = primary_message?;
-            let primary_range =
-                buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
-
-            let blocks = display_map
-                .insert_blocks(
-                    diagnostic_group.iter().map(|entry| {
-                        let diagnostic = entry.diagnostic.clone();
-                        let message_height = diagnostic.message.lines().count() as u8;
-                        BlockProperties {
-                            style: BlockStyle::Fixed,
-                            position: buffer.anchor_after(entry.range.start),
-                            height: message_height,
-                            render: diagnostic_block_renderer(diagnostic, true),
-                            disposition: BlockDisposition::Below,
-                        }
-                    }),
-                    cx,
-                )
-                .into_iter()
-                .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
-                .collect();
-
-            Some(ActiveDiagnosticGroup {
-                primary_range,
-                primary_message,
-                blocks,
-                is_valid: true,
-            })
-        });
-        self.active_diagnostics.is_some()
-    }
-
-    fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
-        if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
-            self.display_map.update(cx, |display_map, cx| {
-                display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
-            });
-            cx.notify();
-        }
-    }
-
-    pub fn set_selections_from_remote(
-        &mut self,
-        selections: Vec<Selection<Anchor>>,
-        pending_selection: Option<Selection<Anchor>>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        let old_cursor_position = self.selections.newest_anchor().head();
-        self.selections.change_with(cx, |s| {
-            s.select_anchors(selections);
-            if let Some(pending_selection) = pending_selection {
-                s.set_pending(pending_selection, SelectMode::Character);
-            } else {
-                s.clear_pending();
-            }
-        });
-        self.selections_did_change(false, &old_cursor_position, cx);
-    }
-
-    fn push_to_selection_history(&mut self) {
-        self.selection_history.push(SelectionHistoryEntry {
-            selections: self.selections.disjoint_anchors(),
-            select_next_state: self.select_next_state.clone(),
-            select_prev_state: self.select_prev_state.clone(),
-            add_selections_state: self.add_selections_state.clone(),
-        });
-    }
-
-    pub fn transact(
-        &mut self,
-        cx: &mut ViewContext<Self>,
-        update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
-    ) -> Option<TransactionId> {
-        self.start_transaction_at(Instant::now(), cx);
-        update(self, cx);
-        self.end_transaction_at(Instant::now(), cx)
-    }
-
-    fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
-        self.end_selection(cx);
-        if let Some(tx_id) = self
-            .buffer
-            .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
-        {
-            self.selection_history
-                .insert_transaction(tx_id, self.selections.disjoint_anchors());
-        }
-    }
-
-    fn end_transaction_at(
-        &mut self,
-        now: Instant,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<TransactionId> {
-        if let Some(tx_id) = self
-            .buffer
-            .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
-        {
-            if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
-                *end_selections = Some(self.selections.disjoint_anchors());
-            } else {
-                log::error!("unexpectedly ended a transaction that wasn't started by this editor");
-            }
-
-            cx.emit(EditorEvent::Edited);
-            Some(tx_id)
-        } else {
-            None
-        }
-    }
-
-    pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
-        let mut fold_ranges = Vec::new();
-
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-
-        let selections = self.selections.all_adjusted(cx);
-        for selection in selections {
-            let range = selection.range().sorted();
-            let buffer_start_row = range.start.row;
-
-            for row in (0..=range.end.row).rev() {
-                let fold_range = display_map.foldable_range(row);
-
-                if let Some(fold_range) = fold_range {
-                    if fold_range.end.row >= buffer_start_row {
-                        fold_ranges.push(fold_range);
-                        if row <= range.start.row {
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-
-        self.fold_ranges(fold_ranges, true, cx);
-    }
-
-    pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
-        let buffer_row = fold_at.buffer_row;
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-
-        if let Some(fold_range) = display_map.foldable_range(buffer_row) {
-            let autoscroll = self
-                .selections
-                .all::<Point>(cx)
-                .iter()
-                .any(|selection| fold_range.overlaps(&selection.range()));
-
-            self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
-        }
-    }
-
-    pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let buffer = &display_map.buffer_snapshot;
-        let selections = self.selections.all::<Point>(cx);
-        let ranges = selections
-            .iter()
-            .map(|s| {
-                let range = s.display_range(&display_map).sorted();
-                let mut start = range.start.to_point(&display_map);
-                let mut end = range.end.to_point(&display_map);
-                start.column = 0;
-                end.column = buffer.line_len(end.row);
-                start..end
-            })
-            .collect::<Vec<_>>();
-
-        self.unfold_ranges(ranges, true, true, cx);
-    }
-
-    pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-
-        let intersection_range = Point::new(unfold_at.buffer_row, 0)
-            ..Point::new(
-                unfold_at.buffer_row,
-                display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
-            );
-
-        let autoscroll = self
-            .selections
-            .all::<Point>(cx)
-            .iter()
-            .any(|selection| selection.range().overlaps(&intersection_range));
-
-        self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
-    }
-
-    pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
-        let selections = self.selections.all::<Point>(cx);
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let line_mode = self.selections.line_mode;
-        let ranges = selections.into_iter().map(|s| {
-            if line_mode {
-                let start = Point::new(s.start.row, 0);
-                let end = Point::new(s.end.row, display_map.buffer_snapshot.line_len(s.end.row));
-                start..end
-            } else {
-                s.start..s.end
-            }
-        });
-        self.fold_ranges(ranges, true, cx);
-    }
-
-    pub fn fold_ranges<T: ToOffset + Clone>(
-        &mut self,
-        ranges: impl IntoIterator<Item = Range<T>>,
-        auto_scroll: bool,
-        cx: &mut ViewContext<Self>,
-    ) {
-        let mut ranges = ranges.into_iter().peekable();
-        if ranges.peek().is_some() {
-            self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
-
-            if auto_scroll {
-                self.request_autoscroll(Autoscroll::fit(), cx);
-            }
-
-            cx.notify();
-        }
-    }
-
-    pub fn unfold_ranges<T: ToOffset + Clone>(
-        &mut self,
-        ranges: impl IntoIterator<Item = Range<T>>,
-        inclusive: bool,
-        auto_scroll: bool,
-        cx: &mut ViewContext<Self>,
-    ) {
-        let mut ranges = ranges.into_iter().peekable();
-        if ranges.peek().is_some() {
-            self.display_map
-                .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
-            if auto_scroll {
-                self.request_autoscroll(Autoscroll::fit(), cx);
-            }
-
-            cx.notify();
-        }
-    }
-
-    pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
-        if hovered != self.gutter_hovered {
-            self.gutter_hovered = hovered;
-            cx.notify();
-        }
-    }
-
-    pub fn insert_blocks(
-        &mut self,
-        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
-        autoscroll: Option<Autoscroll>,
-        cx: &mut ViewContext<Self>,
-    ) -> Vec<BlockId> {
-        let blocks = self
-            .display_map
-            .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
-        if let Some(autoscroll) = autoscroll {
-            self.request_autoscroll(autoscroll, cx);
-        }
-        blocks
-    }
-
-    pub fn replace_blocks(
-        &mut self,
-        blocks: HashMap<BlockId, RenderBlock>,
-        autoscroll: Option<Autoscroll>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.display_map
-            .update(cx, |display_map, _| display_map.replace_blocks(blocks));
-        if let Some(autoscroll) = autoscroll {
-            self.request_autoscroll(autoscroll, cx);
-        }
-    }
-
-    pub fn remove_blocks(
-        &mut self,
-        block_ids: HashSet<BlockId>,
-        autoscroll: Option<Autoscroll>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.display_map.update(cx, |display_map, cx| {
-            display_map.remove_blocks(block_ids, cx)
-        });
-        if let Some(autoscroll) = autoscroll {
-            self.request_autoscroll(autoscroll, cx);
-        }
-    }
-
-    pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
-        self.display_map
-            .update(cx, |map, cx| map.snapshot(cx))
-            .longest_row()
-    }
-
-    pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
-        self.display_map
-            .update(cx, |map, cx| map.snapshot(cx))
-            .max_point()
-    }
-
-    pub fn text(&self, cx: &AppContext) -> String {
-        self.buffer.read(cx).read(cx).text()
-    }
-
-    pub fn text_option(&self, cx: &AppContext) -> Option<String> {
-        let text = self.text(cx);
-        let text = text.trim();
-
-        if text.is_empty() {
-            return None;
-        }
-
-        Some(text.to_string())
-    }
-
-    pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
-        self.transact(cx, |this, cx| {
-            this.buffer
-                .read(cx)
-                .as_singleton()
-                .expect("you can only call set_text on editors for singleton buffers")
-                .update(cx, |buffer, cx| buffer.set_text(text, cx));
-        });
-    }
-
-    pub fn display_text(&self, cx: &mut AppContext) -> String {
-        self.display_map
-            .update(cx, |map, cx| map.snapshot(cx))
-            .text()
-    }
-
-    pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
-        let mut wrap_guides = smallvec::smallvec![];
-
-        if self.show_wrap_guides == Some(false) {
-            return wrap_guides;
-        }
-
-        let settings = self.buffer.read(cx).settings_at(0, cx);
-        if settings.show_wrap_guides {
-            if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
-                wrap_guides.push((soft_wrap as usize, true));
-            }
-            wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
-        }
-
-        wrap_guides
-    }
-
-    pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
-        let settings = self.buffer.read(cx).settings_at(0, cx);
-        let mode = self
-            .soft_wrap_mode_override
-            .unwrap_or_else(|| settings.soft_wrap);
-        match mode {
-            language_settings::SoftWrap::None => SoftWrap::None,
-            language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
-            language_settings::SoftWrap::PreferredLineLength => {
-                SoftWrap::Column(settings.preferred_line_length)
-            }
-        }
-    }
-
-    pub fn set_soft_wrap_mode(
-        &mut self,
-        mode: language_settings::SoftWrap,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.soft_wrap_mode_override = Some(mode);
-        cx.notify();
-    }
-
-    pub fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
-        let rem_size = cx.rem_size();
-        self.display_map.update(cx, |map, cx| {
-            map.set_font(
-                style.text.font(),
-                style.text.font_size.to_pixels(rem_size),
-                cx,
-            )
-        });
-        self.style = Some(style);
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn style(&self) -> Option<&EditorStyle> {
-        self.style.as_ref()
-    }
-
-    // Called by the element. This method is not designed to be called outside of the editor
-    // element's layout code because it does not notify when rewrapping is computed synchronously.
-    pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
-        self.display_map
-            .update(cx, |map, cx| map.set_wrap_width(width, cx))
-    }
-
-    pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
-        if self.soft_wrap_mode_override.is_some() {
-            self.soft_wrap_mode_override.take();
-        } else {
-            let soft_wrap = match self.soft_wrap_mode(cx) {
-                SoftWrap::None => language_settings::SoftWrap::EditorWidth,
-                SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
-            };
-            self.soft_wrap_mode_override = Some(soft_wrap);
-        }
-        cx.notify();
-    }
-
-    pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
-        self.show_gutter = show_gutter;
-        cx.notify();
-    }
-
-    pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
-        self.show_wrap_guides = Some(show_gutter);
-        cx.notify();
-    }
-
-    pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
-        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
-            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
-                cx.reveal_path(&file.abs_path(cx));
-            }
-        }
-    }
-
-    pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
-        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
-            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
-                if let Some(path) = file.abs_path(cx).to_str() {
-                    cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
-                }
-            }
-        }
-    }
-
-    pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
-        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
-            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
-                if let Some(path) = file.path().to_str() {
-                    cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
-                }
-            }
-        }
-    }
-
-    pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
-        self.highlighted_rows = rows;
-    }
-
-    pub fn highlighted_rows(&self) -> Option<Range<u32>> {
-        self.highlighted_rows.clone()
-    }
-
-    pub fn highlight_background<T: 'static>(
-        &mut self,
-        ranges: Vec<Range<Anchor>>,
-        color_fetcher: fn(&ThemeColors) -> Hsla,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.background_highlights
-            .insert(TypeId::of::<T>(), (color_fetcher, ranges));
-        cx.notify();
-    }
-
-    pub fn highlight_inlay_background<T: 'static>(
-        &mut self,
-        ranges: Vec<InlayHighlight>,
-        color_fetcher: fn(&ThemeColors) -> Hsla,
-        cx: &mut ViewContext<Self>,
-    ) {
-        // TODO: no actual highlights happen for inlays currently, find a way to do that
-        self.inlay_background_highlights
-            .insert(Some(TypeId::of::<T>()), (color_fetcher, ranges));
-        cx.notify();
-    }
-
-    pub fn clear_background_highlights<T: 'static>(
-        &mut self,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<BackgroundHighlight> {
-        let text_highlights = self.background_highlights.remove(&TypeId::of::<T>());
-        let inlay_highlights = self
-            .inlay_background_highlights
-            .remove(&Some(TypeId::of::<T>()));
-        if text_highlights.is_some() || inlay_highlights.is_some() {
-            cx.notify();
-        }
-        text_highlights
-    }
-
-    #[cfg(feature = "test-support")]
-    pub fn all_text_background_highlights(
-        &mut self,
-        cx: &mut ViewContext<Self>,
-    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
-        let snapshot = self.snapshot(cx);
-        let buffer = &snapshot.buffer_snapshot;
-        let start = buffer.anchor_before(0);
-        let end = buffer.anchor_after(buffer.len());
-        let theme = cx.theme().colors();
-        self.background_highlights_in_range(start..end, &snapshot, theme)
-    }
-
-    fn document_highlights_for_position<'a>(
-        &'a self,
-        position: Anchor,
-        buffer: &'a MultiBufferSnapshot,
-    ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
-        let read_highlights = self
-            .background_highlights
-            .get(&TypeId::of::<DocumentHighlightRead>())
-            .map(|h| &h.1);
-        let write_highlights = self
-            .background_highlights
-            .get(&TypeId::of::<DocumentHighlightWrite>())
-            .map(|h| &h.1);
-        let left_position = position.bias_left(buffer);
-        let right_position = position.bias_right(buffer);
-        read_highlights
-            .into_iter()
-            .chain(write_highlights)
-            .flat_map(move |ranges| {
-                let start_ix = match ranges.binary_search_by(|probe| {
-                    let cmp = probe.end.cmp(&left_position, buffer);
-                    if cmp.is_ge() {
-                        Ordering::Greater
-                    } else {
-                        Ordering::Less
-                    }
-                }) {
-                    Ok(i) | Err(i) => i,
-                };
-
-                let right_position = right_position.clone();
-                ranges[start_ix..]
-                    .iter()
-                    .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
-            })
-    }
-
-    pub fn background_highlights_in_range(
-        &self,
-        search_range: Range<Anchor>,
-        display_snapshot: &DisplaySnapshot,
-        theme: &ThemeColors,
-    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
-        let mut results = Vec::new();
-        for (color_fetcher, ranges) in self.background_highlights.values() {
-            let color = color_fetcher(theme);
-            let start_ix = match ranges.binary_search_by(|probe| {
-                let cmp = probe
-                    .end
-                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
-                if cmp.is_gt() {
-                    Ordering::Greater
-                } else {
-                    Ordering::Less
-                }
-            }) {
-                Ok(i) | Err(i) => i,
-            };
-            for range in &ranges[start_ix..] {
-                if range
-                    .start
-                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
-                    .is_ge()
-                {
-                    break;
-                }
-
-                let start = range.start.to_display_point(&display_snapshot);
-                let end = range.end.to_display_point(&display_snapshot);
-                results.push((start..end, color))
-            }
-        }
-        results
-    }
-
-    pub fn background_highlight_row_ranges<T: 'static>(
-        &self,
-        search_range: Range<Anchor>,
-        display_snapshot: &DisplaySnapshot,
-        count: usize,
-    ) -> Vec<RangeInclusive<DisplayPoint>> {
-        let mut results = Vec::new();
-        let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
-            return vec![];
-        };
-
-        let start_ix = match ranges.binary_search_by(|probe| {
-            let cmp = probe
-                .end
-                .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
-            if cmp.is_gt() {
-                Ordering::Greater
-            } else {
-                Ordering::Less
-            }
-        }) {
-            Ok(i) | Err(i) => i,
-        };
-        let mut push_region = |start: Option<Point>, end: Option<Point>| {
-            if let (Some(start_display), Some(end_display)) = (start, end) {
-                results.push(
-                    start_display.to_display_point(display_snapshot)
-                        ..=end_display.to_display_point(display_snapshot),
-                );
-            }
-        };
-        let mut start_row: Option<Point> = None;
-        let mut end_row: Option<Point> = None;
-        if ranges.len() > count {
-            return Vec::new();
-        }
-        for range in &ranges[start_ix..] {
-            if range
-                .start
-                .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
-                .is_ge()
-            {
-                break;
-            }
-            let end = range.end.to_point(&display_snapshot.buffer_snapshot);
-            if let Some(current_row) = &end_row {
-                if end.row == current_row.row {
-                    continue;
-                }
-            }
-            let start = range.start.to_point(&display_snapshot.buffer_snapshot);
-            if start_row.is_none() {
-                assert_eq!(end_row, None);
-                start_row = Some(start);
-                end_row = Some(end);
-                continue;
-            }
-            if let Some(current_end) = end_row.as_mut() {
-                if start.row > current_end.row + 1 {
-                    push_region(start_row, end_row);
-                    start_row = Some(start);
-                    end_row = Some(end);
-                } else {
-                    // Merge two hunks.
-                    *current_end = end;
-                }
-            } else {
-                unreachable!();
-            }
-        }
-        // We might still have a hunk that was not rendered (if there was a search hit on the last line)
-        push_region(start_row, end_row);
-        results
-    }
-
-    pub fn highlight_text<T: 'static>(
-        &mut self,
-        ranges: Vec<Range<Anchor>>,
-        style: HighlightStyle,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.display_map.update(cx, |map, _| {
-            map.highlight_text(TypeId::of::<T>(), ranges, style)
-        });
-        cx.notify();
-    }
-
-    pub fn highlight_inlays<T: 'static>(
-        &mut self,
-        highlights: Vec<InlayHighlight>,
-        style: HighlightStyle,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.display_map.update(cx, |map, _| {
-            map.highlight_inlays(TypeId::of::<T>(), highlights, style)
-        });
-        cx.notify();
-    }
-
-    pub fn text_highlights<'a, T: 'static>(
-        &'a self,
-        cx: &'a AppContext,
-    ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
-        self.display_map.read(cx).text_highlights(TypeId::of::<T>())
-    }
-
-    pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
-        let cleared = self
-            .display_map
-            .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
-        if cleared {
-            cx.notify();
-        }
-    }
-
-    pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
-        self.blink_manager.read(cx).visible() && self.focus_handle.is_focused(cx)
-    }
-
-    fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
-        cx.notify();
-    }
-
-    fn on_buffer_event(
-        &mut self,
-        multibuffer: Model<MultiBuffer>,
-        event: &multi_buffer::Event,
-        cx: &mut ViewContext<Self>,
-    ) {
-        match event {
-            multi_buffer::Event::Edited {
-                sigleton_buffer_edited,
-            } => {
-                self.refresh_active_diagnostics(cx);
-                self.refresh_code_actions(cx);
-                if self.has_active_copilot_suggestion(cx) {
-                    self.update_visible_copilot_suggestion(cx);
-                }
-                cx.emit(EditorEvent::BufferEdited);
-                cx.emit(SearchEvent::MatchesInvalidated);
-
-                if *sigleton_buffer_edited {
-                    if let Some(project) = &self.project {
-                        let project = project.read(cx);
-                        let languages_affected = multibuffer
-                            .read(cx)
-                            .all_buffers()
-                            .into_iter()
-                            .filter_map(|buffer| {
-                                let buffer = buffer.read(cx);
-                                let language = buffer.language()?;
-                                if project.is_local()
-                                    && project.language_servers_for_buffer(buffer, cx).count() == 0
-                                {
-                                    None
-                                } else {
-                                    Some(language)
-                                }
-                            })
-                            .cloned()
-                            .collect::<HashSet<_>>();
-                        if !languages_affected.is_empty() {
-                            self.refresh_inlay_hints(
-                                InlayHintRefreshReason::BufferEdited(languages_affected),
-                                cx,
-                            );
-                        }
-                    }
-                }
-            }
-            multi_buffer::Event::ExcerptsAdded {
-                buffer,
-                predecessor,
-                excerpts,
-            } => {
-                cx.emit(EditorEvent::ExcerptsAdded {
-                    buffer: buffer.clone(),
-                    predecessor: *predecessor,
-                    excerpts: excerpts.clone(),
-                });
-                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
-            }
-            multi_buffer::Event::ExcerptsRemoved { ids } => {
-                self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
-                cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
-            }
-            multi_buffer::Event::Reparsed => cx.emit(EditorEvent::Reparsed),
-            multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
-            multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
-            multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
-                cx.emit(EditorEvent::TitleChanged)
-            }
-            multi_buffer::Event::DiffBaseChanged => cx.emit(EditorEvent::DiffBaseChanged),
-            multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
-            multi_buffer::Event::DiagnosticsUpdated => {
-                self.refresh_active_diagnostics(cx);
-            }
-            _ => {}
-        };
-    }
-
-    fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
-        cx.notify();
-    }
-
-    fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
-        self.refresh_copilot_suggestions(true, cx);
-        self.refresh_inlay_hints(
-            InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
-                self.selections.newest_anchor().head(),
-                &self.buffer.read(cx).snapshot(cx),
-                cx,
-            )),
-            cx,
-        );
-    }
-
-    pub fn set_searchable(&mut self, searchable: bool) {
-        self.searchable = searchable;
-    }
-
-    pub fn searchable(&self) -> bool {
-        self.searchable
-    }
-
-    fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
-        let buffer = self.buffer.read(cx);
-        if buffer.is_singleton() {
-            cx.propagate();
-            return;
-        }
-
-        let Some(workspace) = self.workspace() else {
-            cx.propagate();
-            return;
-        };
-
-        let mut new_selections_by_buffer = HashMap::default();
-        for selection in self.selections.all::<usize>(cx) {
-            for (buffer, mut range, _) in
-                buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
-            {
-                if selection.reversed {
-                    mem::swap(&mut range.start, &mut range.end);
-                }
-                new_selections_by_buffer
-                    .entry(buffer)
-                    .or_insert(Vec::new())
-                    .push(range)
-            }
-        }
-
-        self.push_to_nav_history(self.selections.newest_anchor().head(), None, cx);
-
-        // We defer the pane interaction because we ourselves are a workspace item
-        // and activating a new item causes the pane to call a method on us reentrantly,
-        // which panics if we're on the stack.
-        cx.window_context().defer(move |cx| {
-            workspace.update(cx, |workspace, cx| {
-                let pane = workspace.active_pane().clone();
-                pane.update(cx, |pane, _| pane.disable_history());
-
-                for (buffer, ranges) in new_selections_by_buffer.into_iter() {
-                    let editor = workspace.open_project_item::<Self>(buffer, cx);
-                    editor.update(cx, |editor, cx| {
-                        editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
-                            s.select_ranges(ranges);
-                        });
-                    });
-                }
-
-                pane.update(cx, |pane, _| pane.enable_history());
-            })
-        });
-    }
-
-    fn jump(
-        &mut self,
-        path: ProjectPath,
-        position: Point,
-        anchor: language::Anchor,
-        cx: &mut ViewContext<Self>,
-    ) {
-        let workspace = self.workspace();
-        cx.spawn(|_, mut cx| async move {
-            let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
-            let editor = workspace.update(&mut cx, |workspace, cx| {
-                workspace.open_path(path, None, true, cx)
-            })?;
-            let editor = editor
-                .await?
-                .downcast::<Editor>()
-                .ok_or_else(|| anyhow!("opened item was not an editor"))?
-                .downgrade();
-            editor.update(&mut cx, |editor, cx| {
-                let buffer = editor
-                    .buffer()
-                    .read(cx)
-                    .as_singleton()
-                    .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
-                let buffer = buffer.read(cx);
-                let cursor = if buffer.can_resolve(&anchor) {
-                    language::ToPoint::to_point(&anchor, buffer)
-                } else {
-                    buffer.clip_point(position, Bias::Left)
-                };
-
-                let nav_history = editor.nav_history.take();
-                editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
-                    s.select_ranges([cursor..cursor]);
-                });
-                editor.nav_history = nav_history;
-
-                anyhow::Ok(())
-            })??;
-
-            anyhow::Ok(())
-        })
-        .detach_and_log_err(cx);
-    }
-
-    fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
-        let snapshot = self.buffer.read(cx).read(cx);
-        let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
-        Some(
-            ranges
-                .iter()
-                .map(move |range| {
-                    range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
-                })
-                .collect(),
-        )
-    }
-
-    fn selection_replacement_ranges(
-        &self,
-        range: Range<OffsetUtf16>,
-        cx: &AppContext,
-    ) -> Vec<Range<OffsetUtf16>> {
-        let selections = self.selections.all::<OffsetUtf16>(cx);
-        let newest_selection = selections
-            .iter()
-            .max_by_key(|selection| selection.id)
-            .unwrap();
-        let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
-        let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
-        let snapshot = self.buffer.read(cx).read(cx);
-        selections
-            .into_iter()
-            .map(|mut selection| {
-                selection.start.0 =
-                    (selection.start.0 as isize).saturating_add(start_delta) as usize;
-                selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
-                snapshot.clip_offset_utf16(selection.start, Bias::Left)
-                    ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
-            })
-            .collect()
-    }
-
-    fn report_copilot_event(
-        &self,
-        suggestion_id: Option<String>,
-        suggestion_accepted: bool,
-        cx: &AppContext,
-    ) {
-        let Some(project) = &self.project else { return };
-
-        // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
-        let file_extension = self
-            .buffer
-            .read(cx)
-            .as_singleton()
-            .and_then(|b| b.read(cx).file())
-            .and_then(|file| Path::new(file.file_name(cx)).extension())
-            .and_then(|e| e.to_str())
-            .map(|a| a.to_string());
-
-        let telemetry = project.read(cx).client().telemetry().clone();
-        let telemetry_settings = *TelemetrySettings::get_global(cx);
-
-        telemetry.report_copilot_event(
-            telemetry_settings,
-            suggestion_id,
-            suggestion_accepted,
-            file_extension,
-        )
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    fn report_editor_event(
-        &self,
-        _operation: &'static str,
-        _file_extension: Option<String>,
-        _cx: &AppContext,
-    ) {
-    }
-
-    #[cfg(not(any(test, feature = "test-support")))]
-    fn report_editor_event(
-        &self,
-        operation: &'static str,
-        file_extension: Option<String>,
-        cx: &AppContext,
-    ) {
-        let Some(project) = &self.project else { return };
-
-        // If None, we are in a file without an extension
-        let file = self
-            .buffer
-            .read(cx)
-            .as_singleton()
-            .and_then(|b| b.read(cx).file());
-        let file_extension = file_extension.or(file
-            .as_ref()
-            .and_then(|file| Path::new(file.file_name(cx)).extension())
-            .and_then(|e| e.to_str())
-            .map(|a| a.to_string()));
-
-        let vim_mode = cx
-            .global::<SettingsStore>()
-            .raw_user_settings()
-            .get("vim_mode")
-            == Some(&serde_json::Value::Bool(true));
-        let telemetry_settings = *TelemetrySettings::get_global(cx);
-        let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
-        let copilot_enabled_for_language = self
-            .buffer
-            .read(cx)
-            .settings_at(0, cx)
-            .show_copilot_suggestions;
-
-        let telemetry = project.read(cx).client().telemetry().clone();
-        telemetry.report_editor_event(
-            telemetry_settings,
-            file_extension,
-            vim_mode,
-            operation,
-            copilot_enabled,
-            copilot_enabled_for_language,
-        )
-    }
-
-    /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
-    /// with each line being an array of {text, highlight} objects.
-    fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
-        let Some(buffer) = self.buffer.read(cx).as_singleton() else {
-            return;
-        };
-
-        #[derive(Serialize)]
-        struct Chunk<'a> {
-            text: String,
-            highlight: Option<&'a str>,
-        }
-
-        let snapshot = buffer.read(cx).snapshot();
-        let range = self
-            .selected_text_range(cx)
-            .and_then(|selected_range| {
-                if selected_range.is_empty() {
-                    None
-                } else {
-                    Some(selected_range)
-                }
-            })
-            .unwrap_or_else(|| 0..snapshot.len());
-
-        let chunks = snapshot.chunks(range, true);
-        let mut lines = Vec::new();
-        let mut line: VecDeque<Chunk> = VecDeque::new();
-
-        let Some(style) = self.style.as_ref() else {
-            return;
-        };
-
-        for chunk in chunks {
-            let highlight = chunk
-                .syntax_highlight_id
-                .and_then(|id| id.name(&style.syntax));
-            let mut chunk_lines = chunk.text.split("\n").peekable();
-            while let Some(text) = chunk_lines.next() {
-                let mut merged_with_last_token = false;
-                if let Some(last_token) = line.back_mut() {
-                    if last_token.highlight == highlight {
-                        last_token.text.push_str(text);
-                        merged_with_last_token = true;
-                    }
-                }
-
-                if !merged_with_last_token {
-                    line.push_back(Chunk {
-                        text: text.into(),
-                        highlight,
-                    });
-                }
-
-                if chunk_lines.peek().is_some() {
-                    if line.len() > 1 && line.front().unwrap().text.is_empty() {
-                        line.pop_front();
-                    }
-                    if line.len() > 1 && line.back().unwrap().text.is_empty() {
-                        line.pop_back();
-                    }
-
-                    lines.push(mem::take(&mut line));
-                }
-            }
-        }
-
-        let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
-            return;
-        };
-        cx.write_to_clipboard(ClipboardItem::new(lines));
-    }
-
-    pub fn inlay_hint_cache(&self) -> &InlayHintCache {
-        &self.inlay_hint_cache
-    }
-
-    pub fn replay_insert_event(
-        &mut self,
-        text: &str,
-        relative_utf16_range: Option<Range<isize>>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        if !self.input_enabled {
-            cx.emit(EditorEvent::InputIgnored { text: text.into() });
-            return;
-        }
-        if let Some(relative_utf16_range) = relative_utf16_range {
-            let selections = self.selections.all::<OffsetUtf16>(cx);
-            self.change_selections(None, cx, |s| {
-                let new_ranges = selections.into_iter().map(|range| {
-                    let start = OffsetUtf16(
-                        range
-                            .head()
-                            .0
-                            .saturating_add_signed(relative_utf16_range.start),
-                    );
-                    let end = OffsetUtf16(
-                        range
-                            .head()
-                            .0
-                            .saturating_add_signed(relative_utf16_range.end),
-                    );
-                    start..end
-                });
-                s.select_ranges(new_ranges);
-            });
-        }
-
-        self.handle_input(text, cx);
-    }
-
-    pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
-        let Some(project) = self.project.as_ref() else {
-            return false;
-        };
-        let project = project.read(cx);
-
-        let mut supports = false;
-        self.buffer().read(cx).for_each_buffer(|buffer| {
-            if !supports {
-                supports = project
-                    .language_servers_for_buffer(buffer.read(cx), cx)
-                    .any(
-                        |(_, server)| match server.capabilities().inlay_hint_provider {
-                            Some(lsp::OneOf::Left(enabled)) => enabled,
-                            Some(lsp::OneOf::Right(_)) => true,
-                            None => false,
-                        },
-                    )
-            }
-        });
-        supports
-    }
-
-    pub fn focus(&self, cx: &mut WindowContext) {
-        cx.focus(&self.focus_handle)
-    }
-
-    pub fn is_focused(&self, cx: &WindowContext) -> bool {
-        self.focus_handle.is_focused(cx)
-    }
-
-    fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
-        cx.emit(EditorEvent::Focused);
-
-        if let Some(rename) = self.pending_rename.as_ref() {
-            let rename_editor_focus_handle = rename.editor.read(cx).focus_handle.clone();
-            cx.focus(&rename_editor_focus_handle);
-        } else {
-            self.blink_manager.update(cx, BlinkManager::enable);
-            self.buffer.update(cx, |buffer, cx| {
-                buffer.finalize_last_transaction(cx);
-                if self.leader_peer_id.is_none() {
-                    buffer.set_active_selections(
-                        &self.selections.disjoint_anchors(),
-                        self.selections.line_mode,
-                        self.cursor_shape,
-                        cx,
-                    );
-                }
-            });
-        }
-    }
-
-    pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
-        self.blink_manager.update(cx, BlinkManager::disable);
-        self.buffer
-            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
-        self.hide_context_menu(cx);
-        hide_hover(self, cx);
-        cx.emit(EditorEvent::Blurred);
-        cx.notify();
-    }
-
-    pub fn register_action<A: Action>(
-        &mut self,
-        listener: impl Fn(&A, &mut WindowContext) + 'static,
-    ) -> &mut Self {
-        let listener = Arc::new(listener);
-
-        self.editor_actions.push(Box::new(move |cx| {
-            let _view = cx.view().clone();
-            let cx = cx.window_context();
-            let listener = listener.clone();
-            cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
-                let action = action.downcast_ref().unwrap();
-                if phase == DispatchPhase::Bubble {
-                    listener(action, cx)
-                }
-            })
-        }));
-        self
-    }
-}
-
-pub trait CollaborationHub {
-    fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
-    fn user_participant_indices<'a>(
-        &self,
-        cx: &'a AppContext,
-    ) -> &'a HashMap<u64, ParticipantIndex>;
-}
-
-impl CollaborationHub for Model<Project> {
-    fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
-        self.read(cx).collaborators()
-    }
-
-    fn user_participant_indices<'a>(
-        &self,
-        cx: &'a AppContext,
-    ) -> &'a HashMap<u64, ParticipantIndex> {
-        self.read(cx).user_store().read(cx).participant_indices()
-    }
-}
-
-fn inlay_hint_settings(
-    location: Anchor,
-    snapshot: &MultiBufferSnapshot,
-    cx: &mut ViewContext<'_, Editor>,
-) -> InlayHintSettings {
-    let file = snapshot.file_at(location);
-    let language = snapshot.language_at(location);
-    let settings = all_language_settings(file, cx);
-    settings
-        .language(language.map(|l| l.name()).as_deref())
-        .inlay_hints
-}
-
-fn consume_contiguous_rows(
-    contiguous_row_selections: &mut Vec<Selection<Point>>,
-    selection: &Selection<Point>,
-    display_map: &DisplaySnapshot,
-    selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
-) -> (u32, u32) {
-    contiguous_row_selections.push(selection.clone());
-    let start_row = selection.start.row;
-    let mut end_row = ending_row(selection, display_map);
-
-    while let Some(next_selection) = selections.peek() {
-        if next_selection.start.row <= end_row {
-            end_row = ending_row(next_selection, display_map);
-            contiguous_row_selections.push(selections.next().unwrap().clone());
-        } else {
-            break;
-        }
-    }
-    (start_row, end_row)
-}
-
-fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
-    if next_selection.end.column > 0 || next_selection.is_empty() {
-        display_map.next_line_boundary(next_selection.end).0.row + 1
-    } else {
-        next_selection.end.row
-    }
-}
-
-impl EditorSnapshot {
-    pub fn remote_selections_in_range<'a>(
-        &'a self,
-        range: &'a Range<Anchor>,
-        collaboration_hub: &dyn CollaborationHub,
-        cx: &'a AppContext,
-    ) -> impl 'a + Iterator<Item = RemoteSelection> {
-        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
-            .iter()
-            .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
-            .collect::<HashMap<_, _>>();
-        self.buffer_snapshot
-            .remote_selections_in_range(range)
-            .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();
-                Some(RemoteSelection {
-                    replica_id,
-                    selection,
-                    cursor_shape,
-                    line_mode,
-                    participant_index,
-                    peer_id: collaborator.peer_id,
-                })
-            })
-    }
-
-    pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
-        self.display_snapshot.buffer_snapshot.language_at(position)
-    }
-
-    pub fn is_focused(&self) -> bool {
-        self.is_focused
-    }
-
-    pub fn placeholder_text(&self) -> Option<&Arc<str>> {
-        self.placeholder_text.as_ref()
-    }
-
-    pub fn scroll_position(&self) -> gpui::Point<f32> {
-        self.scroll_anchor.scroll_position(&self.display_snapshot)
-    }
-}
-
-impl Deref for EditorSnapshot {
-    type Target = DisplaySnapshot;
-
-    fn deref(&self) -> &Self::Target {
-        &self.display_snapshot
-    }
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum EditorEvent {
-    InputIgnored {
-        text: Arc<str>,
-    },
-    InputHandled {
-        utf16_range_to_replace: Option<Range<isize>>,
-        text: Arc<str>,
-    },
-    ExcerptsAdded {
-        buffer: Model<Buffer>,
-        predecessor: ExcerptId,
-        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
-    },
-    ExcerptsRemoved {
-        ids: Vec<ExcerptId>,
-    },
-    BufferEdited,
-    Edited,
-    Reparsed,
-    Focused,
-    Blurred,
-    DirtyChanged,
-    Saved,
-    TitleChanged,
-    DiffBaseChanged,
-    SelectionsChanged {
-        local: bool,
-    },
-    ScrollPositionChanged {
-        local: bool,
-        autoscroll: bool,
-    },
-    Closed,
-}
-
-impl EventEmitter<EditorEvent> for Editor {}
-
-impl FocusableView for Editor {
-    fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
-        self.focus_handle.clone()
-    }
-}
-
-impl Render for Editor {
-    fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
-        let settings = ThemeSettings::get_global(cx);
-        let text_style = match self.mode {
-            EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
-                color: cx.theme().colors().editor_foreground,
-                font_family: settings.ui_font.family.clone(),
-                font_features: settings.ui_font.features,
-                font_size: rems(0.875).into(),
-                font_weight: FontWeight::NORMAL,
-                font_style: FontStyle::Normal,
-                line_height: relative(settings.buffer_line_height.value()),
-                background_color: None,
-                underline: None,
-                white_space: WhiteSpace::Normal,
-            },
-
-            EditorMode::Full => TextStyle {
-                color: cx.theme().colors().editor_foreground,
-                font_family: settings.buffer_font.family.clone(),
-                font_features: settings.buffer_font.features,
-                font_size: settings.buffer_font_size(cx).into(),
-                font_weight: FontWeight::NORMAL,
-                font_style: FontStyle::Normal,
-                line_height: relative(settings.buffer_line_height.value()),
-                background_color: None,
-                underline: None,
-                white_space: WhiteSpace::Normal,
-            },
-        };
-
-        let background = match self.mode {
-            EditorMode::SingleLine => cx.theme().system().transparent,
-            EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
-            EditorMode::Full => cx.theme().colors().editor_background,
-        };
-
-        EditorElement::new(
-            cx.view(),
-            EditorStyle {
-                background,
-                local_player: cx.theme().players().local(),
-                text: text_style,
-                scrollbar_width: px(12.),
-                syntax: cx.theme().syntax().clone(),
-                status: cx.theme().status().clone(),
-                // todo!("what about the rest of the highlight style parts?")
-                inlays_style: HighlightStyle {
-                    color: Some(cx.theme().status().hint),
-                    font_weight: Some(FontWeight::BOLD),
-                    ..HighlightStyle::default()
-                },
-                suggestions_style: HighlightStyle {
-                    color: Some(cx.theme().status().predictive),
-                    ..HighlightStyle::default()
-                },
-            },
-        )
-    }
-}
-
-impl InputHandler for Editor {
-    fn text_for_range(
-        &mut self,
-        range_utf16: Range<usize>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<String> {
-        Some(
-            self.buffer
-                .read(cx)
-                .read(cx)
-                .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
-                .collect(),
-        )
-    }
-
-    fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
-        // Prevent the IME menu from appearing when holding down an alphabetic key
-        // while input is disabled.
-        if !self.input_enabled {
-            return None;
-        }
-
-        let range = self.selections.newest::<OffsetUtf16>(cx).range();
-        Some(range.start.0..range.end.0)
-    }
-
-    fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
-        let snapshot = self.buffer.read(cx).read(cx);
-        let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
-        Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
-    }
-
-    fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
-        self.clear_highlights::<InputComposition>(cx);
-        self.ime_transaction.take();
-    }
-
-    fn replace_text_in_range(
-        &mut self,
-        range_utf16: Option<Range<usize>>,
-        text: &str,
-        cx: &mut ViewContext<Self>,
-    ) {
-        if !self.input_enabled {
-            cx.emit(EditorEvent::InputIgnored { text: text.into() });
-            return;
-        }
-
-        self.transact(cx, |this, cx| {
-            let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
-                let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
-                Some(this.selection_replacement_ranges(range_utf16, cx))
-            } else {
-                this.marked_text_ranges(cx)
-            };
-
-            let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
-                let newest_selection_id = this.selections.newest_anchor().id;
-                this.selections
-                    .all::<OffsetUtf16>(cx)
-                    .iter()
-                    .zip(ranges_to_replace.iter())
-                    .find_map(|(selection, range)| {
-                        if selection.id == newest_selection_id {
-                            Some(
-                                (range.start.0 as isize - selection.head().0 as isize)
-                                    ..(range.end.0 as isize - selection.head().0 as isize),
-                            )
-                        } else {
-                            None
-                        }
-                    })
-            });
-
-            cx.emit(EditorEvent::InputHandled {
-                utf16_range_to_replace: range_to_replace,
-                text: text.into(),
-            });
-
-            if let Some(new_selected_ranges) = new_selected_ranges {
-                this.change_selections(None, cx, |selections| {
-                    selections.select_ranges(new_selected_ranges)
-                });
-            }
-
-            this.handle_input(text, cx);
-        });
-
-        if let Some(transaction) = self.ime_transaction {
-            self.buffer.update(cx, |buffer, cx| {
-                buffer.group_until_transaction(transaction, cx);
-            });
-        }
-
-        self.unmark_text(cx);
-    }
-
-    fn replace_and_mark_text_in_range(
-        &mut self,
-        range_utf16: Option<Range<usize>>,
-        text: &str,
-        new_selected_range_utf16: Option<Range<usize>>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        if !self.input_enabled {
-            cx.emit(EditorEvent::InputIgnored { text: text.into() });
-            return;
-        }
-
-        let transaction = self.transact(cx, |this, cx| {
-            let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
-                let snapshot = this.buffer.read(cx).read(cx);
-                if let Some(relative_range_utf16) = range_utf16.as_ref() {
-                    for marked_range in &mut marked_ranges {
-                        marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
-                        marked_range.start.0 += relative_range_utf16.start;
-                        marked_range.start =
-                            snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
-                        marked_range.end =
-                            snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
-                    }
-                }
-                Some(marked_ranges)
-            } else if let Some(range_utf16) = range_utf16 {
-                let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
-                Some(this.selection_replacement_ranges(range_utf16, cx))
-            } else {
-                None
-            };
-
-            let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
-                let newest_selection_id = this.selections.newest_anchor().id;
-                this.selections
-                    .all::<OffsetUtf16>(cx)
-                    .iter()
-                    .zip(ranges_to_replace.iter())
-                    .find_map(|(selection, range)| {
-                        if selection.id == newest_selection_id {
-                            Some(
-                                (range.start.0 as isize - selection.head().0 as isize)
-                                    ..(range.end.0 as isize - selection.head().0 as isize),
-                            )
-                        } else {
-                            None
-                        }
-                    })
-            });
-
-            cx.emit(EditorEvent::InputHandled {
-                utf16_range_to_replace: range_to_replace,
-                text: text.into(),
-            });
-
-            if let Some(ranges) = ranges_to_replace {
-                this.change_selections(None, cx, |s| s.select_ranges(ranges));
-            }
-
-            let marked_ranges = {
-                let snapshot = this.buffer.read(cx).read(cx);
-                this.selections
-                    .disjoint_anchors()
-                    .iter()
-                    .map(|selection| {
-                        selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
-                    })
-                    .collect::<Vec<_>>()
-            };
-
-            if text.is_empty() {
-                this.unmark_text(cx);
-            } else {
-                this.highlight_text::<InputComposition>(
-                    marked_ranges.clone(),
-                    HighlightStyle::default(), // todo!() this.style(cx).composition_mark,
-                    cx,
-                );
-            }
-
-            this.handle_input(text, cx);
-
-            if let Some(new_selected_range) = new_selected_range_utf16 {
-                let snapshot = this.buffer.read(cx).read(cx);
-                let new_selected_ranges = marked_ranges
-                    .into_iter()
-                    .map(|marked_range| {
-                        let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
-                        let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
-                        let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
-                        snapshot.clip_offset_utf16(new_start, Bias::Left)
-                            ..snapshot.clip_offset_utf16(new_end, Bias::Right)
-                    })
-                    .collect::<Vec<_>>();
-
-                drop(snapshot);
-                this.change_selections(None, cx, |selections| {
-                    selections.select_ranges(new_selected_ranges)
-                });
-            }
-        });
-
-        self.ime_transaction = self.ime_transaction.or(transaction);
-        if let Some(transaction) = self.ime_transaction {
-            self.buffer.update(cx, |buffer, cx| {
-                buffer.group_until_transaction(transaction, cx);
-            });
-        }
-
-        if self.text_highlights::<InputComposition>(cx).is_none() {
-            self.ime_transaction.take();
-        }
-    }
-
-    fn bounds_for_range(
-        &mut self,
-        range_utf16: Range<usize>,
-        element_bounds: gpui::Bounds<Pixels>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<gpui::Bounds<Pixels>> {
-        let text_layout_details = self.text_layout_details(cx);
-        let style = &text_layout_details.editor_style;
-        let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
-        let font_size = style.text.font_size.to_pixels(cx.rem_size());
-        let line_height = style.text.line_height_in_pixels(cx.rem_size());
-        let em_width = cx
-            .text_system()
-            .typographic_bounds(font_id, font_size, 'm')
-            .unwrap()
-            .size
-            .width;
-
-        let snapshot = self.snapshot(cx);
-        let scroll_position = snapshot.scroll_position();
-        let scroll_left = scroll_position.x * em_width;
-
-        let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
-        let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
-            + self.gutter_width;
-        let y = line_height * (start.row() as f32 - scroll_position.y);
-
-        Some(Bounds {
-            origin: element_bounds.origin + point(x, y),
-            size: size(em_width, line_height),
-        })
-    }
-}
-
-trait SelectionExt {
-    fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
-    fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
-    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
-    fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
-        -> Range<u32>;
-}
-
-impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
-    fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
-        let start = self.start.to_point(buffer);
-        let end = self.end.to_point(buffer);
-        if self.reversed {
-            end..start
-        } else {
-            start..end
-        }
-    }
-
-    fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
-        let start = self.start.to_offset(buffer);
-        let end = self.end.to_offset(buffer);
-        if self.reversed {
-            end..start
-        } else {
-            start..end
-        }
-    }
-
-    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
-        let start = self
-            .start
-            .to_point(&map.buffer_snapshot)
-            .to_display_point(map);
-        let end = self
-            .end
-            .to_point(&map.buffer_snapshot)
-            .to_display_point(map);
-        if self.reversed {
-            end..start
-        } else {
-            start..end
-        }
-    }
-
-    fn spanned_rows(
-        &self,
-        include_end_if_at_line_start: bool,
-        map: &DisplaySnapshot,
-    ) -> Range<u32> {
-        let start = self.start.to_point(&map.buffer_snapshot);
-        let mut end = self.end.to_point(&map.buffer_snapshot);
-        if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
-            end.row -= 1;
-        }
-
-        let buffer_start = map.prev_line_boundary(start).0;
-        let buffer_end = map.next_line_boundary(end).0;
-        buffer_start.row..buffer_end.row + 1
-    }
-}
-
-impl<T: InvalidationRegion> InvalidationStack<T> {
-    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
-    where
-        S: Clone + ToOffset,
-    {
-        while let Some(region) = self.last() {
-            let all_selections_inside_invalidation_ranges =
-                if selections.len() == region.ranges().len() {
-                    selections
-                        .iter()
-                        .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
-                        .all(|(selection, invalidation_range)| {
-                            let head = selection.head().to_offset(buffer);
-                            invalidation_range.start <= head && invalidation_range.end >= head
-                        })
-                } else {
-                    false
-                };
-
-            if all_selections_inside_invalidation_ranges {
-                break;
-            } else {
-                self.pop();
-            }
-        }
-    }
-}
-
-impl<T> Default for InvalidationStack<T> {
-    fn default() -> Self {
-        Self(Default::default())
-    }
-}
-
-impl<T> Deref for InvalidationStack<T> {
-    type Target = Vec<T>;
-
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-
-impl<T> DerefMut for InvalidationStack<T> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.0
-    }
-}
-
-impl InvalidationRegion for SnippetState {
-    fn ranges(&self) -> &[Range<Anchor>] {
-        &self.ranges[self.active_index]
-    }
-}
-
-pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> RenderBlock {
-    let (text_without_backticks, code_ranges) = highlight_diagnostic_message(&diagnostic);
-
-    Arc::new(move |cx: &mut BlockContext| {
-        let color = Some(cx.theme().colors().text_accent);
-        let group_id: SharedString = cx.block_id.to_string().into();
-        // TODO: Nate: We should tint the background of the block with the severity color
-        // We need to extend the theme before we can do this
-        h_stack()
-            .id(cx.block_id)
-            .group(group_id.clone())
-            .relative()
-            .pl(cx.anchor_x)
-            .size_full()
-            .gap_2()
-            .child(
-                StyledText::new(text_without_backticks.clone()).with_highlights(
-                    &cx.text_style(),
-                    code_ranges.iter().map(|range| {
-                        (
-                            range.clone(),
-                            HighlightStyle {
-                                color,
-                                ..Default::default()
-                            },
-                        )
-                    }),
-                ),
-            )
-            .child(
-                IconButton::new(("copy-block", cx.block_id), Icon::Copy)
-                    .icon_color(Color::Muted)
-                    .size(ButtonSize::Compact)
-                    .style(ButtonStyle::Transparent)
-                    .visible_on_hover(group_id)
-                    .on_click(cx.listener({
-                        let message = diagnostic.message.clone();
-                        move |_, _, cx| cx.write_to_clipboard(ClipboardItem::new(message.clone()))
-                    }))
-                    .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
-            )
-            .into_any_element()
-    })
-}
-
-pub fn highlight_diagnostic_message(diagnostic: &Diagnostic) -> (SharedString, Vec<Range<usize>>) {
-    let mut text_without_backticks = String::new();
-    let mut code_ranges = Vec::new();
-
-    if let Some(source) = &diagnostic.source {
-        text_without_backticks.push_str(&source);
-        code_ranges.push(0..source.len());
-        text_without_backticks.push_str(": ");
-    }
-
-    let mut prev_offset = 0;
-    let mut in_code_block = false;
-    for (ix, _) in diagnostic
-        .message
-        .match_indices('`')
-        .chain([(diagnostic.message.len(), "")])
-    {
-        let prev_len = text_without_backticks.len();
-        text_without_backticks.push_str(&diagnostic.message[prev_offset..ix]);
-        prev_offset = ix + 1;
-        if in_code_block {
-            code_ranges.push(prev_len..text_without_backticks.len());
-            in_code_block = false;
-        } else {
-            in_code_block = true;
-        }
-    }
-
-    (text_without_backticks.into(), code_ranges)
-}
-
-pub fn diagnostic_style(severity: DiagnosticSeverity, valid: bool, colors: &StatusColors) -> Hsla {
-    match (severity, valid) {
-        (DiagnosticSeverity::ERROR, true) => colors.error,
-        (DiagnosticSeverity::ERROR, false) => colors.error,
-        (DiagnosticSeverity::WARNING, true) => colors.warning,
-        (DiagnosticSeverity::WARNING, false) => colors.warning,
-        (DiagnosticSeverity::INFORMATION, true) => colors.info,
-        (DiagnosticSeverity::INFORMATION, false) => colors.info,
-        (DiagnosticSeverity::HINT, true) => colors.info,
-        (DiagnosticSeverity::HINT, false) => colors.info,
-        _ => colors.ignored,
-    }
-}
-
-pub fn styled_runs_for_code_label<'a>(
-    label: &'a CodeLabel,
-    syntax_theme: &'a theme::SyntaxTheme,
-) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
-    let fade_out = HighlightStyle {
-        fade_out: Some(0.35),
-        ..Default::default()
-    };
-
-    let mut prev_end = label.filter_range.end;
-    label
-        .runs
-        .iter()
-        .enumerate()
-        .flat_map(move |(ix, (range, highlight_id))| {
-            let style = if let Some(style) = highlight_id.style(syntax_theme) {
-                style
-            } else {
-                return Default::default();
-            };
-            let mut muted_style = style;
-            muted_style.highlight(fade_out);
-
-            let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
-            if range.start >= label.filter_range.end {
-                if range.start > prev_end {
-                    runs.push((prev_end..range.start, fade_out));
-                }
-                runs.push((range.clone(), muted_style));
-            } else if range.end <= label.filter_range.end {
-                runs.push((range.clone(), style));
-            } else {
-                runs.push((range.start..label.filter_range.end, style));
-                runs.push((label.filter_range.end..range.end, muted_style));
-            }
-            prev_end = cmp::max(prev_end, range.end);
-
-            if ix + 1 == label.runs.len() && label.text.len() > prev_end {
-                runs.push((prev_end..label.text.len(), fade_out));
-            }
-
-            runs
-        })
-}
-
-pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
-    let mut index = 0;
-    let mut codepoints = text.char_indices().peekable();
-
-    std::iter::from_fn(move || {
-        let start_index = index;
-        while let Some((new_index, codepoint)) = codepoints.next() {
-            index = new_index + codepoint.len_utf8();
-            let current_upper = codepoint.is_uppercase();
-            let next_upper = codepoints
-                .peek()
-                .map(|(_, c)| c.is_uppercase())
-                .unwrap_or(false);
-
-            if !current_upper && next_upper {
-                return Some(&text[start_index..index]);
-            }
-        }
-
-        index = text.len();
-        if start_index < text.len() {
-            return Some(&text[start_index..]);
-        }
-        None
-    })
-    .flat_map(|word| word.split_inclusive('_'))
-    .flat_map(|word| word.split_inclusive('-'))
-}
-
-trait RangeToAnchorExt {
-    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
-}
-
-impl<T: ToOffset> RangeToAnchorExt for Range<T> {
-    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
-        snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
-    }
-}

crates/editor2/src/editor_settings.rs ๐Ÿ”—

@@ -1,72 +0,0 @@
-use schemars::JsonSchema;
-use serde::{Deserialize, Serialize};
-use settings::Settings;
-
-#[derive(Deserialize)]
-pub struct EditorSettings {
-    pub cursor_blink: bool,
-    pub hover_popover_enabled: bool,
-    pub show_completions_on_input: bool,
-    pub show_completion_documentation: bool,
-    pub use_on_type_format: bool,
-    pub scrollbar: Scrollbar,
-    pub relative_line_numbers: bool,
-    pub seed_search_query_from_cursor: SeedQuerySetting,
-}
-
-#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
-#[serde(rename_all = "snake_case")]
-pub enum SeedQuerySetting {
-    Always,
-    Selection,
-    Never,
-}
-
-#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
-pub struct Scrollbar {
-    pub show: ShowScrollbar,
-    pub git_diff: bool,
-    pub selections: bool,
-}
-
-#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
-#[serde(rename_all = "snake_case")]
-pub enum ShowScrollbar {
-    Auto,
-    System,
-    Always,
-    Never,
-}
-
-#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
-pub struct EditorSettingsContent {
-    pub cursor_blink: Option<bool>,
-    pub hover_popover_enabled: Option<bool>,
-    pub show_completions_on_input: Option<bool>,
-    pub show_completion_documentation: Option<bool>,
-    pub use_on_type_format: Option<bool>,
-    pub scrollbar: Option<ScrollbarContent>,
-    pub relative_line_numbers: Option<bool>,
-    pub seed_search_query_from_cursor: Option<SeedQuerySetting>,
-}
-
-#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
-pub struct ScrollbarContent {
-    pub show: Option<ShowScrollbar>,
-    pub git_diff: Option<bool>,
-    pub selections: Option<bool>,
-}
-
-impl Settings for EditorSettings {
-    const KEY: Option<&'static str> = None;
-
-    type FileContent = EditorSettingsContent;
-
-    fn load(
-        default_value: &Self::FileContent,
-        user_values: &[&Self::FileContent],
-        _: &mut gpui::AppContext,
-    ) -> anyhow::Result<Self> {
-        Self::load_via_json_merge(default_value, user_values)
-    }
-}

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

@@ -1,3815 +0,0 @@
-use crate::{
-    display_map::{
-        BlockContext, BlockStyle, DisplaySnapshot, FoldStatus, HighlightedChunk, ToDisplayPoint,
-        TransformBlock,
-    },
-    editor_settings::ShowScrollbar,
-    git::{diff_hunk_to_display, DisplayDiffHunk},
-    hover_popover::{
-        self, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT,
-    },
-    link_go_to_definition::{
-        go_to_fetched_definition, go_to_fetched_type_definition, show_link_definition,
-        update_go_to_definition_link, update_inlay_link_and_hover_points, GoToDefinitionTrigger,
-        LinkGoToDefinitionState,
-    },
-    mouse_context_menu,
-    scroll::scroll_amount::ScrollAmount,
-    CursorShape, DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle,
-    HalfPageDown, HalfPageUp, LineDown, LineUp, OpenExcerpts, PageDown, PageUp, Point, SelectPhase,
-    Selection, SoftWrap, ToPoint, MAX_LINE_LEN,
-};
-use anyhow::Result;
-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, Hsla, InteractiveBounds,
-    InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton, MouseDownEvent,
-    MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollWheelEvent, ShapedLine,
-    SharedString, Size, StackingOrder, StatefulInteractiveElement, Style, Styled, TextRun,
-    TextStyle, View, ViewContext, WindowContext,
-};
-use itertools::Itertools;
-use language::language_settings::ShowWhitespaceSetting;
-use multi_buffer::Anchor;
-use project::{
-    project_settings::{GitGutterSetting, ProjectSettings},
-    ProjectPath,
-};
-use settings::Settings;
-use smallvec::SmallVec;
-use std::{
-    any::TypeId,
-    borrow::Cow,
-    cmp::{self, Ordering},
-    fmt::Write,
-    iter,
-    ops::Range,
-    sync::Arc,
-};
-use sum_tree::Bias;
-use theme::{ActiveTheme, PlayerColor};
-use ui::prelude::*;
-use ui::{h_stack, ButtonLike, ButtonStyle, IconButton, Tooltip};
-use util::ResultExt;
-use workspace::item::Item;
-
-struct SelectionLayout {
-    head: DisplayPoint,
-    cursor_shape: CursorShape,
-    is_newest: bool,
-    is_local: bool,
-    range: Range<DisplayPoint>,
-    active_rows: Range<u32>,
-}
-
-impl SelectionLayout {
-    fn new<T: ToPoint + ToDisplayPoint + Clone>(
-        selection: Selection<T>,
-        line_mode: bool,
-        cursor_shape: CursorShape,
-        map: &DisplaySnapshot,
-        is_newest: bool,
-        is_local: bool,
-    ) -> 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));
-        let mut range = display_selection.range();
-        let mut head = display_selection.head();
-        let mut active_rows = map.prev_line_boundary(point_selection.start).1.row()
-            ..map.next_line_boundary(point_selection.end).1.row();
-
-        // vim visual line mode
-        if line_mode {
-            let point_range = map.expand_to_line(point_selection.range());
-            range = point_range.start.to_display_point(map)..point_range.end.to_display_point(map);
-        }
-
-        // any vim visual mode (including line mode)
-        if cursor_shape == CursorShape::Block && !range.is_empty() && !selection.reversed {
-            if head.column() > 0 {
-                head = map.clip_point(DisplayPoint::new(head.row(), head.column() - 1), Bias::Left)
-            } else if head.row() > 0 && head != map.max_point() {
-                head = map.clip_point(
-                    DisplayPoint::new(head.row() - 1, map.line_len(head.row() - 1)),
-                    Bias::Left,
-                );
-                // updating range.end is a no-op unless you're cursor is
-                // on the newline containing a multi-buffer divider
-                // in which case the clip_point may have moved the head up
-                // an additional row.
-                range.end = DisplayPoint::new(head.row() + 1, 0);
-                active_rows.end = head.row();
-            }
-        }
-
-        Self {
-            head,
-            cursor_shape,
-            is_newest,
-            is_local,
-            range,
-            active_rows,
-        }
-    }
-}
-
-pub struct EditorElement {
-    editor: View<Editor>,
-    style: EditorStyle,
-}
-
-impl EditorElement {
-    pub fn new(editor: &View<Editor>, style: EditorStyle) -> Self {
-        Self {
-            editor: editor.clone(),
-            style,
-        }
-    }
-
-    fn register_actions(&self, cx: &mut WindowContext) {
-        let view = &self.editor;
-        view.update(cx, |editor, cx| {
-            for action in editor.editor_actions.iter() {
-                (action)(cx)
-            }
-        });
-
-        crate::rust_analyzer_ext::apply_related_actions(view, cx);
-        register_action(view, cx, Editor::move_left);
-        register_action(view, cx, Editor::move_right);
-        register_action(view, cx, Editor::move_down);
-        register_action(view, cx, Editor::move_up);
-        register_action(view, cx, Editor::cancel);
-        register_action(view, cx, Editor::newline);
-        register_action(view, cx, Editor::newline_above);
-        register_action(view, cx, Editor::newline_below);
-        register_action(view, cx, Editor::backspace);
-        register_action(view, cx, Editor::delete);
-        register_action(view, cx, Editor::tab);
-        register_action(view, cx, Editor::tab_prev);
-        register_action(view, cx, Editor::indent);
-        register_action(view, cx, Editor::outdent);
-        register_action(view, cx, Editor::delete_line);
-        register_action(view, cx, Editor::join_lines);
-        register_action(view, cx, Editor::sort_lines_case_sensitive);
-        register_action(view, cx, Editor::sort_lines_case_insensitive);
-        register_action(view, cx, Editor::reverse_lines);
-        register_action(view, cx, Editor::shuffle_lines);
-        register_action(view, cx, Editor::convert_to_upper_case);
-        register_action(view, cx, Editor::convert_to_lower_case);
-        register_action(view, cx, Editor::convert_to_title_case);
-        register_action(view, cx, Editor::convert_to_snake_case);
-        register_action(view, cx, Editor::convert_to_kebab_case);
-        register_action(view, cx, Editor::convert_to_upper_camel_case);
-        register_action(view, cx, Editor::convert_to_lower_camel_case);
-        register_action(view, cx, Editor::delete_to_previous_word_start);
-        register_action(view, cx, Editor::delete_to_previous_subword_start);
-        register_action(view, cx, Editor::delete_to_next_word_end);
-        register_action(view, cx, Editor::delete_to_next_subword_end);
-        register_action(view, cx, Editor::delete_to_beginning_of_line);
-        register_action(view, cx, Editor::delete_to_end_of_line);
-        register_action(view, cx, Editor::cut_to_end_of_line);
-        register_action(view, cx, Editor::duplicate_line);
-        register_action(view, cx, Editor::move_line_up);
-        register_action(view, cx, Editor::move_line_down);
-        register_action(view, cx, Editor::transpose);
-        register_action(view, cx, Editor::cut);
-        register_action(view, cx, Editor::copy);
-        register_action(view, cx, Editor::paste);
-        register_action(view, cx, Editor::undo);
-        register_action(view, cx, Editor::redo);
-        register_action(view, cx, Editor::move_page_up);
-        register_action(view, cx, Editor::move_page_down);
-        register_action(view, cx, Editor::next_screen);
-        register_action(view, cx, Editor::scroll_cursor_top);
-        register_action(view, cx, Editor::scroll_cursor_center);
-        register_action(view, cx, Editor::scroll_cursor_bottom);
-        register_action(view, cx, |editor, _: &LineDown, cx| {
-            editor.scroll_screen(&ScrollAmount::Line(1.), cx)
-        });
-        register_action(view, cx, |editor, _: &LineUp, cx| {
-            editor.scroll_screen(&ScrollAmount::Line(-1.), cx)
-        });
-        register_action(view, cx, |editor, _: &HalfPageDown, cx| {
-            editor.scroll_screen(&ScrollAmount::Page(0.5), cx)
-        });
-        register_action(view, cx, |editor, _: &HalfPageUp, cx| {
-            editor.scroll_screen(&ScrollAmount::Page(-0.5), cx)
-        });
-        register_action(view, cx, |editor, _: &PageDown, cx| {
-            editor.scroll_screen(&ScrollAmount::Page(1.), cx)
-        });
-        register_action(view, cx, |editor, _: &PageUp, cx| {
-            editor.scroll_screen(&ScrollAmount::Page(-1.), cx)
-        });
-        register_action(view, cx, Editor::move_to_previous_word_start);
-        register_action(view, cx, Editor::move_to_previous_subword_start);
-        register_action(view, cx, Editor::move_to_next_word_end);
-        register_action(view, cx, Editor::move_to_next_subword_end);
-        register_action(view, cx, Editor::move_to_beginning_of_line);
-        register_action(view, cx, Editor::move_to_end_of_line);
-        register_action(view, cx, Editor::move_to_start_of_paragraph);
-        register_action(view, cx, Editor::move_to_end_of_paragraph);
-        register_action(view, cx, Editor::move_to_beginning);
-        register_action(view, cx, Editor::move_to_end);
-        register_action(view, cx, Editor::select_up);
-        register_action(view, cx, Editor::select_down);
-        register_action(view, cx, Editor::select_left);
-        register_action(view, cx, Editor::select_right);
-        register_action(view, cx, Editor::select_to_previous_word_start);
-        register_action(view, cx, Editor::select_to_previous_subword_start);
-        register_action(view, cx, Editor::select_to_next_word_end);
-        register_action(view, cx, Editor::select_to_next_subword_end);
-        register_action(view, cx, Editor::select_to_beginning_of_line);
-        register_action(view, cx, Editor::select_to_end_of_line);
-        register_action(view, cx, Editor::select_to_start_of_paragraph);
-        register_action(view, cx, Editor::select_to_end_of_paragraph);
-        register_action(view, cx, Editor::select_to_beginning);
-        register_action(view, cx, Editor::select_to_end);
-        register_action(view, cx, Editor::select_all);
-        register_action(view, cx, |editor, action, cx| {
-            editor.select_all_matches(action, cx).log_err();
-        });
-        register_action(view, cx, Editor::select_line);
-        register_action(view, cx, Editor::split_selection_into_lines);
-        register_action(view, cx, Editor::add_selection_above);
-        register_action(view, cx, Editor::add_selection_below);
-        register_action(view, cx, |editor, action, cx| {
-            editor.select_next(action, cx).log_err();
-        });
-        register_action(view, cx, |editor, action, cx| {
-            editor.select_previous(action, cx).log_err();
-        });
-        register_action(view, cx, Editor::toggle_comments);
-        register_action(view, cx, Editor::select_larger_syntax_node);
-        register_action(view, cx, Editor::select_smaller_syntax_node);
-        register_action(view, cx, Editor::move_to_enclosing_bracket);
-        register_action(view, cx, Editor::undo_selection);
-        register_action(view, cx, Editor::redo_selection);
-        register_action(view, cx, Editor::go_to_diagnostic);
-        register_action(view, cx, Editor::go_to_prev_diagnostic);
-        register_action(view, cx, Editor::go_to_hunk);
-        register_action(view, cx, Editor::go_to_prev_hunk);
-        register_action(view, cx, Editor::go_to_definition);
-        register_action(view, cx, Editor::go_to_definition_split);
-        register_action(view, cx, Editor::go_to_type_definition);
-        register_action(view, cx, Editor::go_to_type_definition_split);
-        register_action(view, cx, Editor::fold);
-        register_action(view, cx, Editor::fold_at);
-        register_action(view, cx, Editor::unfold_lines);
-        register_action(view, cx, Editor::unfold_at);
-        register_action(view, cx, Editor::fold_selected_ranges);
-        register_action(view, cx, Editor::show_completions);
-        register_action(view, cx, Editor::toggle_code_actions);
-        register_action(view, cx, Editor::open_excerpts);
-        register_action(view, cx, Editor::toggle_soft_wrap);
-        register_action(view, cx, Editor::toggle_inlay_hints);
-        register_action(view, cx, hover_popover::hover);
-        register_action(view, cx, Editor::reveal_in_finder);
-        register_action(view, cx, Editor::copy_path);
-        register_action(view, cx, Editor::copy_relative_path);
-        register_action(view, cx, Editor::copy_highlight_json);
-        register_action(view, cx, |editor, action, cx| {
-            if let Some(task) = editor.format(action, cx) {
-                task.detach_and_log_err(cx);
-            } else {
-                cx.propagate();
-            }
-        });
-        register_action(view, cx, Editor::restart_language_server);
-        register_action(view, cx, Editor::show_character_palette);
-        register_action(view, cx, |editor, action, cx| {
-            if let Some(task) = editor.confirm_completion(action, cx) {
-                task.detach_and_log_err(cx);
-            } else {
-                cx.propagate();
-            }
-        });
-        register_action(view, cx, |editor, action, cx| {
-            if let Some(task) = editor.confirm_code_action(action, cx) {
-                task.detach_and_log_err(cx);
-            } else {
-                cx.propagate();
-            }
-        });
-        register_action(view, cx, |editor, action, cx| {
-            if let Some(task) = editor.rename(action, cx) {
-                task.detach_and_log_err(cx);
-            } else {
-                cx.propagate();
-            }
-        });
-        register_action(view, cx, |editor, action, cx| {
-            if let Some(task) = editor.confirm_rename(action, cx) {
-                task.detach_and_log_err(cx);
-            } else {
-                cx.propagate();
-            }
-        });
-        register_action(view, cx, |editor, action, cx| {
-            if let Some(task) = editor.find_all_references(action, cx) {
-                task.detach_and_log_err(cx);
-            } else {
-                cx.propagate();
-            }
-        });
-        register_action(view, cx, Editor::next_copilot_suggestion);
-        register_action(view, cx, Editor::previous_copilot_suggestion);
-        register_action(view, cx, Editor::copilot_suggest);
-        register_action(view, cx, Editor::context_menu_first);
-        register_action(view, cx, Editor::context_menu_prev);
-        register_action(view, cx, Editor::context_menu_next);
-        register_action(view, cx, Editor::context_menu_last);
-    }
-
-    fn register_key_listeners(&self, cx: &mut WindowContext) {
-        cx.on_key_event({
-            let editor = self.editor.clone();
-            move |event: &ModifiersChangedEvent, phase, cx| {
-                if phase != DispatchPhase::Bubble {
-                    return;
-                }
-
-                if editor.update(cx, |editor, cx| Self::modifiers_changed(editor, event, cx)) {
-                    cx.stop_propagation();
-                }
-            }
-        });
-    }
-
-    pub(crate) fn modifiers_changed(
-        editor: &mut Editor,
-        event: &ModifiersChangedEvent,
-        cx: &mut ViewContext<Editor>,
-    ) -> bool {
-        let pending_selection = editor.has_pending_selection();
-
-        if let Some(point) = &editor.link_go_to_definition_state.last_trigger_point {
-            if event.command && !pending_selection {
-                let point = point.clone();
-                let snapshot = editor.snapshot(cx);
-                let kind = point.definition_kind(event.shift);
-
-                show_link_definition(kind, editor, point, snapshot, cx);
-                return false;
-            }
-        }
-
-        {
-            if editor.link_go_to_definition_state.symbol_range.is_some()
-                || !editor.link_go_to_definition_state.definitions.is_empty()
-            {
-                editor.link_go_to_definition_state.symbol_range.take();
-                editor.link_go_to_definition_state.definitions.clear();
-                cx.notify();
-            }
-
-            editor.link_go_to_definition_state.task = None;
-
-            editor.clear_highlights::<LinkGoToDefinitionState>(cx);
-        }
-
-        false
-    }
-
-    fn mouse_left_down(
-        editor: &mut Editor,
-        event: &MouseDownEvent,
-        position_map: &PositionMap,
-        text_bounds: Bounds<Pixels>,
-        gutter_bounds: Bounds<Pixels>,
-        stacking_order: &StackingOrder,
-        cx: &mut ViewContext<Editor>,
-    ) {
-        let mut click_count = event.click_count;
-        let modifiers = event.modifiers;
-
-        if gutter_bounds.contains(&event.position) {
-            click_count = 3; // Simulate triple-click when clicking the gutter to select lines
-        } else if !text_bounds.contains(&event.position) {
-            return;
-        }
-        if !cx.was_top_layer(&event.position, stacking_order) {
-            return;
-        }
-
-        let point_for_position = position_map.point_for_position(text_bounds, event.position);
-        let position = point_for_position.previous_valid;
-        if modifiers.shift && modifiers.alt {
-            editor.select(
-                SelectPhase::BeginColumnar {
-                    position,
-                    goal_column: point_for_position.exact_unclipped.column(),
-                },
-                cx,
-            );
-        } else if modifiers.shift && !modifiers.control && !modifiers.alt && !modifiers.command {
-            editor.select(
-                SelectPhase::Extend {
-                    position,
-                    click_count,
-                },
-                cx,
-            );
-        } else {
-            editor.select(
-                SelectPhase::Begin {
-                    position,
-                    add: modifiers.alt,
-                    click_count,
-                },
-                cx,
-            );
-        }
-
-        cx.stop_propagation();
-    }
-
-    fn mouse_right_down(
-        editor: &mut Editor,
-        event: &MouseDownEvent,
-        position_map: &PositionMap,
-        text_bounds: Bounds<Pixels>,
-        cx: &mut ViewContext<Editor>,
-    ) {
-        if !text_bounds.contains(&event.position) {
-            return;
-        }
-        let point_for_position = position_map.point_for_position(text_bounds, event.position);
-        mouse_context_menu::deploy_context_menu(
-            editor,
-            event.position,
-            point_for_position.previous_valid,
-            cx,
-        );
-        cx.stop_propagation();
-    }
-
-    fn mouse_up(
-        editor: &mut Editor,
-        event: &MouseUpEvent,
-        position_map: &PositionMap,
-        text_bounds: Bounds<Pixels>,
-        stacking_order: &StackingOrder,
-        cx: &mut ViewContext<Editor>,
-    ) {
-        let end_selection = editor.has_pending_selection();
-        let pending_nonempty_selections = editor.has_pending_nonempty_selection();
-
-        if end_selection {
-            editor.select(SelectPhase::End, cx);
-        }
-
-        if !pending_nonempty_selections
-            && event.modifiers.command
-            && text_bounds.contains(&event.position)
-            && cx.was_top_layer(&event.position, stacking_order)
-        {
-            let point = position_map.point_for_position(text_bounds, event.position);
-            let could_be_inlay = point.as_valid().is_none();
-            let split = event.modifiers.alt;
-            if event.modifiers.shift || could_be_inlay {
-                go_to_fetched_type_definition(editor, point, split, cx);
-            } else {
-                go_to_fetched_definition(editor, point, split, cx);
-            }
-
-            cx.stop_propagation();
-        } else if end_selection {
-            cx.stop_propagation();
-        }
-    }
-
-    fn mouse_dragged(
-        editor: &mut Editor,
-        event: &MouseMoveEvent,
-        position_map: &PositionMap,
-        text_bounds: Bounds<Pixels>,
-        _gutter_bounds: Bounds<Pixels>,
-        _stacking_order: &StackingOrder,
-        cx: &mut ViewContext<Editor>,
-    ) {
-        if !editor.has_pending_selection() {
-            return;
-        }
-
-        let point_for_position = position_map.point_for_position(text_bounds, event.position);
-        let mut scroll_delta = gpui::Point::<f32>::default();
-        let vertical_margin = position_map.line_height.min(text_bounds.size.height / 3.0);
-        let top = text_bounds.origin.y + vertical_margin;
-        let bottom = text_bounds.lower_left().y - vertical_margin;
-        if event.position.y < top {
-            scroll_delta.y = -scale_vertical_mouse_autoscroll_delta(top - event.position.y);
-        }
-        if event.position.y > bottom {
-            scroll_delta.y = scale_vertical_mouse_autoscroll_delta(event.position.y - bottom);
-        }
-
-        let horizontal_margin = position_map.line_height.min(text_bounds.size.width / 3.0);
-        let left = text_bounds.origin.x + horizontal_margin;
-        let right = text_bounds.upper_right().x - horizontal_margin;
-        if event.position.x < left {
-            scroll_delta.x = -scale_horizontal_mouse_autoscroll_delta(left - event.position.x);
-        }
-        if event.position.x > right {
-            scroll_delta.x = scale_horizontal_mouse_autoscroll_delta(event.position.x - right);
-        }
-
-        editor.select(
-            SelectPhase::Update {
-                position: point_for_position.previous_valid,
-                goal_column: point_for_position.exact_unclipped.column(),
-                scroll_position: (position_map.snapshot.scroll_position() + scroll_delta)
-                    .clamp(&gpui::Point::default(), &position_map.scroll_max),
-            },
-            cx,
-        );
-    }
-
-    fn mouse_moved(
-        editor: &mut Editor,
-        event: &MouseMoveEvent,
-        position_map: &PositionMap,
-        text_bounds: Bounds<Pixels>,
-        gutter_bounds: Bounds<Pixels>,
-        stacking_order: &StackingOrder,
-        cx: &mut ViewContext<Editor>,
-    ) {
-        let modifiers = event.modifiers;
-        let text_hovered = text_bounds.contains(&event.position);
-        let gutter_hovered = gutter_bounds.contains(&event.position);
-        let was_top = cx.was_top_layer(&event.position, stacking_order);
-
-        editor.set_gutter_hovered(gutter_hovered, cx);
-
-        // Don't trigger hover popover if mouse is hovering over context menu
-        if text_hovered && was_top {
-            let point_for_position = position_map.point_for_position(text_bounds, event.position);
-
-            match point_for_position.as_valid() {
-                Some(point) => {
-                    update_go_to_definition_link(
-                        editor,
-                        Some(GoToDefinitionTrigger::Text(point)),
-                        modifiers.command,
-                        modifiers.shift,
-                        cx,
-                    );
-                    hover_at(editor, Some(point), cx);
-                }
-                None => {
-                    update_inlay_link_and_hover_points(
-                        &position_map.snapshot,
-                        point_for_position,
-                        editor,
-                        modifiers.command,
-                        modifiers.shift,
-                        cx,
-                    );
-                }
-            }
-        } else {
-            update_go_to_definition_link(editor, None, modifiers.command, modifiers.shift, cx);
-            hover_at(editor, None, cx);
-            if gutter_hovered && was_top {
-                cx.stop_propagation();
-            }
-        }
-    }
-
-    fn scroll(
-        editor: &mut Editor,
-        event: &ScrollWheelEvent,
-        position_map: &PositionMap,
-        bounds: &InteractiveBounds,
-        cx: &mut ViewContext<Editor>,
-    ) {
-        if !bounds.visibly_contains(&event.position, cx) {
-            return;
-        }
-
-        let line_height = position_map.line_height;
-        let max_glyph_width = position_map.em_width;
-        let (delta, axis) = match event.delta {
-            gpui::ScrollDelta::Pixels(mut pixels) => {
-                //Trackpad
-                let axis = position_map.snapshot.ongoing_scroll.filter(&mut pixels);
-                (pixels, axis)
-            }
-
-            gpui::ScrollDelta::Lines(lines) => {
-                //Not trackpad
-                let pixels = point(lines.x * max_glyph_width, lines.y * line_height);
-                (pixels, None)
-            }
-        };
-
-        let scroll_position = position_map.snapshot.scroll_position();
-        let x = f32::from((scroll_position.x * max_glyph_width - delta.x) / max_glyph_width);
-        let y = f32::from((scroll_position.y * line_height - delta.y) / line_height);
-        let scroll_position = point(x, y).clamp(&point(0., 0.), &position_map.scroll_max);
-        editor.scroll(scroll_position, axis, cx);
-        cx.stop_propagation();
-    }
-
-    fn paint_background(
-        &self,
-        gutter_bounds: Bounds<Pixels>,
-        text_bounds: Bounds<Pixels>,
-        layout: &LayoutState,
-        cx: &mut WindowContext,
-    ) {
-        let bounds = gutter_bounds.union(&text_bounds);
-        let scroll_top =
-            layout.position_map.snapshot.scroll_position().y * layout.position_map.line_height;
-        let gutter_bg = cx.theme().colors().editor_gutter_background;
-        cx.paint_quad(fill(gutter_bounds, gutter_bg));
-        cx.paint_quad(fill(text_bounds, self.style.background));
-
-        if let EditorMode::Full = layout.mode {
-            let mut active_rows = layout.active_rows.iter().peekable();
-            while let Some((start_row, contains_non_empty_selection)) = active_rows.next() {
-                let mut end_row = *start_row;
-                while active_rows.peek().map_or(false, |r| {
-                    *r.0 == end_row + 1 && r.1 == contains_non_empty_selection
-                }) {
-                    active_rows.next().unwrap();
-                    end_row += 1;
-                }
-
-                if !contains_non_empty_selection {
-                    let origin = point(
-                        bounds.origin.x,
-                        bounds.origin.y + (layout.position_map.line_height * *start_row as f32)
-                            - scroll_top,
-                    );
-                    let size = size(
-                        bounds.size.width,
-                        layout.position_map.line_height * (end_row - start_row + 1) as f32,
-                    );
-                    let active_line_bg = cx.theme().colors().editor_active_line_background;
-                    cx.paint_quad(fill(Bounds { origin, size }, active_line_bg));
-                }
-            }
-
-            if let Some(highlighted_rows) = &layout.highlighted_rows {
-                let origin = point(
-                    bounds.origin.x,
-                    bounds.origin.y
-                        + (layout.position_map.line_height * highlighted_rows.start as f32)
-                        - scroll_top,
-                );
-                let size = size(
-                    bounds.size.width,
-                    layout.position_map.line_height * highlighted_rows.len() as f32,
-                );
-                let highlighted_line_bg = cx.theme().colors().editor_highlighted_line_background;
-                cx.paint_quad(fill(Bounds { origin, size }, highlighted_line_bg));
-            }
-
-            let scroll_left =
-                layout.position_map.snapshot.scroll_position().x * layout.position_map.em_width;
-
-            for (wrap_position, active) in layout.wrap_guides.iter() {
-                let x = (text_bounds.origin.x + *wrap_position + layout.position_map.em_width / 2.)
-                    - scroll_left;
-
-                if x < text_bounds.origin.x
-                    || (layout.show_scrollbars && x > self.scrollbar_left(&bounds))
-                {
-                    continue;
-                }
-
-                let color = if *active {
-                    cx.theme().colors().editor_active_wrap_guide
-                } else {
-                    cx.theme().colors().editor_wrap_guide
-                };
-                cx.paint_quad(fill(
-                    Bounds {
-                        origin: point(x, text_bounds.origin.y),
-                        size: size(px(1.), text_bounds.size.height),
-                    },
-                    color,
-                ));
-            }
-        }
-    }
-
-    fn paint_gutter(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        layout: &mut LayoutState,
-        cx: &mut WindowContext,
-    ) {
-        let line_height = layout.position_map.line_height;
-
-        let scroll_position = layout.position_map.snapshot.scroll_position();
-        let scroll_top = scroll_position.y * line_height;
-
-        let show_gutter = matches!(
-            ProjectSettings::get_global(cx).git.git_gutter,
-            Some(GitGutterSetting::TrackedFiles)
-        );
-
-        if show_gutter {
-            Self::paint_diff_hunks(bounds, layout, cx);
-        }
-
-        for (ix, line) in layout.line_numbers.iter().enumerate() {
-            if let Some(line) = line {
-                let line_origin = bounds.origin
-                    + point(
-                        bounds.size.width - line.width - layout.gutter_padding,
-                        ix as f32 * line_height - (scroll_top % line_height),
-                    );
-
-                line.paint(line_origin, line_height, cx).log_err();
-            }
-        }
-
-        cx.with_z_index(1, |cx| {
-            for (ix, fold_indicator) in layout.fold_indicators.drain(..).enumerate() {
-                if let Some(fold_indicator) = fold_indicator {
-                    let mut fold_indicator = fold_indicator.into_any_element();
-                    let available_space = size(
-                        AvailableSpace::MinContent,
-                        AvailableSpace::Definite(line_height * 0.55),
-                    );
-                    let fold_indicator_size = fold_indicator.measure(available_space, cx);
-
-                    let position = point(
-                        bounds.size.width - layout.gutter_padding,
-                        ix as f32 * line_height - (scroll_top % line_height),
-                    );
-                    let centering_offset = point(
-                        (layout.gutter_padding + layout.gutter_margin - fold_indicator_size.width)
-                            / 2.,
-                        (line_height - fold_indicator_size.height) / 2.,
-                    );
-                    let origin = bounds.origin + position + centering_offset;
-                    fold_indicator.draw(origin, available_space, cx);
-                }
-            }
-
-            if let Some(indicator) = layout.code_actions_indicator.take() {
-                let mut button = indicator.button.into_any_element();
-                let available_space = size(
-                    AvailableSpace::MinContent,
-                    AvailableSpace::Definite(line_height),
-                );
-                let indicator_size = button.measure(available_space, cx);
-
-                let mut x = Pixels::ZERO;
-                let mut y = indicator.row as f32 * line_height - scroll_top;
-                // Center indicator.
-                x += ((layout.gutter_padding + layout.gutter_margin) - indicator_size.width) / 2.;
-                y += (line_height - indicator_size.height) / 2.;
-
-                button.draw(bounds.origin + point(x, y), available_space, cx);
-            }
-        });
-    }
-
-    fn paint_diff_hunks(bounds: Bounds<Pixels>, layout: &LayoutState, cx: &mut WindowContext) {
-        let line_height = layout.position_map.line_height;
-
-        let scroll_position = layout.position_map.snapshot.scroll_position();
-        let scroll_top = scroll_position.y * line_height;
-
-        for hunk in &layout.display_hunks {
-            let (display_row_range, status) = match hunk {
-                //TODO: This rendering is entirely a horrible hack
-                &DisplayDiffHunk::Folded { display_row: row } => {
-                    let start_y = row as f32 * line_height - scroll_top;
-                    let end_y = start_y + line_height;
-
-                    let width = 0.275 * line_height;
-                    let highlight_origin = bounds.origin + point(-width, start_y);
-                    let highlight_size = size(width * 2., end_y - start_y);
-                    let highlight_bounds = Bounds::new(highlight_origin, highlight_size);
-                    cx.paint_quad(quad(
-                        highlight_bounds,
-                        Corners::all(1. * line_height),
-                        gpui::yellow(), // todo!("use the right color")
-                        Edges::default(),
-                        transparent_black(),
-                    ));
-
-                    continue;
-                }
-
-                DisplayDiffHunk::Unfolded {
-                    display_row_range,
-                    status,
-                } => (display_row_range, status),
-            };
-
-            let color = match status {
-                DiffHunkStatus::Added => cx.theme().status().created,
-                DiffHunkStatus::Modified => cx.theme().status().modified,
-
-                //TODO: This rendering is entirely a horrible hack
-                DiffHunkStatus::Removed => {
-                    let row = display_row_range.start;
-
-                    let offset = line_height / 2.;
-                    let start_y = row as f32 * line_height - offset - scroll_top;
-                    let end_y = start_y + line_height;
-
-                    let width = 0.275 * line_height;
-                    let highlight_origin = bounds.origin + point(-width, start_y);
-                    let highlight_size = size(width * 2., end_y - start_y);
-                    let highlight_bounds = Bounds::new(highlight_origin, highlight_size);
-                    cx.paint_quad(quad(
-                        highlight_bounds,
-                        Corners::all(1. * line_height),
-                        cx.theme().status().deleted,
-                        Edges::default(),
-                        transparent_black(),
-                    ));
-
-                    continue;
-                }
-            };
-
-            let start_row = display_row_range.start;
-            let end_row = display_row_range.end;
-
-            let start_y = start_row as f32 * line_height - scroll_top;
-            let end_y = end_row as f32 * line_height - scroll_top;
-
-            let width = 0.275 * line_height;
-            let highlight_origin = bounds.origin + point(-width, start_y);
-            let highlight_size = size(width * 2., end_y - start_y);
-            let highlight_bounds = Bounds::new(highlight_origin, highlight_size);
-            cx.paint_quad(quad(
-                highlight_bounds,
-                Corners::all(0.05 * line_height),
-                color, // todo!("use the right color")
-                Edges::default(),
-                transparent_black(),
-            ));
-        }
-    }
-
-    fn paint_text(
-        &mut self,
-        text_bounds: Bounds<Pixels>,
-        layout: &mut LayoutState,
-        cx: &mut WindowContext,
-    ) {
-        let start_row = layout.visible_display_row_range.start;
-        let content_origin = text_bounds.origin + point(layout.gutter_margin, Pixels::ZERO);
-        let line_end_overshoot = 0.15 * layout.position_map.line_height;
-        let whitespace_setting = self
-            .editor
-            .read(cx)
-            .buffer
-            .read(cx)
-            .settings_at(0, cx)
-            .show_whitespaces;
-
-        cx.with_content_mask(
-            Some(ContentMask {
-                bounds: text_bounds,
-            }),
-            |cx| {
-                let interactive_text_bounds = InteractiveBounds {
-                    bounds: text_bounds,
-                    stacking_order: cx.stacking_order().clone(),
-                };
-                if interactive_text_bounds.visibly_contains(&cx.mouse_position(), cx) {
-                    if self
-                        .editor
-                        .read(cx)
-                        .link_go_to_definition_state
-                        .definitions
-                        .is_empty()
-                    {
-                        cx.set_cursor_style(CursorStyle::IBeam);
-                    } else {
-                        cx.set_cursor_style(CursorStyle::PointingHand);
-                    }
-                }
-
-                let fold_corner_radius = 0.15 * layout.position_map.line_height;
-                cx.with_element_id(Some("folds"), |cx| {
-                    let snapshot = &layout.position_map.snapshot;
-                    for fold in snapshot.folds_in_range(layout.visible_anchor_range.clone()) {
-                        let fold_range = fold.range.clone();
-                        let display_range = fold.range.start.to_display_point(&snapshot)
-                            ..fold.range.end.to_display_point(&snapshot);
-                        debug_assert_eq!(display_range.start.row(), display_range.end.row());
-                        let row = display_range.start.row();
-
-                        let line_layout = &layout.position_map.line_layouts
-                            [(row - layout.visible_display_row_range.start) as usize]
-                            .line;
-                        let start_x = content_origin.x
-                            + line_layout.x_for_index(display_range.start.column() as usize)
-                            - layout.position_map.scroll_position.x;
-                        let start_y = content_origin.y
-                            + row as f32 * layout.position_map.line_height
-                            - layout.position_map.scroll_position.y;
-                        let end_x = content_origin.x
-                            + line_layout.x_for_index(display_range.end.column() as usize)
-                            - layout.position_map.scroll_position.x;
-
-                        let fold_bounds = Bounds {
-                            origin: point(start_x, start_y),
-                            size: size(end_x - start_x, layout.position_map.line_height),
-                        };
-
-                        let fold_background = cx.with_z_index(1, |cx| {
-                            div()
-                                .id(fold.id)
-                                .size_full()
-                                .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
-                                .on_click(cx.listener_for(
-                                    &self.editor,
-                                    move |editor: &mut Editor, _, cx| {
-                                        editor.unfold_ranges(
-                                            [fold_range.start..fold_range.end],
-                                            true,
-                                            false,
-                                            cx,
-                                        );
-                                        cx.stop_propagation();
-                                    },
-                                ))
-                                .draw_and_update_state(
-                                    fold_bounds.origin,
-                                    fold_bounds.size,
-                                    cx,
-                                    |fold_element_state, cx| {
-                                        if fold_element_state.is_active() {
-                                            cx.theme().colors().ghost_element_active
-                                        } else if fold_bounds.contains(&cx.mouse_position()) {
-                                            cx.theme().colors().ghost_element_hover
-                                        } else {
-                                            cx.theme().colors().ghost_element_background
-                                        }
-                                    },
-                                )
-                        });
-
-                        self.paint_highlighted_range(
-                            display_range.clone(),
-                            fold_background,
-                            fold_corner_radius,
-                            fold_corner_radius * 2.,
-                            layout,
-                            content_origin,
-                            text_bounds,
-                            cx,
-                        );
-                    }
-                });
-
-                for (range, color) in &layout.highlighted_ranges {
-                    self.paint_highlighted_range(
-                        range.clone(),
-                        *color,
-                        Pixels::ZERO,
-                        line_end_overshoot,
-                        layout,
-                        content_origin,
-                        text_bounds,
-                        cx,
-                    );
-                }
-
-                let mut cursors = SmallVec::<[Cursor; 32]>::new();
-                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 {
-                        self.paint_highlighted_range(
-                            selection.range.clone(),
-                            selection_style.selection,
-                            corner_radius,
-                            corner_radius * 2.,
-                            layout,
-                            content_origin,
-                            text_bounds,
-                            cx,
-                        );
-
-                        if selection.is_local && !selection.range.is_empty() {
-                            invisible_display_ranges.push(selection.range.clone());
-                        }
-
-                        if !selection.is_local || self.editor.read(cx).show_local_cursors(cx) {
-                            let cursor_position = selection.head;
-                            if layout
-                                .visible_display_row_range
-                                .contains(&cursor_position.row())
-                            {
-                                let cursor_row_layout = &layout.position_map.line_layouts
-                                    [(cursor_position.row() - start_row) as usize]
-                                    .line;
-                                let cursor_column = cursor_position.column() as usize;
-
-                                let cursor_character_x =
-                                    cursor_row_layout.x_for_index(cursor_column);
-                                let mut block_width = cursor_row_layout
-                                    .x_for_index(cursor_column + 1)
-                                    - cursor_character_x;
-                                if block_width == Pixels::ZERO {
-                                    block_width = layout.position_map.em_width;
-                                }
-                                let block_text = if let CursorShape::Block = selection.cursor_shape
-                                {
-                                    layout
-                                        .position_map
-                                        .snapshot
-                                        .chars_at(cursor_position)
-                                        .next()
-                                        .and_then(|(character, _)| {
-                                            // todo!() currently shape_line panics if text conatins newlines
-                                            let text = if character == '\n' {
-                                                SharedString::from(" ")
-                                            } else {
-                                                SharedString::from(character.to_string())
-                                            };
-                                            let len = text.len();
-                                            cx.text_system()
-                                                .shape_line(
-                                                    text,
-                                                    cursor_row_layout.font_size,
-                                                    &[TextRun {
-                                                        len,
-                                                        font: self.style.text.font(),
-                                                        color: self.style.background,
-                                                        background_color: None,
-                                                        underline: None,
-                                                    }],
-                                                )
-                                                .log_err()
-                                        })
-                                } else {
-                                    None
-                                };
-
-                                let x = cursor_character_x - layout.position_map.scroll_position.x;
-                                let y = cursor_position.row() as f32
-                                    * layout.position_map.line_height
-                                    - layout.position_map.scroll_position.y;
-                                if selection.is_newest {
-                                    self.editor.update(cx, |editor, _| {
-                                        editor.pixel_position_of_newest_cursor = Some(point(
-                                            text_bounds.origin.x + x + block_width / 2.,
-                                            text_bounds.origin.y
-                                                + y
-                                                + layout.position_map.line_height / 2.,
-                                        ))
-                                    });
-                                }
-                                cursors.push(Cursor {
-                                    color: selection_style.cursor,
-                                    block_width,
-                                    origin: point(x, y),
-                                    line_height: layout.position_map.line_height,
-                                    shape: selection.cursor_shape,
-                                    block_text,
-                                });
-                            }
-                        }
-                    }
-                }
-
-                for (ix, line_with_invisibles) in
-                    layout.position_map.line_layouts.iter().enumerate()
-                {
-                    let row = start_row + ix as u32;
-                    line_with_invisibles.draw(
-                        layout,
-                        row,
-                        content_origin,
-                        whitespace_setting,
-                        &invisible_display_ranges,
-                        cx,
-                    )
-                }
-
-                cx.with_z_index(0, |cx| {
-                    for cursor in cursors {
-                        cursor.paint(content_origin, cx);
-                    }
-                });
-            },
-        )
-    }
-
-    fn paint_overlays(
-        &mut self,
-        text_bounds: Bounds<Pixels>,
-        layout: &mut LayoutState,
-        cx: &mut WindowContext,
-    ) {
-        let content_origin = text_bounds.origin + point(layout.gutter_margin, Pixels::ZERO);
-        let start_row = layout.visible_display_row_range.start;
-        if let Some((position, mut context_menu)) = layout.context_menu.take() {
-            let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent);
-            let context_menu_size = context_menu.measure(available_space, cx);
-
-            let cursor_row_layout =
-                &layout.position_map.line_layouts[(position.row() - start_row) as usize].line;
-            let x = cursor_row_layout.x_for_index(position.column() as usize)
-                - layout.position_map.scroll_position.x;
-            let y = (position.row() + 1) as f32 * layout.position_map.line_height
-                - layout.position_map.scroll_position.y;
-            let mut list_origin = content_origin + point(x, y);
-            let list_width = context_menu_size.width;
-            let list_height = context_menu_size.height;
-
-            // Snap the right edge of the list to the right edge of the window if
-            // its horizontal bounds overflow.
-            if list_origin.x + list_width > cx.viewport_size().width {
-                list_origin.x = (cx.viewport_size().width - list_width).max(Pixels::ZERO);
-            }
-
-            if list_origin.y + list_height > text_bounds.lower_right().y {
-                list_origin.y -= layout.position_map.line_height + list_height;
-            }
-
-            cx.break_content_mask(|cx| context_menu.draw(list_origin, available_space, cx));
-        }
-
-        if let Some((position, mut hover_popovers)) = layout.hover_popovers.take() {
-            let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent);
-
-            // This is safe because we check on layout whether the required row is available
-            let hovered_row_layout =
-                &layout.position_map.line_layouts[(position.row() - start_row) as usize].line;
-
-            // Minimum required size: Take the first popover, and add 1.5 times the minimum popover
-            // height. This is the size we will use to decide whether to render popovers above or below
-            // the hovered line.
-            let first_size = hover_popovers[0].measure(available_space, cx);
-            let height_to_reserve =
-                first_size.height + 1.5 * MIN_POPOVER_LINE_HEIGHT * layout.position_map.line_height;
-
-            // Compute Hovered Point
-            let x = hovered_row_layout.x_for_index(position.column() as usize)
-                - layout.position_map.scroll_position.x;
-            let y = position.row() as f32 * layout.position_map.line_height
-                - layout.position_map.scroll_position.y;
-            let hovered_point = content_origin + point(x, y);
-
-            if hovered_point.y - height_to_reserve > Pixels::ZERO {
-                // There is enough space above. Render popovers above the hovered point
-                let mut current_y = hovered_point.y;
-                for mut hover_popover in hover_popovers {
-                    let size = hover_popover.measure(available_space, cx);
-                    let mut popover_origin = point(hovered_point.x, current_y - size.height);
-
-                    let x_out_of_bounds =
-                        text_bounds.upper_right().x - (popover_origin.x + size.width);
-                    if x_out_of_bounds < Pixels::ZERO {
-                        popover_origin.x = popover_origin.x + x_out_of_bounds;
-                    }
-
-                    cx.break_content_mask(|cx| {
-                        hover_popover.draw(popover_origin, available_space, cx)
-                    });
-
-                    current_y = popover_origin.y - HOVER_POPOVER_GAP;
-                }
-            } else {
-                // There is not enough space above. Render popovers below the hovered point
-                let mut current_y = hovered_point.y + layout.position_map.line_height;
-                for mut hover_popover in hover_popovers {
-                    let size = hover_popover.measure(available_space, cx);
-                    let mut popover_origin = point(hovered_point.x, current_y);
-
-                    let x_out_of_bounds =
-                        text_bounds.upper_right().x - (popover_origin.x + size.width);
-                    if x_out_of_bounds < Pixels::ZERO {
-                        popover_origin.x = popover_origin.x + x_out_of_bounds;
-                    }
-
-                    hover_popover.draw(popover_origin, available_space, cx);
-
-                    current_y = popover_origin.y + size.height + HOVER_POPOVER_GAP;
-                }
-            }
-        }
-
-        if let Some(mouse_context_menu) = self.editor.read(cx).mouse_context_menu.as_ref() {
-            let element = overlay()
-                .position(mouse_context_menu.position)
-                .child(mouse_context_menu.context_menu.clone())
-                .anchor(AnchorCorner::TopLeft)
-                .snap_to_window();
-            element.into_any().draw(
-                gpui::Point::default(),
-                size(AvailableSpace::MinContent, AvailableSpace::MinContent),
-                cx,
-            );
-        }
-    }
-
-    fn scrollbar_left(&self, bounds: &Bounds<Pixels>) -> Pixels {
-        bounds.upper_right().x - self.style.scrollbar_width
-    }
-
-    fn paint_scrollbar(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        layout: &mut LayoutState,
-        cx: &mut WindowContext,
-    ) {
-        if layout.mode != EditorMode::Full {
-            return;
-        }
-
-        let top = bounds.origin.y;
-        let bottom = bounds.lower_left().y;
-        let right = bounds.lower_right().x;
-        let left = self.scrollbar_left(&bounds);
-        let row_range = layout.scrollbar_row_range.clone();
-        let max_row = layout.max_row as f32 + (row_range.end - row_range.start);
-
-        let mut height = bounds.size.height;
-        let mut first_row_y_offset = px(0.0);
-
-        // Impose a minimum height on the scrollbar thumb
-        let row_height = height / max_row;
-        let min_thumb_height = layout.position_map.line_height;
-        let thumb_height = (row_range.end - row_range.start) * row_height;
-        if thumb_height < min_thumb_height {
-            first_row_y_offset = (min_thumb_height - thumb_height) / 2.0;
-            height -= min_thumb_height - thumb_height;
-        }
-
-        let y_for_row = |row: f32| -> Pixels { top + first_row_y_offset + row * row_height };
-
-        let thumb_top = y_for_row(row_range.start) - first_row_y_offset;
-        let thumb_bottom = y_for_row(row_range.end) + first_row_y_offset;
-        let track_bounds = Bounds::from_corners(point(left, top), point(right, bottom));
-        let thumb_bounds = Bounds::from_corners(point(left, thumb_top), point(right, thumb_bottom));
-
-        if layout.show_scrollbars {
-            cx.paint_quad(quad(
-                track_bounds,
-                Corners::default(),
-                cx.theme().colors().scrollbar_track_background,
-                Edges {
-                    top: Pixels::ZERO,
-                    right: Pixels::ZERO,
-                    bottom: Pixels::ZERO,
-                    left: px(1.),
-                },
-                cx.theme().colors().scrollbar_track_border,
-            ));
-            let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
-            if layout.is_singleton && scrollbar_settings.selections {
-                let start_anchor = Anchor::min();
-                let end_anchor = Anchor::max();
-                let background_ranges = self
-                    .editor
-                    .read(cx)
-                    .background_highlight_row_ranges::<crate::items::BufferSearchHighlights>(
-                        start_anchor..end_anchor,
-                        &layout.position_map.snapshot,
-                        50000,
-                    );
-                for range in background_ranges {
-                    let start_y = y_for_row(range.start().row() as f32);
-                    let mut end_y = y_for_row(range.end().row() as f32);
-                    if end_y - start_y < px(1.) {
-                        end_y = start_y + px(1.);
-                    }
-                    let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
-                    cx.paint_quad(quad(
-                        bounds,
-                        Corners::default(),
-                        cx.theme().status().info,
-                        Edges {
-                            top: Pixels::ZERO,
-                            right: px(1.),
-                            bottom: Pixels::ZERO,
-                            left: px(1.),
-                        },
-                        cx.theme().colors().scrollbar_thumb_border,
-                    ));
-                }
-            }
-
-            if layout.is_singleton && scrollbar_settings.git_diff {
-                for hunk in layout
-                    .position_map
-                    .snapshot
-                    .buffer_snapshot
-                    .git_diff_hunks_in_range(0..(max_row.floor() as u32))
-                {
-                    let start_display = Point::new(hunk.buffer_range.start, 0)
-                        .to_display_point(&layout.position_map.snapshot.display_snapshot);
-                    let end_display = Point::new(hunk.buffer_range.end, 0)
-                        .to_display_point(&layout.position_map.snapshot.display_snapshot);
-                    let start_y = y_for_row(start_display.row() as f32);
-                    let mut end_y = if hunk.buffer_range.start == hunk.buffer_range.end {
-                        y_for_row((end_display.row() + 1) as f32)
-                    } else {
-                        y_for_row((end_display.row()) as f32)
-                    };
-
-                    if end_y - start_y < px(1.) {
-                        end_y = start_y + px(1.);
-                    }
-                    let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
-
-                    let color = match hunk.status() {
-                        DiffHunkStatus::Added => cx.theme().status().created,
-                        DiffHunkStatus::Modified => cx.theme().status().modified,
-                        DiffHunkStatus::Removed => cx.theme().status().deleted,
-                    };
-                    cx.paint_quad(quad(
-                        bounds,
-                        Corners::default(),
-                        color,
-                        Edges {
-                            top: Pixels::ZERO,
-                            right: px(1.),
-                            bottom: Pixels::ZERO,
-                            left: px(1.),
-                        },
-                        cx.theme().colors().scrollbar_thumb_border,
-                    ));
-                }
-            }
-
-            cx.paint_quad(quad(
-                thumb_bounds,
-                Corners::default(),
-                cx.theme().colors().scrollbar_thumb_background,
-                Edges {
-                    top: Pixels::ZERO,
-                    right: px(1.),
-                    bottom: Pixels::ZERO,
-                    left: px(1.),
-                },
-                cx.theme().colors().scrollbar_thumb_border,
-            ));
-        }
-
-        let interactive_track_bounds = InteractiveBounds {
-            bounds: track_bounds,
-            stacking_order: cx.stacking_order().clone(),
-        };
-        let mut mouse_position = cx.mouse_position();
-        if interactive_track_bounds.visibly_contains(&mouse_position, cx) {
-            cx.set_cursor_style(CursorStyle::Arrow);
-        }
-
-        cx.on_mouse_event({
-            let editor = self.editor.clone();
-            move |event: &MouseMoveEvent, phase, cx| {
-                if phase == DispatchPhase::Capture {
-                    return;
-                }
-
-                editor.update(cx, |editor, cx| {
-                    if event.pressed_button == Some(MouseButton::Left)
-                        && editor.scroll_manager.is_dragging_scrollbar()
-                    {
-                        let y = mouse_position.y;
-                        let new_y = event.position.y;
-                        if (track_bounds.top()..track_bounds.bottom()).contains(&y) {
-                            let mut position = editor.scroll_position(cx);
-                            position.y += (new_y - y) * (max_row as f32) / height;
-                            if position.y < 0.0 {
-                                position.y = 0.0;
-                            }
-                            editor.set_scroll_position(position, cx);
-                        }
-
-                        mouse_position = event.position;
-                        cx.stop_propagation();
-                    } else {
-                        editor.scroll_manager.set_is_dragging_scrollbar(false, cx);
-                        if interactive_track_bounds.visibly_contains(&event.position, cx) {
-                            editor.scroll_manager.show_scrollbar(cx);
-                        }
-                    }
-                })
-            }
-        });
-
-        if self.editor.read(cx).scroll_manager.is_dragging_scrollbar() {
-            cx.on_mouse_event({
-                let editor = self.editor.clone();
-                move |_: &MouseUpEvent, phase, cx| {
-                    if phase == DispatchPhase::Capture {
-                        return;
-                    }
-
-                    editor.update(cx, |editor, cx| {
-                        editor.scroll_manager.set_is_dragging_scrollbar(false, cx);
-                        cx.stop_propagation();
-                    });
-                }
-            });
-        } else {
-            cx.on_mouse_event({
-                let editor = self.editor.clone();
-                move |event: &MouseDownEvent, phase, cx| {
-                    if phase == DispatchPhase::Capture {
-                        return;
-                    }
-
-                    editor.update(cx, |editor, cx| {
-                        if track_bounds.contains(&event.position) {
-                            editor.scroll_manager.set_is_dragging_scrollbar(true, cx);
-
-                            let y = event.position.y;
-                            if y < thumb_top || thumb_bottom < y {
-                                let center_row =
-                                    ((y - top) * max_row as f32 / height).round() as u32;
-                                let top_row = center_row
-                                    .saturating_sub((row_range.end - row_range.start) as u32 / 2);
-                                let mut position = editor.scroll_position(cx);
-                                position.y = top_row as f32;
-                                editor.set_scroll_position(position, cx);
-                            } else {
-                                editor.scroll_manager.show_scrollbar(cx);
-                            }
-
-                            cx.stop_propagation();
-                        }
-                    });
-                }
-            });
-        }
-    }
-
-    #[allow(clippy::too_many_arguments)]
-    fn paint_highlighted_range(
-        &self,
-        range: Range<DisplayPoint>,
-        color: Hsla,
-        corner_radius: Pixels,
-        line_end_overshoot: Pixels,
-        layout: &LayoutState,
-        content_origin: gpui::Point<Pixels>,
-        bounds: Bounds<Pixels>,
-        cx: &mut WindowContext,
-    ) {
-        let start_row = layout.visible_display_row_range.start;
-        let end_row = layout.visible_display_row_range.end;
-        if range.start != range.end {
-            let row_range = if range.end.column() == 0 {
-                cmp::max(range.start.row(), start_row)..cmp::min(range.end.row(), end_row)
-            } else {
-                cmp::max(range.start.row(), start_row)..cmp::min(range.end.row() + 1, end_row)
-            };
-
-            let highlighted_range = HighlightedRange {
-                color,
-                line_height: layout.position_map.line_height,
-                corner_radius,
-                start_y: content_origin.y
-                    + row_range.start as f32 * layout.position_map.line_height
-                    - layout.position_map.scroll_position.y,
-                lines: row_range
-                    .into_iter()
-                    .map(|row| {
-                        let line_layout =
-                            &layout.position_map.line_layouts[(row - start_row) as usize].line;
-                        HighlightedRangeLine {
-                            start_x: if row == range.start.row() {
-                                content_origin.x
-                                    + line_layout.x_for_index(range.start.column() as usize)
-                                    - layout.position_map.scroll_position.x
-                            } else {
-                                content_origin.x - layout.position_map.scroll_position.x
-                            },
-                            end_x: if row == range.end.row() {
-                                content_origin.x
-                                    + line_layout.x_for_index(range.end.column() as usize)
-                                    - layout.position_map.scroll_position.x
-                            } else {
-                                content_origin.x + line_layout.width + line_end_overshoot
-                                    - layout.position_map.scroll_position.x
-                            },
-                        }
-                    })
-                    .collect(),
-            };
-
-            highlighted_range.paint(bounds, cx);
-        }
-    }
-
-    fn paint_blocks(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        layout: &mut LayoutState,
-        cx: &mut WindowContext,
-    ) {
-        let scroll_position = layout.position_map.snapshot.scroll_position();
-        let scroll_left = scroll_position.x * layout.position_map.em_width;
-        let scroll_top = scroll_position.y * layout.position_map.line_height;
-
-        for mut block in layout.blocks.drain(..) {
-            let mut origin = bounds.origin
-                + point(
-                    Pixels::ZERO,
-                    block.row as f32 * layout.position_map.line_height - scroll_top,
-                );
-            if !matches!(block.style, BlockStyle::Sticky) {
-                origin += point(-scroll_left, Pixels::ZERO);
-            }
-            block.element.draw(origin, block.available_space, cx);
-        }
-    }
-
-    fn column_pixels(&self, column: usize, cx: &WindowContext) -> Pixels {
-        let style = &self.style;
-        let font_size = style.text.font_size.to_pixels(cx.rem_size());
-        let layout = cx
-            .text_system()
-            .shape_line(
-                SharedString::from(" ".repeat(column)),
-                font_size,
-                &[TextRun {
-                    len: column,
-                    font: style.text.font(),
-                    color: Hsla::default(),
-                    background_color: None,
-                    underline: None,
-                }],
-            )
-            .unwrap();
-
-        layout.width
-    }
-
-    fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &WindowContext) -> Pixels {
-        let digit_count = (snapshot.max_buffer_row() as f32 + 1.).log10().floor() as usize + 1;
-        self.column_pixels(digit_count, cx)
-    }
-
-    //Folds contained in a hunk are ignored apart from shrinking visual size
-    //If a fold contains any hunks then that fold line is marked as modified
-    fn layout_git_gutters(
-        &self,
-        display_rows: Range<u32>,
-        snapshot: &EditorSnapshot,
-    ) -> Vec<DisplayDiffHunk> {
-        let buffer_snapshot = &snapshot.buffer_snapshot;
-
-        let buffer_start_row = DisplayPoint::new(display_rows.start, 0)
-            .to_point(snapshot)
-            .row;
-        let buffer_end_row = DisplayPoint::new(display_rows.end, 0)
-            .to_point(snapshot)
-            .row;
-
-        buffer_snapshot
-            .git_diff_hunks_in_range(buffer_start_row..buffer_end_row)
-            .map(|hunk| diff_hunk_to_display(hunk, snapshot))
-            .dedup()
-            .collect()
-    }
-
-    fn calculate_relative_line_numbers(
-        &self,
-        snapshot: &EditorSnapshot,
-        rows: &Range<u32>,
-        relative_to: Option<u32>,
-    ) -> HashMap<u32, u32> {
-        let mut relative_rows: HashMap<u32, u32> = Default::default();
-        let Some(relative_to) = relative_to else {
-            return relative_rows;
-        };
-
-        let start = rows.start.min(relative_to);
-        let end = rows.end.max(relative_to);
-
-        let buffer_rows = snapshot
-            .buffer_rows(start)
-            .take(1 + (end - start) as usize)
-            .collect::<Vec<_>>();
-
-        let head_idx = relative_to - start;
-        let mut delta = 1;
-        let mut i = head_idx + 1;
-        while i < buffer_rows.len() as u32 {
-            if buffer_rows[i as usize].is_some() {
-                if rows.contains(&(i + start)) {
-                    relative_rows.insert(i + start, delta);
-                }
-                delta += 1;
-            }
-            i += 1;
-        }
-        delta = 1;
-        i = head_idx.min(buffer_rows.len() as u32 - 1);
-        while i > 0 && buffer_rows[i as usize].is_none() {
-            i -= 1;
-        }
-
-        while i > 0 {
-            i -= 1;
-            if buffer_rows[i as usize].is_some() {
-                if rows.contains(&(i + start)) {
-                    relative_rows.insert(i + start, delta);
-                }
-                delta += 1;
-            }
-        }
-
-        relative_rows
-    }
-
-    fn shape_line_numbers(
-        &self,
-        rows: Range<u32>,
-        active_rows: &BTreeMap<u32, bool>,
-        newest_selection_head: DisplayPoint,
-        is_singleton: bool,
-        snapshot: &EditorSnapshot,
-        cx: &ViewContext<Editor>,
-    ) -> (
-        Vec<Option<ShapedLine>>,
-        Vec<Option<(FoldStatus, BufferRow, bool)>>,
-    ) {
-        let font_size = self.style.text.font_size.to_pixels(cx.rem_size());
-        let include_line_numbers = snapshot.mode == EditorMode::Full;
-        let mut shaped_line_numbers = Vec::with_capacity(rows.len());
-        let mut fold_statuses = Vec::with_capacity(rows.len());
-        let mut line_number = String::new();
-        let is_relative = EditorSettings::get_global(cx).relative_line_numbers;
-        let relative_to = if is_relative {
-            Some(newest_selection_head.row())
-        } else {
-            None
-        };
-
-        let relative_rows = self.calculate_relative_line_numbers(&snapshot, &rows, relative_to);
-
-        for (ix, row) in snapshot
-            .buffer_rows(rows.start)
-            .take((rows.end - rows.start) as usize)
-            .enumerate()
-        {
-            let display_row = rows.start + ix as u32;
-            let (active, color) = if active_rows.contains_key(&display_row) {
-                (true, cx.theme().colors().editor_active_line_number)
-            } else {
-                (false, cx.theme().colors().editor_line_number)
-            };
-            if let Some(buffer_row) = row {
-                if include_line_numbers {
-                    line_number.clear();
-                    let default_number = buffer_row + 1;
-                    let number = relative_rows
-                        .get(&(ix as u32 + rows.start))
-                        .unwrap_or(&default_number);
-                    write!(&mut line_number, "{}", number).unwrap();
-                    let run = TextRun {
-                        len: line_number.len(),
-                        font: self.style.text.font(),
-                        color,
-                        background_color: None,
-                        underline: None,
-                    };
-                    let shaped_line = cx
-                        .text_system()
-                        .shape_line(line_number.clone().into(), font_size, &[run])
-                        .unwrap();
-                    shaped_line_numbers.push(Some(shaped_line));
-                    fold_statuses.push(
-                        is_singleton
-                            .then(|| {
-                                snapshot
-                                    .fold_for_line(buffer_row)
-                                    .map(|fold_status| (fold_status, buffer_row, active))
-                            })
-                            .flatten(),
-                    )
-                }
-            } else {
-                fold_statuses.push(None);
-                shaped_line_numbers.push(None);
-            }
-        }
-
-        (shaped_line_numbers, fold_statuses)
-    }
-
-    fn layout_lines(
-        &self,
-        rows: Range<u32>,
-        line_number_layouts: &[Option<ShapedLine>],
-        snapshot: &EditorSnapshot,
-        cx: &ViewContext<Editor>,
-    ) -> Vec<LineWithInvisibles> {
-        if rows.start >= rows.end {
-            return Vec::new();
-        }
-
-        // Show the placeholder when the editor is empty
-        if snapshot.is_empty() {
-            let font_size = self.style.text.font_size.to_pixels(cx.rem_size());
-            let placeholder_color = cx.theme().colors().text_placeholder;
-            let placeholder_text = snapshot.placeholder_text();
-
-            let placeholder_lines = placeholder_text
-                .as_ref()
-                .map_or("", AsRef::as_ref)
-                .split('\n')
-                .skip(rows.start as usize)
-                .chain(iter::repeat(""))
-                .take(rows.len());
-            placeholder_lines
-                .filter_map(move |line| {
-                    let run = TextRun {
-                        len: line.len(),
-                        font: self.style.text.font(),
-                        color: placeholder_color,
-                        background_color: None,
-                        underline: Default::default(),
-                    };
-                    cx.text_system()
-                        .shape_line(line.to_string().into(), font_size, &[run])
-                        .log_err()
-                })
-                .map(|line| LineWithInvisibles {
-                    line,
-                    invisibles: Vec::new(),
-                })
-                .collect()
-        } else {
-            let chunks = snapshot.highlighted_chunks(rows.clone(), true, &self.style);
-            LineWithInvisibles::from_chunks(
-                chunks,
-                &self.style.text,
-                MAX_LINE_LEN,
-                rows.len() as usize,
-                line_number_layouts,
-                snapshot.mode,
-                cx,
-            )
-        }
-    }
-
-    fn compute_layout(&mut self, bounds: Bounds<Pixels>, cx: &mut WindowContext) -> LayoutState {
-        self.editor.update(cx, |editor, cx| {
-            let snapshot = editor.snapshot(cx);
-            let style = self.style.clone();
-
-            let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
-            let font_size = style.text.font_size.to_pixels(cx.rem_size());
-            let line_height = style.text.line_height_in_pixels(cx.rem_size());
-            let em_width = cx
-                .text_system()
-                .typographic_bounds(font_id, font_size, 'm')
-                .unwrap()
-                .size
-                .width;
-            let em_advance = cx
-                .text_system()
-                .advance(font_id, font_size, 'm')
-                .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_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;
-
-            let text_width = bounds.size.width - gutter_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 wrap_width = match editor.soft_wrap_mode(cx) {
-                    SoftWrap::None => (MAX_LINE_LEN / 2) as f32 * em_advance,
-                    SoftWrap::EditorWidth => editor_width,
-                    SoftWrap::Column(column) => editor_width.min(column as f32 * em_advance),
-                };
-
-                if editor.set_wrap_width(Some(wrap_width), cx) {
-                    editor.snapshot(cx)
-                } else {
-                    snapshot
-                }
-            };
-
-            let wrap_guides = editor
-                .wrap_guides(cx)
-                .iter()
-                .map(|(guide, active)| (self.column_pixels(*guide, cx), *active))
-                .collect::<SmallVec<[_; 2]>>();
-
-            let gutter_size = size(gutter_width, bounds.size.height);
-            let text_size = size(text_width, bounds.size.height);
-
-            let autoscroll_horizontally =
-                editor.autoscroll_vertically(bounds.size.height, line_height, cx);
-            let mut snapshot = editor.snapshot(cx);
-
-            let scroll_position = snapshot.scroll_position();
-            // The scroll position is a fractional point, the whole number of which represents
-            // the top of the window in terms of display rows.
-            let start_row = scroll_position.y as u32;
-            let height_in_lines = f32::from(bounds.size.height / line_height);
-            let max_row = snapshot.max_point().row();
-
-            // Add 1 to ensure selections bleed off screen
-            let end_row = 1 + cmp::min((scroll_position.y + height_in_lines).ceil() as u32, max_row);
-
-            let start_anchor = if start_row == 0 {
-                Anchor::min()
-            } else {
-                snapshot
-                    .buffer_snapshot
-                    .anchor_before(DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left))
-            };
-            let end_anchor = if end_row > max_row {
-                Anchor::max()
-            } else {
-                snapshot
-                    .buffer_snapshot
-                    .anchor_before(DisplayPoint::new(end_row, 0).to_offset(&snapshot, Bias::Right))
-            };
-
-            let mut selections: Vec<(PlayerColor, Vec<SelectionLayout>)> = Vec::new();
-            let mut active_rows = BTreeMap::new();
-            let is_singleton = editor.is_singleton(cx);
-
-            let highlighted_rows = editor.highlighted_rows();
-            let highlighted_ranges = editor.background_highlights_in_range(
-                start_anchor..end_anchor,
-                &snapshot.display_snapshot,
-                cx.theme().colors(),
-            );
-
-            let mut newest_selection_head = None;
-
-            if editor.show_local_selections {
-                let mut local_selections: Vec<Selection<Point>> = editor
-                    .selections
-                    .disjoint_in_range(start_anchor..end_anchor, cx);
-                local_selections.extend(editor.selections.pending(cx));
-                let mut layouts = Vec::new();
-                let newest = editor.selections.newest(cx);
-                for selection in local_selections.drain(..) {
-                    let is_empty = selection.start == selection.end;
-                    let is_newest = selection == newest;
-
-                    let layout = SelectionLayout::new(
-                        selection,
-                        editor.selections.line_mode,
-                        editor.cursor_shape,
-                        &snapshot.display_snapshot,
-                        is_newest,
-                        true,
-                    );
-                    if is_newest {
-                        newest_selection_head = Some(layout.head);
-                    }
-
-                    for row in cmp::max(layout.active_rows.start, start_row)
-                        ..=cmp::min(layout.active_rows.end, end_row)
-                    {
-                        let contains_non_empty_selection = active_rows.entry(row).or_insert(!is_empty);
-                        *contains_non_empty_selection |= !is_empty;
-                    }
-                    layouts.push(layout);
-                }
-
-                selections.push((style.local_player, layouts));
-            }
-
-            if let Some(collaboration_hub) = &editor.collaboration_hub {
-                // When following someone, render the local selections in their color.
-                if let Some(leader_id) = editor.leader_peer_id {
-                    if let Some(collaborator) = collaboration_hub.collaborators(cx).get(&leader_id) {
-                        if let Some(participant_index) = collaboration_hub
-                            .user_participant_indices(cx)
-                            .get(&collaborator.user_id)
-                        {
-                            if let Some((local_selection_style, _)) = selections.first_mut() {
-                                *local_selection_style = cx
-                                    .theme()
-                                    .players()
-                                    .color_for_participant(participant_index.0);
-                            }
-                        }
-                    }
-                }
-
-                let mut remote_selections = HashMap::default();
-                for selection in snapshot.remote_selections_in_range(
-                    &(start_anchor..end_anchor),
-                    collaboration_hub.as_ref(),
-                    cx,
-                ) {
-                    let selection_style = if let Some(participant_index) = selection.participant_index {
-                        cx.theme()
-                            .players()
-                            .color_for_participant(participant_index.0)
-                    } else {
-                        cx.theme().players().absent()
-                    };
-
-                    // Don't re-render the leader's selections, since the local selections
-                    // match theirs.
-                    if Some(selection.peer_id) == editor.leader_peer_id {
-                        continue;
-                    }
-
-                    remote_selections
-                        .entry(selection.replica_id)
-                        .or_insert((selection_style, Vec::new()))
-                        .1
-                        .push(SelectionLayout::new(
-                            selection.selection,
-                            selection.line_mode,
-                            selection.cursor_shape,
-                            &snapshot.display_snapshot,
-                            false,
-                            false,
-                        ));
-                }
-
-                selections.extend(remote_selections.into_values());
-            }
-
-            let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
-            let show_scrollbars = match scrollbar_settings.show {
-                ShowScrollbar::Auto => {
-                    // Git
-                    (is_singleton && scrollbar_settings.git_diff && snapshot.buffer_snapshot.has_git_diffs())
-                    ||
-                    // Selections
-                    (is_singleton && scrollbar_settings.selections && !highlighted_ranges.is_empty())
-                    // Scrollmanager
-                    || editor.scroll_manager.scrollbars_visible()
-                }
-                ShowScrollbar::System => editor.scroll_manager.scrollbars_visible(),
-                ShowScrollbar::Always => true,
-                ShowScrollbar::Never => false,
-            };
-
-            let head_for_relative = newest_selection_head.unwrap_or_else(|| {
-                let newest = editor.selections.newest::<Point>(cx);
-                SelectionLayout::new(
-                    newest,
-                    editor.selections.line_mode,
-                    editor.cursor_shape,
-                    &snapshot.display_snapshot,
-                    true,
-                    true,
-                )
-                .head
-            });
-
-            let (line_numbers, fold_statuses) = self.shape_line_numbers(
-                start_row..end_row,
-                &active_rows,
-                head_for_relative,
-                is_singleton,
-                &snapshot,
-                cx,
-            );
-
-            let display_hunks = self.layout_git_gutters(start_row..end_row, &snapshot);
-
-            let scrollbar_row_range = scroll_position.y..(scroll_position.y + height_in_lines);
-
-            let mut max_visible_line_width = Pixels::ZERO;
-            let line_layouts = self.layout_lines(start_row..end_row, &line_numbers, &snapshot, cx);
-            for line_with_invisibles in &line_layouts {
-                if line_with_invisibles.line.width > max_visible_line_width {
-                    max_visible_line_width = line_with_invisibles.line.width;
-                }
-            }
-
-            let longest_line_width = layout_line(snapshot.longest_row(), &snapshot, &style, cx)
-                .unwrap()
-                .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| {
-                self.layout_blocks(
-                    start_row..end_row,
-                    &snapshot,
-                    bounds.size.width,
-                    scroll_width,
-                    gutter_padding,
-                    gutter_width,
-                    em_width,
-                    gutter_width + gutter_margin,
-                    line_height,
-                    &style,
-                    &line_layouts,
-                    editor,
-                    cx,
-                )
-            });
-
-            let scroll_max = point(
-                f32::from((scroll_width - text_size.width) / em_width).max(0.0),
-                max_row as f32,
-            );
-
-            let clamped = editor.scroll_manager.clamp_scroll_left(scroll_max.x);
-
-            let autoscrolled = if autoscroll_horizontally {
-                editor.autoscroll_horizontally(
-                    start_row,
-                    text_size.width,
-                    scroll_width,
-                    em_width,
-                    &line_layouts,
-                    cx,
-                )
-            } else {
-                false
-            };
-
-            if clamped || autoscrolled {
-                snapshot = editor.snapshot(cx);
-            }
-
-            let mut context_menu = None;
-            let mut code_actions_indicator = None;
-            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.);
-                        context_menu =
-                            editor.render_context_menu(newest_selection_head, &self.style, max_height, cx);
-                    }
-
-                    let active = matches!(
-                        editor.context_menu.read().as_ref(),
-                        Some(crate::ContextMenu::CodeActions(_))
-                    );
-
-                    code_actions_indicator = editor
-                        .render_code_actions_indicator(&style, active, cx)
-                        .map(|element| CodeActionsIndicator {
-                            row: newest_selection_head.row(),
-                            button: element,
-                        });
-                }
-            }
-
-            let visible_rows = start_row..start_row + line_layouts.len() as u32;
-            let max_size = size(
-                (120. * em_width) // Default size
-                    .min(bounds.size.width / 2.) // Shrink to half of the editor width
-                    .max(MIN_POPOVER_CHARACTER_WIDTH * em_width), // Apply minimum width of 20 characters
-                (16. * line_height) // Default size
-                    .min(bounds.size.height / 2.) // Shrink to half of the editor height
-                    .max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines
-            );
-
-            let hover = editor.hover_state.render(
-                &snapshot,
-                &style,
-                visible_rows,
-                max_size,
-                editor.workspace.as_ref().map(|(w, _)| w.clone()),
-                cx,
-            );
-
-            let fold_indicators = cx.with_element_id(Some("gutter_fold_indicators"), |cx| {
-                editor.render_fold_indicators(
-                    fold_statuses,
-                    &style,
-                    editor.gutter_hovered,
-                    line_height,
-                    gutter_margin,
-                    cx,
-                )
-            });
-
-            let invisible_symbol_font_size = font_size / 2.;
-            let tab_invisible = cx
-                .text_system()
-                .shape_line(
-                    "โ†’".into(),
-                    invisible_symbol_font_size,
-                    &[TextRun {
-                        len: "โ†’".len(),
-                        font: self.style.text.font(),
-                        color: cx.theme().colors().editor_invisible,
-                        background_color: None,
-                        underline: None,
-                    }],
-                )
-                .unwrap();
-            let space_invisible = cx
-                .text_system()
-                .shape_line(
-                    "โ€ข".into(),
-                    invisible_symbol_font_size,
-                    &[TextRun {
-                        len: "โ€ข".len(),
-                        font: self.style.text.font(),
-                        color: cx.theme().colors().editor_invisible,
-                        background_color: None,
-                        underline: None,
-                    }],
-                )
-                .unwrap();
-
-            LayoutState {
-                mode: snapshot.mode,
-                position_map: Arc::new(PositionMap {
-                    size: bounds.size,
-                    scroll_position: point(
-                        scroll_position.x * em_width,
-                        scroll_position.y * line_height,
-                    ),
-                    scroll_max,
-                    line_layouts,
-                    line_height,
-                    em_width,
-                    em_advance,
-                    snapshot,
-                }),
-                visible_anchor_range: start_anchor..end_anchor,
-                visible_display_row_range: start_row..end_row,
-                wrap_guides,
-                gutter_size,
-                gutter_padding,
-                text_size,
-                scrollbar_row_range,
-                show_scrollbars,
-                is_singleton,
-                max_row,
-                gutter_margin,
-                active_rows,
-                highlighted_rows,
-                highlighted_ranges,
-                line_numbers,
-                display_hunks,
-                blocks,
-                selections,
-                context_menu,
-                code_actions_indicator,
-                fold_indicators,
-                tab_invisible,
-                space_invisible,
-                hover_popovers: hover,
-            }
-        })
-    }
-
-    #[allow(clippy::too_many_arguments)]
-    fn layout_blocks(
-        &self,
-        rows: Range<u32>,
-        snapshot: &EditorSnapshot,
-        editor_width: Pixels,
-        scroll_width: Pixels,
-        gutter_padding: Pixels,
-        gutter_width: Pixels,
-        em_width: Pixels,
-        text_x: Pixels,
-        line_height: Pixels,
-        style: &EditorStyle,
-        line_layouts: &[LineWithInvisibles],
-        editor: &mut Editor,
-        cx: &mut ViewContext<Editor>,
-    ) -> (Pixels, Vec<BlockLayout>) {
-        let mut block_id = 0;
-        let (fixed_blocks, non_fixed_blocks) = snapshot
-            .blocks_in_range(rows.clone())
-            .partition::<Vec<_>, _>(|(_, block)| match block {
-                TransformBlock::ExcerptHeader { .. } => false,
-                TransformBlock::Custom(block) => block.style() == BlockStyle::Fixed,
-            });
-
-        let render_block = |block: &TransformBlock,
-                            available_space: Size<AvailableSpace>,
-                            block_id: usize,
-                            editor: &mut Editor,
-                            cx: &mut ViewContext<Editor>| {
-            let mut element = match block {
-                TransformBlock::Custom(block) => {
-                    let align_to = block
-                        .position()
-                        .to_point(&snapshot.buffer_snapshot)
-                        .to_display_point(snapshot);
-                    let anchor_x = text_x
-                        + if rows.contains(&align_to.row()) {
-                            line_layouts[(align_to.row() - rows.start) as usize]
-                                .line
-                                .x_for_index(align_to.column() as usize)
-                        } else {
-                            layout_line(align_to.row(), snapshot, style, cx)
-                                .unwrap()
-                                .x_for_index(align_to.column() as usize)
-                        };
-
-                    block.render(&mut BlockContext {
-                        view_context: cx,
-                        anchor_x,
-                        gutter_padding,
-                        line_height,
-                        gutter_width,
-                        em_width,
-                        block_id,
-                        editor_style: &self.style,
-                    })
-                }
-
-                TransformBlock::ExcerptHeader {
-                    buffer,
-                    range,
-                    starts_new_buffer,
-                    ..
-                } => {
-                    let include_root = editor
-                        .project
-                        .as_ref()
-                        .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
-                        .unwrap_or_default();
-
-                    let jump_handler = project::File::from_dyn(buffer.file()).map(|file| {
-                        let jump_path = ProjectPath {
-                            worktree_id: file.worktree_id(cx),
-                            path: file.path.clone(),
-                        };
-                        let jump_anchor = range
-                            .primary
-                            .as_ref()
-                            .map_or(range.context.start, |primary| primary.start);
-                        let jump_position = language::ToPoint::to_point(&jump_anchor, buffer);
-
-                        let jump_handler = cx.listener_for(&self.editor, move |editor, _, cx| {
-                            editor.jump(jump_path.clone(), jump_position, jump_anchor, cx);
-                        });
-
-                        jump_handler
-                    });
-
-                    let element = if *starts_new_buffer {
-                        let path = buffer.resolve_file_path(cx, include_root);
-                        let mut filename = None;
-                        let mut parent_path = None;
-                        // Can't use .and_then() because `.file_name()` and `.parent()` return references :(
-                        if let Some(path) = path {
-                            filename = path.file_name().map(|f| f.to_string_lossy().to_string());
-                            parent_path = path
-                                .parent()
-                                .map(|p| SharedString::from(p.to_string_lossy().to_string() + "/"));
-                        }
-
-                        div()
-                            .id(("path header container", block_id))
-                            .size_full()
-                            .p_1p5()
-                            .child(
-                                h_stack()
-                                    .id("path header block")
-                                    .py_1p5()
-                                    .pl_3()
-                                    .pr_2()
-                                    .rounded_lg()
-                                    .shadow_md()
-                                    .border()
-                                    .border_color(cx.theme().colors().border)
-                                    .bg(cx.theme().colors().editor_subheader_background)
-                                    .justify_between()
-                                    .hover(|style| style.bg(cx.theme().colors().element_hover))
-                                    .child(
-                                        h_stack().gap_3().child(
-                                            h_stack()
-                                                .gap_2()
-                                                .child(
-                                                    filename
-                                                        .map(SharedString::from)
-                                                        .unwrap_or_else(|| "untitled".into()),
-                                                )
-                                                .when_some(parent_path, |then, path| {
-                                                    then.child(
-                                                        div().child(path).text_color(
-                                                            cx.theme().colors().text_muted,
-                                                        ),
-                                                    )
-                                                }),
-                                        ),
-                                    )
-                                    .when_some(jump_handler, |this, jump_handler| {
-                                        this.cursor_pointer()
-                                            .tooltip(|cx| {
-                                                Tooltip::for_action(
-                                                    "Jump to Buffer",
-                                                    &OpenExcerpts,
-                                                    cx,
-                                                )
-                                            })
-                                            .on_mouse_down(MouseButton::Left, |_, cx| {
-                                                cx.stop_propagation()
-                                            })
-                                            .on_click(jump_handler)
-                                    }),
-                            )
-                    } else {
-                        h_stack()
-                            .id(("collapsed context", block_id))
-                            .size_full()
-                            .gap(gutter_padding)
-                            .child(
-                                h_stack()
-                                    .justify_end()
-                                    .flex_none()
-                                    .w(gutter_width - gutter_padding)
-                                    .h_full()
-                                    .text_buffer(cx)
-                                    .text_color(cx.theme().colors().editor_line_number)
-                                    .child("..."),
-                            )
-                            .map(|this| {
-                                if let Some(jump_handler) = jump_handler {
-                                    this.child(
-                                        ButtonLike::new("jump to collapsed context")
-                                            .style(ButtonStyle::Transparent)
-                                            .full_width()
-                                            .on_click(jump_handler)
-                                            .tooltip(|cx| {
-                                                Tooltip::for_action(
-                                                    "Jump to Buffer",
-                                                    &OpenExcerpts,
-                                                    cx,
-                                                )
-                                            })
-                                            .child(
-                                                div()
-                                                    .h_px()
-                                                    .w_full()
-                                                    .bg(cx.theme().colors().border_variant)
-                                                    .group_hover("", |style| {
-                                                        style.bg(cx.theme().colors().border)
-                                                    }),
-                                            ),
-                                    )
-                                } else {
-                                    this.child(div().size_full().bg(gpui::green()))
-                                }
-                            })
-                    };
-                    element.into_any()
-                }
-            };
-
-            let size = element.measure(available_space, cx);
-            (element, size)
-        };
-
-        let mut fixed_block_max_width = Pixels::ZERO;
-        let mut blocks = Vec::new();
-        for (row, block) in fixed_blocks {
-            let available_space = size(
-                AvailableSpace::MinContent,
-                AvailableSpace::Definite(block.height() as f32 * line_height),
-            );
-            let (element, element_size) =
-                render_block(block, available_space, block_id, editor, cx);
-            block_id += 1;
-            fixed_block_max_width = fixed_block_max_width.max(element_size.width + em_width);
-            blocks.push(BlockLayout {
-                row,
-                element,
-                available_space,
-                style: BlockStyle::Fixed,
-            });
-        }
-        for (row, block) in non_fixed_blocks {
-            let style = match block {
-                TransformBlock::Custom(block) => block.style(),
-                TransformBlock::ExcerptHeader { .. } => BlockStyle::Sticky,
-            };
-            let width = match style {
-                BlockStyle::Sticky => editor_width,
-                BlockStyle::Flex => editor_width
-                    .max(fixed_block_max_width)
-                    .max(gutter_width + scroll_width),
-                BlockStyle::Fixed => unreachable!(),
-            };
-            let available_space = size(
-                AvailableSpace::Definite(width),
-                AvailableSpace::Definite(block.height() as f32 * line_height),
-            );
-            let (element, _) = render_block(block, available_space, block_id, editor, cx);
-            block_id += 1;
-            blocks.push(BlockLayout {
-                row,
-                element,
-                available_space,
-                style,
-            });
-        }
-        (
-            scroll_width.max(fixed_block_max_width - gutter_width),
-            blocks,
-        )
-    }
-
-    fn paint_mouse_listeners(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        gutter_bounds: Bounds<Pixels>,
-        text_bounds: Bounds<Pixels>,
-        layout: &LayoutState,
-        cx: &mut WindowContext,
-    ) {
-        let interactive_bounds = InteractiveBounds {
-            bounds: bounds.intersect(&cx.content_mask().bounds),
-            stacking_order: cx.stacking_order().clone(),
-        };
-
-        cx.on_mouse_event({
-            let position_map = layout.position_map.clone();
-            let editor = self.editor.clone();
-            let interactive_bounds = interactive_bounds.clone();
-
-            move |event: &ScrollWheelEvent, phase, cx| {
-                if phase == DispatchPhase::Bubble
-                    && interactive_bounds.visibly_contains(&event.position, cx)
-                {
-                    editor.update(cx, |editor, cx| {
-                        Self::scroll(editor, event, &position_map, &interactive_bounds, cx)
-                    });
-                }
-            }
-        });
-
-        cx.on_mouse_event({
-            let position_map = layout.position_map.clone();
-            let editor = self.editor.clone();
-            let stacking_order = cx.stacking_order().clone();
-            let interactive_bounds = interactive_bounds.clone();
-
-            move |event: &MouseDownEvent, phase, cx| {
-                if phase == DispatchPhase::Bubble
-                    && interactive_bounds.visibly_contains(&event.position, cx)
-                {
-                    match event.button {
-                        MouseButton::Left => editor.update(cx, |editor, cx| {
-                            Self::mouse_left_down(
-                                editor,
-                                event,
-                                &position_map,
-                                text_bounds,
-                                gutter_bounds,
-                                &stacking_order,
-                                cx,
-                            );
-                        }),
-                        MouseButton::Right => editor.update(cx, |editor, cx| {
-                            Self::mouse_right_down(editor, event, &position_map, text_bounds, cx);
-                        }),
-                        _ => {}
-                    };
-                }
-            }
-        });
-
-        cx.on_mouse_event({
-            let position_map = layout.position_map.clone();
-            let editor = self.editor.clone();
-            let stacking_order = cx.stacking_order().clone();
-            let interactive_bounds = interactive_bounds.clone();
-
-            move |event: &MouseUpEvent, phase, cx| {
-                if phase == DispatchPhase::Bubble
-                    && interactive_bounds.visibly_contains(&event.position, cx)
-                {
-                    editor.update(cx, |editor, cx| {
-                        Self::mouse_up(
-                            editor,
-                            event,
-                            &position_map,
-                            text_bounds,
-                            &stacking_order,
-                            cx,
-                        )
-                    });
-                }
-            }
-        });
-        cx.on_mouse_event({
-            let position_map = layout.position_map.clone();
-            let editor = self.editor.clone();
-            let stacking_order = cx.stacking_order().clone();
-
-            move |event: &MouseMoveEvent, phase, cx| {
-                // if editor.has_pending_selection() && event.pressed_button == Some(MouseButton::Left) {
-
-                if phase == DispatchPhase::Bubble {
-                    editor.update(cx, |editor, cx| {
-                        if event.pressed_button == Some(MouseButton::Left) {
-                            Self::mouse_dragged(
-                                editor,
-                                event,
-                                &position_map,
-                                text_bounds,
-                                gutter_bounds,
-                                &stacking_order,
-                                cx,
-                            )
-                        }
-
-                        if interactive_bounds.visibly_contains(&event.position, cx) {
-                            Self::mouse_moved(
-                                editor,
-                                event,
-                                &position_map,
-                                text_bounds,
-                                gutter_bounds,
-                                &stacking_order,
-                                cx,
-                            )
-                        }
-                    });
-                }
-            }
-        });
-    }
-}
-
-#[derive(Debug)]
-pub struct LineWithInvisibles {
-    pub line: ShapedLine,
-    invisibles: Vec<Invisible>,
-}
-
-impl LineWithInvisibles {
-    fn from_chunks<'a>(
-        chunks: impl Iterator<Item = HighlightedChunk<'a>>,
-        text_style: &TextStyle,
-        max_line_len: usize,
-        max_line_count: usize,
-        line_number_layouts: &[Option<ShapedLine>],
-        editor_mode: EditorMode,
-        cx: &WindowContext,
-    ) -> Vec<Self> {
-        let mut layouts = Vec::with_capacity(max_line_count);
-        let mut line = String::new();
-        let mut invisibles = Vec::new();
-        let mut styles = Vec::new();
-        let mut non_whitespace_added = false;
-        let mut row = 0;
-        let mut line_exceeded_max_len = false;
-        let font_size = text_style.font_size.to_pixels(cx.rem_size());
-
-        for highlighted_chunk in chunks.chain([HighlightedChunk {
-            chunk: "\n",
-            style: None,
-            is_tab: false,
-        }]) {
-            for (ix, mut line_chunk) in highlighted_chunk.chunk.split('\n').enumerate() {
-                if ix > 0 {
-                    let shaped_line = cx
-                        .text_system()
-                        .shape_line(line.clone().into(), font_size, &styles)
-                        .unwrap();
-                    layouts.push(Self {
-                        line: shaped_line,
-                        invisibles: invisibles.drain(..).collect(),
-                    });
-
-                    line.clear();
-                    styles.clear();
-                    row += 1;
-                    line_exceeded_max_len = false;
-                    non_whitespace_added = false;
-                    if row == max_line_count {
-                        return layouts;
-                    }
-                }
-
-                if !line_chunk.is_empty() && !line_exceeded_max_len {
-                    let text_style = if let Some(style) = highlighted_chunk.style {
-                        Cow::Owned(text_style.clone().highlight(style))
-                    } else {
-                        Cow::Borrowed(text_style)
-                    };
-
-                    if line.len() + line_chunk.len() > max_line_len {
-                        let mut chunk_len = max_line_len - line.len();
-                        while !line_chunk.is_char_boundary(chunk_len) {
-                            chunk_len -= 1;
-                        }
-                        line_chunk = &line_chunk[..chunk_len];
-                        line_exceeded_max_len = true;
-                    }
-
-                    styles.push(TextRun {
-                        len: line_chunk.len(),
-                        font: text_style.font(),
-                        color: text_style.color,
-                        background_color: text_style.background_color,
-                        underline: text_style.underline,
-                    });
-
-                    if editor_mode == EditorMode::Full {
-                        // Line wrap pads its contents with fake whitespaces,
-                        // avoid printing them
-                        let inside_wrapped_string = line_number_layouts
-                            .get(row)
-                            .and_then(|layout| layout.as_ref())
-                            .is_none();
-                        if highlighted_chunk.is_tab {
-                            if non_whitespace_added || !inside_wrapped_string {
-                                invisibles.push(Invisible::Tab {
-                                    line_start_offset: line.len(),
-                                });
-                            }
-                        } else {
-                            invisibles.extend(
-                                line_chunk
-                                    .chars()
-                                    .enumerate()
-                                    .filter(|(_, line_char)| {
-                                        let is_whitespace = line_char.is_whitespace();
-                                        non_whitespace_added |= !is_whitespace;
-                                        is_whitespace
-                                            && (non_whitespace_added || !inside_wrapped_string)
-                                    })
-                                    .map(|(whitespace_index, _)| Invisible::Whitespace {
-                                        line_offset: line.len() + whitespace_index,
-                                    }),
-                            )
-                        }
-                    }
-
-                    line.push_str(line_chunk);
-                }
-            }
-        }
-
-        layouts
-    }
-
-    fn draw(
-        &self,
-        layout: &LayoutState,
-        row: u32,
-        content_origin: gpui::Point<Pixels>,
-        whitespace_setting: ShowWhitespaceSetting,
-        selection_ranges: &[Range<DisplayPoint>],
-        cx: &mut WindowContext,
-    ) {
-        let line_height = layout.position_map.line_height;
-        let line_y = line_height * row as f32 - layout.position_map.scroll_position.y;
-
-        self.line
-            .paint(
-                content_origin + gpui::point(-layout.position_map.scroll_position.x, line_y),
-                line_height,
-                cx,
-            )
-            .log_err();
-
-        self.draw_invisibles(
-            &selection_ranges,
-            layout,
-            content_origin,
-            line_y,
-            row,
-            line_height,
-            whitespace_setting,
-            cx,
-        );
-    }
-
-    fn draw_invisibles(
-        &self,
-        selection_ranges: &[Range<DisplayPoint>],
-        layout: &LayoutState,
-        content_origin: gpui::Point<Pixels>,
-        line_y: Pixels,
-        row: u32,
-        line_height: Pixels,
-        whitespace_setting: ShowWhitespaceSetting,
-        cx: &mut WindowContext,
-    ) {
-        let allowed_invisibles_regions = match whitespace_setting {
-            ShowWhitespaceSetting::None => return,
-            ShowWhitespaceSetting::Selection => Some(selection_ranges),
-            ShowWhitespaceSetting::All => None,
-        };
-
-        for invisible in &self.invisibles {
-            let (&token_offset, invisible_symbol) = match invisible {
-                Invisible::Tab { line_start_offset } => (line_start_offset, &layout.tab_invisible),
-                Invisible::Whitespace { line_offset } => (line_offset, &layout.space_invisible),
-            };
-
-            let x_offset = self.line.x_for_index(token_offset);
-            let invisible_offset =
-                (layout.position_map.em_width - invisible_symbol.width).max(Pixels::ZERO) / 2.0;
-            let origin = content_origin
-                + gpui::point(
-                    x_offset + invisible_offset - layout.position_map.scroll_position.x,
-                    line_y,
-                );
-
-            if let Some(allowed_regions) = allowed_invisibles_regions {
-                let invisible_point = DisplayPoint::new(row, token_offset as u32);
-                if !allowed_regions
-                    .iter()
-                    .any(|region| region.start <= invisible_point && invisible_point < region.end)
-                {
-                    continue;
-                }
-            }
-            invisible_symbol.paint(origin, line_height, cx).log_err();
-        }
-    }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum Invisible {
-    Tab { line_start_offset: usize },
-    Whitespace { line_offset: usize },
-}
-
-impl Element for EditorElement {
-    type State = ();
-
-    fn request_layout(
-        &mut self,
-        _element_state: Option<Self::State>,
-        cx: &mut gpui::WindowContext,
-    ) -> (gpui::LayoutId, Self::State) {
-        self.editor.update(cx, |editor, cx| {
-            editor.set_style(self.style.clone(), cx);
-
-            let layout_id = match editor.mode {
-                EditorMode::SingleLine => {
-                    let rem_size = cx.rem_size();
-                    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)
-                }
-                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()
-                    })
-                }
-                EditorMode::Full => {
-                    let mut style = Style::default();
-                    style.size.width = relative(1.).into();
-                    style.size.height = relative(1.).into();
-                    cx.request_layout(&style, None)
-                }
-            };
-
-            (layout_id, ())
-        })
-    }
-
-    fn paint(
-        &mut self,
-        bounds: Bounds<gpui::Pixels>,
-        _element_state: &mut Self::State,
-        cx: &mut gpui::WindowContext,
-    ) {
-        let editor = self.editor.clone();
-
-        cx.with_text_style(
-            Some(gpui::TextStyleRefinement {
-                font_size: Some(self.style.text.font_size),
-                ..Default::default()
-            }),
-            |cx| {
-                let mut layout = self.compute_layout(bounds, cx);
-                let gutter_bounds = Bounds {
-                    origin: bounds.origin,
-                    size: layout.gutter_size,
-                };
-                let text_bounds = Bounds {
-                    origin: gutter_bounds.upper_right(),
-                    size: layout.text_size,
-                };
-
-                let focus_handle = editor.focus_handle(cx);
-                let key_context = self.editor.read(cx).key_context(cx);
-                cx.with_key_dispatch(Some(key_context), Some(focus_handle.clone()), |_, cx| {
-                    self.register_actions(cx);
-                    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);
-
-                        self.paint_background(gutter_bounds, text_bounds, &layout, cx);
-                        if layout.gutter_size.width > Pixels::ZERO {
-                            self.paint_gutter(gutter_bounds, &mut layout, cx);
-                        }
-                        self.paint_text(text_bounds, &mut layout, cx);
-
-                        cx.with_z_index(0, |cx| {
-                            self.paint_mouse_listeners(
-                                bounds,
-                                gutter_bounds,
-                                text_bounds,
-                                &layout,
-                                cx,
-                            );
-                        });
-                        if !layout.blocks.is_empty() {
-                            cx.with_z_index(0, |cx| {
-                                cx.with_element_id(Some("editor_blocks"), |cx| {
-                                    self.paint_blocks(bounds, &mut layout, cx);
-                                });
-                            })
-                        }
-
-                        cx.with_z_index(1, |cx| {
-                            self.paint_overlays(text_bounds, &mut layout, cx);
-                        });
-
-                        cx.with_z_index(2, |cx| self.paint_scrollbar(bounds, &mut layout, cx));
-                    });
-                })
-            },
-        );
-    }
-}
-
-impl IntoElement for EditorElement {
-    type Element = Self;
-
-    fn element_id(&self) -> Option<gpui::ElementId> {
-        self.editor.element_id()
-    }
-
-    fn into_element(self) -> Self::Element {
-        self
-    }
-}
-
-type BufferRow = u32;
-
-pub struct LayoutState {
-    position_map: Arc<PositionMap>,
-    gutter_size: Size<Pixels>,
-    gutter_padding: Pixels,
-    gutter_margin: Pixels,
-    text_size: gpui::Size<Pixels>,
-    mode: EditorMode,
-    wrap_guides: SmallVec<[(Pixels, bool); 2]>,
-    visible_anchor_range: Range<Anchor>,
-    visible_display_row_range: Range<u32>,
-    active_rows: BTreeMap<u32, bool>,
-    highlighted_rows: Option<Range<u32>>,
-    line_numbers: Vec<Option<ShapedLine>>,
-    display_hunks: Vec<DisplayDiffHunk>,
-    blocks: Vec<BlockLayout>,
-    highlighted_ranges: Vec<(Range<DisplayPoint>, Hsla)>,
-    selections: Vec<(PlayerColor, Vec<SelectionLayout>)>,
-    scrollbar_row_range: Range<f32>,
-    show_scrollbars: bool,
-    is_singleton: bool,
-    max_row: u32,
-    context_menu: Option<(DisplayPoint, AnyElement)>,
-    code_actions_indicator: Option<CodeActionsIndicator>,
-    hover_popovers: Option<(DisplayPoint, Vec<AnyElement>)>,
-    fold_indicators: Vec<Option<IconButton>>,
-    tab_invisible: ShapedLine,
-    space_invisible: ShapedLine,
-}
-
-struct CodeActionsIndicator {
-    row: u32,
-    button: IconButton,
-}
-
-struct PositionMap {
-    size: Size<Pixels>,
-    line_height: Pixels,
-    scroll_position: gpui::Point<Pixels>,
-    scroll_max: gpui::Point<f32>,
-    em_width: Pixels,
-    em_advance: Pixels,
-    line_layouts: Vec<LineWithInvisibles>,
-    snapshot: EditorSnapshot,
-}
-
-#[derive(Debug, Copy, Clone)]
-pub struct PointForPosition {
-    pub previous_valid: DisplayPoint,
-    pub next_valid: DisplayPoint,
-    pub exact_unclipped: DisplayPoint,
-    pub column_overshoot_after_line_end: u32,
-}
-
-impl PointForPosition {
-    #[cfg(test)]
-    pub fn valid(valid: DisplayPoint) -> Self {
-        Self {
-            previous_valid: valid,
-            next_valid: valid,
-            exact_unclipped: valid,
-            column_overshoot_after_line_end: 0,
-        }
-    }
-
-    pub fn as_valid(&self) -> Option<DisplayPoint> {
-        if self.previous_valid == self.exact_unclipped && self.next_valid == self.exact_unclipped {
-            Some(self.previous_valid)
-        } else {
-            None
-        }
-    }
-}
-
-impl PositionMap {
-    fn point_for_position(
-        &self,
-        text_bounds: Bounds<Pixels>,
-        position: gpui::Point<Pixels>,
-    ) -> PointForPosition {
-        let scroll_position = self.snapshot.scroll_position();
-        let position = position - text_bounds.origin;
-        let y = position.y.max(px(0.)).min(self.size.height);
-        let x = position.x + (scroll_position.x * self.em_width);
-        let row = (f32::from(y / self.line_height) + scroll_position.y) as u32;
-
-        let (column, x_overshoot_after_line_end) = if let Some(line) = self
-            .line_layouts
-            .get(row as usize - scroll_position.y as usize)
-            .map(|&LineWithInvisibles { ref line, .. }| line)
-        {
-            if let Some(ix) = line.index_for_x(x) {
-                (ix as u32, px(0.))
-            } else {
-                (line.len as u32, px(0.).max(x - line.width))
-            }
-        } else {
-            (0, x)
-        };
-
-        let mut exact_unclipped = DisplayPoint::new(row, column);
-        let previous_valid = self.snapshot.clip_point(exact_unclipped, Bias::Left);
-        let next_valid = self.snapshot.clip_point(exact_unclipped, Bias::Right);
-
-        let column_overshoot_after_line_end = (x_overshoot_after_line_end / self.em_advance) as u32;
-        *exact_unclipped.column_mut() += column_overshoot_after_line_end;
-        PointForPosition {
-            previous_valid,
-            next_valid,
-            exact_unclipped,
-            column_overshoot_after_line_end,
-        }
-    }
-}
-
-struct BlockLayout {
-    row: u32,
-    element: AnyElement,
-    available_space: Size<AvailableSpace>,
-    style: BlockStyle,
-}
-
-fn layout_line(
-    row: u32,
-    snapshot: &EditorSnapshot,
-    style: &EditorStyle,
-    cx: &WindowContext,
-) -> Result<ShapedLine> {
-    let mut line = snapshot.line(row);
-
-    if line.len() > MAX_LINE_LEN {
-        let mut len = MAX_LINE_LEN;
-        while !line.is_char_boundary(len) {
-            len -= 1;
-        }
-
-        line.truncate(len);
-    }
-
-    cx.text_system().shape_line(
-        line.into(),
-        style.text.font_size.to_pixels(cx.rem_size()),
-        &[TextRun {
-            len: snapshot.line_len(row) as usize,
-            font: style.text.font(),
-            color: Hsla::default(),
-            background_color: None,
-            underline: None,
-        }],
-    )
-}
-
-#[derive(Debug)]
-pub struct Cursor {
-    origin: gpui::Point<Pixels>,
-    block_width: Pixels,
-    line_height: Pixels,
-    color: Hsla,
-    shape: CursorShape,
-    block_text: Option<ShapedLine>,
-}
-
-impl Cursor {
-    pub fn new(
-        origin: gpui::Point<Pixels>,
-        block_width: Pixels,
-        line_height: Pixels,
-        color: Hsla,
-        shape: CursorShape,
-        block_text: Option<ShapedLine>,
-    ) -> Cursor {
-        Cursor {
-            origin,
-            block_width,
-            line_height,
-            color,
-            shape,
-            block_text,
-        }
-    }
-
-    pub fn bounding_rect(&self, origin: gpui::Point<Pixels>) -> Bounds<Pixels> {
-        Bounds {
-            origin: self.origin + origin,
-            size: size(self.block_width, self.line_height),
-        }
-    }
-
-    pub fn paint(&self, origin: gpui::Point<Pixels>, cx: &mut WindowContext) {
-        let bounds = match self.shape {
-            CursorShape::Bar => Bounds {
-                origin: self.origin + origin,
-                size: size(px(2.0), self.line_height),
-            },
-            CursorShape::Block | CursorShape::Hollow => Bounds {
-                origin: self.origin + origin,
-                size: size(self.block_width, self.line_height),
-            },
-            CursorShape::Underscore => Bounds {
-                origin: self.origin
-                    + origin
-                    + gpui::Point::new(Pixels::ZERO, self.line_height - px(2.0)),
-                size: size(self.block_width, px(2.0)),
-            },
-        };
-
-        //Draw background or border quad
-        let cursor = if matches!(self.shape, CursorShape::Hollow) {
-            outline(bounds, self.color)
-        } else {
-            fill(bounds, self.color)
-        };
-
-        cx.paint_quad(cursor);
-
-        if let Some(block_text) = &self.block_text {
-            block_text
-                .paint(self.origin + origin, self.line_height, cx)
-                .log_err();
-        }
-    }
-
-    pub fn shape(&self) -> CursorShape {
-        self.shape
-    }
-}
-
-#[derive(Debug)]
-pub struct HighlightedRange {
-    pub start_y: Pixels,
-    pub line_height: Pixels,
-    pub lines: Vec<HighlightedRangeLine>,
-    pub color: Hsla,
-    pub corner_radius: Pixels,
-}
-
-#[derive(Debug)]
-pub struct HighlightedRangeLine {
-    pub start_x: Pixels,
-    pub end_x: Pixels,
-}
-
-impl HighlightedRange {
-    pub fn paint(&self, bounds: Bounds<Pixels>, cx: &mut WindowContext) {
-        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(
-                self.start_y + self.line_height,
-                &self.lines[1..],
-                bounds,
-                cx,
-            );
-        } else {
-            self.paint_lines(self.start_y, &self.lines, bounds, cx);
-        }
-    }
-
-    fn paint_lines(
-        &self,
-        start_y: Pixels,
-        lines: &[HighlightedRangeLine],
-        _bounds: Bounds<Pixels>,
-        cx: &mut WindowContext,
-    ) {
-        if lines.is_empty() {
-            return;
-        }
-
-        let first_line = lines.first().unwrap();
-        let last_line = lines.last().unwrap();
-
-        let first_top_left = point(first_line.start_x, start_y);
-        let first_top_right = point(first_line.end_x, start_y);
-
-        let curve_height = point(Pixels::ZERO, self.corner_radius);
-        let curve_width = |start_x: Pixels, end_x: Pixels| {
-            let max = (end_x - start_x) / 2.;
-            let width = if max < self.corner_radius {
-                max
-            } else {
-                self.corner_radius
-            };
-
-            point(width, Pixels::ZERO)
-        };
-
-        let top_curve_width = curve_width(first_line.start_x, first_line.end_x);
-        let mut path = gpui::Path::new(first_top_right - top_curve_width);
-        path.curve_to(first_top_right + curve_height, first_top_right);
-
-        let mut iter = lines.iter().enumerate().peekable();
-        while let Some((ix, line)) = iter.next() {
-            let bottom_right = point(line.end_x, start_y + (ix + 1) as f32 * self.line_height);
-
-            if let Some((_, next_line)) = iter.peek() {
-                let next_top_right = point(next_line.end_x, bottom_right.y);
-
-                match next_top_right.x.partial_cmp(&bottom_right.x).unwrap() {
-                    Ordering::Equal => {
-                        path.line_to(bottom_right);
-                    }
-                    Ordering::Less => {
-                        let curve_width = curve_width(next_top_right.x, bottom_right.x);
-                        path.line_to(bottom_right - curve_height);
-                        if self.corner_radius > Pixels::ZERO {
-                            path.curve_to(bottom_right - curve_width, bottom_right);
-                        }
-                        path.line_to(next_top_right + curve_width);
-                        if self.corner_radius > Pixels::ZERO {
-                            path.curve_to(next_top_right + curve_height, next_top_right);
-                        }
-                    }
-                    Ordering::Greater => {
-                        let curve_width = curve_width(bottom_right.x, next_top_right.x);
-                        path.line_to(bottom_right - curve_height);
-                        if self.corner_radius > Pixels::ZERO {
-                            path.curve_to(bottom_right + curve_width, bottom_right);
-                        }
-                        path.line_to(next_top_right - curve_width);
-                        if self.corner_radius > Pixels::ZERO {
-                            path.curve_to(next_top_right + curve_height, next_top_right);
-                        }
-                    }
-                }
-            } else {
-                let curve_width = curve_width(line.start_x, line.end_x);
-                path.line_to(bottom_right - curve_height);
-                if self.corner_radius > Pixels::ZERO {
-                    path.curve_to(bottom_right - curve_width, bottom_right);
-                }
-
-                let bottom_left = point(line.start_x, bottom_right.y);
-                path.line_to(bottom_left + curve_width);
-                if self.corner_radius > Pixels::ZERO {
-                    path.curve_to(bottom_left - curve_height, bottom_left);
-                }
-            }
-        }
-
-        if first_line.start_x > last_line.start_x {
-            let curve_width = curve_width(last_line.start_x, first_line.start_x);
-            let second_top_left = point(last_line.start_x, start_y + self.line_height);
-            path.line_to(second_top_left + curve_height);
-            if self.corner_radius > Pixels::ZERO {
-                path.curve_to(second_top_left + curve_width, second_top_left);
-            }
-            let first_bottom_left = point(first_line.start_x, second_top_left.y);
-            path.line_to(first_bottom_left - curve_width);
-            if self.corner_radius > Pixels::ZERO {
-                path.curve_to(first_bottom_left - curve_height, first_bottom_left);
-            }
-        }
-
-        path.line_to(first_top_left + curve_height);
-        if self.corner_radius > Pixels::ZERO {
-            path.curve_to(first_top_left + top_curve_width, first_top_left);
-        }
-        path.line_to(first_top_right - top_curve_width);
-
-        cx.paint_path(path, self.color);
-    }
-}
-
-pub fn scale_vertical_mouse_autoscroll_delta(delta: Pixels) -> f32 {
-    (delta.pow(1.5) / 100.0).into()
-}
-
-fn scale_horizontal_mouse_autoscroll_delta(delta: Pixels) -> f32 {
-    (delta.pow(1.2) / 300.0).into()
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{
-        display_map::{BlockDisposition, BlockProperties},
-        editor_tests::{init_test, update_test_language_settings},
-        Editor, MultiBuffer,
-    };
-    use gpui::TestAppContext;
-    use language::language_settings;
-    use log::info;
-    use std::{num::NonZeroU32, sync::Arc};
-    use util::test::sample_text;
-
-    #[gpui::test]
-    fn test_shape_line_numbers(cx: &mut TestAppContext) {
-        init_test(cx, |_| {});
-        let window = cx.add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
-            Editor::new(EditorMode::Full, buffer, None, cx)
-        });
-
-        let editor = window.root(cx).unwrap();
-        let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
-        let element = EditorElement::new(&editor, style);
-
-        let layouts = window
-            .update(cx, |editor, cx| {
-                let snapshot = editor.snapshot(cx);
-                element
-                    .shape_line_numbers(
-                        0..6,
-                        &Default::default(),
-                        DisplayPoint::new(0, 0),
-                        false,
-                        &snapshot,
-                        cx,
-                    )
-                    .0
-            })
-            .unwrap();
-        assert_eq!(layouts.len(), 6);
-
-        let relative_rows = window
-            .update(cx, |editor, cx| {
-                let snapshot = editor.snapshot(cx);
-                element.calculate_relative_line_numbers(&snapshot, &(0..6), Some(3))
-            })
-            .unwrap();
-        assert_eq!(relative_rows[&0], 3);
-        assert_eq!(relative_rows[&1], 2);
-        assert_eq!(relative_rows[&2], 1);
-        // current line has no relative number
-        assert_eq!(relative_rows[&4], 1);
-        assert_eq!(relative_rows[&5], 2);
-
-        // works if cursor is before screen
-        let relative_rows = window
-            .update(cx, |editor, cx| {
-                let snapshot = editor.snapshot(cx);
-
-                element.calculate_relative_line_numbers(&snapshot, &(3..6), Some(1))
-            })
-            .unwrap();
-        assert_eq!(relative_rows.len(), 3);
-        assert_eq!(relative_rows[&3], 2);
-        assert_eq!(relative_rows[&4], 3);
-        assert_eq!(relative_rows[&5], 4);
-
-        // works if cursor is after screen
-        let relative_rows = window
-            .update(cx, |editor, cx| {
-                let snapshot = editor.snapshot(cx);
-
-                element.calculate_relative_line_numbers(&snapshot, &(0..3), Some(6))
-            })
-            .unwrap();
-        assert_eq!(relative_rows.len(), 3);
-        assert_eq!(relative_rows[&0], 5);
-        assert_eq!(relative_rows[&1], 4);
-        assert_eq!(relative_rows[&2], 3);
-    }
-
-    #[gpui::test]
-    async fn test_vim_visual_selections(cx: &mut TestAppContext) {
-        init_test(cx, |_| {});
-
-        let window = cx.add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx);
-            Editor::new(EditorMode::Full, buffer, None, cx)
-        });
-        let editor = window.root(cx).unwrap();
-        let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
-        let mut element = EditorElement::new(&editor, style);
-
-        window
-            .update(cx, |editor, cx| {
-                editor.cursor_shape = CursorShape::Block;
-                editor.change_selections(None, cx, |s| {
-                    s.select_ranges([
-                        Point::new(0, 0)..Point::new(1, 0),
-                        Point::new(3, 2)..Point::new(3, 3),
-                        Point::new(5, 6)..Point::new(6, 0),
-                    ]);
-                });
-            })
-            .unwrap();
-        let state = cx
-            .update_window(window.into(), |_, cx| {
-                element.compute_layout(
-                    Bounds {
-                        origin: point(px(500.), px(500.)),
-                        size: size(px(500.), px(500.)),
-                    },
-                    cx,
-                )
-            })
-            .unwrap();
-
-        assert_eq!(state.selections.len(), 1);
-        let local_selections = &state.selections[0].1;
-        assert_eq!(local_selections.len(), 3);
-        // moves cursor back one line
-        assert_eq!(local_selections[0].head, DisplayPoint::new(0, 6));
-        assert_eq!(
-            local_selections[0].range,
-            DisplayPoint::new(0, 0)..DisplayPoint::new(1, 0)
-        );
-
-        // moves cursor back one column
-        assert_eq!(
-            local_selections[1].range,
-            DisplayPoint::new(3, 2)..DisplayPoint::new(3, 3)
-        );
-        assert_eq!(local_selections[1].head, DisplayPoint::new(3, 2));
-
-        // leaves cursor on the max point
-        assert_eq!(
-            local_selections[2].range,
-            DisplayPoint::new(5, 6)..DisplayPoint::new(6, 0)
-        );
-        assert_eq!(local_selections[2].head, DisplayPoint::new(6, 0));
-
-        // active lines does not include 1 (even though the range of the selection does)
-        assert_eq!(
-            state.active_rows.keys().cloned().collect::<Vec<u32>>(),
-            vec![0, 3, 5, 6]
-        );
-
-        // multi-buffer support
-        // in DisplayPoint co-ordinates, this is what we're dealing with:
-        //  0: [[file
-        //  1:   header]]
-        //  2: aaaaaa
-        //  3: bbbbbb
-        //  4: cccccc
-        //  5:
-        //  6: ...
-        //  7: ffffff
-        //  8: gggggg
-        //  9: hhhhhh
-        // 10:
-        // 11: [[file
-        // 12:   header]]
-        // 13: bbbbbb
-        // 14: cccccc
-        // 15: dddddd
-        let window = cx.add_window(|cx| {
-            let buffer = MultiBuffer::build_multi(
-                [
-                    (
-                        &(sample_text(8, 6, 'a') + "\n"),
-                        vec![
-                            Point::new(0, 0)..Point::new(3, 0),
-                            Point::new(4, 0)..Point::new(7, 0),
-                        ],
-                    ),
-                    (
-                        &(sample_text(8, 6, 'a') + "\n"),
-                        vec![Point::new(1, 0)..Point::new(3, 0)],
-                    ),
-                ],
-                cx,
-            );
-            Editor::new(EditorMode::Full, buffer, None, cx)
-        });
-        let editor = window.root(cx).unwrap();
-        let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
-        let mut element = EditorElement::new(&editor, style);
-        let _state = window.update(cx, |editor, cx| {
-            editor.cursor_shape = CursorShape::Block;
-            editor.change_selections(None, cx, |s| {
-                s.select_display_ranges([
-                    DisplayPoint::new(4, 0)..DisplayPoint::new(7, 0),
-                    DisplayPoint::new(10, 0)..DisplayPoint::new(13, 0),
-                ]);
-            });
-        });
-
-        let state = cx
-            .update_window(window.into(), |_, cx| {
-                element.compute_layout(
-                    Bounds {
-                        origin: point(px(500.), px(500.)),
-                        size: size(px(500.), px(500.)),
-                    },
-                    cx,
-                )
-            })
-            .unwrap();
-        assert_eq!(state.selections.len(), 1);
-        let local_selections = &state.selections[0].1;
-        assert_eq!(local_selections.len(), 2);
-
-        // moves cursor on excerpt boundary back a line
-        // and doesn't allow selection to bleed through
-        assert_eq!(
-            local_selections[0].range,
-            DisplayPoint::new(4, 0)..DisplayPoint::new(6, 0)
-        );
-        assert_eq!(local_selections[0].head, DisplayPoint::new(5, 0));
-        // moves cursor on buffer boundary back two lines
-        // and doesn't allow selection to bleed through
-        assert_eq!(
-            local_selections[1].range,
-            DisplayPoint::new(10, 0)..DisplayPoint::new(11, 0)
-        );
-        assert_eq!(local_selections[1].head, DisplayPoint::new(10, 0));
-    }
-
-    #[gpui::test]
-    fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) {
-        init_test(cx, |_| {});
-
-        let window = cx.add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("", cx);
-            Editor::new(EditorMode::Full, buffer, None, cx)
-        });
-        let editor = window.root(cx).unwrap();
-        let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
-        window
-            .update(cx, |editor, cx| {
-                editor.set_placeholder_text("hello", cx);
-                editor.insert_blocks(
-                    [BlockProperties {
-                        style: BlockStyle::Fixed,
-                        disposition: BlockDisposition::Above,
-                        height: 3,
-                        position: Anchor::min(),
-                        render: Arc::new(|_| div().into_any()),
-                    }],
-                    None,
-                    cx,
-                );
-
-                // Blur the editor so that it displays placeholder text.
-                cx.blur();
-            })
-            .unwrap();
-
-        let mut element = EditorElement::new(&editor, style);
-        let state = cx
-            .update_window(window.into(), |_, cx| {
-                element.compute_layout(
-                    Bounds {
-                        origin: point(px(500.), px(500.)),
-                        size: size(px(500.), px(500.)),
-                    },
-                    cx,
-                )
-            })
-            .unwrap();
-        let size = state.position_map.size;
-
-        assert_eq!(state.position_map.line_layouts.len(), 4);
-        assert_eq!(
-            state
-                .line_numbers
-                .iter()
-                .map(Option::is_some)
-                .collect::<Vec<_>>(),
-            &[false, false, false, true]
-        );
-
-        // Don't panic.
-        let bounds = Bounds::<Pixels>::new(Default::default(), size);
-        cx.update_window(window.into(), |_, cx| {
-            element.paint(bounds, &mut (), cx);
-        })
-        .unwrap()
-    }
-
-    #[gpui::test]
-    fn test_all_invisibles_drawing(cx: &mut TestAppContext) {
-        const TAB_SIZE: u32 = 4;
-
-        let input_text = "\t \t|\t| a b";
-        let expected_invisibles = vec![
-            Invisible::Tab {
-                line_start_offset: 0,
-            },
-            Invisible::Whitespace {
-                line_offset: TAB_SIZE as usize,
-            },
-            Invisible::Tab {
-                line_start_offset: TAB_SIZE as usize + 1,
-            },
-            Invisible::Tab {
-                line_start_offset: TAB_SIZE as usize * 2 + 1,
-            },
-            Invisible::Whitespace {
-                line_offset: TAB_SIZE as usize * 3 + 1,
-            },
-            Invisible::Whitespace {
-                line_offset: TAB_SIZE as usize * 3 + 3,
-            },
-        ];
-        assert_eq!(
-            expected_invisibles.len(),
-            input_text
-                .chars()
-                .filter(|initial_char| initial_char.is_whitespace())
-                .count(),
-            "Hardcoded expected invisibles differ from the actual ones in '{input_text}'"
-        );
-
-        init_test(cx, |s| {
-            s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
-            s.defaults.tab_size = NonZeroU32::new(TAB_SIZE);
-        });
-
-        let actual_invisibles =
-            collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, px(500.0));
-
-        assert_eq!(expected_invisibles, actual_invisibles);
-    }
-
-    #[gpui::test]
-    fn test_invisibles_dont_appear_in_certain_editors(cx: &mut TestAppContext) {
-        init_test(cx, |s| {
-            s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
-            s.defaults.tab_size = NonZeroU32::new(4);
-        });
-
-        for editor_mode_without_invisibles in [
-            EditorMode::SingleLine,
-            EditorMode::AutoHeight { max_lines: 100 },
-        ] {
-            let invisibles = collect_invisibles_from_new_editor(
-                cx,
-                editor_mode_without_invisibles,
-                "\t\t\t| | a b",
-                px(500.0),
-            );
-            assert!(invisibles.is_empty(),
-                    "For editor mode {editor_mode_without_invisibles:?} no invisibles was expected but got {invisibles:?}");
-        }
-    }
-
-    #[gpui::test]
-    fn test_wrapped_invisibles_drawing(cx: &mut TestAppContext) {
-        let tab_size = 4;
-        let input_text = "a\tbcd   ".repeat(9);
-        let repeated_invisibles = [
-            Invisible::Tab {
-                line_start_offset: 1,
-            },
-            Invisible::Whitespace {
-                line_offset: tab_size as usize + 3,
-            },
-            Invisible::Whitespace {
-                line_offset: tab_size as usize + 4,
-            },
-            Invisible::Whitespace {
-                line_offset: tab_size as usize + 5,
-            },
-        ];
-        let expected_invisibles = std::iter::once(repeated_invisibles)
-            .cycle()
-            .take(9)
-            .flatten()
-            .collect::<Vec<_>>();
-        assert_eq!(
-            expected_invisibles.len(),
-            input_text
-                .chars()
-                .filter(|initial_char| initial_char.is_whitespace())
-                .count(),
-            "Hardcoded expected invisibles differ from the actual ones in '{input_text}'"
-        );
-        info!("Expected invisibles: {expected_invisibles:?}");
-
-        init_test(cx, |_| {});
-
-        // Put the same string with repeating whitespace pattern into editors of various size,
-        // take deliberately small steps during resizing, to put all whitespace kinds near the wrap point.
-        let resize_step = 10.0;
-        let mut editor_width = 200.0;
-        while editor_width <= 1000.0 {
-            update_test_language_settings(cx, |s| {
-                s.defaults.tab_size = NonZeroU32::new(tab_size);
-                s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
-                s.defaults.preferred_line_length = Some(editor_width as u32);
-                s.defaults.soft_wrap = Some(language_settings::SoftWrap::PreferredLineLength);
-            });
-
-            let actual_invisibles = collect_invisibles_from_new_editor(
-                cx,
-                EditorMode::Full,
-                &input_text,
-                px(editor_width),
-            );
-
-            // Whatever the editor size is, ensure it has the same invisible kinds in the same order
-            // (no good guarantees about the offsets: wrapping could trigger padding and its tests should check the offsets).
-            let mut i = 0;
-            for (actual_index, actual_invisible) in actual_invisibles.iter().enumerate() {
-                i = actual_index;
-                match expected_invisibles.get(i) {
-                    Some(expected_invisible) => match (expected_invisible, actual_invisible) {
-                        (Invisible::Whitespace { .. }, Invisible::Whitespace { .. })
-                        | (Invisible::Tab { .. }, Invisible::Tab { .. }) => {}
-                        _ => {
-                            panic!("At index {i}, expected invisible {expected_invisible:?} does not match actual {actual_invisible:?} by kind. Actual invisibles: {actual_invisibles:?}")
-                        }
-                    },
-                    None => panic!("Unexpected extra invisible {actual_invisible:?} at index {i}"),
-                }
-            }
-            let missing_expected_invisibles = &expected_invisibles[i + 1..];
-            assert!(
-                missing_expected_invisibles.is_empty(),
-                "Missing expected invisibles after index {i}: {missing_expected_invisibles:?}"
-            );
-
-            editor_width += resize_step;
-        }
-    }
-
-    fn collect_invisibles_from_new_editor(
-        cx: &mut TestAppContext,
-        editor_mode: EditorMode,
-        input_text: &str,
-        editor_width: Pixels,
-    ) -> Vec<Invisible> {
-        info!(
-            "Creating editor with mode {editor_mode:?}, width {}px and text '{input_text}'",
-            editor_width.0
-        );
-        let window = cx.add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(&input_text, cx);
-            Editor::new(editor_mode, buffer, None, cx)
-        });
-        let editor = window.root(cx).unwrap();
-        let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
-        let mut element = EditorElement::new(&editor, style);
-        window
-            .update(cx, |editor, cx| {
-                editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx);
-                editor.set_wrap_width(Some(editor_width), cx);
-            })
-            .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,
-                )
-            })
-            .unwrap();
-
-        layout_state
-            .position_map
-            .line_layouts
-            .iter()
-            .map(|line_with_invisibles| &line_with_invisibles.invisibles)
-            .flatten()
-            .cloned()
-            .collect()
-    }
-}
-
-pub fn register_action<T: Action>(
-    view: &View<Editor>,
-    cx: &mut WindowContext,
-    listener: impl Fn(&mut Editor, &T, &mut ViewContext<Editor>) + 'static,
-) {
-    let view = view.clone();
-    cx.on_action(TypeId::of::<T>(), move |action, phase, cx| {
-        let action = action.downcast_ref().unwrap();
-        if phase == DispatchPhase::Bubble {
-            view.update(cx, |editor, cx| {
-                listener(editor, action, cx);
-            })
-        }
-    })
-}
-
-fn compute_auto_height_layout(
-    editor: &mut Editor,
-    max_lines: usize,
-    max_line_number_width: Pixels,
-    known_dimensions: Size<Option<Pixels>>,
-    cx: &mut ViewContext<Editor>,
-) -> Option<Size<Pixels>> {
-    let width = known_dimensions.width?;
-    if let Some(height) = known_dimensions.height {
-        return Some(size(width, height));
-    }
-
-    let style = editor.style.as_ref().unwrap();
-    let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
-    let font_size = style.text.font_size.to_pixels(cx.rem_size());
-    let line_height = style.text.line_height_in_pixels(cx.rem_size());
-    let em_width = cx
-        .text_system()
-        .typographic_bounds(font_id, font_size, 'm')
-        .unwrap()
-        .size
-        .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;
-    };
-
-    editor.gutter_width = gutter_width;
-    let text_width = width - gutter_width;
-    let overscroll = size(em_width, px(0.));
-
-    let editor_width = text_width - gutter_margin - overscroll.width - em_width;
-    if editor.set_wrap_width(Some(editor_width), cx) {
-        snapshot = editor.snapshot(cx);
-    }
-
-    let scroll_height = Pixels::from(snapshot.max_point().row() + 1) * line_height;
-    let height = scroll_height
-        .max(line_height)
-        .min(line_height * max_lines as f32);
-
-    Some(size(width, height))
-}

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

@@ -1,282 +0,0 @@
-use std::ops::Range;
-
-use git::diff::{DiffHunk, DiffHunkStatus};
-use language::Point;
-
-use crate::{
-    display_map::{DisplaySnapshot, ToDisplayPoint},
-    AnchorRangeExt,
-};
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum DisplayDiffHunk {
-    Folded {
-        display_row: u32,
-    },
-
-    Unfolded {
-        display_row_range: Range<u32>,
-        status: DiffHunkStatus,
-    },
-}
-
-impl DisplayDiffHunk {
-    pub fn start_display_row(&self) -> u32 {
-        match self {
-            &DisplayDiffHunk::Folded { display_row } => display_row,
-            DisplayDiffHunk::Unfolded {
-                display_row_range, ..
-            } => display_row_range.start,
-        }
-    }
-
-    pub fn contains_display_row(&self, display_row: u32) -> bool {
-        let range = match self {
-            &DisplayDiffHunk::Folded { display_row } => display_row..=display_row,
-
-            DisplayDiffHunk::Unfolded {
-                display_row_range, ..
-            } => display_row_range.start..=display_row_range.end,
-        };
-
-        range.contains(&display_row)
-    }
-}
-
-pub fn diff_hunk_to_display(hunk: DiffHunk<u32>, snapshot: &DisplaySnapshot) -> DisplayDiffHunk {
-    let hunk_start_point = Point::new(hunk.buffer_range.start, 0);
-    let hunk_start_point_sub = Point::new(hunk.buffer_range.start.saturating_sub(1), 0);
-    let hunk_end_point_sub = Point::new(
-        hunk.buffer_range
-            .end
-            .saturating_sub(1)
-            .max(hunk.buffer_range.start),
-        0,
-    );
-
-    let is_removal = hunk.status() == DiffHunkStatus::Removed;
-
-    let folds_start = Point::new(hunk.buffer_range.start.saturating_sub(2), 0);
-    let folds_end = Point::new(hunk.buffer_range.end + 2, 0);
-    let folds_range = folds_start..folds_end;
-
-    let containing_fold = snapshot.folds_in_range(folds_range).find(|fold| {
-        let fold_point_range = fold.range.to_point(&snapshot.buffer_snapshot);
-        let fold_point_range = fold_point_range.start..=fold_point_range.end;
-
-        let folded_start = fold_point_range.contains(&hunk_start_point);
-        let folded_end = fold_point_range.contains(&hunk_end_point_sub);
-        let folded_start_sub = fold_point_range.contains(&hunk_start_point_sub);
-
-        (folded_start && folded_end) || (is_removal && folded_start_sub)
-    });
-
-    if let Some(fold) = containing_fold {
-        let row = fold.range.start.to_display_point(snapshot).row();
-        DisplayDiffHunk::Folded { display_row: row }
-    } else {
-        let start = hunk_start_point.to_display_point(snapshot).row();
-
-        let hunk_end_row = hunk.buffer_range.end.max(hunk.buffer_range.start);
-        let hunk_end_point = Point::new(hunk_end_row, 0);
-        let end = hunk_end_point.to_display_point(snapshot).row();
-
-        DisplayDiffHunk::Unfolded {
-            display_row_range: start..end,
-            status: hunk.status(),
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::editor_tests::init_test;
-    use crate::Point;
-    use gpui::{Context, TestAppContext};
-    use multi_buffer::{ExcerptRange, MultiBuffer};
-    use project::{FakeFs, Project};
-    use unindent::Unindent;
-    #[gpui::test]
-    async fn test_diff_hunks_in_range(cx: &mut TestAppContext) {
-        use git::diff::DiffHunkStatus;
-        init_test(cx, |_| {});
-
-        let fs = FakeFs::new(cx.background_executor.clone());
-        let project = Project::test(fs, [], cx).await;
-
-        // buffer has two modified hunks with two rows each
-        let buffer_1 = project
-            .update(cx, |project, cx| {
-                project.create_buffer(
-                    "
-                        1.zero
-                        1.ONE
-                        1.TWO
-                        1.three
-                        1.FOUR
-                        1.FIVE
-                        1.six
-                    "
-                    .unindent()
-                    .as_str(),
-                    None,
-                    cx,
-                )
-            })
-            .unwrap();
-        buffer_1.update(cx, |buffer, cx| {
-            buffer.set_diff_base(
-                Some(
-                    "
-                        1.zero
-                        1.one
-                        1.two
-                        1.three
-                        1.four
-                        1.five
-                        1.six
-                    "
-                    .unindent(),
-                ),
-                cx,
-            );
-        });
-
-        // buffer has a deletion hunk and an insertion hunk
-        let buffer_2 = project
-            .update(cx, |project, cx| {
-                project.create_buffer(
-                    "
-                        2.zero
-                        2.one
-                        2.two
-                        2.three
-                        2.four
-                        2.five
-                        2.six
-                    "
-                    .unindent()
-                    .as_str(),
-                    None,
-                    cx,
-                )
-            })
-            .unwrap();
-        buffer_2.update(cx, |buffer, cx| {
-            buffer.set_diff_base(
-                Some(
-                    "
-                        2.zero
-                        2.one
-                        2.one-and-a-half
-                        2.two
-                        2.three
-                        2.four
-                        2.six
-                    "
-                    .unindent(),
-                ),
-                cx,
-            );
-        });
-
-        cx.background_executor.run_until_parked();
-
-        let multibuffer = cx.new_model(|cx| {
-            let mut multibuffer = MultiBuffer::new(0);
-            multibuffer.push_excerpts(
-                buffer_1.clone(),
-                [
-                    // excerpt ends in the middle of a modified hunk
-                    ExcerptRange {
-                        context: Point::new(0, 0)..Point::new(1, 5),
-                        primary: Default::default(),
-                    },
-                    // excerpt begins in the middle of a modified hunk
-                    ExcerptRange {
-                        context: Point::new(5, 0)..Point::new(6, 5),
-                        primary: Default::default(),
-                    },
-                ],
-                cx,
-            );
-            multibuffer.push_excerpts(
-                buffer_2.clone(),
-                [
-                    // excerpt ends at a deletion
-                    ExcerptRange {
-                        context: Point::new(0, 0)..Point::new(1, 5),
-                        primary: Default::default(),
-                    },
-                    // excerpt starts at a deletion
-                    ExcerptRange {
-                        context: Point::new(2, 0)..Point::new(2, 5),
-                        primary: Default::default(),
-                    },
-                    // excerpt fully contains a deletion hunk
-                    ExcerptRange {
-                        context: Point::new(1, 0)..Point::new(2, 5),
-                        primary: Default::default(),
-                    },
-                    // excerpt fully contains an insertion hunk
-                    ExcerptRange {
-                        context: Point::new(4, 0)..Point::new(6, 5),
-                        primary: Default::default(),
-                    },
-                ],
-                cx,
-            );
-            multibuffer
-        });
-
-        let snapshot = multibuffer.read_with(cx, |b, cx| b.snapshot(cx));
-
-        assert_eq!(
-            snapshot.text(),
-            "
-                1.zero
-                1.ONE
-                1.FIVE
-                1.six
-                2.zero
-                2.one
-                2.two
-                2.one
-                2.two
-                2.four
-                2.five
-                2.six"
-                .unindent()
-        );
-
-        let expected = [
-            (DiffHunkStatus::Modified, 1..2),
-            (DiffHunkStatus::Modified, 2..3),
-            //TODO: Define better when and where removed hunks show up at range extremities
-            (DiffHunkStatus::Removed, 6..6),
-            (DiffHunkStatus::Removed, 8..8),
-            (DiffHunkStatus::Added, 10..11),
-        ];
-
-        assert_eq!(
-            snapshot
-                .git_diff_hunks_in_range(0..12)
-                .map(|hunk| (hunk.status(), hunk.buffer_range))
-                .collect::<Vec<_>>(),
-            &expected,
-        );
-
-        assert_eq!(
-            snapshot
-                .git_diff_hunks_in_range_rev(0..12)
-                .map(|hunk| (hunk.status(), hunk.buffer_range))
-                .collect::<Vec<_>>(),
-            expected
-                .iter()
-                .rev()
-                .cloned()
-                .collect::<Vec<_>>()
-                .as_slice(),
-        );
-    }
-}

crates/editor2/src/highlight_matching_bracket.rs ๐Ÿ”—

@@ -1,138 +0,0 @@
-use gpui::ViewContext;
-
-use crate::{Editor, RangeToAnchorExt};
-
-enum MatchingBracketHighlight {}
-
-pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
-    editor.clear_background_highlights::<MatchingBracketHighlight>(cx);
-
-    let newest_selection = editor.selections.newest::<usize>(cx);
-    // Don't highlight brackets if the selection isn't empty
-    if !newest_selection.is_empty() {
-        return;
-    }
-
-    let head = newest_selection.head();
-    let snapshot = editor.snapshot(cx);
-    if let Some((opening_range, closing_range)) = snapshot
-        .buffer_snapshot
-        .innermost_enclosing_bracket_ranges(head..head)
-    {
-        editor.highlight_background::<MatchingBracketHighlight>(
-            vec![
-                opening_range.to_anchors(&snapshot.buffer_snapshot),
-                closing_range.to_anchors(&snapshot.buffer_snapshot),
-            ],
-            |theme| theme.editor_document_highlight_read_background,
-            cx,
-        )
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
-    use indoc::indoc;
-    use language::{BracketPair, BracketPairConfig, Language, LanguageConfig};
-
-    #[gpui::test]
-    async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
-
-        let mut cx = EditorLspTestContext::new(
-            Language::new(
-                LanguageConfig {
-                    name: "Rust".into(),
-                    path_suffixes: vec!["rs".to_string()],
-                    brackets: BracketPairConfig {
-                        pairs: vec![
-                            BracketPair {
-                                start: "{".to_string(),
-                                end: "}".to_string(),
-                                close: false,
-                                newline: true,
-                            },
-                            BracketPair {
-                                start: "(".to_string(),
-                                end: ")".to_string(),
-                                close: false,
-                                newline: true,
-                            },
-                        ],
-                        ..Default::default()
-                    },
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            )
-            .with_brackets_query(indoc! {r#"
-                ("{" @open "}" @close)
-                ("(" @open ")" @close)
-                "#})
-            .unwrap(),
-            Default::default(),
-            cx,
-        )
-        .await;
-
-        // positioning cursor inside bracket highlights both
-        cx.set_state(indoc! {r#"
-            pub fn test("Test ห‡argument") {
-                another_test(1, 2, 3);
-            }
-        "#});
-        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
-            pub fn testยซ(ยป"Test argument"ยซ)ยป {
-                another_test(1, 2, 3);
-            }
-        "#});
-
-        cx.set_state(indoc! {r#"
-            pub fn test("Test argument") {
-                another_test(1, ห‡2, 3);
-            }
-        "#});
-        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
-            pub fn test("Test argument") {
-                another_testยซ(ยป1, 2, 3ยซ)ยป;
-            }
-        "#});
-
-        cx.set_state(indoc! {r#"
-            pub fn test("Test argument") {
-                anotherห‡_test(1, 2, 3);
-            }
-        "#});
-        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
-            pub fn test("Test argument") ยซ{ยป
-                another_test(1, 2, 3);
-            ยซ}ยป
-        "#});
-
-        // positioning outside of brackets removes highlight
-        cx.set_state(indoc! {r#"
-            pub fห‡n test("Test argument") {
-                another_test(1, 2, 3);
-            }
-        "#});
-        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
-            pub fn test("Test argument") {
-                another_test(1, 2, 3);
-            }
-        "#});
-
-        // non empty selection dismisses highlight
-        cx.set_state(indoc! {r#"
-            pub fn test("Teยซst argห‡ยปument") {
-                another_test(1, 2, 3);
-            }
-        "#});
-        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
-            pub fn test("Test argument") {
-                another_test(1, 2, 3);
-            }
-        "#});
-    }
-}

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

@@ -1,1345 +0,0 @@
-use crate::{
-    display_map::{InlayOffset, ToDisplayPoint},
-    link_go_to_definition::{InlayHighlight, RangeInEditor},
-    Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings, EditorSnapshot, EditorStyle,
-    ExcerptId, RangeToAnchorExt,
-};
-use futures::FutureExt;
-use gpui::{
-    actions, div, px, AnyElement, CursorStyle, Hsla, InteractiveElement, IntoElement, Model,
-    MouseButton, ParentElement, Pixels, SharedString, Size, StatefulInteractiveElement, Styled,
-    Task, ViewContext, WeakView,
-};
-use language::{markdown, Bias, DiagnosticEntry, Language, LanguageRegistry, ParsedMarkdown};
-
-use lsp::DiagnosticSeverity;
-use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart, Project};
-use settings::Settings;
-use std::{ops::Range, sync::Arc, time::Duration};
-use ui::{StyledExt, Tooltip};
-use util::TryFutureExt;
-use workspace::Workspace;
-
-pub const HOVER_DELAY_MILLIS: u64 = 350;
-pub const HOVER_REQUEST_DELAY_MILLIS: u64 = 200;
-
-pub const MIN_POPOVER_CHARACTER_WIDTH: f32 = 20.;
-pub const MIN_POPOVER_LINE_HEIGHT: Pixels = px(4.);
-pub const HOVER_POPOVER_GAP: Pixels = px(10.);
-
-actions!(editor, [Hover]);
-
-/// Bindable action which uses the most recent selection head to trigger a hover
-pub fn hover(editor: &mut Editor, _: &Hover, cx: &mut ViewContext<Editor>) {
-    let head = editor.selections.newest_display(cx).head();
-    show_hover(editor, head, true, cx);
-}
-
-/// The internal hover action dispatches between `show_hover` or `hide_hover`
-/// depending on whether a point to hover over is provided.
-pub fn hover_at(editor: &mut Editor, point: Option<DisplayPoint>, cx: &mut ViewContext<Editor>) {
-    if EditorSettings::get_global(cx).hover_popover_enabled {
-        if let Some(point) = point {
-            show_hover(editor, point, false, cx);
-        } else {
-            hide_hover(editor, cx);
-        }
-    }
-}
-
-pub struct InlayHover {
-    pub excerpt: ExcerptId,
-    pub range: InlayHighlight,
-    pub tooltip: HoverBlock,
-}
-
-pub fn find_hovered_hint_part(
-    label_parts: Vec<InlayHintLabelPart>,
-    hint_start: InlayOffset,
-    hovered_offset: InlayOffset,
-) -> Option<(InlayHintLabelPart, Range<InlayOffset>)> {
-    if hovered_offset >= hint_start {
-        let mut hovered_character = (hovered_offset - hint_start).0;
-        let mut part_start = hint_start;
-        for part in label_parts {
-            let part_len = part.value.chars().count();
-            if hovered_character > part_len {
-                hovered_character -= part_len;
-                part_start.0 += part_len;
-            } else {
-                let part_end = InlayOffset(part_start.0 + part_len);
-                return Some((part, part_start..part_end));
-            }
-        }
-    }
-    None
-}
-
-pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut ViewContext<Editor>) {
-    if EditorSettings::get_global(cx).hover_popover_enabled {
-        if editor.pending_rename.is_some() {
-            return;
-        }
-
-        let Some(project) = editor.project.clone() else {
-            return;
-        };
-
-        if let Some(InfoPopover { symbol_range, .. }) = &editor.hover_state.info_popover {
-            if let RangeInEditor::Inlay(range) = symbol_range {
-                if range == &inlay_hover.range {
-                    // Hover triggered from same location as last time. Don't show again.
-                    return;
-                }
-            }
-            hide_hover(editor, cx);
-        }
-
-        let task = cx.spawn(|this, mut cx| {
-            async move {
-                cx.background_executor()
-                    .timer(Duration::from_millis(HOVER_DELAY_MILLIS))
-                    .await;
-                this.update(&mut cx, |this, _| {
-                    this.hover_state.diagnostic_popover = None;
-                })?;
-
-                let language_registry = project.update(&mut cx, |p, _| p.languages().clone())?;
-                let blocks = vec![inlay_hover.tooltip];
-                let parsed_content = parse_blocks(&blocks, &language_registry, None).await;
-
-                let hover_popover = InfoPopover {
-                    project: project.clone(),
-                    symbol_range: RangeInEditor::Inlay(inlay_hover.range.clone()),
-                    blocks,
-                    parsed_content,
-                };
-
-                this.update(&mut cx, |this, cx| {
-                    // Highlight the selected symbol using a background highlight
-                    this.highlight_inlay_background::<HoverState>(
-                        vec![inlay_hover.range],
-                        |theme| theme.element_hover, // todo!("use a proper background here")
-                        cx,
-                    );
-                    this.hover_state.info_popover = Some(hover_popover);
-                    cx.notify();
-                })?;
-
-                anyhow::Ok(())
-            }
-            .log_err()
-        });
-
-        editor.hover_state.info_task = Some(task);
-    }
-}
-
-/// Hides the type information popup.
-/// Triggered by the `Hover` action when the cursor is not over a symbol or when the
-/// selections changed.
-pub fn hide_hover(editor: &mut Editor, cx: &mut ViewContext<Editor>) -> bool {
-    let did_hide = editor.hover_state.info_popover.take().is_some()
-        | editor.hover_state.diagnostic_popover.take().is_some();
-
-    editor.hover_state.info_task = None;
-    editor.hover_state.triggered_from = None;
-
-    editor.clear_background_highlights::<HoverState>(cx);
-
-    if did_hide {
-        cx.notify();
-    }
-
-    did_hide
-}
-
-/// Queries the LSP and shows type info and documentation
-/// about the symbol the mouse is currently hovering over.
-/// Triggered by the `Hover` action when the cursor may be over a symbol.
-fn show_hover(
-    editor: &mut Editor,
-    point: DisplayPoint,
-    ignore_timeout: bool,
-    cx: &mut ViewContext<Editor>,
-) {
-    if editor.pending_rename.is_some() {
-        return;
-    }
-
-    let snapshot = editor.snapshot(cx);
-    let multibuffer_offset = point.to_offset(&snapshot.display_snapshot, Bias::Left);
-
-    let (buffer, buffer_position) = if let Some(output) = editor
-        .buffer
-        .read(cx)
-        .text_anchor_for_position(multibuffer_offset, cx)
-    {
-        output
-    } else {
-        return;
-    };
-
-    let excerpt_id = if let Some((excerpt_id, _, _)) = editor
-        .buffer()
-        .read(cx)
-        .excerpt_containing(multibuffer_offset, cx)
-    {
-        excerpt_id
-    } else {
-        return;
-    };
-
-    let project = if let Some(project) = editor.project.clone() {
-        project
-    } else {
-        return;
-    };
-
-    if !ignore_timeout {
-        if let Some(InfoPopover { symbol_range, .. }) = &editor.hover_state.info_popover {
-            if symbol_range
-                .as_text_range()
-                .map(|range| {
-                    range
-                        .to_offset(&snapshot.buffer_snapshot)
-                        .contains(&multibuffer_offset)
-                })
-                .unwrap_or(false)
-            {
-                // Hover triggered from same location as last time. Don't show again.
-                return;
-            } else {
-                hide_hover(editor, cx);
-            }
-        }
-    }
-
-    // Get input anchor
-    let anchor = snapshot
-        .buffer_snapshot
-        .anchor_at(multibuffer_offset, Bias::Left);
-
-    // Don't request again if the location is the same as the previous request
-    if let Some(triggered_from) = &editor.hover_state.triggered_from {
-        if triggered_from
-            .cmp(&anchor, &snapshot.buffer_snapshot)
-            .is_eq()
-        {
-            return;
-        }
-    }
-
-    let task = cx.spawn(|this, mut cx| {
-        async move {
-            // If we need to delay, delay a set amount initially before making the lsp request
-            let delay = if !ignore_timeout {
-                // Construct delay task to wait for later
-                let total_delay = Some(
-                    cx.background_executor()
-                        .timer(Duration::from_millis(HOVER_DELAY_MILLIS)),
-                );
-
-                cx.background_executor()
-                    .timer(Duration::from_millis(HOVER_REQUEST_DELAY_MILLIS))
-                    .await;
-                total_delay
-            } else {
-                None
-            };
-
-            // query the LSP for hover info
-            let hover_request = cx.update(|_, cx| {
-                project.update(cx, |project, cx| {
-                    project.hover(&buffer, buffer_position, cx)
-                })
-            })?;
-
-            if let Some(delay) = delay {
-                delay.await;
-            }
-
-            // If there's a diagnostic, assign it on the hover state and notify
-            let local_diagnostic = snapshot
-                .buffer_snapshot
-                .diagnostics_in_range::<_, usize>(multibuffer_offset..multibuffer_offset, false)
-                // Find the entry with the most specific range
-                .min_by_key(|entry| entry.range.end - entry.range.start)
-                .map(|entry| DiagnosticEntry {
-                    diagnostic: entry.diagnostic,
-                    range: entry.range.to_anchors(&snapshot.buffer_snapshot),
-                });
-
-            // Pull the primary diagnostic out so we can jump to it if the popover is clicked
-            let primary_diagnostic = local_diagnostic.as_ref().and_then(|local_diagnostic| {
-                snapshot
-                    .buffer_snapshot
-                    .diagnostic_group::<usize>(local_diagnostic.diagnostic.group_id)
-                    .find(|diagnostic| diagnostic.diagnostic.is_primary)
-                    .map(|entry| DiagnosticEntry {
-                        diagnostic: entry.diagnostic,
-                        range: entry.range.to_anchors(&snapshot.buffer_snapshot),
-                    })
-            });
-
-            this.update(&mut cx, |this, _| {
-                this.hover_state.diagnostic_popover =
-                    local_diagnostic.map(|local_diagnostic| DiagnosticPopover {
-                        local_diagnostic,
-                        primary_diagnostic,
-                    });
-            })?;
-
-            let hover_result = hover_request.await.ok().flatten();
-            let hover_popover = match hover_result {
-                Some(hover_result) if !hover_result.is_empty() => {
-                    // Create symbol range of anchors for highlighting and filtering of future requests.
-                    let range = if let Some(range) = hover_result.range {
-                        let start = snapshot
-                            .buffer_snapshot
-                            .anchor_in_excerpt(excerpt_id.clone(), range.start);
-                        let end = snapshot
-                            .buffer_snapshot
-                            .anchor_in_excerpt(excerpt_id.clone(), range.end);
-
-                        start..end
-                    } else {
-                        anchor..anchor
-                    };
-
-                    let language_registry =
-                        project.update(&mut cx, |p, _| p.languages().clone())?;
-                    let blocks = hover_result.contents;
-                    let language = hover_result.language;
-                    let parsed_content = parse_blocks(&blocks, &language_registry, language).await;
-
-                    Some(InfoPopover {
-                        project: project.clone(),
-                        symbol_range: RangeInEditor::Text(range),
-                        blocks,
-                        parsed_content,
-                    })
-                }
-
-                _ => None,
-            };
-
-            this.update(&mut cx, |this, cx| {
-                if let Some(symbol_range) = hover_popover
-                    .as_ref()
-                    .and_then(|hover_popover| hover_popover.symbol_range.as_text_range())
-                {
-                    // Highlight the selected symbol using a background highlight
-                    this.highlight_background::<HoverState>(
-                        vec![symbol_range],
-                        |theme| theme.element_hover, // todo! update theme
-                        cx,
-                    );
-                } else {
-                    this.clear_background_highlights::<HoverState>(cx);
-                }
-
-                this.hover_state.info_popover = hover_popover;
-                cx.notify();
-            })?;
-
-            Ok::<_, anyhow::Error>(())
-        }
-        .log_err()
-    });
-
-    editor.hover_state.info_task = Some(task);
-}
-
-async fn parse_blocks(
-    blocks: &[HoverBlock],
-    language_registry: &Arc<LanguageRegistry>,
-    language: Option<Arc<Language>>,
-) -> markdown::ParsedMarkdown {
-    let mut text = String::new();
-    let mut highlights = Vec::new();
-    let mut region_ranges = Vec::new();
-    let mut regions = Vec::new();
-
-    for block in blocks {
-        match &block.kind {
-            HoverBlockKind::PlainText => {
-                markdown::new_paragraph(&mut text, &mut Vec::new());
-                text.push_str(&block.text);
-            }
-
-            HoverBlockKind::Markdown => {
-                markdown::parse_markdown_block(
-                    &block.text,
-                    language_registry,
-                    language.clone(),
-                    &mut text,
-                    &mut highlights,
-                    &mut region_ranges,
-                    &mut regions,
-                )
-                .await
-            }
-
-            HoverBlockKind::Code { language } => {
-                if let Some(language) = language_registry
-                    .language_for_name(language)
-                    .now_or_never()
-                    .and_then(Result::ok)
-                {
-                    markdown::highlight_code(&mut text, &mut highlights, &block.text, &language);
-                } else {
-                    text.push_str(&block.text);
-                }
-            }
-        }
-    }
-
-    ParsedMarkdown {
-        text: text.trim().to_string(),
-        highlights,
-        region_ranges,
-        regions,
-    }
-}
-
-#[derive(Default)]
-pub struct HoverState {
-    pub info_popover: Option<InfoPopover>,
-    pub diagnostic_popover: Option<DiagnosticPopover>,
-    pub triggered_from: Option<Anchor>,
-    pub info_task: Option<Task<Option<()>>>,
-}
-
-impl HoverState {
-    pub fn visible(&self) -> bool {
-        self.info_popover.is_some() || self.diagnostic_popover.is_some()
-    }
-
-    pub fn render(
-        &mut self,
-        snapshot: &EditorSnapshot,
-        style: &EditorStyle,
-        visible_rows: Range<u32>,
-        max_size: Size<Pixels>,
-        workspace: Option<WeakView<Workspace>>,
-        cx: &mut ViewContext<Editor>,
-    ) -> Option<(DisplayPoint, Vec<AnyElement>)> {
-        // If there is a diagnostic, position the popovers based on that.
-        // Otherwise use the start of the hover range
-        let anchor = self
-            .diagnostic_popover
-            .as_ref()
-            .map(|diagnostic_popover| &diagnostic_popover.local_diagnostic.range.start)
-            .or_else(|| {
-                self.info_popover
-                    .as_ref()
-                    .map(|info_popover| match &info_popover.symbol_range {
-                        RangeInEditor::Text(range) => &range.start,
-                        RangeInEditor::Inlay(range) => &range.inlay_position,
-                    })
-            })?;
-        let point = anchor.to_display_point(&snapshot.display_snapshot);
-
-        // Don't render if the relevant point isn't on screen
-        if !self.visible() || !visible_rows.contains(&point.row()) {
-            return None;
-        }
-
-        let mut elements = Vec::new();
-
-        if let Some(diagnostic_popover) = self.diagnostic_popover.as_ref() {
-            elements.push(diagnostic_popover.render(style, max_size, cx));
-        }
-        if let Some(info_popover) = self.info_popover.as_mut() {
-            elements.push(info_popover.render(style, max_size, workspace, cx));
-        }
-
-        Some((point, elements))
-    }
-}
-
-#[derive(Debug, Clone)]
-pub struct InfoPopover {
-    pub project: Model<Project>,
-    symbol_range: RangeInEditor,
-    pub blocks: Vec<HoverBlock>,
-    parsed_content: ParsedMarkdown,
-}
-
-impl InfoPopover {
-    pub fn render(
-        &mut self,
-        style: &EditorStyle,
-        max_size: Size<Pixels>,
-        workspace: Option<WeakView<Workspace>>,
-        cx: &mut ViewContext<Editor>,
-    ) -> AnyElement {
-        div()
-            .id("info_popover")
-            .elevation_2(cx)
-            .p_2()
-            .overflow_y_scroll()
-            .max_w(max_size.width)
-            .max_h(max_size.height)
-            // Prevent a mouse move on the popover from being propagated to the editor,
-            // because that would dismiss the popover.
-            .on_mouse_move(|_, cx| cx.stop_propagation())
-            .child(crate::render_parsed_markdown(
-                "content",
-                &self.parsed_content,
-                style,
-                workspace,
-                cx,
-            ))
-            .into_any_element()
-    }
-}
-
-#[derive(Debug, Clone)]
-pub struct DiagnosticPopover {
-    local_diagnostic: DiagnosticEntry<Anchor>,
-    primary_diagnostic: Option<DiagnosticEntry<Anchor>>,
-}
-
-impl DiagnosticPopover {
-    pub fn render(
-        &self,
-        style: &EditorStyle,
-        max_size: Size<Pixels>,
-        cx: &mut ViewContext<Editor>,
-    ) -> AnyElement {
-        let text = match &self.local_diagnostic.diagnostic.source {
-            Some(source) => format!("{source}: {}", self.local_diagnostic.diagnostic.message),
-            None => self.local_diagnostic.diagnostic.message.clone(),
-        };
-
-        struct DiagnosticColors {
-            pub text: Hsla,
-            pub background: Hsla,
-            pub border: Hsla,
-        }
-
-        let diagnostic_colors = match self.local_diagnostic.diagnostic.severity {
-            DiagnosticSeverity::ERROR => DiagnosticColors {
-                text: style.status.error,
-                background: style.status.error_background,
-                border: style.status.error_border,
-            },
-            DiagnosticSeverity::WARNING => DiagnosticColors {
-                text: style.status.warning,
-                background: style.status.warning_background,
-                border: style.status.warning_border,
-            },
-            DiagnosticSeverity::INFORMATION => DiagnosticColors {
-                text: style.status.info,
-                background: style.status.info_background,
-                border: style.status.info_border,
-            },
-            DiagnosticSeverity::HINT => DiagnosticColors {
-                text: style.status.hint,
-                background: style.status.hint_background,
-                border: style.status.hint_border,
-            },
-            _ => DiagnosticColors {
-                text: style.status.ignored,
-                background: style.status.ignored_background,
-                border: style.status.ignored_border,
-            },
-        };
-
-        div()
-            .id("diagnostic")
-            .overflow_y_scroll()
-            .px_2()
-            .py_1()
-            .bg(diagnostic_colors.background)
-            .text_color(diagnostic_colors.text)
-            .border_1()
-            .border_color(diagnostic_colors.border)
-            .rounded_md()
-            .max_w(max_size.width)
-            .max_h(max_size.height)
-            .cursor(CursorStyle::PointingHand)
-            .tooltip(move |cx| Tooltip::for_action("Go To Diagnostic", &crate::GoToDiagnostic, cx))
-            // Prevent a mouse move on the popover from being propagated to the editor,
-            // because that would dismiss the popover.
-            .on_mouse_move(|_, cx| cx.stop_propagation())
-            // Prevent a mouse down on the popover from being propagated to the editor,
-            // because that would move the cursor.
-            .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
-            .on_click(cx.listener(|editor, _, cx| editor.go_to_diagnostic(&Default::default(), cx)))
-            .child(SharedString::from(text))
-            .into_any_element()
-    }
-
-    pub fn activation_info(&self) -> (usize, Anchor) {
-        let entry = self
-            .primary_diagnostic
-            .as_ref()
-            .unwrap_or(&self.local_diagnostic);
-
-        (entry.diagnostic.group_id, entry.range.start.clone())
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{
-        editor_tests::init_test,
-        element::PointForPosition,
-        inlay_hint_cache::tests::{cached_hint_labels, visible_hint_labels},
-        link_go_to_definition::update_inlay_link_and_hover_points,
-        test::editor_lsp_test_context::EditorLspTestContext,
-        InlayId,
-    };
-    use collections::BTreeSet;
-    use gpui::{FontWeight, HighlightStyle, UnderlineStyle};
-    use indoc::indoc;
-    use language::{language_settings::InlayHintSettings, Diagnostic, DiagnosticSet};
-    use lsp::LanguageServerId;
-    use project::{HoverBlock, HoverBlockKind};
-    use smol::stream::StreamExt;
-    use unindent::Unindent;
-    use util::test::marked_text_ranges;
-
-    #[gpui::test]
-    async fn test_mouse_hover_info_popover(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-
-        // Basic hover delays and then pops without moving the mouse
-        cx.set_state(indoc! {"
-            fn ห‡test() { println!(); }
-        "});
-        let hover_point = cx.display_point(indoc! {"
-            fn test() { printห‡ln!(); }
-        "});
-
-        cx.update_editor(|editor, cx| hover_at(editor, Some(hover_point), cx));
-        assert!(!cx.editor(|editor, _| editor.hover_state.visible()));
-
-        // After delay, hover should be visible.
-        let symbol_range = cx.lsp_range(indoc! {"
-            fn test() { ยซprintln!ยป(); }
-        "});
-        let mut requests =
-            cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
-                Ok(Some(lsp::Hover {
-                    contents: lsp::HoverContents::Markup(lsp::MarkupContent {
-                        kind: lsp::MarkupKind::Markdown,
-                        value: "some basic docs".to_string(),
-                    }),
-                    range: Some(symbol_range),
-                }))
-            });
-        cx.background_executor
-            .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
-        requests.next().await;
-
-        cx.editor(|editor, _| {
-            assert!(editor.hover_state.visible());
-            assert_eq!(
-                editor.hover_state.info_popover.clone().unwrap().blocks,
-                vec![HoverBlock {
-                    text: "some basic docs".to_string(),
-                    kind: HoverBlockKind::Markdown,
-                },]
-            )
-        });
-
-        // Mouse moved with no hover response dismisses
-        let hover_point = cx.display_point(indoc! {"
-            fn teห‡st() { println!(); }
-        "});
-        let mut request = cx
-            .lsp
-            .handle_request::<lsp::request::HoverRequest, _, _>(|_, _| async move { Ok(None) });
-        cx.update_editor(|editor, cx| hover_at(editor, Some(hover_point), cx));
-        cx.background_executor
-            .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
-        request.next().await;
-        cx.editor(|editor, _| {
-            assert!(!editor.hover_state.visible());
-        });
-    }
-
-    #[gpui::test]
-    async fn test_keyboard_hover_info_popover(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-
-        // Hover with keyboard has no delay
-        cx.set_state(indoc! {"
-            fห‡n test() { println!(); }
-        "});
-        cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
-        let symbol_range = cx.lsp_range(indoc! {"
-            ยซfnยป test() { println!(); }
-        "});
-        cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
-            Ok(Some(lsp::Hover {
-                contents: lsp::HoverContents::Markup(lsp::MarkupContent {
-                    kind: lsp::MarkupKind::Markdown,
-                    value: "some other basic docs".to_string(),
-                }),
-                range: Some(symbol_range),
-            }))
-        })
-        .next()
-        .await;
-
-        cx.condition(|editor, _| editor.hover_state.visible()).await;
-        cx.editor(|editor, _| {
-            assert_eq!(
-                editor.hover_state.info_popover.clone().unwrap().blocks,
-                vec![HoverBlock {
-                    text: "some other basic docs".to_string(),
-                    kind: HoverBlockKind::Markdown,
-                }]
-            )
-        });
-    }
-
-    #[gpui::test]
-    async fn test_empty_hovers_filtered(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-
-        // Hover with keyboard has no delay
-        cx.set_state(indoc! {"
-            fห‡n test() { println!(); }
-        "});
-        cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
-        let symbol_range = cx.lsp_range(indoc! {"
-            ยซfnยป test() { println!(); }
-        "});
-        cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
-            Ok(Some(lsp::Hover {
-                contents: lsp::HoverContents::Array(vec![
-                    lsp::MarkedString::String("regular text for hover to show".to_string()),
-                    lsp::MarkedString::String("".to_string()),
-                    lsp::MarkedString::LanguageString(lsp::LanguageString {
-                        language: "Rust".to_string(),
-                        value: "".to_string(),
-                    }),
-                ]),
-                range: Some(symbol_range),
-            }))
-        })
-        .next()
-        .await;
-
-        cx.condition(|editor, _| editor.hover_state.visible()).await;
-        cx.editor(|editor, _| {
-            assert_eq!(
-                editor.hover_state.info_popover.clone().unwrap().blocks,
-                vec![HoverBlock {
-                    text: "regular text for hover to show".to_string(),
-                    kind: HoverBlockKind::Markdown,
-                }],
-                "No empty string hovers should be shown"
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_line_ends_trimmed(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-
-        // Hover with keyboard has no delay
-        cx.set_state(indoc! {"
-            fห‡n test() { println!(); }
-        "});
-        cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
-        let symbol_range = cx.lsp_range(indoc! {"
-            ยซfnยป test() { println!(); }
-        "});
-
-        let code_str = "\nlet hovered_point: Vector2F // size = 8, align = 0x4\n";
-        let markdown_string = format!("\n```rust\n{code_str}```");
-
-        let closure_markdown_string = markdown_string.clone();
-        cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| {
-            let future_markdown_string = closure_markdown_string.clone();
-            async move {
-                Ok(Some(lsp::Hover {
-                    contents: lsp::HoverContents::Markup(lsp::MarkupContent {
-                        kind: lsp::MarkupKind::Markdown,
-                        value: future_markdown_string,
-                    }),
-                    range: Some(symbol_range),
-                }))
-            }
-        })
-        .next()
-        .await;
-
-        cx.condition(|editor, _| editor.hover_state.visible()).await;
-        cx.editor(|editor, _| {
-            let blocks = editor.hover_state.info_popover.clone().unwrap().blocks;
-            assert_eq!(
-                blocks,
-                vec![HoverBlock {
-                    text: markdown_string,
-                    kind: HoverBlockKind::Markdown,
-                }],
-            );
-
-            let rendered = smol::block_on(parse_blocks(&blocks, &Default::default(), None));
-            assert_eq!(
-                rendered.text,
-                code_str.trim(),
-                "Should not have extra line breaks at end of rendered hover"
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_hover_diagnostic_and_info_popovers(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-
-        // Hover with just diagnostic, pops DiagnosticPopover immediately and then
-        // info popover once request completes
-        cx.set_state(indoc! {"
-            fn teห‡st() { println!(); }
-        "});
-
-        // Send diagnostic to client
-        let range = cx.text_anchor_range(indoc! {"
-            fn ยซtestยป() { println!(); }
-        "});
-        cx.update_buffer(|buffer, cx| {
-            let snapshot = buffer.text_snapshot();
-            let set = DiagnosticSet::from_sorted_entries(
-                vec![DiagnosticEntry {
-                    range,
-                    diagnostic: Diagnostic {
-                        message: "A test diagnostic message.".to_string(),
-                        ..Default::default()
-                    },
-                }],
-                &snapshot,
-            );
-            buffer.update_diagnostics(LanguageServerId(0), set, cx);
-        });
-
-        // Hover pops diagnostic immediately
-        cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
-        cx.background_executor.run_until_parked();
-
-        cx.editor(|Editor { hover_state, .. }, _| {
-            assert!(hover_state.diagnostic_popover.is_some() && hover_state.info_popover.is_none())
-        });
-
-        // Info Popover shows after request responded to
-        let range = cx.lsp_range(indoc! {"
-            fn ยซtestยป() { println!(); }
-        "});
-        cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
-            Ok(Some(lsp::Hover {
-                contents: lsp::HoverContents::Markup(lsp::MarkupContent {
-                    kind: lsp::MarkupKind::Markdown,
-                    value: "some new docs".to_string(),
-                }),
-                range: Some(range),
-            }))
-        });
-        cx.background_executor
-            .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
-
-        cx.background_executor.run_until_parked();
-        cx.editor(|Editor { hover_state, .. }, _| {
-            hover_state.diagnostic_popover.is_some() && hover_state.info_task.is_some()
-        });
-    }
-
-    #[gpui::test]
-    fn test_render_blocks(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
-
-        let editor = cx.add_window(|cx| Editor::single_line(cx));
-        editor
-            .update(cx, |editor, _cx| {
-                let style = editor.style.clone().unwrap();
-
-                struct Row {
-                    blocks: Vec<HoverBlock>,
-                    expected_marked_text: String,
-                    expected_styles: Vec<HighlightStyle>,
-                }
-
-                let rows = &[
-                    // Strong emphasis
-                    Row {
-                        blocks: vec![HoverBlock {
-                            text: "one **two** three".to_string(),
-                            kind: HoverBlockKind::Markdown,
-                        }],
-                        expected_marked_text: "one ยซtwoยป three".to_string(),
-                        expected_styles: vec![HighlightStyle {
-                            font_weight: Some(FontWeight::BOLD),
-                            ..Default::default()
-                        }],
-                    },
-                    // Links
-                    Row {
-                        blocks: vec![HoverBlock {
-                            text: "one [two](https://the-url) three".to_string(),
-                            kind: HoverBlockKind::Markdown,
-                        }],
-                        expected_marked_text: "one ยซtwoยป three".to_string(),
-                        expected_styles: vec![HighlightStyle {
-                            underline: Some(UnderlineStyle {
-                                thickness: 1.0.into(),
-                                ..Default::default()
-                            }),
-                            ..Default::default()
-                        }],
-                    },
-                    // Lists
-                    Row {
-                        blocks: vec![HoverBlock {
-                            text: "
-                            lists:
-                            * one
-                                - a
-                                - b
-                            * two
-                                - [c](https://the-url)
-                                - d"
-                            .unindent(),
-                            kind: HoverBlockKind::Markdown,
-                        }],
-                        expected_marked_text: "
-                        lists:
-                        - one
-                          - a
-                          - b
-                        - two
-                          - ยซcยป
-                          - d"
-                        .unindent(),
-                        expected_styles: vec![HighlightStyle {
-                            underline: Some(UnderlineStyle {
-                                thickness: 1.0.into(),
-                                ..Default::default()
-                            }),
-                            ..Default::default()
-                        }],
-                    },
-                    // Multi-paragraph list items
-                    Row {
-                        blocks: vec![HoverBlock {
-                            text: "
-                            * one two
-                              three
-
-                            * four five
-                                * six seven
-                                  eight
-
-                                  nine
-                                * ten
-                            * six"
-                                .unindent(),
-                            kind: HoverBlockKind::Markdown,
-                        }],
-                        expected_marked_text: "
-                        - one two three
-                        - four five
-                          - six seven eight
-
-                            nine
-                          - ten
-                        - six"
-                            .unindent(),
-                        expected_styles: vec![HighlightStyle {
-                            underline: Some(UnderlineStyle {
-                                thickness: 1.0.into(),
-                                ..Default::default()
-                            }),
-                            ..Default::default()
-                        }],
-                    },
-                ];
-
-                for Row {
-                    blocks,
-                    expected_marked_text,
-                    expected_styles,
-                } in &rows[0..]
-                {
-                    let rendered = smol::block_on(parse_blocks(&blocks, &Default::default(), None));
-
-                    let (expected_text, ranges) = marked_text_ranges(expected_marked_text, false);
-                    let expected_highlights = ranges
-                        .into_iter()
-                        .zip(expected_styles.iter().cloned())
-                        .collect::<Vec<_>>();
-                    assert_eq!(
-                        rendered.text, expected_text,
-                        "wrong text for input {blocks:?}"
-                    );
-
-                    let rendered_highlights: Vec<_> = rendered
-                        .highlights
-                        .iter()
-                        .filter_map(|(range, highlight)| {
-                            let highlight = highlight.to_highlight_style(&style.syntax)?;
-                            Some((range.clone(), highlight))
-                        })
-                        .collect();
-
-                    assert_eq!(
-                        rendered_highlights, expected_highlights,
-                        "wrong highlights for input {blocks:?}"
-                    );
-                }
-            })
-            .unwrap();
-    }
-
-    #[gpui::test]
-    async fn test_hover_inlay_label_parts(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                inlay_hint_provider: Some(lsp::OneOf::Right(
-                    lsp::InlayHintServerCapabilities::Options(lsp::InlayHintOptions {
-                        resolve_provider: Some(true),
-                        ..Default::default()
-                    }),
-                )),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-
-        cx.set_state(indoc! {"
-            struct TestStruct;
-
-            // ==================
-
-            struct TestNewType<T>(T);
-
-            fn main() {
-                let variableห‡ = TestNewType(TestStruct);
-            }
-        "});
-
-        let hint_start_offset = cx.ranges(indoc! {"
-            struct TestStruct;
-
-            // ==================
-
-            struct TestNewType<T>(T);
-
-            fn main() {
-                let variableห‡ = TestNewType(TestStruct);
-            }
-        "})[0]
-            .start;
-        let hint_position = cx.to_lsp(hint_start_offset);
-        let new_type_target_range = cx.lsp_range(indoc! {"
-            struct TestStruct;
-
-            // ==================
-
-            struct ยซTestNewTypeยป<T>(T);
-
-            fn main() {
-                let variable = TestNewType(TestStruct);
-            }
-        "});
-        let struct_target_range = cx.lsp_range(indoc! {"
-            struct ยซTestStructยป;
-
-            // ==================
-
-            struct TestNewType<T>(T);
-
-            fn main() {
-                let variable = TestNewType(TestStruct);
-            }
-        "});
-
-        let uri = cx.buffer_lsp_url.clone();
-        let new_type_label = "TestNewType";
-        let struct_label = "TestStruct";
-        let entire_hint_label = ": TestNewType<TestStruct>";
-        let closure_uri = uri.clone();
-        cx.lsp
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_uri = closure_uri.clone();
-                async move {
-                    assert_eq!(params.text_document.uri, task_uri);
-                    Ok(Some(vec![lsp::InlayHint {
-                        position: hint_position,
-                        label: lsp::InlayHintLabel::LabelParts(vec![lsp::InlayHintLabelPart {
-                            value: entire_hint_label.to_string(),
-                            ..Default::default()
-                        }]),
-                        kind: Some(lsp::InlayHintKind::TYPE),
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: Some(false),
-                        padding_right: Some(false),
-                        data: None,
-                    }]))
-                }
-            })
-            .next()
-            .await;
-        cx.background_executor.run_until_parked();
-        cx.update_editor(|editor, cx| {
-            let expected_layers = vec![entire_hint_label.to_string()];
-            assert_eq!(expected_layers, cached_hint_labels(editor));
-            assert_eq!(expected_layers, visible_hint_labels(editor, cx));
-        });
-
-        let inlay_range = cx
-            .ranges(indoc! {"
-                struct TestStruct;
-
-                // ==================
-
-                struct TestNewType<T>(T);
-
-                fn main() {
-                    let variableยซ ยป= TestNewType(TestStruct);
-                }
-        "})
-            .get(0)
-            .cloned()
-            .unwrap();
-        let new_type_hint_part_hover_position = cx.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
-            let previous_valid = inlay_range.start.to_display_point(&snapshot);
-            let next_valid = inlay_range.end.to_display_point(&snapshot);
-            assert_eq!(previous_valid.row(), next_valid.row());
-            assert!(previous_valid.column() < next_valid.column());
-            let exact_unclipped = DisplayPoint::new(
-                previous_valid.row(),
-                previous_valid.column()
-                    + (entire_hint_label.find(new_type_label).unwrap() + new_type_label.len() / 2)
-                        as u32,
-            );
-            PointForPosition {
-                previous_valid,
-                next_valid,
-                exact_unclipped,
-                column_overshoot_after_line_end: 0,
-            }
-        });
-        cx.update_editor(|editor, cx| {
-            update_inlay_link_and_hover_points(
-                &editor.snapshot(cx),
-                new_type_hint_part_hover_position,
-                editor,
-                true,
-                false,
-                cx,
-            );
-        });
-
-        let resolve_closure_uri = uri.clone();
-        cx.lsp
-            .handle_request::<lsp::request::InlayHintResolveRequest, _, _>(
-                move |mut hint_to_resolve, _| {
-                    let mut resolved_hint_positions = BTreeSet::new();
-                    let task_uri = resolve_closure_uri.clone();
-                    async move {
-                        let inserted = resolved_hint_positions.insert(hint_to_resolve.position);
-                        assert!(inserted, "Hint {hint_to_resolve:?} was resolved twice");
-
-                        // `: TestNewType<TestStruct>`
-                        hint_to_resolve.label = lsp::InlayHintLabel::LabelParts(vec![
-                            lsp::InlayHintLabelPart {
-                                value: ": ".to_string(),
-                                ..Default::default()
-                            },
-                            lsp::InlayHintLabelPart {
-                                value: new_type_label.to_string(),
-                                location: Some(lsp::Location {
-                                    uri: task_uri.clone(),
-                                    range: new_type_target_range,
-                                }),
-                                tooltip: Some(lsp::InlayHintLabelPartTooltip::String(format!(
-                                    "A tooltip for `{new_type_label}`"
-                                ))),
-                                ..Default::default()
-                            },
-                            lsp::InlayHintLabelPart {
-                                value: "<".to_string(),
-                                ..Default::default()
-                            },
-                            lsp::InlayHintLabelPart {
-                                value: struct_label.to_string(),
-                                location: Some(lsp::Location {
-                                    uri: task_uri,
-                                    range: struct_target_range,
-                                }),
-                                tooltip: Some(lsp::InlayHintLabelPartTooltip::MarkupContent(
-                                    lsp::MarkupContent {
-                                        kind: lsp::MarkupKind::Markdown,
-                                        value: format!("A tooltip for `{struct_label}`"),
-                                    },
-                                )),
-                                ..Default::default()
-                            },
-                            lsp::InlayHintLabelPart {
-                                value: ">".to_string(),
-                                ..Default::default()
-                            },
-                        ]);
-
-                        Ok(hint_to_resolve)
-                    }
-                },
-            )
-            .next()
-            .await;
-        cx.background_executor.run_until_parked();
-
-        cx.update_editor(|editor, cx| {
-            update_inlay_link_and_hover_points(
-                &editor.snapshot(cx),
-                new_type_hint_part_hover_position,
-                editor,
-                true,
-                false,
-                cx,
-            );
-        });
-        cx.background_executor
-            .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
-        cx.background_executor.run_until_parked();
-        cx.update_editor(|editor, cx| {
-            let hover_state = &editor.hover_state;
-            assert!(hover_state.diagnostic_popover.is_none() && hover_state.info_popover.is_some());
-            let popover = hover_state.info_popover.as_ref().unwrap();
-            let buffer_snapshot = editor.buffer().update(cx, |buffer, cx| buffer.snapshot(cx));
-            assert_eq!(
-                popover.symbol_range,
-                RangeInEditor::Inlay(InlayHighlight {
-                    inlay: InlayId::Hint(0),
-                    inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right),
-                    range: ": ".len()..": ".len() + new_type_label.len(),
-                }),
-                "Popover range should match the new type label part"
-            );
-            assert_eq!(
-                popover.parsed_content.text,
-                format!("A tooltip for `{new_type_label}`"),
-                "Rendered text should not anyhow alter backticks"
-            );
-        });
-
-        let struct_hint_part_hover_position = cx.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
-            let previous_valid = inlay_range.start.to_display_point(&snapshot);
-            let next_valid = inlay_range.end.to_display_point(&snapshot);
-            assert_eq!(previous_valid.row(), next_valid.row());
-            assert!(previous_valid.column() < next_valid.column());
-            let exact_unclipped = DisplayPoint::new(
-                previous_valid.row(),
-                previous_valid.column()
-                    + (entire_hint_label.find(struct_label).unwrap() + struct_label.len() / 2)
-                        as u32,
-            );
-            PointForPosition {
-                previous_valid,
-                next_valid,
-                exact_unclipped,
-                column_overshoot_after_line_end: 0,
-            }
-        });
-        cx.update_editor(|editor, cx| {
-            update_inlay_link_and_hover_points(
-                &editor.snapshot(cx),
-                struct_hint_part_hover_position,
-                editor,
-                true,
-                false,
-                cx,
-            );
-        });
-        cx.background_executor
-            .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
-        cx.background_executor.run_until_parked();
-        cx.update_editor(|editor, cx| {
-            let hover_state = &editor.hover_state;
-            assert!(hover_state.diagnostic_popover.is_none() && hover_state.info_popover.is_some());
-            let popover = hover_state.info_popover.as_ref().unwrap();
-            let buffer_snapshot = editor.buffer().update(cx, |buffer, cx| buffer.snapshot(cx));
-            assert_eq!(
-                popover.symbol_range,
-                RangeInEditor::Inlay(InlayHighlight {
-                    inlay: InlayId::Hint(0),
-                    inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right),
-                    range: ": ".len() + new_type_label.len() + "<".len()
-                        ..": ".len() + new_type_label.len() + "<".len() + struct_label.len(),
-                }),
-                "Popover range should match the struct label part"
-            );
-            assert_eq!(
-                popover.parsed_content.text,
-                format!("A tooltip for {struct_label}"),
-                "Rendered markdown element should remove backticks from text"
-            );
-        });
-    }
-}

crates/editor2/src/inlay_hint_cache.rs ๐Ÿ”—

@@ -1,3268 +0,0 @@
-use std::{
-    cmp,
-    ops::{ControlFlow, Range},
-    sync::Arc,
-    time::Duration,
-};
-
-use crate::{
-    display_map::Inlay, Anchor, Editor, ExcerptId, InlayId, MultiBuffer, MultiBufferSnapshot,
-};
-use anyhow::Context;
-use clock::Global;
-use futures::future;
-use gpui::{Model, ModelContext, Task, ViewContext};
-use language::{language_settings::InlayHintKind, Buffer, BufferSnapshot};
-use parking_lot::RwLock;
-use project::{InlayHint, ResolveState};
-
-use collections::{hash_map, HashMap, HashSet};
-use language::language_settings::InlayHintSettings;
-use smol::lock::Semaphore;
-use sum_tree::Bias;
-use text::{ToOffset, ToPoint};
-use util::post_inc;
-
-pub struct InlayHintCache {
-    hints: HashMap<ExcerptId, Arc<RwLock<CachedExcerptHints>>>,
-    allowed_hint_kinds: HashSet<Option<InlayHintKind>>,
-    version: usize,
-    pub(super) enabled: bool,
-    update_tasks: HashMap<ExcerptId, TasksForRanges>,
-    lsp_request_limiter: Arc<Semaphore>,
-}
-
-#[derive(Debug)]
-struct TasksForRanges {
-    tasks: Vec<Task<()>>,
-    sorted_ranges: Vec<Range<language::Anchor>>,
-}
-
-#[derive(Debug)]
-pub struct CachedExcerptHints {
-    version: usize,
-    buffer_version: Global,
-    buffer_id: u64,
-    ordered_hints: Vec<InlayId>,
-    hints_by_id: HashMap<InlayId, InlayHint>,
-}
-
-#[derive(Debug, Clone, Copy)]
-pub enum InvalidationStrategy {
-    RefreshRequested,
-    BufferEdited,
-    None,
-}
-
-#[derive(Debug, Default)]
-pub struct InlaySplice {
-    pub to_remove: Vec<InlayId>,
-    pub to_insert: Vec<Inlay>,
-}
-
-#[derive(Debug)]
-struct ExcerptHintsUpdate {
-    excerpt_id: ExcerptId,
-    remove_from_visible: Vec<InlayId>,
-    remove_from_cache: HashSet<InlayId>,
-    add_to_cache: Vec<InlayHint>,
-}
-
-#[derive(Debug, Clone, Copy)]
-struct ExcerptQuery {
-    buffer_id: u64,
-    excerpt_id: ExcerptId,
-    cache_version: usize,
-    invalidate: InvalidationStrategy,
-    reason: &'static str,
-}
-
-impl InvalidationStrategy {
-    fn should_invalidate(&self) -> bool {
-        matches!(
-            self,
-            InvalidationStrategy::RefreshRequested | InvalidationStrategy::BufferEdited
-        )
-    }
-}
-
-impl TasksForRanges {
-    fn new(query_ranges: QueryRanges, task: Task<()>) -> Self {
-        let mut sorted_ranges = Vec::new();
-        sorted_ranges.extend(query_ranges.before_visible);
-        sorted_ranges.extend(query_ranges.visible);
-        sorted_ranges.extend(query_ranges.after_visible);
-        Self {
-            tasks: vec![task],
-            sorted_ranges,
-        }
-    }
-
-    fn update_cached_tasks(
-        &mut self,
-        buffer_snapshot: &BufferSnapshot,
-        query_ranges: QueryRanges,
-        invalidate: InvalidationStrategy,
-        spawn_task: impl FnOnce(QueryRanges) -> Task<()>,
-    ) {
-        let query_ranges = if invalidate.should_invalidate() {
-            self.tasks.clear();
-            self.sorted_ranges.clear();
-            query_ranges
-        } else {
-            let mut non_cached_query_ranges = query_ranges;
-            non_cached_query_ranges.before_visible = non_cached_query_ranges
-                .before_visible
-                .into_iter()
-                .flat_map(|query_range| {
-                    self.remove_cached_ranges_from_query(buffer_snapshot, query_range)
-                })
-                .collect();
-            non_cached_query_ranges.visible = non_cached_query_ranges
-                .visible
-                .into_iter()
-                .flat_map(|query_range| {
-                    self.remove_cached_ranges_from_query(buffer_snapshot, query_range)
-                })
-                .collect();
-            non_cached_query_ranges.after_visible = non_cached_query_ranges
-                .after_visible
-                .into_iter()
-                .flat_map(|query_range| {
-                    self.remove_cached_ranges_from_query(buffer_snapshot, query_range)
-                })
-                .collect();
-            non_cached_query_ranges
-        };
-
-        if !query_ranges.is_empty() {
-            self.tasks.push(spawn_task(query_ranges));
-        }
-    }
-
-    fn remove_cached_ranges_from_query(
-        &mut self,
-        buffer_snapshot: &BufferSnapshot,
-        query_range: Range<language::Anchor>,
-    ) -> Vec<Range<language::Anchor>> {
-        let mut ranges_to_query = Vec::new();
-        let mut latest_cached_range = None::<&mut Range<language::Anchor>>;
-        for cached_range in self
-            .sorted_ranges
-            .iter_mut()
-            .skip_while(|cached_range| {
-                cached_range
-                    .end
-                    .cmp(&query_range.start, buffer_snapshot)
-                    .is_lt()
-            })
-            .take_while(|cached_range| {
-                cached_range
-                    .start
-                    .cmp(&query_range.end, buffer_snapshot)
-                    .is_le()
-            })
-        {
-            match latest_cached_range {
-                Some(latest_cached_range) => {
-                    if latest_cached_range.end.offset.saturating_add(1) < cached_range.start.offset
-                    {
-                        ranges_to_query.push(latest_cached_range.end..cached_range.start);
-                        cached_range.start = latest_cached_range.end;
-                    }
-                }
-                None => {
-                    if query_range
-                        .start
-                        .cmp(&cached_range.start, buffer_snapshot)
-                        .is_lt()
-                    {
-                        ranges_to_query.push(query_range.start..cached_range.start);
-                        cached_range.start = query_range.start;
-                    }
-                }
-            }
-            latest_cached_range = Some(cached_range);
-        }
-
-        match latest_cached_range {
-            Some(latest_cached_range) => {
-                if latest_cached_range.end.offset.saturating_add(1) < query_range.end.offset {
-                    ranges_to_query.push(latest_cached_range.end..query_range.end);
-                    latest_cached_range.end = query_range.end;
-                }
-            }
-            None => {
-                ranges_to_query.push(query_range.clone());
-                self.sorted_ranges.push(query_range);
-                self.sorted_ranges
-                    .sort_by(|range_a, range_b| range_a.start.cmp(&range_b.start, buffer_snapshot));
-            }
-        }
-
-        ranges_to_query
-    }
-
-    fn invalidate_range(&mut self, buffer: &BufferSnapshot, range: &Range<language::Anchor>) {
-        self.sorted_ranges = self
-            .sorted_ranges
-            .drain(..)
-            .filter_map(|mut cached_range| {
-                if cached_range.start.cmp(&range.end, buffer).is_gt()
-                    || cached_range.end.cmp(&range.start, buffer).is_lt()
-                {
-                    Some(vec![cached_range])
-                } else if cached_range.start.cmp(&range.start, buffer).is_ge()
-                    && cached_range.end.cmp(&range.end, buffer).is_le()
-                {
-                    None
-                } else if range.start.cmp(&cached_range.start, buffer).is_ge()
-                    && range.end.cmp(&cached_range.end, buffer).is_le()
-                {
-                    Some(vec![
-                        cached_range.start..range.start,
-                        range.end..cached_range.end,
-                    ])
-                } else if cached_range.start.cmp(&range.start, buffer).is_ge() {
-                    cached_range.start = range.end;
-                    Some(vec![cached_range])
-                } else {
-                    cached_range.end = range.start;
-                    Some(vec![cached_range])
-                }
-            })
-            .flatten()
-            .collect();
-    }
-}
-
-impl InlayHintCache {
-    pub fn new(inlay_hint_settings: InlayHintSettings) -> Self {
-        Self {
-            allowed_hint_kinds: inlay_hint_settings.enabled_inlay_hint_kinds(),
-            enabled: inlay_hint_settings.enabled,
-            hints: HashMap::default(),
-            update_tasks: HashMap::default(),
-            version: 0,
-            lsp_request_limiter: Arc::new(Semaphore::new(MAX_CONCURRENT_LSP_REQUESTS)),
-        }
-    }
-
-    pub fn update_settings(
-        &mut self,
-        multi_buffer: &Model<MultiBuffer>,
-        new_hint_settings: InlayHintSettings,
-        visible_hints: Vec<Inlay>,
-        cx: &mut ViewContext<Editor>,
-    ) -> ControlFlow<Option<InlaySplice>> {
-        let new_allowed_hint_kinds = new_hint_settings.enabled_inlay_hint_kinds();
-        match (self.enabled, new_hint_settings.enabled) {
-            (false, false) => {
-                self.allowed_hint_kinds = new_allowed_hint_kinds;
-                ControlFlow::Break(None)
-            }
-            (true, true) => {
-                if new_allowed_hint_kinds == self.allowed_hint_kinds {
-                    ControlFlow::Break(None)
-                } else {
-                    let new_splice = self.new_allowed_hint_kinds_splice(
-                        multi_buffer,
-                        &visible_hints,
-                        &new_allowed_hint_kinds,
-                        cx,
-                    );
-                    if new_splice.is_some() {
-                        self.version += 1;
-                        self.allowed_hint_kinds = new_allowed_hint_kinds;
-                    }
-                    ControlFlow::Break(new_splice)
-                }
-            }
-            (true, false) => {
-                self.enabled = new_hint_settings.enabled;
-                self.allowed_hint_kinds = new_allowed_hint_kinds;
-                if self.hints.is_empty() {
-                    ControlFlow::Break(None)
-                } else {
-                    self.clear();
-                    ControlFlow::Break(Some(InlaySplice {
-                        to_remove: visible_hints.iter().map(|inlay| inlay.id).collect(),
-                        to_insert: Vec::new(),
-                    }))
-                }
-            }
-            (false, true) => {
-                self.enabled = new_hint_settings.enabled;
-                self.allowed_hint_kinds = new_allowed_hint_kinds;
-                ControlFlow::Continue(())
-            }
-        }
-    }
-
-    pub fn spawn_hint_refresh(
-        &mut self,
-        reason: &'static str,
-        excerpts_to_query: HashMap<ExcerptId, (Model<Buffer>, Global, Range<usize>)>,
-        invalidate: InvalidationStrategy,
-        cx: &mut ViewContext<Editor>,
-    ) -> Option<InlaySplice> {
-        if !self.enabled {
-            return None;
-        }
-
-        let mut invalidated_hints = Vec::new();
-        if invalidate.should_invalidate() {
-            self.update_tasks
-                .retain(|task_excerpt_id, _| excerpts_to_query.contains_key(task_excerpt_id));
-            self.hints.retain(|cached_excerpt, cached_hints| {
-                let retain = excerpts_to_query.contains_key(cached_excerpt);
-                if !retain {
-                    invalidated_hints.extend(cached_hints.read().ordered_hints.iter().copied());
-                }
-                retain
-            });
-        }
-        if excerpts_to_query.is_empty() && invalidated_hints.is_empty() {
-            return None;
-        }
-
-        let cache_version = self.version + 1;
-        cx.spawn(|editor, mut cx| async move {
-            editor
-                .update(&mut cx, |editor, cx| {
-                    spawn_new_update_tasks(
-                        editor,
-                        reason,
-                        excerpts_to_query,
-                        invalidate,
-                        cache_version,
-                        cx,
-                    )
-                })
-                .ok();
-        })
-        .detach();
-
-        if invalidated_hints.is_empty() {
-            None
-        } else {
-            Some(InlaySplice {
-                to_remove: invalidated_hints,
-                to_insert: Vec::new(),
-            })
-        }
-    }
-
-    fn new_allowed_hint_kinds_splice(
-        &self,
-        multi_buffer: &Model<MultiBuffer>,
-        visible_hints: &[Inlay],
-        new_kinds: &HashSet<Option<InlayHintKind>>,
-        cx: &mut ViewContext<Editor>,
-    ) -> Option<InlaySplice> {
-        let old_kinds = &self.allowed_hint_kinds;
-        if new_kinds == old_kinds {
-            return None;
-        }
-
-        let mut to_remove = Vec::new();
-        let mut to_insert = Vec::new();
-        let mut shown_hints_to_remove = visible_hints.iter().fold(
-            HashMap::<ExcerptId, Vec<(Anchor, InlayId)>>::default(),
-            |mut current_hints, inlay| {
-                current_hints
-                    .entry(inlay.position.excerpt_id)
-                    .or_default()
-                    .push((inlay.position, inlay.id));
-                current_hints
-            },
-        );
-
-        let multi_buffer = multi_buffer.read(cx);
-        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
-
-        for (excerpt_id, excerpt_cached_hints) in &self.hints {
-            let shown_excerpt_hints_to_remove =
-                shown_hints_to_remove.entry(*excerpt_id).or_default();
-            let excerpt_cached_hints = excerpt_cached_hints.read();
-            let mut excerpt_cache = excerpt_cached_hints.ordered_hints.iter().fuse().peekable();
-            shown_excerpt_hints_to_remove.retain(|(shown_anchor, shown_hint_id)| {
-                let Some(buffer) = shown_anchor
-                    .buffer_id
-                    .and_then(|buffer_id| multi_buffer.buffer(buffer_id))
-                else {
-                    return false;
-                };
-                let buffer_snapshot = buffer.read(cx).snapshot();
-                loop {
-                    match excerpt_cache.peek() {
-                        Some(&cached_hint_id) => {
-                            let cached_hint = &excerpt_cached_hints.hints_by_id[cached_hint_id];
-                            if cached_hint_id == shown_hint_id {
-                                excerpt_cache.next();
-                                return !new_kinds.contains(&cached_hint.kind);
-                            }
-
-                            match cached_hint
-                                .position
-                                .cmp(&shown_anchor.text_anchor, &buffer_snapshot)
-                            {
-                                cmp::Ordering::Less | cmp::Ordering::Equal => {
-                                    if !old_kinds.contains(&cached_hint.kind)
-                                        && new_kinds.contains(&cached_hint.kind)
-                                    {
-                                        to_insert.push(Inlay::hint(
-                                            cached_hint_id.id(),
-                                            multi_buffer_snapshot.anchor_in_excerpt(
-                                                *excerpt_id,
-                                                cached_hint.position,
-                                            ),
-                                            &cached_hint,
-                                        ));
-                                    }
-                                    excerpt_cache.next();
-                                }
-                                cmp::Ordering::Greater => return true,
-                            }
-                        }
-                        None => return true,
-                    }
-                }
-            });
-
-            for cached_hint_id in excerpt_cache {
-                let maybe_missed_cached_hint = &excerpt_cached_hints.hints_by_id[cached_hint_id];
-                let cached_hint_kind = maybe_missed_cached_hint.kind;
-                if !old_kinds.contains(&cached_hint_kind) && new_kinds.contains(&cached_hint_kind) {
-                    to_insert.push(Inlay::hint(
-                        cached_hint_id.id(),
-                        multi_buffer_snapshot
-                            .anchor_in_excerpt(*excerpt_id, maybe_missed_cached_hint.position),
-                        &maybe_missed_cached_hint,
-                    ));
-                }
-            }
-        }
-
-        to_remove.extend(
-            shown_hints_to_remove
-                .into_values()
-                .flatten()
-                .map(|(_, hint_id)| hint_id),
-        );
-        if to_remove.is_empty() && to_insert.is_empty() {
-            None
-        } else {
-            Some(InlaySplice {
-                to_remove,
-                to_insert,
-            })
-        }
-    }
-
-    pub fn remove_excerpts(&mut self, excerpts_removed: Vec<ExcerptId>) -> Option<InlaySplice> {
-        let mut to_remove = Vec::new();
-        for excerpt_to_remove in excerpts_removed {
-            self.update_tasks.remove(&excerpt_to_remove);
-            if let Some(cached_hints) = self.hints.remove(&excerpt_to_remove) {
-                let cached_hints = cached_hints.read();
-                to_remove.extend(cached_hints.ordered_hints.iter().copied());
-            }
-        }
-        if to_remove.is_empty() {
-            None
-        } else {
-            self.version += 1;
-            Some(InlaySplice {
-                to_remove,
-                to_insert: Vec::new(),
-            })
-        }
-    }
-
-    pub fn clear(&mut self) {
-        if !self.update_tasks.is_empty() || !self.hints.is_empty() {
-            self.version += 1;
-        }
-        self.update_tasks.clear();
-        self.hints.clear();
-    }
-
-    pub fn hint_by_id(&self, excerpt_id: ExcerptId, hint_id: InlayId) -> Option<InlayHint> {
-        self.hints
-            .get(&excerpt_id)?
-            .read()
-            .hints_by_id
-            .get(&hint_id)
-            .cloned()
-    }
-
-    pub fn hints(&self) -> Vec<InlayHint> {
-        let mut hints = Vec::new();
-        for excerpt_hints in self.hints.values() {
-            let excerpt_hints = excerpt_hints.read();
-            hints.extend(
-                excerpt_hints
-                    .ordered_hints
-                    .iter()
-                    .map(|id| &excerpt_hints.hints_by_id[id])
-                    .cloned(),
-            );
-        }
-        hints
-    }
-
-    pub fn version(&self) -> usize {
-        self.version
-    }
-
-    pub fn spawn_hint_resolve(
-        &self,
-        buffer_id: u64,
-        excerpt_id: ExcerptId,
-        id: InlayId,
-        cx: &mut ViewContext<'_, Editor>,
-    ) {
-        if let Some(excerpt_hints) = self.hints.get(&excerpt_id) {
-            let mut guard = excerpt_hints.write();
-            if let Some(cached_hint) = guard.hints_by_id.get_mut(&id) {
-                if let ResolveState::CanResolve(server_id, _) = &cached_hint.resolve_state {
-                    let hint_to_resolve = cached_hint.clone();
-                    let server_id = *server_id;
-                    cached_hint.resolve_state = ResolveState::Resolving;
-                    drop(guard);
-                    cx.spawn(|editor, mut cx| async move {
-                        let resolved_hint_task = editor.update(&mut cx, |editor, cx| {
-                            editor
-                                .buffer()
-                                .read(cx)
-                                .buffer(buffer_id)
-                                .and_then(|buffer| {
-                                    let project = editor.project.as_ref()?;
-                                    Some(project.update(cx, |project, cx| {
-                                        project.resolve_inlay_hint(
-                                            hint_to_resolve,
-                                            buffer,
-                                            server_id,
-                                            cx,
-                                        )
-                                    }))
-                                })
-                        })?;
-                        if let Some(resolved_hint_task) = resolved_hint_task {
-                            let mut resolved_hint =
-                                resolved_hint_task.await.context("hint resolve task")?;
-                            editor.update(&mut cx, |editor, _| {
-                                if let Some(excerpt_hints) =
-                                    editor.inlay_hint_cache.hints.get(&excerpt_id)
-                                {
-                                    let mut guard = excerpt_hints.write();
-                                    if let Some(cached_hint) = guard.hints_by_id.get_mut(&id) {
-                                        if cached_hint.resolve_state == ResolveState::Resolving {
-                                            resolved_hint.resolve_state = ResolveState::Resolved;
-                                            *cached_hint = resolved_hint;
-                                        }
-                                    }
-                                }
-                            })?;
-                        }
-
-                        anyhow::Ok(())
-                    })
-                    .detach_and_log_err(cx);
-                }
-            }
-        }
-    }
-}
-
-fn spawn_new_update_tasks(
-    editor: &mut Editor,
-    reason: &'static str,
-    excerpts_to_query: HashMap<ExcerptId, (Model<Buffer>, Global, Range<usize>)>,
-    invalidate: InvalidationStrategy,
-    update_cache_version: usize,
-    cx: &mut ViewContext<'_, Editor>,
-) {
-    let visible_hints = Arc::new(editor.visible_inlay_hints(cx));
-    for (excerpt_id, (excerpt_buffer, new_task_buffer_version, excerpt_visible_range)) in
-        excerpts_to_query
-    {
-        if excerpt_visible_range.is_empty() {
-            continue;
-        }
-        let buffer = excerpt_buffer.read(cx);
-        let buffer_id = buffer.remote_id();
-        let buffer_snapshot = buffer.snapshot();
-        if buffer_snapshot
-            .version()
-            .changed_since(&new_task_buffer_version)
-        {
-            continue;
-        }
-
-        let cached_excerpt_hints = editor.inlay_hint_cache.hints.get(&excerpt_id).cloned();
-        if let Some(cached_excerpt_hints) = &cached_excerpt_hints {
-            let cached_excerpt_hints = cached_excerpt_hints.read();
-            let cached_buffer_version = &cached_excerpt_hints.buffer_version;
-            if cached_excerpt_hints.version > update_cache_version
-                || cached_buffer_version.changed_since(&new_task_buffer_version)
-            {
-                continue;
-            }
-        };
-
-        let (multi_buffer_snapshot, Some(query_ranges)) =
-            editor.buffer.update(cx, |multi_buffer, cx| {
-                (
-                    multi_buffer.snapshot(cx),
-                    determine_query_ranges(
-                        multi_buffer,
-                        excerpt_id,
-                        &excerpt_buffer,
-                        excerpt_visible_range,
-                        cx,
-                    ),
-                )
-            })
-        else {
-            return;
-        };
-        let query = ExcerptQuery {
-            buffer_id,
-            excerpt_id,
-            cache_version: update_cache_version,
-            invalidate,
-            reason,
-        };
-
-        let new_update_task = |query_ranges| {
-            new_update_task(
-                query,
-                query_ranges,
-                multi_buffer_snapshot,
-                buffer_snapshot.clone(),
-                Arc::clone(&visible_hints),
-                cached_excerpt_hints,
-                Arc::clone(&editor.inlay_hint_cache.lsp_request_limiter),
-                cx,
-            )
-        };
-
-        match editor.inlay_hint_cache.update_tasks.entry(excerpt_id) {
-            hash_map::Entry::Occupied(mut o) => {
-                o.get_mut().update_cached_tasks(
-                    &buffer_snapshot,
-                    query_ranges,
-                    invalidate,
-                    new_update_task,
-                );
-            }
-            hash_map::Entry::Vacant(v) => {
-                v.insert(TasksForRanges::new(
-                    query_ranges.clone(),
-                    new_update_task(query_ranges),
-                ));
-            }
-        }
-    }
-}
-
-#[derive(Debug, Clone)]
-struct QueryRanges {
-    before_visible: Vec<Range<language::Anchor>>,
-    visible: Vec<Range<language::Anchor>>,
-    after_visible: Vec<Range<language::Anchor>>,
-}
-
-impl QueryRanges {
-    fn is_empty(&self) -> bool {
-        self.before_visible.is_empty() && self.visible.is_empty() && self.after_visible.is_empty()
-    }
-}
-
-fn determine_query_ranges(
-    multi_buffer: &mut MultiBuffer,
-    excerpt_id: ExcerptId,
-    excerpt_buffer: &Model<Buffer>,
-    excerpt_visible_range: Range<usize>,
-    cx: &mut ModelContext<'_, MultiBuffer>,
-) -> Option<QueryRanges> {
-    let full_excerpt_range = multi_buffer
-        .excerpts_for_buffer(excerpt_buffer, cx)
-        .into_iter()
-        .find(|(id, _)| id == &excerpt_id)
-        .map(|(_, range)| range.context)?;
-    let buffer = excerpt_buffer.read(cx);
-    let snapshot = buffer.snapshot();
-    let excerpt_visible_len = excerpt_visible_range.end - excerpt_visible_range.start;
-
-    let visible_range = if excerpt_visible_range.start == excerpt_visible_range.end {
-        return None;
-    } else {
-        vec![
-            buffer.anchor_before(snapshot.clip_offset(excerpt_visible_range.start, Bias::Left))
-                ..buffer.anchor_after(snapshot.clip_offset(excerpt_visible_range.end, Bias::Right)),
-        ]
-    };
-
-    let full_excerpt_range_end_offset = full_excerpt_range.end.to_offset(&snapshot);
-    let after_visible_range_start = excerpt_visible_range
-        .end
-        .saturating_add(1)
-        .min(full_excerpt_range_end_offset)
-        .min(buffer.len());
-    let after_visible_range = if after_visible_range_start == full_excerpt_range_end_offset {
-        Vec::new()
-    } else {
-        let after_range_end_offset = after_visible_range_start
-            .saturating_add(excerpt_visible_len)
-            .min(full_excerpt_range_end_offset)
-            .min(buffer.len());
-        vec![
-            buffer.anchor_before(snapshot.clip_offset(after_visible_range_start, Bias::Left))
-                ..buffer.anchor_after(snapshot.clip_offset(after_range_end_offset, Bias::Right)),
-        ]
-    };
-
-    let full_excerpt_range_start_offset = full_excerpt_range.start.to_offset(&snapshot);
-    let before_visible_range_end = excerpt_visible_range
-        .start
-        .saturating_sub(1)
-        .max(full_excerpt_range_start_offset);
-    let before_visible_range = if before_visible_range_end == full_excerpt_range_start_offset {
-        Vec::new()
-    } else {
-        let before_range_start_offset = before_visible_range_end
-            .saturating_sub(excerpt_visible_len)
-            .max(full_excerpt_range_start_offset);
-        vec![
-            buffer.anchor_before(snapshot.clip_offset(before_range_start_offset, Bias::Left))
-                ..buffer.anchor_after(snapshot.clip_offset(before_visible_range_end, Bias::Right)),
-        ]
-    };
-
-    Some(QueryRanges {
-        before_visible: before_visible_range,
-        visible: visible_range,
-        after_visible: after_visible_range,
-    })
-}
-
-const MAX_CONCURRENT_LSP_REQUESTS: usize = 5;
-const INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS: u64 = 400;
-
-fn new_update_task(
-    query: ExcerptQuery,
-    query_ranges: QueryRanges,
-    multi_buffer_snapshot: MultiBufferSnapshot,
-    buffer_snapshot: BufferSnapshot,
-    visible_hints: Arc<Vec<Inlay>>,
-    cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>,
-    lsp_request_limiter: Arc<Semaphore>,
-    cx: &mut ViewContext<'_, Editor>,
-) -> Task<()> {
-    cx.spawn(|editor, mut cx| async move {
-        let closure_cx = cx.clone();
-        let fetch_and_update_hints = |invalidate, range| {
-            fetch_and_update_hints(
-                editor.clone(),
-                multi_buffer_snapshot.clone(),
-                buffer_snapshot.clone(),
-                Arc::clone(&visible_hints),
-                cached_excerpt_hints.as_ref().map(Arc::clone),
-                query,
-                invalidate,
-                range,
-                Arc::clone(&lsp_request_limiter),
-                closure_cx.clone(),
-            )
-        };
-        let visible_range_update_results = future::join_all(query_ranges.visible.into_iter().map(
-            |visible_range| async move {
-                (
-                    visible_range.clone(),
-                    fetch_and_update_hints(query.invalidate.should_invalidate(), visible_range)
-                        .await,
-                )
-            },
-        ))
-        .await;
-
-        let hint_delay = cx.background_executor().timer(Duration::from_millis(
-            INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS,
-        ));
-
-        let mut query_range_failed = |range: &Range<language::Anchor>, e: anyhow::Error| {
-            log::error!("inlay hint update task for range {range:?} failed: {e:#}");
-            editor
-                .update(&mut cx, |editor, _| {
-                    if let Some(task_ranges) = editor
-                        .inlay_hint_cache
-                        .update_tasks
-                        .get_mut(&query.excerpt_id)
-                    {
-                        task_ranges.invalidate_range(&buffer_snapshot, &range);
-                    }
-                })
-                .ok()
-        };
-
-        for (range, result) in visible_range_update_results {
-            if let Err(e) = result {
-                query_range_failed(&range, e);
-            }
-        }
-
-        hint_delay.await;
-        let invisible_range_update_results = future::join_all(
-            query_ranges
-                .before_visible
-                .into_iter()
-                .chain(query_ranges.after_visible.into_iter())
-                .map(|invisible_range| async move {
-                    (
-                        invisible_range.clone(),
-                        fetch_and_update_hints(false, invisible_range).await,
-                    )
-                }),
-        )
-        .await;
-        for (range, result) in invisible_range_update_results {
-            if let Err(e) = result {
-                query_range_failed(&range, e);
-            }
-        }
-    })
-}
-
-async fn fetch_and_update_hints(
-    editor: gpui::WeakView<Editor>,
-    multi_buffer_snapshot: MultiBufferSnapshot,
-    buffer_snapshot: BufferSnapshot,
-    visible_hints: Arc<Vec<Inlay>>,
-    cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>,
-    query: ExcerptQuery,
-    invalidate: bool,
-    fetch_range: Range<language::Anchor>,
-    lsp_request_limiter: Arc<Semaphore>,
-    mut cx: gpui::AsyncWindowContext,
-) -> anyhow::Result<()> {
-    let (lsp_request_guard, got_throttled) = if query.invalidate.should_invalidate() {
-        (None, false)
-    } else {
-        match lsp_request_limiter.try_acquire() {
-            Some(guard) => (Some(guard), false),
-            None => (Some(lsp_request_limiter.acquire().await), true),
-        }
-    };
-    let fetch_range_to_log =
-        fetch_range.start.to_point(&buffer_snapshot)..fetch_range.end.to_point(&buffer_snapshot);
-    let inlay_hints_fetch_task = editor
-        .update(&mut cx, |editor, cx| {
-            if got_throttled {
-                let query_not_around_visible_range = match editor.excerpts_for_inlay_hints_query(None, cx).remove(&query.excerpt_id) {
-                    Some((_, _, current_visible_range)) => {
-                        let visible_offset_length = current_visible_range.len();
-                        let double_visible_range = current_visible_range
-                            .start
-                            .saturating_sub(visible_offset_length)
-                            ..current_visible_range
-                                .end
-                                .saturating_add(visible_offset_length)
-                                .min(buffer_snapshot.len());
-                        !double_visible_range
-                            .contains(&fetch_range.start.to_offset(&buffer_snapshot))
-                            && !double_visible_range
-                                .contains(&fetch_range.end.to_offset(&buffer_snapshot))
-                    },
-                    None => true,
-                };
-                if query_not_around_visible_range {
-                    log::trace!("Fetching inlay hints for range {fetch_range_to_log:?} got throttled and fell off the current visible range, skipping.");
-                    if let Some(task_ranges) = editor
-                        .inlay_hint_cache
-                        .update_tasks
-                        .get_mut(&query.excerpt_id)
-                    {
-                        task_ranges.invalidate_range(&buffer_snapshot, &fetch_range);
-                    }
-                    return None;
-                }
-            }
-            editor
-                .buffer()
-                .read(cx)
-                .buffer(query.buffer_id)
-                .and_then(|buffer| {
-                    let project = editor.project.as_ref()?;
-                    Some(project.update(cx, |project, cx| {
-                        project.inlay_hints(buffer, fetch_range.clone(), cx)
-                    }))
-                })
-        })
-        .ok()
-        .flatten();
-    let new_hints = match inlay_hints_fetch_task {
-        Some(fetch_task) => {
-            log::debug!(
-                "Fetching inlay hints for range {fetch_range_to_log:?}, reason: {query_reason}, invalidate: {invalidate}",
-                query_reason = query.reason,
-            );
-            log::trace!(
-                "Currently visible hints: {visible_hints:?}, cached hints present: {}",
-                cached_excerpt_hints.is_some(),
-            );
-            fetch_task.await.context("inlay hint fetch task")?
-        }
-        None => return Ok(()),
-    };
-    drop(lsp_request_guard);
-    log::debug!(
-        "Fetched {} hints for range {fetch_range_to_log:?}",
-        new_hints.len()
-    );
-    log::trace!("Fetched hints: {new_hints:?}");
-
-    let background_task_buffer_snapshot = buffer_snapshot.clone();
-    let backround_fetch_range = fetch_range.clone();
-    let new_update = cx
-        .background_executor()
-        .spawn(async move {
-            calculate_hint_updates(
-                query.excerpt_id,
-                invalidate,
-                backround_fetch_range,
-                new_hints,
-                &background_task_buffer_snapshot,
-                cached_excerpt_hints,
-                &visible_hints,
-            )
-        })
-        .await;
-    if let Some(new_update) = new_update {
-        log::debug!(
-            "Applying update for range {fetch_range_to_log:?}: remove from editor: {}, remove from cache: {}, add to cache: {}",
-            new_update.remove_from_visible.len(),
-            new_update.remove_from_cache.len(),
-            new_update.add_to_cache.len()
-        );
-        log::trace!("New update: {new_update:?}");
-        editor
-            .update(&mut cx, |editor, cx| {
-                apply_hint_update(
-                    editor,
-                    new_update,
-                    query,
-                    invalidate,
-                    buffer_snapshot,
-                    multi_buffer_snapshot,
-                    cx,
-                );
-            })
-            .ok();
-    }
-    Ok(())
-}
-
-fn calculate_hint_updates(
-    excerpt_id: ExcerptId,
-    invalidate: bool,
-    fetch_range: Range<language::Anchor>,
-    new_excerpt_hints: Vec<InlayHint>,
-    buffer_snapshot: &BufferSnapshot,
-    cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>,
-    visible_hints: &[Inlay],
-) -> Option<ExcerptHintsUpdate> {
-    let mut add_to_cache = Vec::<InlayHint>::new();
-    let mut excerpt_hints_to_persist = HashMap::default();
-    for new_hint in new_excerpt_hints {
-        if !contains_position(&fetch_range, new_hint.position, buffer_snapshot) {
-            continue;
-        }
-        let missing_from_cache = match &cached_excerpt_hints {
-            Some(cached_excerpt_hints) => {
-                let cached_excerpt_hints = cached_excerpt_hints.read();
-                match cached_excerpt_hints
-                    .ordered_hints
-                    .binary_search_by(|probe| {
-                        cached_excerpt_hints.hints_by_id[probe]
-                            .position
-                            .cmp(&new_hint.position, buffer_snapshot)
-                    }) {
-                    Ok(ix) => {
-                        let mut missing_from_cache = true;
-                        for id in &cached_excerpt_hints.ordered_hints[ix..] {
-                            let cached_hint = &cached_excerpt_hints.hints_by_id[id];
-                            if new_hint
-                                .position
-                                .cmp(&cached_hint.position, buffer_snapshot)
-                                .is_gt()
-                            {
-                                break;
-                            }
-                            if cached_hint == &new_hint {
-                                excerpt_hints_to_persist.insert(*id, cached_hint.kind);
-                                missing_from_cache = false;
-                            }
-                        }
-                        missing_from_cache
-                    }
-                    Err(_) => true,
-                }
-            }
-            None => true,
-        };
-        if missing_from_cache {
-            add_to_cache.push(new_hint);
-        }
-    }
-
-    let mut remove_from_visible = Vec::new();
-    let mut remove_from_cache = HashSet::default();
-    if invalidate {
-        remove_from_visible.extend(
-            visible_hints
-                .iter()
-                .filter(|hint| hint.position.excerpt_id == excerpt_id)
-                .map(|inlay_hint| inlay_hint.id)
-                .filter(|hint_id| !excerpt_hints_to_persist.contains_key(hint_id)),
-        );
-
-        if let Some(cached_excerpt_hints) = &cached_excerpt_hints {
-            let cached_excerpt_hints = cached_excerpt_hints.read();
-            remove_from_cache.extend(
-                cached_excerpt_hints
-                    .ordered_hints
-                    .iter()
-                    .filter(|cached_inlay_id| {
-                        !excerpt_hints_to_persist.contains_key(cached_inlay_id)
-                    })
-                    .copied(),
-            );
-        }
-    }
-
-    if remove_from_visible.is_empty() && remove_from_cache.is_empty() && add_to_cache.is_empty() {
-        None
-    } else {
-        Some(ExcerptHintsUpdate {
-            excerpt_id,
-            remove_from_visible,
-            remove_from_cache,
-            add_to_cache,
-        })
-    }
-}
-
-fn contains_position(
-    range: &Range<language::Anchor>,
-    position: language::Anchor,
-    buffer_snapshot: &BufferSnapshot,
-) -> bool {
-    range.start.cmp(&position, buffer_snapshot).is_le()
-        && range.end.cmp(&position, buffer_snapshot).is_ge()
-}
-
-fn apply_hint_update(
-    editor: &mut Editor,
-    new_update: ExcerptHintsUpdate,
-    query: ExcerptQuery,
-    invalidate: bool,
-    buffer_snapshot: BufferSnapshot,
-    multi_buffer_snapshot: MultiBufferSnapshot,
-    cx: &mut ViewContext<'_, Editor>,
-) {
-    let cached_excerpt_hints = editor
-        .inlay_hint_cache
-        .hints
-        .entry(new_update.excerpt_id)
-        .or_insert_with(|| {
-            Arc::new(RwLock::new(CachedExcerptHints {
-                version: query.cache_version,
-                buffer_version: buffer_snapshot.version().clone(),
-                buffer_id: query.buffer_id,
-                ordered_hints: Vec::new(),
-                hints_by_id: HashMap::default(),
-            }))
-        });
-    let mut cached_excerpt_hints = cached_excerpt_hints.write();
-    match query.cache_version.cmp(&cached_excerpt_hints.version) {
-        cmp::Ordering::Less => return,
-        cmp::Ordering::Greater | cmp::Ordering::Equal => {
-            cached_excerpt_hints.version = query.cache_version;
-        }
-    }
-
-    let mut cached_inlays_changed = !new_update.remove_from_cache.is_empty();
-    cached_excerpt_hints
-        .ordered_hints
-        .retain(|hint_id| !new_update.remove_from_cache.contains(hint_id));
-    cached_excerpt_hints
-        .hints_by_id
-        .retain(|hint_id, _| !new_update.remove_from_cache.contains(hint_id));
-    let mut splice = InlaySplice {
-        to_remove: new_update.remove_from_visible,
-        to_insert: Vec::new(),
-    };
-    for new_hint in new_update.add_to_cache {
-        let insert_position = match cached_excerpt_hints
-            .ordered_hints
-            .binary_search_by(|probe| {
-                cached_excerpt_hints.hints_by_id[probe]
-                    .position
-                    .cmp(&new_hint.position, &buffer_snapshot)
-            }) {
-            Ok(i) => {
-                let mut insert_position = Some(i);
-                for id in &cached_excerpt_hints.ordered_hints[i..] {
-                    let cached_hint = &cached_excerpt_hints.hints_by_id[id];
-                    if new_hint
-                        .position
-                        .cmp(&cached_hint.position, &buffer_snapshot)
-                        .is_gt()
-                    {
-                        break;
-                    }
-                    if cached_hint.text() == new_hint.text() {
-                        insert_position = None;
-                        break;
-                    }
-                }
-                insert_position
-            }
-            Err(i) => Some(i),
-        };
-
-        if let Some(insert_position) = insert_position {
-            let new_inlay_id = post_inc(&mut editor.next_inlay_id);
-            if editor
-                .inlay_hint_cache
-                .allowed_hint_kinds
-                .contains(&new_hint.kind)
-            {
-                let new_hint_position =
-                    multi_buffer_snapshot.anchor_in_excerpt(query.excerpt_id, new_hint.position);
-                splice
-                    .to_insert
-                    .push(Inlay::hint(new_inlay_id, new_hint_position, &new_hint));
-            }
-            let new_id = InlayId::Hint(new_inlay_id);
-            cached_excerpt_hints.hints_by_id.insert(new_id, new_hint);
-            cached_excerpt_hints
-                .ordered_hints
-                .insert(insert_position, new_id);
-            cached_inlays_changed = true;
-        }
-    }
-    cached_excerpt_hints.buffer_version = buffer_snapshot.version().clone();
-    drop(cached_excerpt_hints);
-
-    if invalidate {
-        let mut outdated_excerpt_caches = HashSet::default();
-        for (excerpt_id, excerpt_hints) in &editor.inlay_hint_cache().hints {
-            let excerpt_hints = excerpt_hints.read();
-            if excerpt_hints.buffer_id == query.buffer_id
-                && excerpt_id != &query.excerpt_id
-                && buffer_snapshot
-                    .version()
-                    .changed_since(&excerpt_hints.buffer_version)
-            {
-                outdated_excerpt_caches.insert(*excerpt_id);
-                splice
-                    .to_remove
-                    .extend(excerpt_hints.ordered_hints.iter().copied());
-            }
-        }
-        cached_inlays_changed |= !outdated_excerpt_caches.is_empty();
-        editor
-            .inlay_hint_cache
-            .hints
-            .retain(|excerpt_id, _| !outdated_excerpt_caches.contains(excerpt_id));
-    }
-
-    let InlaySplice {
-        to_remove,
-        to_insert,
-    } = splice;
-    let displayed_inlays_changed = !to_remove.is_empty() || !to_insert.is_empty();
-    if cached_inlays_changed || displayed_inlays_changed {
-        editor.inlay_hint_cache.version += 1;
-    }
-    if displayed_inlays_changed {
-        editor.splice_inlay_hints(to_remove, to_insert, cx)
-    }
-}
-
-#[cfg(test)]
-pub mod tests {
-    use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
-
-    use crate::{
-        scroll::{autoscroll::Autoscroll, scroll_amount::ScrollAmount},
-        ExcerptRange,
-    };
-    use futures::StreamExt;
-    use gpui::{Context, TestAppContext, WindowHandle};
-    use itertools::Itertools;
-    use language::{
-        language_settings::AllLanguageSettingsContent, FakeLspAdapter, Language, LanguageConfig,
-    };
-    use lsp::FakeLanguageServer;
-    use parking_lot::Mutex;
-    use project::{FakeFs, Project};
-    use serde_json::json;
-    use settings::SettingsStore;
-    use text::{Point, ToPoint};
-
-    use crate::editor_tests::update_test_language_settings;
-
-    use super::*;
-
-    #[gpui::test]
-    async fn test_basic_cache_update_with_duplicate_hints(cx: &mut gpui::TestAppContext) {
-        let allowed_hint_kinds = HashSet::from_iter([None, Some(InlayHintKind::Type)]);
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
-                show_parameter_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
-                show_other_hints: allowed_hint_kinds.contains(&None),
-            })
-        });
-
-        let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
-        let lsp_request_count = Arc::new(AtomicU32::new(0));
-        fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_lsp_request_count = Arc::clone(&lsp_request_count);
-                async move {
-                    assert_eq!(
-                        params.text_document.uri,
-                        lsp::Url::from_file_path(file_with_hints).unwrap(),
-                    );
-                    let current_call_id =
-                        Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
-                    let mut new_hints = Vec::with_capacity(2 * current_call_id as usize);
-                    for _ in 0..2 {
-                        let mut i = current_call_id;
-                        loop {
-                            new_hints.push(lsp::InlayHint {
-                                position: lsp::Position::new(0, i),
-                                label: lsp::InlayHintLabel::String(i.to_string()),
-                                kind: None,
-                                text_edits: None,
-                                tooltip: None,
-                                padding_left: None,
-                                padding_right: None,
-                                data: None,
-                            });
-                            if i == 0 {
-                                break;
-                            }
-                            i -= 1;
-                        }
-                    }
-
-                    Ok(Some(new_hints))
-                }
-            })
-            .next()
-            .await;
-        cx.executor().run_until_parked();
-
-        let mut edits_made = 1;
-        _ = editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["0".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should get its first hints when opening the editor"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            let inlay_cache = editor.inlay_hint_cache();
-            assert_eq!(
-                inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
-                "Cache should use editor settings to get the allowed hint kinds"
-            );
-            assert_eq!(
-                inlay_cache.version, edits_made,
-                "The editor update the cache version after every cache/view change"
-            );
-        });
-
-        _ = editor.update(cx, |editor, cx| {
-            editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-            editor.handle_input("some change", cx);
-            edits_made += 1;
-        });
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["0".to_string(), "1".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should get new hints after an edit"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            let inlay_cache = editor.inlay_hint_cache();
-            assert_eq!(
-                inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
-                "Cache should use editor settings to get the allowed hint kinds"
-            );
-            assert_eq!(
-                inlay_cache.version, edits_made,
-                "The editor update the cache version after every cache/view change"
-            );
-        });
-
-        fake_server
-            .request::<lsp::request::InlayHintRefreshRequest>(())
-            .await
-            .expect("inlay refresh request failed");
-        edits_made += 1;
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["0".to_string(), "1".to_string(), "2".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should get new hints after hint refresh/ request"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            let inlay_cache = editor.inlay_hint_cache();
-            assert_eq!(
-                inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
-                "Cache should use editor settings to get the allowed hint kinds"
-            );
-            assert_eq!(
-                inlay_cache.version, edits_made,
-                "The editor update the cache version after every cache/view change"
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_cache_update_on_lsp_completion_tasks(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-
-        let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
-        let lsp_request_count = Arc::new(AtomicU32::new(0));
-        fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_lsp_request_count = Arc::clone(&lsp_request_count);
-                async move {
-                    assert_eq!(
-                        params.text_document.uri,
-                        lsp::Url::from_file_path(file_with_hints).unwrap(),
-                    );
-                    let current_call_id =
-                        Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
-                    Ok(Some(vec![lsp::InlayHint {
-                        position: lsp::Position::new(0, current_call_id),
-                        label: lsp::InlayHintLabel::String(current_call_id.to_string()),
-                        kind: None,
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: None,
-                        padding_right: None,
-                        data: None,
-                    }]))
-                }
-            })
-            .next()
-            .await;
-        cx.executor().run_until_parked();
-
-        let mut edits_made = 1;
-        _ = editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["0".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should get its first hints when opening the editor"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                edits_made,
-                "The editor update the cache version after every cache/view change"
-            );
-        });
-
-        let progress_token = "test_progress_token";
-        fake_server
-            .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
-                token: lsp::ProgressToken::String(progress_token.to_string()),
-            })
-            .await
-            .expect("work done progress create request failed");
-        cx.executor().run_until_parked();
-        fake_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
-            token: lsp::ProgressToken::String(progress_token.to_string()),
-            value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
-                lsp::WorkDoneProgressBegin::default(),
-            )),
-        });
-        cx.executor().run_until_parked();
-
-        _ = editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["0".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should not update hints while the work task is running"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                edits_made,
-                "Should not update the cache while the work task is running"
-            );
-        });
-
-        fake_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
-            token: lsp::ProgressToken::String(progress_token.to_string()),
-            value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
-                lsp::WorkDoneProgressEnd::default(),
-            )),
-        });
-        cx.executor().run_until_parked();
-
-        edits_made += 1;
-        _ = editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["1".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "New hints should be queried after the work task is done"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                edits_made,
-                "Cache version should udpate once after the work task is done"
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_no_hint_updates_for_unrelated_language_files(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-
-        let fs = FakeFs::new(cx.background_executor.clone());
-        fs.insert_tree(
-                    "/a",
-                    json!({
-                        "main.rs": "fn main() { a } // and some long comment to ensure inlays are not trimmed out",
-                        "other.md": "Test md file with some text",
-                    }),
-                )
-                .await;
-        let project = Project::test(fs, ["/a".as_ref()], cx).await;
-
-        let mut rs_fake_servers = None;
-        let mut md_fake_servers = None;
-        for (name, path_suffix) in [("Rust", "rs"), ("Markdown", "md")] {
-            let mut language = Language::new(
-                LanguageConfig {
-                    name: name.into(),
-                    path_suffixes: vec![path_suffix.to_string()],
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            );
-            let fake_servers = language
-                .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-                    name,
-                    capabilities: lsp::ServerCapabilities {
-                        inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-                        ..Default::default()
-                    },
-                    ..Default::default()
-                }))
-                .await;
-            match name {
-                "Rust" => rs_fake_servers = Some(fake_servers),
-                "Markdown" => md_fake_servers = Some(fake_servers),
-                _ => unreachable!(),
-            }
-            project.update(cx, |project, _| {
-                project.languages().add(Arc::new(language));
-            });
-        }
-
-        let rs_buffer = project
-            .update(cx, |project, cx| {
-                project.open_local_buffer("/a/main.rs", cx)
-            })
-            .await
-            .unwrap();
-        cx.executor().run_until_parked();
-        cx.executor().start_waiting();
-        let rs_fake_server = rs_fake_servers.unwrap().next().await.unwrap();
-        let rs_editor =
-            cx.add_window(|cx| Editor::for_buffer(rs_buffer, Some(project.clone()), cx));
-        let rs_lsp_request_count = Arc::new(AtomicU32::new(0));
-        rs_fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_lsp_request_count = Arc::clone(&rs_lsp_request_count);
-                async move {
-                    assert_eq!(
-                        params.text_document.uri,
-                        lsp::Url::from_file_path("/a/main.rs").unwrap(),
-                    );
-                    let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
-                    Ok(Some(vec![lsp::InlayHint {
-                        position: lsp::Position::new(0, i),
-                        label: lsp::InlayHintLabel::String(i.to_string()),
-                        kind: None,
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: None,
-                        padding_right: None,
-                        data: None,
-                    }]))
-                }
-            })
-            .next()
-            .await;
-        cx.executor().run_until_parked();
-        _ = rs_editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["0".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should get its first hints when opening the editor"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                1,
-                "Rust editor update the cache version after every cache/view change"
-            );
-        });
-
-        cx.executor().run_until_parked();
-        let md_buffer = project
-            .update(cx, |project, cx| {
-                project.open_local_buffer("/a/other.md", cx)
-            })
-            .await
-            .unwrap();
-        cx.executor().run_until_parked();
-        cx.executor().start_waiting();
-        let md_fake_server = md_fake_servers.unwrap().next().await.unwrap();
-        let md_editor = cx.add_window(|cx| Editor::for_buffer(md_buffer, Some(project), cx));
-        let md_lsp_request_count = Arc::new(AtomicU32::new(0));
-        md_fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_lsp_request_count = Arc::clone(&md_lsp_request_count);
-                async move {
-                    assert_eq!(
-                        params.text_document.uri,
-                        lsp::Url::from_file_path("/a/other.md").unwrap(),
-                    );
-                    let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
-                    Ok(Some(vec![lsp::InlayHint {
-                        position: lsp::Position::new(0, i),
-                        label: lsp::InlayHintLabel::String(i.to_string()),
-                        kind: None,
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: None,
-                        padding_right: None,
-                        data: None,
-                    }]))
-                }
-            })
-            .next()
-            .await;
-        cx.executor().run_until_parked();
-        _ = md_editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["0".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Markdown editor should have a separate verison, repeating Rust editor rules"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, 1);
-        });
-
-        _ = rs_editor.update(cx, |editor, cx| {
-            editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-            editor.handle_input("some rs change", cx);
-        });
-        cx.executor().run_until_parked();
-        _ = rs_editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["1".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Rust inlay cache should change after the edit"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                2,
-                "Every time hint cache changes, cache version should be incremented"
-            );
-        });
-        _ = md_editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["0".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Markdown editor should not be affected by Rust editor changes"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, 1);
-        });
-
-        _ = md_editor.update(cx, |editor, cx| {
-            editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-            editor.handle_input("some md change", cx);
-        });
-        cx.executor().run_until_parked();
-        _ = md_editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["1".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Rust editor should not be affected by Markdown editor changes"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, 2);
-        });
-        _ = rs_editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["1".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Markdown editor should also change independently"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, 2);
-        });
-    }
-
-    #[gpui::test]
-    async fn test_hint_setting_changes(cx: &mut gpui::TestAppContext) {
-        let allowed_hint_kinds = HashSet::from_iter([None, Some(InlayHintKind::Type)]);
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
-                show_parameter_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
-                show_other_hints: allowed_hint_kinds.contains(&None),
-            })
-        });
-
-        let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
-        let lsp_request_count = Arc::new(AtomicU32::new(0));
-        let another_lsp_request_count = Arc::clone(&lsp_request_count);
-        fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_lsp_request_count = Arc::clone(&another_lsp_request_count);
-                async move {
-                    Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
-                    assert_eq!(
-                        params.text_document.uri,
-                        lsp::Url::from_file_path(file_with_hints).unwrap(),
-                    );
-                    Ok(Some(vec![
-                        lsp::InlayHint {
-                            position: lsp::Position::new(0, 1),
-                            label: lsp::InlayHintLabel::String("type hint".to_string()),
-                            kind: Some(lsp::InlayHintKind::TYPE),
-                            text_edits: None,
-                            tooltip: None,
-                            padding_left: None,
-                            padding_right: None,
-                            data: None,
-                        },
-                        lsp::InlayHint {
-                            position: lsp::Position::new(0, 2),
-                            label: lsp::InlayHintLabel::String("parameter hint".to_string()),
-                            kind: Some(lsp::InlayHintKind::PARAMETER),
-                            text_edits: None,
-                            tooltip: None,
-                            padding_left: None,
-                            padding_right: None,
-                            data: None,
-                        },
-                        lsp::InlayHint {
-                            position: lsp::Position::new(0, 3),
-                            label: lsp::InlayHintLabel::String("other hint".to_string()),
-                            kind: None,
-                            text_edits: None,
-                            tooltip: None,
-                            padding_left: None,
-                            padding_right: None,
-                            data: None,
-                        },
-                    ]))
-                }
-            })
-            .next()
-            .await;
-        cx.executor().run_until_parked();
-
-        let mut edits_made = 1;
-        _ = editor.update(cx, |editor, cx| {
-            assert_eq!(
-                lsp_request_count.load(Ordering::Relaxed),
-                1,
-                "Should query new hints once"
-            );
-            assert_eq!(
-                vec![
-                    "other hint".to_string(),
-                    "parameter hint".to_string(),
-                    "type hint".to_string(),
-                ],
-                cached_hint_labels(editor),
-                "Should get its first hints when opening the editor"
-            );
-            assert_eq!(
-                vec!["other hint".to_string(), "type hint".to_string()],
-                visible_hint_labels(editor, cx)
-            );
-            let inlay_cache = editor.inlay_hint_cache();
-            assert_eq!(
-                inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
-                "Cache should use editor settings to get the allowed hint kinds"
-            );
-            assert_eq!(
-                inlay_cache.version, edits_made,
-                "The editor update the cache version after every cache/view change"
-            );
-        });
-
-        fake_server
-            .request::<lsp::request::InlayHintRefreshRequest>(())
-            .await
-            .expect("inlay refresh request failed");
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-            assert_eq!(
-                lsp_request_count.load(Ordering::Relaxed),
-                2,
-                "Should load new hints twice"
-            );
-            assert_eq!(
-                vec![
-                    "other hint".to_string(),
-                    "parameter hint".to_string(),
-                    "type hint".to_string(),
-                ],
-                cached_hint_labels(editor),
-                "Cached hints should not change due to allowed hint kinds settings update"
-            );
-            assert_eq!(
-                vec!["other hint".to_string(), "type hint".to_string()],
-                visible_hint_labels(editor, cx)
-            );
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                edits_made,
-                "Should not update cache version due to new loaded hints being the same"
-            );
-        });
-
-        for (new_allowed_hint_kinds, expected_visible_hints) in [
-            (HashSet::from_iter([None]), vec!["other hint".to_string()]),
-            (
-                HashSet::from_iter([Some(InlayHintKind::Type)]),
-                vec!["type hint".to_string()],
-            ),
-            (
-                HashSet::from_iter([Some(InlayHintKind::Parameter)]),
-                vec!["parameter hint".to_string()],
-            ),
-            (
-                HashSet::from_iter([None, Some(InlayHintKind::Type)]),
-                vec!["other hint".to_string(), "type hint".to_string()],
-            ),
-            (
-                HashSet::from_iter([None, Some(InlayHintKind::Parameter)]),
-                vec!["other hint".to_string(), "parameter hint".to_string()],
-            ),
-            (
-                HashSet::from_iter([Some(InlayHintKind::Type), Some(InlayHintKind::Parameter)]),
-                vec!["parameter hint".to_string(), "type hint".to_string()],
-            ),
-            (
-                HashSet::from_iter([
-                    None,
-                    Some(InlayHintKind::Type),
-                    Some(InlayHintKind::Parameter),
-                ]),
-                vec![
-                    "other hint".to_string(),
-                    "parameter hint".to_string(),
-                    "type hint".to_string(),
-                ],
-            ),
-        ] {
-            edits_made += 1;
-            update_test_language_settings(cx, |settings| {
-                settings.defaults.inlay_hints = Some(InlayHintSettings {
-                    enabled: true,
-                    show_type_hints: new_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
-                    show_parameter_hints: new_allowed_hint_kinds
-                        .contains(&Some(InlayHintKind::Parameter)),
-                    show_other_hints: new_allowed_hint_kinds.contains(&None),
-                })
-            });
-            cx.executor().run_until_parked();
-            _ = editor.update(cx, |editor, cx| {
-                assert_eq!(
-                    lsp_request_count.load(Ordering::Relaxed),
-                    2,
-                    "Should not load new hints on allowed hint kinds change for hint kinds {new_allowed_hint_kinds:?}"
-                );
-                assert_eq!(
-                    vec![
-                        "other hint".to_string(),
-                        "parameter hint".to_string(),
-                        "type hint".to_string(),
-                    ],
-                    cached_hint_labels(editor),
-                    "Should get its cached hints unchanged after the settings change for hint kinds {new_allowed_hint_kinds:?}"
-                );
-                assert_eq!(
-                    expected_visible_hints,
-                    visible_hint_labels(editor, cx),
-                    "Should get its visible hints filtered after the settings change for hint kinds {new_allowed_hint_kinds:?}"
-                );
-                let inlay_cache = editor.inlay_hint_cache();
-                assert_eq!(
-                    inlay_cache.allowed_hint_kinds, new_allowed_hint_kinds,
-                    "Cache should use editor settings to get the allowed hint kinds for hint kinds {new_allowed_hint_kinds:?}"
-                );
-                assert_eq!(
-                    inlay_cache.version, edits_made,
-                    "The editor should update the cache version after every cache/view change for hint kinds {new_allowed_hint_kinds:?} due to visible hints change"
-                );
-            });
-        }
-
-        edits_made += 1;
-        let another_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Type)]);
-        update_test_language_settings(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: false,
-                show_type_hints: another_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
-                show_parameter_hints: another_allowed_hint_kinds
-                    .contains(&Some(InlayHintKind::Parameter)),
-                show_other_hints: another_allowed_hint_kinds.contains(&None),
-            })
-        });
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-            assert_eq!(
-                lsp_request_count.load(Ordering::Relaxed),
-                2,
-                "Should not load new hints when hints got disabled"
-            );
-            assert!(
-                cached_hint_labels(editor).is_empty(),
-                "Should clear the cache when hints got disabled"
-            );
-            assert!(
-                visible_hint_labels(editor, cx).is_empty(),
-                "Should clear visible hints when hints got disabled"
-            );
-            let inlay_cache = editor.inlay_hint_cache();
-            assert_eq!(
-                inlay_cache.allowed_hint_kinds, another_allowed_hint_kinds,
-                "Should update its allowed hint kinds even when hints got disabled"
-            );
-            assert_eq!(
-                inlay_cache.version, edits_made,
-                "The editor should update the cache version after hints got disabled"
-            );
-        });
-
-        fake_server
-            .request::<lsp::request::InlayHintRefreshRequest>(())
-            .await
-            .expect("inlay refresh request failed");
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-            assert_eq!(
-                lsp_request_count.load(Ordering::Relaxed),
-                2,
-                "Should not load new hints when they got disabled"
-            );
-            assert!(cached_hint_labels(editor).is_empty());
-            assert!(visible_hint_labels(editor, cx).is_empty());
-            assert_eq!(
-                editor.inlay_hint_cache().version, edits_made,
-                "The editor should not update the cache version after /refresh query without updates"
-            );
-        });
-
-        let final_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Parameter)]);
-        edits_made += 1;
-        update_test_language_settings(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: final_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
-                show_parameter_hints: final_allowed_hint_kinds
-                    .contains(&Some(InlayHintKind::Parameter)),
-                show_other_hints: final_allowed_hint_kinds.contains(&None),
-            })
-        });
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-            assert_eq!(
-                lsp_request_count.load(Ordering::Relaxed),
-                3,
-                "Should query for new hints when they got reenabled"
-            );
-            assert_eq!(
-                vec![
-                    "other hint".to_string(),
-                    "parameter hint".to_string(),
-                    "type hint".to_string(),
-                ],
-                cached_hint_labels(editor),
-                "Should get its cached hints fully repopulated after the hints got reenabled"
-            );
-            assert_eq!(
-                vec!["parameter hint".to_string()],
-                visible_hint_labels(editor, cx),
-                "Should get its visible hints repopulated and filtered after the h"
-            );
-            let inlay_cache = editor.inlay_hint_cache();
-            assert_eq!(
-                inlay_cache.allowed_hint_kinds, final_allowed_hint_kinds,
-                "Cache should update editor settings when hints got reenabled"
-            );
-            assert_eq!(
-                inlay_cache.version, edits_made,
-                "Cache should update its version after hints got reenabled"
-            );
-        });
-
-        fake_server
-            .request::<lsp::request::InlayHintRefreshRequest>(())
-            .await
-            .expect("inlay refresh request failed");
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-            assert_eq!(
-                lsp_request_count.load(Ordering::Relaxed),
-                4,
-                "Should query for new hints again"
-            );
-            assert_eq!(
-                vec![
-                    "other hint".to_string(),
-                    "parameter hint".to_string(),
-                    "type hint".to_string(),
-                ],
-                cached_hint_labels(editor),
-            );
-            assert_eq!(
-                vec!["parameter hint".to_string()],
-                visible_hint_labels(editor, cx),
-            );
-            assert_eq!(editor.inlay_hint_cache().version, edits_made);
-        });
-    }
-
-    #[gpui::test]
-    async fn test_hint_request_cancellation(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-
-        let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
-        let fake_server = Arc::new(fake_server);
-        let lsp_request_count = Arc::new(AtomicU32::new(0));
-        let another_lsp_request_count = Arc::clone(&lsp_request_count);
-        fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_lsp_request_count = Arc::clone(&another_lsp_request_count);
-                async move {
-                    let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst) + 1;
-                    assert_eq!(
-                        params.text_document.uri,
-                        lsp::Url::from_file_path(file_with_hints).unwrap(),
-                    );
-                    Ok(Some(vec![lsp::InlayHint {
-                        position: lsp::Position::new(0, i),
-                        label: lsp::InlayHintLabel::String(i.to_string()),
-                        kind: None,
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: None,
-                        padding_right: None,
-                        data: None,
-                    }]))
-                }
-            })
-            .next()
-            .await;
-
-        let mut expected_changes = Vec::new();
-        for change_after_opening in [
-            "initial change #1",
-            "initial change #2",
-            "initial change #3",
-        ] {
-            _ = editor.update(cx, |editor, cx| {
-                editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-                editor.handle_input(change_after_opening, cx);
-            });
-            expected_changes.push(change_after_opening);
-        }
-
-        cx.executor().run_until_parked();
-
-        _ = editor.update(cx, |editor, cx| {
-            let current_text = editor.text(cx);
-            for change in &expected_changes {
-                assert!(
-                    current_text.contains(change),
-                    "Should apply all changes made"
-                );
-            }
-            assert_eq!(
-                lsp_request_count.load(Ordering::Relaxed),
-                2,
-                "Should query new hints twice: for editor init and for the last edit that interrupted all others"
-            );
-            let expected_hints = vec!["2".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should get hints from the last edit landed only"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(
-                editor.inlay_hint_cache().version, 1,
-                "Only one update should be registered in the cache after all cancellations"
-            );
-        });
-
-        let mut edits = Vec::new();
-        for async_later_change in [
-            "another change #1",
-            "another change #2",
-            "another change #3",
-        ] {
-            expected_changes.push(async_later_change);
-            let task_editor = editor.clone();
-            edits.push(cx.spawn(|mut cx| async move {
-                _ = task_editor.update(&mut cx, |editor, cx| {
-                    editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-                    editor.handle_input(async_later_change, cx);
-                });
-            }));
-        }
-        let _ = future::join_all(edits).await;
-        cx.executor().run_until_parked();
-
-        _ = editor.update(cx, |editor, cx| {
-            let current_text = editor.text(cx);
-            for change in &expected_changes {
-                assert!(
-                    current_text.contains(change),
-                    "Should apply all changes made"
-                );
-            }
-            assert_eq!(
-                lsp_request_count.load(Ordering::SeqCst),
-                3,
-                "Should query new hints one more time, for the last edit only"
-            );
-            let expected_hints = vec!["3".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should get hints from the last edit landed only"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                2,
-                "Should update the cache version once more, for the new change"
-            );
-        });
-    }
-
-    #[gpui::test(iterations = 10)]
-    async fn test_large_buffer_inlay_requests_split(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-
-        let mut language = Language::new(
-            LanguageConfig {
-                name: "Rust".into(),
-                path_suffixes: vec!["rs".to_string()],
-                ..Default::default()
-            },
-            Some(tree_sitter_rust::language()),
-        );
-        let mut fake_servers = language
-            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-                capabilities: lsp::ServerCapabilities {
-                    inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-                    ..Default::default()
-                },
-                ..Default::default()
-            }))
-            .await;
-        let fs = FakeFs::new(cx.background_executor.clone());
-        fs.insert_tree(
-            "/a",
-            json!({
-                "main.rs": format!("fn main() {{\n{}\n}}", "let i = 5;\n".repeat(500)),
-                "other.rs": "// Test file",
-            }),
-        )
-        .await;
-        let project = Project::test(fs, ["/a".as_ref()], cx).await;
-        project.update(cx, |project, _| project.languages().add(Arc::new(language)));
-        let buffer = project
-            .update(cx, |project, cx| {
-                project.open_local_buffer("/a/main.rs", cx)
-            })
-            .await
-            .unwrap();
-        cx.executor().run_until_parked();
-        cx.executor().start_waiting();
-        let fake_server = fake_servers.next().await.unwrap();
-        let editor = cx.add_window(|cx| Editor::for_buffer(buffer, Some(project), cx));
-        let lsp_request_ranges = Arc::new(Mutex::new(Vec::new()));
-        let lsp_request_count = Arc::new(AtomicUsize::new(0));
-        let closure_lsp_request_ranges = Arc::clone(&lsp_request_ranges);
-        let closure_lsp_request_count = Arc::clone(&lsp_request_count);
-        fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_lsp_request_ranges = Arc::clone(&closure_lsp_request_ranges);
-                let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
-                async move {
-                    assert_eq!(
-                        params.text_document.uri,
-                        lsp::Url::from_file_path("/a/main.rs").unwrap(),
-                    );
-
-                    task_lsp_request_ranges.lock().push(params.range);
-                    let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::Release) + 1;
-                    Ok(Some(vec![lsp::InlayHint {
-                        position: params.range.end,
-                        label: lsp::InlayHintLabel::String(i.to_string()),
-                        kind: None,
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: None,
-                        padding_right: None,
-                        data: None,
-                    }]))
-                }
-            })
-            .next()
-            .await;
-
-        fn editor_visible_range(
-            editor: &WindowHandle<Editor>,
-            cx: &mut gpui::TestAppContext,
-        ) -> Range<Point> {
-            let ranges = editor
-                .update(cx, |editor, cx| {
-                    editor.excerpts_for_inlay_hints_query(None, cx)
-                })
-                .unwrap();
-            assert_eq!(
-                ranges.len(),
-                1,
-                "Single buffer should produce a single excerpt with visible range"
-            );
-            let (_, (excerpt_buffer, _, excerpt_visible_range)) =
-                ranges.into_iter().next().unwrap();
-            excerpt_buffer.update(cx, |buffer, _| {
-                let snapshot = buffer.snapshot();
-                let start = buffer
-                    .anchor_before(excerpt_visible_range.start)
-                    .to_point(&snapshot);
-                let end = buffer
-                    .anchor_after(excerpt_visible_range.end)
-                    .to_point(&snapshot);
-                start..end
-            })
-        }
-
-        // in large buffers, requests are made for more than visible range of a buffer.
-        // invisible parts are queried later, to avoid excessive requests on quick typing.
-        // wait the timeout needed to get all requests.
-        cx.executor().advance_clock(Duration::from_millis(
-            INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
-        ));
-        cx.executor().run_until_parked();
-        let initial_visible_range = editor_visible_range(&editor, cx);
-        let lsp_initial_visible_range = lsp::Range::new(
-            lsp::Position::new(
-                initial_visible_range.start.row,
-                initial_visible_range.start.column,
-            ),
-            lsp::Position::new(
-                initial_visible_range.end.row,
-                initial_visible_range.end.column,
-            ),
-        );
-        let expected_initial_query_range_end =
-            lsp::Position::new(initial_visible_range.end.row * 2, 2);
-        let mut expected_invisible_query_start = lsp_initial_visible_range.end;
-        expected_invisible_query_start.character += 1;
-        _ = editor.update(cx, |editor, cx| {
-            let ranges = lsp_request_ranges.lock().drain(..).collect::<Vec<_>>();
-            assert_eq!(ranges.len(), 2,
-                "When scroll is at the edge of a big document, its visible part and the same range further should be queried in order, but got: {ranges:?}");
-            let visible_query_range = &ranges[0];
-            assert_eq!(visible_query_range.start, lsp_initial_visible_range.start);
-            assert_eq!(visible_query_range.end, lsp_initial_visible_range.end);
-            let invisible_query_range = &ranges[1];
-
-            assert_eq!(invisible_query_range.start, expected_invisible_query_start, "Should initially query visible edge of the document");
-            assert_eq!(invisible_query_range.end, expected_initial_query_range_end, "Should initially query visible edge of the document");
-
-            let requests_count = lsp_request_count.load(Ordering::Acquire);
-            assert_eq!(requests_count, 2, "Visible + invisible request");
-            let expected_hints = vec!["1".to_string(), "2".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should have hints from both LSP requests made for a big file"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx), "Should display only hints from the visible range");
-            assert_eq!(
-                editor.inlay_hint_cache().version, requests_count,
-                "LSP queries should've bumped the cache version"
-            );
-        });
-
-        _ = editor.update(cx, |editor, cx| {
-            editor.scroll_screen(&ScrollAmount::Page(1.0), cx);
-            editor.scroll_screen(&ScrollAmount::Page(1.0), cx);
-        });
-        cx.executor().advance_clock(Duration::from_millis(
-            INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
-        ));
-        cx.executor().run_until_parked();
-        let visible_range_after_scrolls = editor_visible_range(&editor, cx);
-        let visible_line_count = editor
-            .update(cx, |editor, _| editor.visible_line_count().unwrap())
-            .unwrap();
-        let selection_in_cached_range = editor
-            .update(cx, |editor, cx| {
-                let ranges = lsp_request_ranges
-                    .lock()
-                    .drain(..)
-                    .sorted_by_key(|r| r.start)
-                    .collect::<Vec<_>>();
-                assert_eq!(
-                    ranges.len(),
-                    2,
-                    "Should query 2 ranges after both scrolls, but got: {ranges:?}"
-                );
-                let first_scroll = &ranges[0];
-                let second_scroll = &ranges[1];
-                assert_eq!(
-                    first_scroll.end, second_scroll.start,
-                    "Should query 2 adjacent ranges after the scrolls, but got: {ranges:?}"
-                );
-                assert_eq!(
-                first_scroll.start, expected_initial_query_range_end,
-                "First scroll should start the query right after the end of the original scroll",
-            );
-                assert_eq!(
-                second_scroll.end,
-                lsp::Position::new(
-                    visible_range_after_scrolls.end.row
-                        + visible_line_count.ceil() as u32,
-                    1,
-                ),
-                "Second scroll should query one more screen down after the end of the visible range"
-            );
-
-                let lsp_requests = lsp_request_count.load(Ordering::Acquire);
-                assert_eq!(lsp_requests, 4, "Should query for hints after every scroll");
-                let expected_hints = vec![
-                    "1".to_string(),
-                    "2".to_string(),
-                    "3".to_string(),
-                    "4".to_string(),
-                ];
-                assert_eq!(
-                    expected_hints,
-                    cached_hint_labels(editor),
-                    "Should have hints from the new LSP response after the edit"
-                );
-                assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-                assert_eq!(
-                    editor.inlay_hint_cache().version,
-                    lsp_requests,
-                    "Should update the cache for every LSP response with hints added"
-                );
-
-                let mut selection_in_cached_range = visible_range_after_scrolls.end;
-                selection_in_cached_range.row -= visible_line_count.ceil() as u32;
-                selection_in_cached_range
-            })
-            .unwrap();
-
-        _ = editor.update(cx, |editor, cx| {
-            editor.change_selections(Some(Autoscroll::center()), cx, |s| {
-                s.select_ranges([selection_in_cached_range..selection_in_cached_range])
-            });
-        });
-        cx.executor().advance_clock(Duration::from_millis(
-            INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
-        ));
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |_, _| {
-            let ranges = lsp_request_ranges
-                .lock()
-                .drain(..)
-                .sorted_by_key(|r| r.start)
-                .collect::<Vec<_>>();
-            assert!(ranges.is_empty(), "No new ranges or LSP queries should be made after returning to the selection with cached hints");
-            assert_eq!(lsp_request_count.load(Ordering::Acquire), 4);
-        });
-
-        _ = editor.update(cx, |editor, cx| {
-            editor.handle_input("++++more text++++", cx);
-        });
-        cx.executor().advance_clock(Duration::from_millis(
-            INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
-        ));
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-            let mut ranges = lsp_request_ranges.lock().drain(..).collect::<Vec<_>>();
-            ranges.sort_by_key(|r| r.start);
-
-            assert_eq!(ranges.len(), 3,
-                "On edit, should scroll to selection and query a range around it: visible + same range above and below. Instead, got query ranges {ranges:?}");
-            let above_query_range = &ranges[0];
-            let visible_query_range = &ranges[1];
-            let below_query_range = &ranges[2];
-            assert!(above_query_range.end.character < visible_query_range.start.character || above_query_range.end.line + 1 == visible_query_range.start.line,
-                "Above range {above_query_range:?} should be before visible range {visible_query_range:?}");
-            assert!(visible_query_range.end.character < below_query_range.start.character || visible_query_range.end.line  + 1 == below_query_range.start.line,
-                "Visible range {visible_query_range:?} should be before below range {below_query_range:?}");
-            assert!(above_query_range.start.line < selection_in_cached_range.row,
-                "Hints should be queried with the selected range after the query range start");
-            assert!(below_query_range.end.line > selection_in_cached_range.row,
-                "Hints should be queried with the selected range before the query range end");
-            assert!(above_query_range.start.line <= selection_in_cached_range.row - (visible_line_count * 3.0 / 2.0) as u32,
-                "Hints query range should contain one more screen before");
-            assert!(below_query_range.end.line >= selection_in_cached_range.row + (visible_line_count * 3.0 / 2.0) as u32,
-                "Hints query range should contain one more screen after");
-
-            let lsp_requests = lsp_request_count.load(Ordering::Acquire);
-            assert_eq!(lsp_requests, 7, "There should be a visible range and two ranges above and below it queried");
-            let expected_hints = vec!["5".to_string(), "6".to_string(), "7".to_string()];
-            assert_eq!(expected_hints, cached_hint_labels(editor),
-                "Should have hints from the new LSP response after the edit");
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, lsp_requests, "Should update the cache for every LSP response with hints added");
-        });
-    }
-
-    #[gpui::test(iterations = 10)]
-    async fn test_multiple_excerpts_large_multibuffer(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-
-        let mut language = Language::new(
-            LanguageConfig {
-                name: "Rust".into(),
-                path_suffixes: vec!["rs".to_string()],
-                ..Default::default()
-            },
-            Some(tree_sitter_rust::language()),
-        );
-        let mut fake_servers = language
-            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-                capabilities: lsp::ServerCapabilities {
-                    inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-                    ..Default::default()
-                },
-                ..Default::default()
-            }))
-            .await;
-        let language = Arc::new(language);
-        let fs = FakeFs::new(cx.background_executor.clone());
-        fs.insert_tree(
-                "/a",
-                json!({
-                    "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")),
-                    "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")),
-                }),
-            )
-            .await;
-        let project = Project::test(fs, ["/a".as_ref()], cx).await;
-        project.update(cx, |project, _| {
-            project.languages().add(Arc::clone(&language))
-        });
-        let worktree_id = project.update(cx, |project, cx| {
-            project.worktrees().next().unwrap().read(cx).id()
-        });
-
-        let buffer_1 = project
-            .update(cx, |project, cx| {
-                project.open_buffer((worktree_id, "main.rs"), cx)
-            })
-            .await
-            .unwrap();
-        let buffer_2 = project
-            .update(cx, |project, cx| {
-                project.open_buffer((worktree_id, "other.rs"), cx)
-            })
-            .await
-            .unwrap();
-        let multibuffer = cx.new_model(|cx| {
-            let mut multibuffer = MultiBuffer::new(0);
-            multibuffer.push_excerpts(
-                buffer_1.clone(),
-                [
-                    ExcerptRange {
-                        context: Point::new(0, 0)..Point::new(2, 0),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(4, 0)..Point::new(11, 0),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(22, 0)..Point::new(33, 0),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(44, 0)..Point::new(55, 0),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(56, 0)..Point::new(66, 0),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(67, 0)..Point::new(77, 0),
-                        primary: None,
-                    },
-                ],
-                cx,
-            );
-            multibuffer.push_excerpts(
-                buffer_2.clone(),
-                [
-                    ExcerptRange {
-                        context: Point::new(0, 1)..Point::new(2, 1),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(4, 1)..Point::new(11, 1),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(22, 1)..Point::new(33, 1),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(44, 1)..Point::new(55, 1),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(56, 1)..Point::new(66, 1),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(67, 1)..Point::new(77, 1),
-                        primary: None,
-                    },
-                ],
-                cx,
-            );
-            multibuffer
-        });
-
-        cx.executor().run_until_parked();
-        let editor =
-            cx.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx));
-        let editor_edited = Arc::new(AtomicBool::new(false));
-        let fake_server = fake_servers.next().await.unwrap();
-        let closure_editor_edited = Arc::clone(&editor_edited);
-        fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_editor_edited = Arc::clone(&closure_editor_edited);
-                async move {
-                    let hint_text = if params.text_document.uri
-                        == lsp::Url::from_file_path("/a/main.rs").unwrap()
-                    {
-                        "main hint"
-                    } else if params.text_document.uri
-                        == lsp::Url::from_file_path("/a/other.rs").unwrap()
-                    {
-                        "other hint"
-                    } else {
-                        panic!("unexpected uri: {:?}", params.text_document.uri);
-                    };
-
-                    // one hint per excerpt
-                    let positions = [
-                        lsp::Position::new(0, 2),
-                        lsp::Position::new(4, 2),
-                        lsp::Position::new(22, 2),
-                        lsp::Position::new(44, 2),
-                        lsp::Position::new(56, 2),
-                        lsp::Position::new(67, 2),
-                    ];
-                    let out_of_range_hint = lsp::InlayHint {
-                        position: lsp::Position::new(
-                            params.range.start.line + 99,
-                            params.range.start.character + 99,
-                        ),
-                        label: lsp::InlayHintLabel::String(
-                            "out of excerpt range, should be ignored".to_string(),
-                        ),
-                        kind: None,
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: None,
-                        padding_right: None,
-                        data: None,
-                    };
-
-                    let edited = task_editor_edited.load(Ordering::Acquire);
-                    Ok(Some(
-                        std::iter::once(out_of_range_hint)
-                            .chain(positions.into_iter().enumerate().map(|(i, position)| {
-                                lsp::InlayHint {
-                                    position,
-                                    label: lsp::InlayHintLabel::String(format!(
-                                        "{hint_text}{} #{i}",
-                                        if edited { "(edited)" } else { "" },
-                                    )),
-                                    kind: None,
-                                    text_edits: None,
-                                    tooltip: None,
-                                    padding_left: None,
-                                    padding_right: None,
-                                    data: None,
-                                }
-                            }))
-                            .collect(),
-                    ))
-                }
-            })
-            .next()
-            .await;
-        cx.executor().run_until_parked();
-
-        _ = editor.update(cx, |editor, cx| {
-                let expected_hints = vec![
-                    "main hint #0".to_string(),
-                    "main hint #1".to_string(),
-                    "main hint #2".to_string(),
-                    "main hint #3".to_string(),
-                    "main hint #4".to_string(),
-                    "main hint #5".to_string(),
-                ];
-                assert_eq!(
-                    expected_hints,
-                    cached_hint_labels(editor),
-                    "When scroll is at the edge of a multibuffer, its visible excerpts only should be queried for inlay hints"
-                );
-                assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-                assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(), "Every visible excerpt hints should bump the verison");
-            });
-
-        _ = editor.update(cx, |editor, cx| {
-            editor.change_selections(Some(Autoscroll::Next), cx, |s| {
-                s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
-            });
-            editor.change_selections(Some(Autoscroll::Next), cx, |s| {
-                s.select_ranges([Point::new(22, 0)..Point::new(22, 0)])
-            });
-            editor.change_selections(Some(Autoscroll::Next), cx, |s| {
-                s.select_ranges([Point::new(50, 0)..Point::new(50, 0)])
-            });
-        });
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-                let expected_hints = vec![
-                    "main hint #0".to_string(),
-                    "main hint #1".to_string(),
-                    "main hint #2".to_string(),
-                    "main hint #3".to_string(),
-                    "main hint #4".to_string(),
-                    "main hint #5".to_string(),
-                    "other hint #0".to_string(),
-                    "other hint #1".to_string(),
-                    "other hint #2".to_string(),
-                ];
-                assert_eq!(expected_hints, cached_hint_labels(editor),
-                    "With more scrolls of the multibuffer, more hints should be added into the cache and nothing invalidated without edits");
-                assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-                assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(),
-                    "Due to every excerpt having one hint, we update cache per new excerpt scrolled");
-            });
-
-        _ = editor.update(cx, |editor, cx| {
-            editor.change_selections(Some(Autoscroll::Next), cx, |s| {
-                s.select_ranges([Point::new(100, 0)..Point::new(100, 0)])
-            });
-        });
-        cx.executor().advance_clock(Duration::from_millis(
-            INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
-        ));
-        cx.executor().run_until_parked();
-        let last_scroll_update_version = editor.update(cx, |editor, cx| {
-                let expected_hints = vec![
-                    "main hint #0".to_string(),
-                    "main hint #1".to_string(),
-                    "main hint #2".to_string(),
-                    "main hint #3".to_string(),
-                    "main hint #4".to_string(),
-                    "main hint #5".to_string(),
-                    "other hint #0".to_string(),
-                    "other hint #1".to_string(),
-                    "other hint #2".to_string(),
-                    "other hint #3".to_string(),
-                    "other hint #4".to_string(),
-                    "other hint #5".to_string(),
-                ];
-                assert_eq!(expected_hints, cached_hint_labels(editor),
-                    "After multibuffer was scrolled to the end, all hints for all excerpts should be fetched");
-                assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-                assert_eq!(editor.inlay_hint_cache().version, expected_hints.len());
-                expected_hints.len()
-            }).unwrap();
-
-        _ = editor.update(cx, |editor, cx| {
-            editor.change_selections(Some(Autoscroll::Next), cx, |s| {
-                s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
-            });
-        });
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-                let expected_hints = vec![
-                    "main hint #0".to_string(),
-                    "main hint #1".to_string(),
-                    "main hint #2".to_string(),
-                    "main hint #3".to_string(),
-                    "main hint #4".to_string(),
-                    "main hint #5".to_string(),
-                    "other hint #0".to_string(),
-                    "other hint #1".to_string(),
-                    "other hint #2".to_string(),
-                    "other hint #3".to_string(),
-                    "other hint #4".to_string(),
-                    "other hint #5".to_string(),
-                ];
-                assert_eq!(expected_hints, cached_hint_labels(editor),
-                    "After multibuffer was scrolled to the end, further scrolls up should not bring more hints");
-                assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-                assert_eq!(editor.inlay_hint_cache().version, last_scroll_update_version, "No updates should happen during scrolling already scolled buffer");
-            });
-
-        editor_edited.store(true, Ordering::Release);
-        _ = editor.update(cx, |editor, cx| {
-            editor.change_selections(None, cx, |s| {
-                // TODO if this gets set to hint boundary (e.g. 56) we sometimes get an extra cache version bump, why?
-                s.select_ranges([Point::new(57, 0)..Point::new(57, 0)])
-            });
-            editor.handle_input("++++more text++++", cx);
-        });
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-            let expected_hints = vec![
-                "main hint(edited) #0".to_string(),
-                "main hint(edited) #1".to_string(),
-                "main hint(edited) #2".to_string(),
-                "main hint(edited) #3".to_string(),
-                "main hint(edited) #4".to_string(),
-                "main hint(edited) #5".to_string(),
-                "other hint(edited) #0".to_string(),
-                "other hint(edited) #1".to_string(),
-            ];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "After multibuffer edit, editor gets scolled back to the last selection; \
-    all hints should be invalidated and requeried for all of its visible excerpts"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-
-            let current_cache_version = editor.inlay_hint_cache().version;
-            assert_eq!(
-                current_cache_version,
-                last_scroll_update_version + expected_hints.len(),
-                "We should have updated cache N times == N of new hints arrived (separately from each excerpt)"
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_excerpts_removed(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: false,
-                show_parameter_hints: false,
-                show_other_hints: false,
-            })
-        });
-
-        let mut language = Language::new(
-            LanguageConfig {
-                name: "Rust".into(),
-                path_suffixes: vec!["rs".to_string()],
-                ..Default::default()
-            },
-            Some(tree_sitter_rust::language()),
-        );
-        let mut fake_servers = language
-            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-                capabilities: lsp::ServerCapabilities {
-                    inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-                    ..Default::default()
-                },
-                ..Default::default()
-            }))
-            .await;
-        let language = Arc::new(language);
-        let fs = FakeFs::new(cx.background_executor.clone());
-        fs.insert_tree(
-            "/a",
-            json!({
-                "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")),
-                "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")),
-            }),
-        )
-        .await;
-        let project = Project::test(fs, ["/a".as_ref()], cx).await;
-        project.update(cx, |project, _| {
-            project.languages().add(Arc::clone(&language))
-        });
-        let worktree_id = project.update(cx, |project, cx| {
-            project.worktrees().next().unwrap().read(cx).id()
-        });
-
-        let buffer_1 = project
-            .update(cx, |project, cx| {
-                project.open_buffer((worktree_id, "main.rs"), cx)
-            })
-            .await
-            .unwrap();
-        let buffer_2 = project
-            .update(cx, |project, cx| {
-                project.open_buffer((worktree_id, "other.rs"), cx)
-            })
-            .await
-            .unwrap();
-        let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
-        let (buffer_1_excerpts, buffer_2_excerpts) = multibuffer.update(cx, |multibuffer, cx| {
-            let buffer_1_excerpts = multibuffer.push_excerpts(
-                buffer_1.clone(),
-                [ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(2, 0),
-                    primary: None,
-                }],
-                cx,
-            );
-            let buffer_2_excerpts = multibuffer.push_excerpts(
-                buffer_2.clone(),
-                [ExcerptRange {
-                    context: Point::new(0, 1)..Point::new(2, 1),
-                    primary: None,
-                }],
-                cx,
-            );
-            (buffer_1_excerpts, buffer_2_excerpts)
-        });
-
-        assert!(!buffer_1_excerpts.is_empty());
-        assert!(!buffer_2_excerpts.is_empty());
-
-        cx.executor().run_until_parked();
-        let editor =
-            cx.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx));
-        let editor_edited = Arc::new(AtomicBool::new(false));
-        let fake_server = fake_servers.next().await.unwrap();
-        let closure_editor_edited = Arc::clone(&editor_edited);
-        fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_editor_edited = Arc::clone(&closure_editor_edited);
-                async move {
-                    let hint_text = if params.text_document.uri
-                        == lsp::Url::from_file_path("/a/main.rs").unwrap()
-                    {
-                        "main hint"
-                    } else if params.text_document.uri
-                        == lsp::Url::from_file_path("/a/other.rs").unwrap()
-                    {
-                        "other hint"
-                    } else {
-                        panic!("unexpected uri: {:?}", params.text_document.uri);
-                    };
-
-                    let positions = [
-                        lsp::Position::new(0, 2),
-                        lsp::Position::new(4, 2),
-                        lsp::Position::new(22, 2),
-                        lsp::Position::new(44, 2),
-                        lsp::Position::new(56, 2),
-                        lsp::Position::new(67, 2),
-                    ];
-                    let out_of_range_hint = lsp::InlayHint {
-                        position: lsp::Position::new(
-                            params.range.start.line + 99,
-                            params.range.start.character + 99,
-                        ),
-                        label: lsp::InlayHintLabel::String(
-                            "out of excerpt range, should be ignored".to_string(),
-                        ),
-                        kind: None,
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: None,
-                        padding_right: None,
-                        data: None,
-                    };
-
-                    let edited = task_editor_edited.load(Ordering::Acquire);
-                    Ok(Some(
-                        std::iter::once(out_of_range_hint)
-                            .chain(positions.into_iter().enumerate().map(|(i, position)| {
-                                lsp::InlayHint {
-                                    position,
-                                    label: lsp::InlayHintLabel::String(format!(
-                                        "{hint_text}{} #{i}",
-                                        if edited { "(edited)" } else { "" },
-                                    )),
-                                    kind: None,
-                                    text_edits: None,
-                                    tooltip: None,
-                                    padding_left: None,
-                                    padding_right: None,
-                                    data: None,
-                                }
-                            }))
-                            .collect(),
-                    ))
-                }
-            })
-            .next()
-            .await;
-        cx.executor().run_until_parked();
-
-        _ = editor.update(cx, |editor, cx| {
-            assert_eq!(
-                vec!["main hint #0".to_string(), "other hint #0".to_string()],
-                cached_hint_labels(editor),
-                "Cache should update for both excerpts despite hints display was disabled"
-            );
-            assert!(
-                visible_hint_labels(editor, cx).is_empty(),
-                "All hints are disabled and should not be shown despite being present in the cache"
-            );
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                2,
-                "Cache should update once per excerpt query"
-            );
-        });
-
-        _ = editor.update(cx, |editor, cx| {
-            editor.buffer().update(cx, |multibuffer, cx| {
-                multibuffer.remove_excerpts(buffer_2_excerpts, cx)
-            })
-        });
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-            assert_eq!(
-                vec!["main hint #0".to_string()],
-                cached_hint_labels(editor),
-                "For the removed excerpt, should clean corresponding cached hints"
-            );
-            assert!(
-                visible_hint_labels(editor, cx).is_empty(),
-                "All hints are disabled and should not be shown despite being present in the cache"
-            );
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                3,
-                "Excerpt removal should trigger a cache update"
-            );
-        });
-
-        update_test_language_settings(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["main hint #0".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Hint display settings change should not change the cache"
-            );
-            assert_eq!(
-                expected_hints,
-                visible_hint_labels(editor, cx),
-                "Settings change should make cached hints visible"
-            );
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                4,
-                "Settings change should trigger a cache update"
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_inside_char_boundary_range_hints(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-
-        let mut language = Language::new(
-            LanguageConfig {
-                name: "Rust".into(),
-                path_suffixes: vec!["rs".to_string()],
-                ..Default::default()
-            },
-            Some(tree_sitter_rust::language()),
-        );
-        let mut fake_servers = language
-            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-                capabilities: lsp::ServerCapabilities {
-                    inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-                    ..Default::default()
-                },
-                ..Default::default()
-            }))
-            .await;
-        let fs = FakeFs::new(cx.background_executor.clone());
-        fs.insert_tree(
-            "/a",
-            json!({
-                "main.rs": format!(r#"fn main() {{\n{}\n}}"#, format!("let i = {};\n", "โˆš".repeat(10)).repeat(500)),
-                "other.rs": "// Test file",
-            }),
-        )
-        .await;
-        let project = Project::test(fs, ["/a".as_ref()], cx).await;
-        project.update(cx, |project, _| project.languages().add(Arc::new(language)));
-        let buffer = project
-            .update(cx, |project, cx| {
-                project.open_local_buffer("/a/main.rs", cx)
-            })
-            .await
-            .unwrap();
-        cx.executor().run_until_parked();
-        cx.executor().start_waiting();
-        let fake_server = fake_servers.next().await.unwrap();
-        let editor = cx.add_window(|cx| Editor::for_buffer(buffer, Some(project), cx));
-        let lsp_request_count = Arc::new(AtomicU32::new(0));
-        let closure_lsp_request_count = Arc::clone(&lsp_request_count);
-        fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
-                async move {
-                    assert_eq!(
-                        params.text_document.uri,
-                        lsp::Url::from_file_path("/a/main.rs").unwrap(),
-                    );
-                    let query_start = params.range.start;
-                    let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::Release) + 1;
-                    Ok(Some(vec![lsp::InlayHint {
-                        position: query_start,
-                        label: lsp::InlayHintLabel::String(i.to_string()),
-                        kind: None,
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: None,
-                        padding_right: None,
-                        data: None,
-                    }]))
-                }
-            })
-            .next()
-            .await;
-
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-            editor.change_selections(None, cx, |s| {
-                s.select_ranges([Point::new(10, 0)..Point::new(10, 0)])
-            })
-        });
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["1".to_string()];
-            assert_eq!(expected_hints, cached_hint_labels(editor));
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, 1);
-        });
-    }
-
-    #[gpui::test]
-    async fn test_toggle_inlay_hints(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: false,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-
-        let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
-
-        _ = editor.update(cx, |editor, cx| {
-            editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
-        });
-        cx.executor().start_waiting();
-        let lsp_request_count = Arc::new(AtomicU32::new(0));
-        let closure_lsp_request_count = Arc::clone(&lsp_request_count);
-        fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
-                async move {
-                    assert_eq!(
-                        params.text_document.uri,
-                        lsp::Url::from_file_path(file_with_hints).unwrap(),
-                    );
-
-                    let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst) + 1;
-                    Ok(Some(vec![lsp::InlayHint {
-                        position: lsp::Position::new(0, i),
-                        label: lsp::InlayHintLabel::String(i.to_string()),
-                        kind: None,
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: None,
-                        padding_right: None,
-                        data: None,
-                    }]))
-                }
-            })
-            .next()
-            .await;
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["1".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should display inlays after toggle despite them disabled in settings"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                1,
-                "First toggle should be cache's first update"
-            );
-        });
-
-        _ = editor.update(cx, |editor, cx| {
-            editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
-        });
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-            assert!(
-                cached_hint_labels(editor).is_empty(),
-                "Should clear hints after 2nd toggle"
-            );
-            assert!(visible_hint_labels(editor, cx).is_empty());
-            assert_eq!(editor.inlay_hint_cache().version, 2);
-        });
-
-        update_test_language_settings(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["2".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should query LSP hints for the 2nd time after enabling hints in settings"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, 3);
-        });
-
-        _ = editor.update(cx, |editor, cx| {
-            editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
-        });
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-            assert!(
-                cached_hint_labels(editor).is_empty(),
-                "Should clear hints after enabling in settings and a 3rd toggle"
-            );
-            assert!(visible_hint_labels(editor, cx).is_empty());
-            assert_eq!(editor.inlay_hint_cache().version, 4);
-        });
-
-        _ = editor.update(cx, |editor, cx| {
-            editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
-        });
-        cx.executor().run_until_parked();
-        _ = editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["3".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should query LSP hints for the 3rd time after enabling hints in settings and toggling them back on"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, 5);
-        });
-    }
-
-    pub(crate) fn init_test(cx: &mut TestAppContext, f: impl Fn(&mut AllLanguageSettingsContent)) {
-        cx.update(|cx| {
-            let settings_store = SettingsStore::test(cx);
-            cx.set_global(settings_store);
-            theme::init(theme::LoadThemes::JustBase, cx);
-            client::init_settings(cx);
-            language::init(cx);
-            Project::init_settings(cx);
-            workspace::init_settings(cx);
-            crate::init(cx);
-        });
-
-        update_test_language_settings(cx, f);
-    }
-
-    async fn prepare_test_objects(
-        cx: &mut TestAppContext,
-    ) -> (&'static str, WindowHandle<Editor>, FakeLanguageServer) {
-        let mut language = Language::new(
-            LanguageConfig {
-                name: "Rust".into(),
-                path_suffixes: vec!["rs".to_string()],
-                ..Default::default()
-            },
-            Some(tree_sitter_rust::language()),
-        );
-        let mut fake_servers = language
-            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-                capabilities: lsp::ServerCapabilities {
-                    inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-                    ..Default::default()
-                },
-                ..Default::default()
-            }))
-            .await;
-
-        let fs = FakeFs::new(cx.background_executor.clone());
-        fs.insert_tree(
-            "/a",
-            json!({
-                "main.rs": "fn main() { a } // and some long comment to ensure inlays are not trimmed out",
-                "other.rs": "// Test file",
-            }),
-        )
-        .await;
-
-        let project = Project::test(fs, ["/a".as_ref()], cx).await;
-        _ = project.update(cx, |project, _| project.languages().add(Arc::new(language)));
-        let buffer = project
-            .update(cx, |project, cx| {
-                project.open_local_buffer("/a/main.rs", cx)
-            })
-            .await
-            .unwrap();
-        cx.executor().run_until_parked();
-        cx.executor().start_waiting();
-        let fake_server = fake_servers.next().await.unwrap();
-        let editor = cx.add_window(|cx| Editor::for_buffer(buffer, Some(project), cx));
-
-        _ = editor.update(cx, |editor, cx| {
-            assert!(cached_hint_labels(editor).is_empty());
-            assert!(visible_hint_labels(editor, cx).is_empty());
-            assert_eq!(editor.inlay_hint_cache().version, 0);
-        });
-
-        ("/a/main.rs", editor, fake_server)
-    }
-
-    pub fn cached_hint_labels(editor: &Editor) -> Vec<String> {
-        let mut labels = Vec::new();
-        for (_, excerpt_hints) in &editor.inlay_hint_cache().hints {
-            let excerpt_hints = excerpt_hints.read();
-            for id in &excerpt_hints.ordered_hints {
-                labels.push(excerpt_hints.hints_by_id[id].text());
-            }
-        }
-
-        labels.sort();
-        labels
-    }
-
-    pub fn visible_hint_labels(editor: &Editor, cx: &ViewContext<'_, Editor>) -> Vec<String> {
-        let mut hints = editor
-            .visible_inlay_hints(cx)
-            .into_iter()
-            .map(|hint| hint.text.to_string())
-            .collect::<Vec<_>>();
-        hints.sort();
-        hints
-    }
-}

crates/editor2/src/items.rs ๐Ÿ”—

@@ -1,1339 +0,0 @@
-use crate::{
-    editor_settings::SeedQuerySetting, link_go_to_definition::hide_link_definition,
-    persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor, EditorEvent, EditorSettings,
-    ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _,
-};
-use anyhow::{anyhow, Context as _, Result};
-use collections::HashSet;
-use futures::future::try_join_all;
-use gpui::{
-    div, point, AnyElement, AppContext, AsyncWindowContext, Context, Entity, EntityId,
-    EventEmitter, IntoElement, Model, ParentElement, Pixels, Render, SharedString, Styled,
-    Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
-};
-use language::{
-    proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, CharKind, OffsetRangeExt,
-    Point, SelectionGoal,
-};
-use project::{search::SearchQuery, FormatTrigger, Item as _, Project, ProjectPath};
-use rpc::proto::{self, update_view, PeerId};
-use settings::Settings;
-
-use std::fmt::Write;
-use std::{
-    borrow::Cow,
-    cmp::{self, Ordering},
-    iter,
-    ops::Range,
-    path::{Path, PathBuf},
-    sync::Arc,
-};
-use text::Selection;
-use theme::{ActiveTheme, Theme};
-use ui::{h_stack, prelude::*, Label};
-use util::{paths::PathExt, paths::FILE_ROW_COLUMN_DELIMITER, ResultExt, TryFutureExt};
-use workspace::{
-    item::{BreadcrumbText, FollowEvent, FollowableItemHandle},
-    StatusItemView,
-};
-use workspace::{
-    item::{FollowableItem, Item, ItemEvent, ItemHandle, ProjectItem},
-    searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle},
-    ItemId, ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace, WorkspaceId,
-};
-
-pub const MAX_TAB_TITLE_LEN: usize = 24;
-
-impl FollowableItem for Editor {
-    fn remote_id(&self) -> Option<ViewId> {
-        self.remote_id
-    }
-
-    fn from_state_proto(
-        pane: View<workspace::Pane>,
-        workspace: View<Workspace>,
-        remote_id: ViewId,
-        state: &mut Option<proto::view::Variant>,
-        cx: &mut WindowContext,
-    ) -> Option<Task<Result<View<Self>>>> {
-        let project = workspace.read(cx).project().to_owned();
-        let Some(proto::view::Variant::Editor(_)) = state else {
-            return None;
-        };
-        let Some(proto::view::Variant::Editor(state)) = state.take() else {
-            unreachable!()
-        };
-
-        let client = project.read(cx).client();
-        let replica_id = project.read(cx).replica_id();
-        let buffer_ids = state
-            .excerpts
-            .iter()
-            .map(|excerpt| excerpt.buffer_id)
-            .collect::<HashSet<_>>();
-        let buffers = project.update(cx, |project, cx| {
-            buffer_ids
-                .iter()
-                .map(|id| project.open_buffer_by_id(*id, cx))
-                .collect::<Vec<_>>()
-        });
-
-        let pane = pane.downgrade();
-        Some(cx.spawn(|mut cx| async move {
-            let mut buffers = futures::future::try_join_all(buffers).await?;
-            let editor = pane.update(&mut cx, |pane, cx| {
-                let mut editors = pane.items_of_type::<Self>();
-                editors.find(|editor| {
-                    let ids_match = editor.remote_id(&client, cx) == Some(remote_id);
-                    let singleton_buffer_matches = state.singleton
-                        && buffers.first()
-                            == editor.read(cx).buffer.read(cx).as_singleton().as_ref();
-                    ids_match || singleton_buffer_matches
-                })
-            })?;
-
-            let editor = if let Some(editor) = editor {
-                editor
-            } else {
-                pane.update(&mut cx, |_, cx| {
-                    let multibuffer = cx.new_model(|cx| {
-                        let mut multibuffer;
-                        if state.singleton && buffers.len() == 1 {
-                            multibuffer = MultiBuffer::singleton(buffers.pop().unwrap(), cx)
-                        } else {
-                            multibuffer = MultiBuffer::new(replica_id);
-                            let mut excerpts = state.excerpts.into_iter().peekable();
-                            while let Some(excerpt) = excerpts.peek() {
-                                let buffer_id = excerpt.buffer_id;
-                                let buffer_excerpts = iter::from_fn(|| {
-                                    let excerpt = excerpts.peek()?;
-                                    (excerpt.buffer_id == buffer_id)
-                                        .then(|| excerpts.next().unwrap())
-                                });
-                                let buffer =
-                                    buffers.iter().find(|b| b.read(cx).remote_id() == buffer_id);
-                                if let Some(buffer) = buffer {
-                                    multibuffer.push_excerpts(
-                                        buffer.clone(),
-                                        buffer_excerpts.filter_map(deserialize_excerpt_range),
-                                        cx,
-                                    );
-                                }
-                            }
-                        };
-
-                        if let Some(title) = &state.title {
-                            multibuffer = multibuffer.with_title(title.clone())
-                        }
-
-                        multibuffer
-                    });
-
-                    cx.new_view(|cx| {
-                        let mut editor =
-                            Editor::for_multibuffer(multibuffer, Some(project.clone()), cx);
-                        editor.remote_id = Some(remote_id);
-                        editor
-                    })
-                })?
-            };
-
-            update_editor_from_message(
-                editor.downgrade(),
-                project,
-                proto::update_view::Editor {
-                    selections: state.selections,
-                    pending_selection: state.pending_selection,
-                    scroll_top_anchor: state.scroll_top_anchor,
-                    scroll_x: state.scroll_x,
-                    scroll_y: state.scroll_y,
-                    ..Default::default()
-                },
-                &mut cx,
-            )
-            .await?;
-
-            Ok(editor)
-        }))
-    }
-
-    fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>) {
-        self.leader_peer_id = leader_peer_id;
-        if self.leader_peer_id.is_some() {
-            self.buffer.update(cx, |buffer, cx| {
-                buffer.remove_active_selections(cx);
-            });
-        } else if self.focus_handle.is_focused(cx) {
-            self.buffer.update(cx, |buffer, cx| {
-                buffer.set_active_selections(
-                    &self.selections.disjoint_anchors(),
-                    self.selections.line_mode,
-                    self.cursor_shape,
-                    cx,
-                );
-            });
-        }
-        cx.notify();
-    }
-
-    fn to_state_proto(&self, cx: &WindowContext) -> Option<proto::view::Variant> {
-        let buffer = self.buffer.read(cx);
-        let scroll_anchor = self.scroll_manager.anchor();
-        let excerpts = buffer
-            .read(cx)
-            .excerpts()
-            .map(|(id, buffer, range)| proto::Excerpt {
-                id: id.to_proto(),
-                buffer_id: buffer.remote_id(),
-                context_start: Some(serialize_text_anchor(&range.context.start)),
-                context_end: Some(serialize_text_anchor(&range.context.end)),
-                primary_start: range
-                    .primary
-                    .as_ref()
-                    .map(|range| serialize_text_anchor(&range.start)),
-                primary_end: range
-                    .primary
-                    .as_ref()
-                    .map(|range| serialize_text_anchor(&range.end)),
-            })
-            .collect();
-
-        Some(proto::view::Variant::Editor(proto::view::Editor {
-            singleton: buffer.is_singleton(),
-            title: (!buffer.is_singleton()).then(|| buffer.title(cx).into()),
-            excerpts,
-            scroll_top_anchor: Some(serialize_anchor(&scroll_anchor.anchor)),
-            scroll_x: scroll_anchor.offset.x,
-            scroll_y: scroll_anchor.offset.y,
-            selections: self
-                .selections
-                .disjoint_anchors()
-                .iter()
-                .map(serialize_selection)
-                .collect(),
-            pending_selection: self
-                .selections
-                .pending_anchor()
-                .as_ref()
-                .map(serialize_selection),
-        }))
-    }
-
-    fn to_follow_event(event: &EditorEvent) -> Option<workspace::item::FollowEvent> {
-        match event {
-            EditorEvent::Edited => Some(FollowEvent::Unfollow),
-            EditorEvent::SelectionsChanged { local }
-            | EditorEvent::ScrollPositionChanged { local, .. } => {
-                if *local {
-                    Some(FollowEvent::Unfollow)
-                } else {
-                    None
-                }
-            }
-            _ => None,
-        }
-    }
-
-    fn add_event_to_update_proto(
-        &self,
-        event: &EditorEvent,
-        update: &mut Option<proto::update_view::Variant>,
-        cx: &WindowContext,
-    ) -> bool {
-        let update =
-            update.get_or_insert_with(|| proto::update_view::Variant::Editor(Default::default()));
-
-        match update {
-            proto::update_view::Variant::Editor(update) => match event {
-                EditorEvent::ExcerptsAdded {
-                    buffer,
-                    predecessor,
-                    excerpts,
-                } => {
-                    let buffer_id = buffer.read(cx).remote_id();
-                    let mut excerpts = excerpts.iter();
-                    if let Some((id, range)) = excerpts.next() {
-                        update.inserted_excerpts.push(proto::ExcerptInsertion {
-                            previous_excerpt_id: Some(predecessor.to_proto()),
-                            excerpt: serialize_excerpt(buffer_id, id, range),
-                        });
-                        update.inserted_excerpts.extend(excerpts.map(|(id, range)| {
-                            proto::ExcerptInsertion {
-                                previous_excerpt_id: None,
-                                excerpt: serialize_excerpt(buffer_id, id, range),
-                            }
-                        }))
-                    }
-                    true
-                }
-                EditorEvent::ExcerptsRemoved { ids } => {
-                    update
-                        .deleted_excerpts
-                        .extend(ids.iter().map(ExcerptId::to_proto));
-                    true
-                }
-                EditorEvent::ScrollPositionChanged { .. } => {
-                    let scroll_anchor = self.scroll_manager.anchor();
-                    update.scroll_top_anchor = Some(serialize_anchor(&scroll_anchor.anchor));
-                    update.scroll_x = scroll_anchor.offset.x;
-                    update.scroll_y = scroll_anchor.offset.y;
-                    true
-                }
-                EditorEvent::SelectionsChanged { .. } => {
-                    update.selections = self
-                        .selections
-                        .disjoint_anchors()
-                        .iter()
-                        .map(serialize_selection)
-                        .collect();
-                    update.pending_selection = self
-                        .selections
-                        .pending_anchor()
-                        .as_ref()
-                        .map(serialize_selection);
-                    true
-                }
-                _ => false,
-            },
-        }
-    }
-
-    fn apply_update_proto(
-        &mut self,
-        project: &Model<Project>,
-        message: update_view::Variant,
-        cx: &mut ViewContext<Self>,
-    ) -> Task<Result<()>> {
-        let update_view::Variant::Editor(message) = message;
-        let project = project.clone();
-        cx.spawn(|this, mut cx| async move {
-            update_editor_from_message(this, project, message, &mut cx).await
-        })
-    }
-
-    fn is_project_item(&self, _cx: &WindowContext) -> bool {
-        true
-    }
-}
-
-async fn update_editor_from_message(
-    this: WeakView<Editor>,
-    project: Model<Project>,
-    message: proto::update_view::Editor,
-    cx: &mut AsyncWindowContext,
-) -> Result<()> {
-    // Open all of the buffers of which excerpts were added to the editor.
-    let inserted_excerpt_buffer_ids = message
-        .inserted_excerpts
-        .iter()
-        .filter_map(|insertion| Some(insertion.excerpt.as_ref()?.buffer_id))
-        .collect::<HashSet<_>>();
-    let inserted_excerpt_buffers = project.update(cx, |project, cx| {
-        inserted_excerpt_buffer_ids
-            .into_iter()
-            .map(|id| project.open_buffer_by_id(id, cx))
-            .collect::<Vec<_>>()
-    })?;
-    let _inserted_excerpt_buffers = try_join_all(inserted_excerpt_buffers).await?;
-
-    // Update the editor's excerpts.
-    this.update(cx, |editor, cx| {
-        editor.buffer.update(cx, |multibuffer, cx| {
-            let mut removed_excerpt_ids = message
-                .deleted_excerpts
-                .into_iter()
-                .map(ExcerptId::from_proto)
-                .collect::<Vec<_>>();
-            removed_excerpt_ids.sort_by({
-                let multibuffer = multibuffer.read(cx);
-                move |a, b| a.cmp(&b, &multibuffer)
-            });
-
-            let mut insertions = message.inserted_excerpts.into_iter().peekable();
-            while let Some(insertion) = insertions.next() {
-                let Some(excerpt) = insertion.excerpt else {
-                    continue;
-                };
-                let Some(previous_excerpt_id) = insertion.previous_excerpt_id else {
-                    continue;
-                };
-                let buffer_id = excerpt.buffer_id;
-                let Some(buffer) = project.read(cx).buffer_for_id(buffer_id) else {
-                    continue;
-                };
-
-                let adjacent_excerpts = iter::from_fn(|| {
-                    let insertion = insertions.peek()?;
-                    if insertion.previous_excerpt_id.is_none()
-                        && insertion.excerpt.as_ref()?.buffer_id == buffer_id
-                    {
-                        insertions.next()?.excerpt
-                    } else {
-                        None
-                    }
-                });
-
-                multibuffer.insert_excerpts_with_ids_after(
-                    ExcerptId::from_proto(previous_excerpt_id),
-                    buffer,
-                    [excerpt]
-                        .into_iter()
-                        .chain(adjacent_excerpts)
-                        .filter_map(|excerpt| {
-                            Some((
-                                ExcerptId::from_proto(excerpt.id),
-                                deserialize_excerpt_range(excerpt)?,
-                            ))
-                        }),
-                    cx,
-                );
-            }
-
-            multibuffer.remove_excerpts(removed_excerpt_ids, cx);
-        });
-    })?;
-
-    // Deserialize the editor state.
-    let (selections, pending_selection, scroll_top_anchor) = this.update(cx, |editor, cx| {
-        let buffer = editor.buffer.read(cx).read(cx);
-        let selections = message
-            .selections
-            .into_iter()
-            .filter_map(|selection| deserialize_selection(&buffer, selection))
-            .collect::<Vec<_>>();
-        let pending_selection = message
-            .pending_selection
-            .and_then(|selection| deserialize_selection(&buffer, selection));
-        let scroll_top_anchor = message
-            .scroll_top_anchor
-            .and_then(|anchor| deserialize_anchor(&buffer, anchor));
-        anyhow::Ok((selections, pending_selection, scroll_top_anchor))
-    })??;
-
-    // Wait until the buffer has received all of the operations referenced by
-    // the editor's new state.
-    this.update(cx, |editor, cx| {
-        editor.buffer.update(cx, |buffer, cx| {
-            buffer.wait_for_anchors(
-                selections
-                    .iter()
-                    .chain(pending_selection.as_ref())
-                    .flat_map(|selection| [selection.start, selection.end])
-                    .chain(scroll_top_anchor),
-                cx,
-            )
-        })
-    })?
-    .await?;
-
-    // Update the editor's state.
-    this.update(cx, |editor, cx| {
-        if !selections.is_empty() || pending_selection.is_some() {
-            editor.set_selections_from_remote(selections, pending_selection, cx);
-            editor.request_autoscroll_remotely(Autoscroll::newest(), cx);
-        } else if let Some(scroll_top_anchor) = scroll_top_anchor {
-            editor.set_scroll_anchor_remote(
-                ScrollAnchor {
-                    anchor: scroll_top_anchor,
-                    offset: point(message.scroll_x, message.scroll_y),
-                },
-                cx,
-            );
-        }
-    })?;
-    Ok(())
-}
-
-fn serialize_excerpt(
-    buffer_id: u64,
-    id: &ExcerptId,
-    range: &ExcerptRange<language::Anchor>,
-) -> Option<proto::Excerpt> {
-    Some(proto::Excerpt {
-        id: id.to_proto(),
-        buffer_id,
-        context_start: Some(serialize_text_anchor(&range.context.start)),
-        context_end: Some(serialize_text_anchor(&range.context.end)),
-        primary_start: range
-            .primary
-            .as_ref()
-            .map(|r| serialize_text_anchor(&r.start)),
-        primary_end: range
-            .primary
-            .as_ref()
-            .map(|r| serialize_text_anchor(&r.end)),
-    })
-}
-
-fn serialize_selection(selection: &Selection<Anchor>) -> proto::Selection {
-    proto::Selection {
-        id: selection.id as u64,
-        start: Some(serialize_anchor(&selection.start)),
-        end: Some(serialize_anchor(&selection.end)),
-        reversed: selection.reversed,
-    }
-}
-
-fn serialize_anchor(anchor: &Anchor) -> proto::EditorAnchor {
-    proto::EditorAnchor {
-        excerpt_id: anchor.excerpt_id.to_proto(),
-        anchor: Some(serialize_text_anchor(&anchor.text_anchor)),
-    }
-}
-
-fn deserialize_excerpt_range(excerpt: proto::Excerpt) -> Option<ExcerptRange<language::Anchor>> {
-    let context = {
-        let start = language::proto::deserialize_anchor(excerpt.context_start?)?;
-        let end = language::proto::deserialize_anchor(excerpt.context_end?)?;
-        start..end
-    };
-    let primary = excerpt
-        .primary_start
-        .zip(excerpt.primary_end)
-        .and_then(|(start, end)| {
-            let start = language::proto::deserialize_anchor(start)?;
-            let end = language::proto::deserialize_anchor(end)?;
-            Some(start..end)
-        });
-    Some(ExcerptRange { context, primary })
-}
-
-fn deserialize_selection(
-    buffer: &MultiBufferSnapshot,
-    selection: proto::Selection,
-) -> Option<Selection<Anchor>> {
-    Some(Selection {
-        id: selection.id as usize,
-        start: deserialize_anchor(buffer, selection.start?)?,
-        end: deserialize_anchor(buffer, selection.end?)?,
-        reversed: selection.reversed,
-        goal: SelectionGoal::None,
-    })
-}
-
-fn deserialize_anchor(buffer: &MultiBufferSnapshot, anchor: proto::EditorAnchor) -> Option<Anchor> {
-    let excerpt_id = ExcerptId::from_proto(anchor.excerpt_id);
-    Some(Anchor {
-        excerpt_id,
-        text_anchor: language::proto::deserialize_anchor(anchor.anchor?)?,
-        buffer_id: buffer.buffer_id_for_excerpt(excerpt_id),
-    })
-}
-
-impl Item for Editor {
-    type Event = EditorEvent;
-
-    fn navigate(&mut self, data: Box<dyn std::any::Any>, cx: &mut ViewContext<Self>) -> bool {
-        if let Ok(data) = data.downcast::<NavigationData>() {
-            let newest_selection = self.selections.newest::<Point>(cx);
-            let buffer = self.buffer.read(cx).read(cx);
-            let offset = if buffer.can_resolve(&data.cursor_anchor) {
-                data.cursor_anchor.to_point(&buffer)
-            } else {
-                buffer.clip_point(data.cursor_position, Bias::Left)
-            };
-
-            let mut scroll_anchor = data.scroll_anchor;
-            if !buffer.can_resolve(&scroll_anchor.anchor) {
-                scroll_anchor.anchor = buffer.anchor_before(
-                    buffer.clip_point(Point::new(data.scroll_top_row, 0), Bias::Left),
-                );
-            }
-
-            drop(buffer);
-
-            if newest_selection.head() == offset {
-                false
-            } else {
-                let nav_history = self.nav_history.take();
-                self.set_scroll_anchor(scroll_anchor, cx);
-                self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                    s.select_ranges([offset..offset])
-                });
-                self.nav_history = nav_history;
-                true
-            }
-        } else {
-            false
-        }
-    }
-
-    fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString> {
-        let file_path = self
-            .buffer()
-            .read(cx)
-            .as_singleton()?
-            .read(cx)
-            .file()
-            .and_then(|f| f.as_local())?
-            .abs_path(cx);
-
-        let file_path = file_path.compact().to_string_lossy().to_string();
-
-        Some(file_path.into())
-    }
-
-    fn tab_description<'a>(&self, detail: usize, cx: &'a AppContext) -> Option<SharedString> {
-        let path = path_for_buffer(&self.buffer, detail, true, cx)?;
-        Some(path.to_string_lossy().to_string().into())
-    }
-
-    fn tab_content(&self, detail: Option<usize>, selected: bool, cx: &WindowContext) -> AnyElement {
-        let _theme = cx.theme();
-
-        let description = detail.and_then(|detail| {
-            let path = path_for_buffer(&self.buffer, detail, false, cx)?;
-            let description = path.to_string_lossy();
-            let description = description.trim();
-
-            if description.is_empty() {
-                return None;
-            }
-
-            Some(util::truncate_and_trailoff(&description, MAX_TAB_TITLE_LEN))
-        });
-
-        h_stack()
-            .gap_2()
-            .child(Label::new(self.title(cx).to_string()).color(if selected {
-                Color::Default
-            } else {
-                Color::Muted
-            }))
-            .when_some(description, |this, description| {
-                this.child(
-                    Label::new(description)
-                        .size(LabelSize::XSmall)
-                        .color(Color::Muted),
-                )
-            })
-            .into_any_element()
-    }
-
-    fn for_each_project_item(
-        &self,
-        cx: &AppContext,
-        f: &mut dyn FnMut(EntityId, &dyn project::Item),
-    ) {
-        self.buffer
-            .read(cx)
-            .for_each_buffer(|buffer| f(buffer.entity_id(), buffer.read(cx)));
-    }
-
-    fn is_singleton(&self, cx: &AppContext) -> bool {
-        self.buffer.read(cx).is_singleton()
-    }
-
-    fn clone_on_split(
-        &self,
-        _workspace_id: WorkspaceId,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<View<Editor>>
-    where
-        Self: Sized,
-    {
-        Some(cx.new_view(|cx| self.clone(cx)))
-    }
-
-    fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
-        self.nav_history = Some(history);
-    }
-
-    fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
-        let selection = self.selections.newest_anchor();
-        self.push_to_nav_history(selection.head(), None, cx);
-    }
-
-    fn workspace_deactivated(&mut self, cx: &mut ViewContext<Self>) {
-        hide_link_definition(self, cx);
-        self.link_go_to_definition_state.last_trigger_point = None;
-    }
-
-    fn is_dirty(&self, cx: &AppContext) -> bool {
-        self.buffer().read(cx).read(cx).is_dirty()
-    }
-
-    fn has_conflict(&self, cx: &AppContext) -> bool {
-        self.buffer().read(cx).read(cx).has_conflict()
-    }
-
-    fn can_save(&self, cx: &AppContext) -> bool {
-        let buffer = &self.buffer().read(cx);
-        if let Some(buffer) = buffer.as_singleton() {
-            buffer.read(cx).project_path(cx).is_some()
-        } else {
-            true
-        }
-    }
-
-    fn save(&mut self, project: Model<Project>, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
-        self.report_editor_event("save", None, cx);
-        let format = self.perform_format(project.clone(), FormatTrigger::Save, cx);
-        let buffers = self.buffer().clone().read(cx).all_buffers();
-        cx.spawn(|_, mut cx| async move {
-            format.await?;
-
-            if buffers.len() == 1 {
-                project
-                    .update(&mut cx, |project, cx| project.save_buffers(buffers, cx))?
-                    .await?;
-            } else {
-                // For multi-buffers, only save those ones that contain changes. For clean buffers
-                // we simulate saving by calling `Buffer::did_save`, so that language servers or
-                // other downstream listeners of save events get notified.
-                let (dirty_buffers, clean_buffers) = buffers.into_iter().partition(|buffer| {
-                    buffer
-                        .update(&mut cx, |buffer, _| {
-                            buffer.is_dirty() || buffer.has_conflict()
-                        })
-                        .unwrap_or(false)
-                });
-
-                project
-                    .update(&mut cx, |project, cx| {
-                        project.save_buffers(dirty_buffers, cx)
-                    })?
-                    .await?;
-                for buffer in clean_buffers {
-                    buffer
-                        .update(&mut cx, |buffer, cx| {
-                            let version = buffer.saved_version().clone();
-                            let fingerprint = buffer.saved_version_fingerprint();
-                            let mtime = buffer.saved_mtime();
-                            buffer.did_save(version, fingerprint, mtime, cx);
-                        })
-                        .ok();
-                }
-            }
-
-            Ok(())
-        })
-    }
-
-    fn save_as(
-        &mut self,
-        project: Model<Project>,
-        abs_path: PathBuf,
-        cx: &mut ViewContext<Self>,
-    ) -> Task<Result<()>> {
-        let buffer = self
-            .buffer()
-            .read(cx)
-            .as_singleton()
-            .expect("cannot call save_as on an excerpt list");
-
-        let file_extension = abs_path
-            .extension()
-            .map(|a| a.to_string_lossy().to_string());
-        self.report_editor_event("save", file_extension, cx);
-
-        project.update(cx, |project, cx| {
-            project.save_buffer_as(buffer, abs_path, cx)
-        })
-    }
-
-    fn reload(&mut self, project: Model<Project>, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
-        let buffer = self.buffer().clone();
-        let buffers = self.buffer.read(cx).all_buffers();
-        let reload_buffers =
-            project.update(cx, |project, cx| project.reload_buffers(buffers, true, cx));
-        cx.spawn(|this, mut cx| async move {
-            let transaction = reload_buffers.log_err().await;
-            this.update(&mut cx, |editor, cx| {
-                editor.request_autoscroll(Autoscroll::fit(), cx)
-            })?;
-            buffer
-                .update(&mut cx, |buffer, cx| {
-                    if let Some(transaction) = transaction {
-                        if !buffer.is_singleton() {
-                            buffer.push_transaction(&transaction.0, cx);
-                        }
-                    }
-                })
-                .ok();
-            Ok(())
-        })
-    }
-
-    fn as_searchable(&self, handle: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
-        Some(Box::new(handle.clone()))
-    }
-
-    fn pixel_position_of_cursor(&self, _: &AppContext) -> Option<gpui::Point<Pixels>> {
-        self.pixel_position_of_newest_cursor
-    }
-
-    fn breadcrumb_location(&self) -> ToolbarItemLocation {
-        ToolbarItemLocation::PrimaryLeft
-    }
-
-    fn breadcrumbs(&self, variant: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
-        let cursor = self.selections.newest_anchor().head();
-        let multibuffer = &self.buffer().read(cx);
-        let (buffer_id, symbols) =
-            multibuffer.symbols_containing(cursor, Some(&variant.syntax()), cx)?;
-        let buffer = multibuffer.buffer(buffer_id)?;
-
-        let buffer = buffer.read(cx);
-        let filename = buffer
-            .snapshot()
-            .resolve_file_path(
-                cx,
-                self.project
-                    .as_ref()
-                    .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
-                    .unwrap_or_default(),
-            )
-            .map(|path| path.to_string_lossy().to_string())
-            .unwrap_or_else(|| "untitled".to_string());
-
-        let mut breadcrumbs = vec![BreadcrumbText {
-            text: filename,
-            highlights: None,
-        }];
-        breadcrumbs.extend(symbols.into_iter().map(|symbol| BreadcrumbText {
-            text: symbol.text,
-            highlights: Some(symbol.highlight_ranges),
-        }));
-        Some(breadcrumbs)
-    }
-
-    fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
-        let workspace_id = workspace.database_id();
-        let item_id = cx.view().item_id().as_u64() as ItemId;
-        self.workspace = Some((workspace.weak_handle(), workspace.database_id()));
-
-        fn serialize(
-            buffer: Model<Buffer>,
-            workspace_id: WorkspaceId,
-            item_id: ItemId,
-            cx: &mut AppContext,
-        ) {
-            if let Some(file) = buffer.read(cx).file().and_then(|file| file.as_local()) {
-                let path = file.abs_path(cx);
-
-                cx.background_executor()
-                    .spawn(async move {
-                        DB.save_path(item_id, workspace_id, path.clone())
-                            .await
-                            .log_err()
-                    })
-                    .detach();
-            }
-        }
-
-        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
-            serialize(buffer.clone(), workspace_id, item_id, cx);
-
-            cx.subscribe(&buffer, |this, buffer, event, cx| {
-                if let Some((_, workspace_id)) = this.workspace.as_ref() {
-                    if let language::Event::FileHandleChanged = event {
-                        serialize(
-                            buffer,
-                            *workspace_id,
-                            cx.view().item_id().as_u64() as ItemId,
-                            cx,
-                        );
-                    }
-                }
-            })
-            .detach();
-        }
-    }
-
-    fn serialized_item_kind() -> Option<&'static str> {
-        Some("Editor")
-    }
-
-    fn to_item_events(event: &EditorEvent, mut f: impl FnMut(ItemEvent)) {
-        match event {
-            EditorEvent::Closed => f(ItemEvent::CloseItem),
-
-            EditorEvent::Saved | EditorEvent::TitleChanged => {
-                f(ItemEvent::UpdateTab);
-                f(ItemEvent::UpdateBreadcrumbs);
-            }
-
-            EditorEvent::Reparsed => {
-                f(ItemEvent::UpdateBreadcrumbs);
-            }
-
-            EditorEvent::SelectionsChanged { local } if *local => {
-                f(ItemEvent::UpdateBreadcrumbs);
-            }
-
-            EditorEvent::DirtyChanged => {
-                f(ItemEvent::UpdateTab);
-            }
-
-            EditorEvent::BufferEdited => {
-                f(ItemEvent::Edit);
-                f(ItemEvent::UpdateBreadcrumbs);
-            }
-
-            EditorEvent::ExcerptsAdded { .. } | EditorEvent::ExcerptsRemoved { .. } => {
-                f(ItemEvent::Edit);
-            }
-
-            _ => {}
-        }
-    }
-
-    fn deserialize(
-        project: Model<Project>,
-        _workspace: WeakView<Workspace>,
-        workspace_id: workspace::WorkspaceId,
-        item_id: ItemId,
-        cx: &mut ViewContext<Pane>,
-    ) -> Task<Result<View<Self>>> {
-        let project_item: Result<_> = project.update(cx, |project, cx| {
-            // Look up the path with this key associated, create a self with that path
-            let path = DB
-                .get_path(item_id, workspace_id)?
-                .context("No path stored for this editor")?;
-
-            let (worktree, path) = project
-                .find_local_worktree(&path, cx)
-                .with_context(|| format!("No worktree for path: {path:?}"))?;
-            let project_path = ProjectPath {
-                worktree_id: worktree.read(cx).id(),
-                path: path.into(),
-            };
-
-            Ok(project.open_path(project_path, cx))
-        });
-
-        project_item
-            .map(|project_item| {
-                cx.spawn(|pane, mut cx| async move {
-                    let (_, project_item) = project_item.await?;
-                    let buffer = project_item
-                        .downcast::<Buffer>()
-                        .map_err(|_| anyhow!("Project item at stored path was not a buffer"))?;
-                    Ok(pane.update(&mut cx, |_, cx| {
-                        cx.new_view(|cx| {
-                            let mut editor = Editor::for_buffer(buffer, Some(project), cx);
-
-                            editor.read_scroll_position_from_db(item_id, workspace_id, cx);
-                            editor
-                        })
-                    })?)
-                })
-            })
-            .unwrap_or_else(|error| Task::ready(Err(error)))
-    }
-}
-
-impl ProjectItem for Editor {
-    type Item = Buffer;
-
-    fn for_project_item(
-        project: Model<Project>,
-        buffer: Model<Buffer>,
-        cx: &mut ViewContext<Self>,
-    ) -> Self {
-        Self::for_buffer(buffer, Some(project), cx)
-    }
-}
-
-impl EventEmitter<SearchEvent> for Editor {}
-
-pub(crate) enum BufferSearchHighlights {}
-impl SearchableItem for Editor {
-    type Match = Range<Anchor>;
-
-    fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
-        self.clear_background_highlights::<BufferSearchHighlights>(cx);
-    }
-
-    fn update_matches(&mut self, matches: Vec<Range<Anchor>>, cx: &mut ViewContext<Self>) {
-        self.highlight_background::<BufferSearchHighlights>(
-            matches,
-            |theme| theme.search_match_background,
-            cx,
-        );
-    }
-
-    fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
-        let setting = EditorSettings::get_global(cx).seed_search_query_from_cursor;
-        let snapshot = &self.snapshot(cx).buffer_snapshot;
-        let selection = self.selections.newest::<usize>(cx);
-
-        match setting {
-            SeedQuerySetting::Never => String::new(),
-            SeedQuerySetting::Selection | SeedQuerySetting::Always if !selection.is_empty() => {
-                snapshot
-                    .text_for_range(selection.start..selection.end)
-                    .collect()
-            }
-            SeedQuerySetting::Selection => String::new(),
-            SeedQuerySetting::Always => {
-                let (range, kind) = snapshot.surrounding_word(selection.start);
-                if kind == Some(CharKind::Word) {
-                    let text: String = snapshot.text_for_range(range).collect();
-                    if !text.trim().is_empty() {
-                        return text;
-                    }
-                }
-                String::new()
-            }
-        }
-    }
-
-    fn activate_match(
-        &mut self,
-        index: usize,
-        matches: Vec<Range<Anchor>>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.unfold_ranges([matches[index].clone()], false, true, cx);
-        let range = self.range_for_match(&matches[index]);
-        self.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.select_ranges([range]);
-        })
-    }
-
-    fn select_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>) {
-        self.unfold_ranges(matches.clone(), false, false, cx);
-        let mut ranges = Vec::new();
-        for m in &matches {
-            ranges.push(self.range_for_match(&m))
-        }
-        self.change_selections(None, cx, |s| s.select_ranges(ranges));
-    }
-    fn replace(
-        &mut self,
-        identifier: &Self::Match,
-        query: &SearchQuery,
-        cx: &mut ViewContext<Self>,
-    ) {
-        let text = self.buffer.read(cx);
-        let text = text.snapshot(cx);
-        let text = text.text_for_range(identifier.clone()).collect::<Vec<_>>();
-        let text: Cow<_> = if text.len() == 1 {
-            text.first().cloned().unwrap().into()
-        } else {
-            let joined_chunks = text.join("");
-            joined_chunks.into()
-        };
-
-        if let Some(replacement) = query.replacement_for(&text) {
-            self.transact(cx, |this, cx| {
-                this.edit([(identifier.clone(), Arc::from(&*replacement))], cx);
-            });
-        }
-    }
-    fn match_index_for_direction(
-        &mut self,
-        matches: &Vec<Range<Anchor>>,
-        current_index: usize,
-        direction: Direction,
-        count: usize,
-        cx: &mut ViewContext<Self>,
-    ) -> usize {
-        let buffer = self.buffer().read(cx).snapshot(cx);
-        let current_index_position = if self.selections.disjoint_anchors().len() == 1 {
-            self.selections.newest_anchor().head()
-        } else {
-            matches[current_index].start
-        };
-
-        let mut count = count % matches.len();
-        if count == 0 {
-            return current_index;
-        }
-        match direction {
-            Direction::Next => {
-                if matches[current_index]
-                    .start
-                    .cmp(&current_index_position, &buffer)
-                    .is_gt()
-                {
-                    count = count - 1
-                }
-
-                (current_index + count) % matches.len()
-            }
-            Direction::Prev => {
-                if matches[current_index]
-                    .end
-                    .cmp(&current_index_position, &buffer)
-                    .is_lt()
-                {
-                    count = count - 1;
-                }
-
-                if current_index >= count {
-                    current_index - count
-                } else {
-                    matches.len() - (count - current_index)
-                }
-            }
-        }
-    }
-
-    fn find_matches(
-        &mut self,
-        query: Arc<project::search::SearchQuery>,
-        cx: &mut ViewContext<Self>,
-    ) -> Task<Vec<Range<Anchor>>> {
-        let buffer = self.buffer().read(cx).snapshot(cx);
-        cx.background_executor().spawn(async move {
-            let mut ranges = Vec::new();
-            if let Some((_, _, excerpt_buffer)) = buffer.as_singleton() {
-                ranges.extend(
-                    query
-                        .search(excerpt_buffer, None)
-                        .await
-                        .into_iter()
-                        .map(|range| {
-                            buffer.anchor_after(range.start)..buffer.anchor_before(range.end)
-                        }),
-                );
-            } else {
-                for excerpt in buffer.excerpt_boundaries_in_range(0..buffer.len()) {
-                    let excerpt_range = excerpt.range.context.to_offset(&excerpt.buffer);
-                    ranges.extend(
-                        query
-                            .search(&excerpt.buffer, Some(excerpt_range.clone()))
-                            .await
-                            .into_iter()
-                            .map(|range| {
-                                let start = excerpt
-                                    .buffer
-                                    .anchor_after(excerpt_range.start + range.start);
-                                let end = excerpt
-                                    .buffer
-                                    .anchor_before(excerpt_range.start + range.end);
-                                buffer.anchor_in_excerpt(excerpt.id.clone(), start)
-                                    ..buffer.anchor_in_excerpt(excerpt.id.clone(), end)
-                            }),
-                    );
-                }
-            }
-            ranges
-        })
-    }
-
-    fn active_match_index(
-        &mut self,
-        matches: Vec<Range<Anchor>>,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<usize> {
-        active_match_index(
-            &matches,
-            &self.selections.newest_anchor().head(),
-            &self.buffer().read(cx).snapshot(cx),
-        )
-    }
-}
-
-pub fn active_match_index(
-    ranges: &[Range<Anchor>],
-    cursor: &Anchor,
-    buffer: &MultiBufferSnapshot,
-) -> Option<usize> {
-    if ranges.is_empty() {
-        None
-    } else {
-        match ranges.binary_search_by(|probe| {
-            if probe.end.cmp(cursor, &*buffer).is_lt() {
-                Ordering::Less
-            } else if probe.start.cmp(cursor, &*buffer).is_gt() {
-                Ordering::Greater
-            } else {
-                Ordering::Equal
-            }
-        }) {
-            Ok(i) | Err(i) => Some(cmp::min(i, ranges.len() - 1)),
-        }
-    }
-}
-
-pub struct CursorPosition {
-    position: Option<Point>,
-    selected_count: usize,
-    _observe_active_editor: Option<Subscription>,
-}
-
-impl Default for CursorPosition {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
-impl CursorPosition {
-    pub fn new() -> Self {
-        Self {
-            position: None,
-            selected_count: 0,
-            _observe_active_editor: None,
-        }
-    }
-
-    fn update_position(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
-        let editor = editor.read(cx);
-        let buffer = editor.buffer().read(cx).snapshot(cx);
-
-        self.selected_count = 0;
-        let mut last_selection: Option<Selection<usize>> = None;
-        for selection in editor.selections.all::<usize>(cx) {
-            self.selected_count += selection.end - selection.start;
-            if last_selection
-                .as_ref()
-                .map_or(true, |last_selection| selection.id > last_selection.id)
-            {
-                last_selection = Some(selection);
-            }
-        }
-        self.position = last_selection.map(|s| s.head().to_point(&buffer));
-
-        cx.notify();
-    }
-}
-
-impl Render for CursorPosition {
-    fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
-        div().when_some(self.position, |el, position| {
-            let mut text = format!(
-                "{}{FILE_ROW_COLUMN_DELIMITER}{}",
-                position.row + 1,
-                position.column + 1
-            );
-            if self.selected_count > 0 {
-                write!(text, " ({} selected)", self.selected_count).unwrap();
-            }
-
-            el.child(Label::new(text).size(LabelSize::Small))
-        })
-    }
-}
-
-impl StatusItemView for CursorPosition {
-    fn set_active_pane_item(
-        &mut self,
-        active_pane_item: Option<&dyn ItemHandle>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        if let Some(editor) = active_pane_item.and_then(|item| item.act_as::<Editor>(cx)) {
-            self._observe_active_editor = Some(cx.observe(&editor, Self::update_position));
-            self.update_position(editor, cx);
-        } else {
-            self.position = None;
-            self._observe_active_editor = None;
-        }
-
-        cx.notify();
-    }
-}
-
-fn path_for_buffer<'a>(
-    buffer: &Model<MultiBuffer>,
-    height: usize,
-    include_filename: bool,
-    cx: &'a AppContext,
-) -> Option<Cow<'a, Path>> {
-    let file = buffer.read(cx).as_singleton()?.read(cx).file()?;
-    path_for_file(file.as_ref(), height, include_filename, cx)
-}
-
-fn path_for_file<'a>(
-    file: &'a dyn language::File,
-    mut height: usize,
-    include_filename: bool,
-    cx: &'a AppContext,
-) -> Option<Cow<'a, Path>> {
-    // Ensure we always render at least the filename.
-    height += 1;
-
-    let mut prefix = file.path().as_ref();
-    while height > 0 {
-        if let Some(parent) = prefix.parent() {
-            prefix = parent;
-            height -= 1;
-        } else {
-            break;
-        }
-    }
-
-    // Here we could have just always used `full_path`, but that is very
-    // allocation-heavy and so we try to use a `Cow<Path>` if we haven't
-    // traversed all the way up to the worktree's root.
-    if height > 0 {
-        let full_path = file.full_path(cx);
-        if include_filename {
-            Some(full_path.into())
-        } else {
-            Some(full_path.parent()?.to_path_buf().into())
-        }
-    } else {
-        let mut path = file.path().strip_prefix(prefix).ok()?;
-        if !include_filename {
-            path = path.parent()?;
-        }
-        Some(path.into())
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use gpui::AppContext;
-    use std::{
-        path::{Path, PathBuf},
-        sync::Arc,
-        time::SystemTime,
-    };
-
-    #[gpui::test]
-    fn test_path_for_file(cx: &mut AppContext) {
-        let file = TestFile {
-            path: Path::new("").into(),
-            full_path: PathBuf::from(""),
-        };
-        assert_eq!(path_for_file(&file, 0, false, cx), None);
-    }
-
-    struct TestFile {
-        path: Arc<Path>,
-        full_path: PathBuf,
-    }
-
-    impl language::File for TestFile {
-        fn path(&self) -> &Arc<Path> {
-            &self.path
-        }
-
-        fn full_path(&self, _: &gpui::AppContext) -> PathBuf {
-            self.full_path.clone()
-        }
-
-        fn as_local(&self) -> Option<&dyn language::LocalFile> {
-            unimplemented!()
-        }
-
-        fn mtime(&self) -> SystemTime {
-            unimplemented!()
-        }
-
-        fn file_name<'a>(&'a self, _: &'a gpui::AppContext) -> &'a std::ffi::OsStr {
-            unimplemented!()
-        }
-
-        fn worktree_id(&self) -> usize {
-            0
-        }
-
-        fn is_deleted(&self) -> bool {
-            unimplemented!()
-        }
-
-        fn as_any(&self) -> &dyn std::any::Any {
-            unimplemented!()
-        }
-
-        fn to_proto(&self) -> rpc::proto::File {
-            unimplemented!()
-        }
-    }
-}
@@ -1,1279 +0,0 @@
-use crate::{
-    display_map::DisplaySnapshot,
-    element::PointForPosition,
-    hover_popover::{self, InlayHover},
-    Anchor, DisplayPoint, Editor, EditorSnapshot, GoToDefinition, GoToTypeDefinition, InlayId,
-    SelectPhase,
-};
-use gpui::{px, Task, ViewContext};
-use language::{Bias, ToOffset};
-use lsp::LanguageServerId;
-use project::{
-    HoverBlock, HoverBlockKind, InlayHintLabelPartTooltip, InlayHintTooltip, LocationLink,
-    ResolveState,
-};
-use std::ops::Range;
-use theme::ActiveTheme as _;
-use util::TryFutureExt;
-
-#[derive(Debug, Default)]
-pub struct LinkGoToDefinitionState {
-    pub last_trigger_point: Option<TriggerPoint>,
-    pub symbol_range: Option<RangeInEditor>,
-    pub kind: Option<LinkDefinitionKind>,
-    pub definitions: Vec<GoToDefinitionLink>,
-    pub task: Option<Task<Option<()>>>,
-}
-
-#[derive(Debug, Eq, PartialEq, Clone)]
-pub enum RangeInEditor {
-    Text(Range<Anchor>),
-    Inlay(InlayHighlight),
-}
-
-impl RangeInEditor {
-    pub fn as_text_range(&self) -> Option<Range<Anchor>> {
-        match self {
-            Self::Text(range) => Some(range.clone()),
-            Self::Inlay(_) => None,
-        }
-    }
-
-    fn point_within_range(&self, trigger_point: &TriggerPoint, snapshot: &EditorSnapshot) -> bool {
-        match (self, trigger_point) {
-            (Self::Text(range), TriggerPoint::Text(point)) => {
-                let point_after_start = range.start.cmp(point, &snapshot.buffer_snapshot).is_le();
-                point_after_start && range.end.cmp(point, &snapshot.buffer_snapshot).is_ge()
-            }
-            (Self::Inlay(highlight), TriggerPoint::InlayHint(point, _, _)) => {
-                highlight.inlay == point.inlay
-                    && highlight.range.contains(&point.range.start)
-                    && highlight.range.contains(&point.range.end)
-            }
-            (Self::Inlay(_), TriggerPoint::Text(_))
-            | (Self::Text(_), TriggerPoint::InlayHint(_, _, _)) => false,
-        }
-    }
-}
-
-#[derive(Debug)]
-pub enum GoToDefinitionTrigger {
-    Text(DisplayPoint),
-    InlayHint(InlayHighlight, lsp::Location, LanguageServerId),
-}
-
-#[derive(Debug, Clone)]
-pub enum GoToDefinitionLink {
-    Text(LocationLink),
-    InlayHint(lsp::Location, LanguageServerId),
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct InlayHighlight {
-    pub inlay: InlayId,
-    pub inlay_position: Anchor,
-    pub range: Range<usize>,
-}
-
-#[derive(Debug, Clone)]
-pub enum TriggerPoint {
-    Text(Anchor),
-    InlayHint(InlayHighlight, lsp::Location, LanguageServerId),
-}
-
-impl TriggerPoint {
-    pub fn definition_kind(&self, shift: bool) -> LinkDefinitionKind {
-        match self {
-            TriggerPoint::Text(_) => {
-                if shift {
-                    LinkDefinitionKind::Type
-                } else {
-                    LinkDefinitionKind::Symbol
-                }
-            }
-            TriggerPoint::InlayHint(_, _, _) => LinkDefinitionKind::Type,
-        }
-    }
-
-    fn anchor(&self) -> &Anchor {
-        match self {
-            TriggerPoint::Text(anchor) => anchor,
-            TriggerPoint::InlayHint(inlay_range, _, _) => &inlay_range.inlay_position,
-        }
-    }
-}
-
-pub fn update_go_to_definition_link(
-    editor: &mut Editor,
-    origin: Option<GoToDefinitionTrigger>,
-    cmd_held: bool,
-    shift_held: bool,
-    cx: &mut ViewContext<Editor>,
-) {
-    let pending_nonempty_selection = editor.has_pending_nonempty_selection();
-
-    // Store new mouse point as an anchor
-    let snapshot = editor.snapshot(cx);
-    let trigger_point = match origin {
-        Some(GoToDefinitionTrigger::Text(p)) => {
-            Some(TriggerPoint::Text(snapshot.buffer_snapshot.anchor_before(
-                p.to_offset(&snapshot.display_snapshot, Bias::Left),
-            )))
-        }
-        Some(GoToDefinitionTrigger::InlayHint(p, lsp_location, language_server_id)) => {
-            Some(TriggerPoint::InlayHint(p, lsp_location, language_server_id))
-        }
-        None => None,
-    };
-
-    // If the new point is the same as the previously stored one, return early
-    if let (Some(a), Some(b)) = (
-        &trigger_point,
-        &editor.link_go_to_definition_state.last_trigger_point,
-    ) {
-        match (a, b) {
-            (TriggerPoint::Text(anchor_a), TriggerPoint::Text(anchor_b)) => {
-                if anchor_a.cmp(anchor_b, &snapshot.buffer_snapshot).is_eq() {
-                    return;
-                }
-            }
-            (TriggerPoint::InlayHint(range_a, _, _), TriggerPoint::InlayHint(range_b, _, _)) => {
-                if range_a == range_b {
-                    return;
-                }
-            }
-            _ => {}
-        }
-    }
-
-    editor.link_go_to_definition_state.last_trigger_point = trigger_point.clone();
-
-    if pending_nonempty_selection {
-        hide_link_definition(editor, cx);
-        return;
-    }
-
-    if cmd_held {
-        if let Some(trigger_point) = trigger_point {
-            let kind = trigger_point.definition_kind(shift_held);
-            show_link_definition(kind, editor, trigger_point, snapshot, cx);
-            return;
-        }
-    }
-
-    hide_link_definition(editor, cx);
-}
-
-pub fn update_inlay_link_and_hover_points(
-    snapshot: &DisplaySnapshot,
-    point_for_position: PointForPosition,
-    editor: &mut Editor,
-    cmd_held: bool,
-    shift_held: bool,
-    cx: &mut ViewContext<'_, Editor>,
-) {
-    let hovered_offset = if point_for_position.column_overshoot_after_line_end == 0 {
-        Some(snapshot.display_point_to_inlay_offset(point_for_position.exact_unclipped, Bias::Left))
-    } else {
-        None
-    };
-    let mut go_to_definition_updated = false;
-    let mut hover_updated = false;
-    if let Some(hovered_offset) = hovered_offset {
-        let buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
-        let previous_valid_anchor = buffer_snapshot.anchor_at(
-            point_for_position.previous_valid.to_point(snapshot),
-            Bias::Left,
-        );
-        let next_valid_anchor = buffer_snapshot.anchor_at(
-            point_for_position.next_valid.to_point(snapshot),
-            Bias::Right,
-        );
-        if let Some(hovered_hint) = editor
-            .visible_inlay_hints(cx)
-            .into_iter()
-            .skip_while(|hint| {
-                hint.position
-                    .cmp(&previous_valid_anchor, &buffer_snapshot)
-                    .is_lt()
-            })
-            .take_while(|hint| {
-                hint.position
-                    .cmp(&next_valid_anchor, &buffer_snapshot)
-                    .is_le()
-            })
-            .max_by_key(|hint| hint.id)
-        {
-            let inlay_hint_cache = editor.inlay_hint_cache();
-            let excerpt_id = previous_valid_anchor.excerpt_id;
-            if let Some(cached_hint) = inlay_hint_cache.hint_by_id(excerpt_id, hovered_hint.id) {
-                match cached_hint.resolve_state {
-                    ResolveState::CanResolve(_, _) => {
-                        if let Some(buffer_id) = previous_valid_anchor.buffer_id {
-                            inlay_hint_cache.spawn_hint_resolve(
-                                buffer_id,
-                                excerpt_id,
-                                hovered_hint.id,
-                                cx,
-                            );
-                        }
-                    }
-                    ResolveState::Resolved => {
-                        let mut extra_shift_left = 0;
-                        let mut extra_shift_right = 0;
-                        if cached_hint.padding_left {
-                            extra_shift_left += 1;
-                            extra_shift_right += 1;
-                        }
-                        if cached_hint.padding_right {
-                            extra_shift_right += 1;
-                        }
-                        match cached_hint.label {
-                            project::InlayHintLabel::String(_) => {
-                                if let Some(tooltip) = cached_hint.tooltip {
-                                    hover_popover::hover_at_inlay(
-                                        editor,
-                                        InlayHover {
-                                            excerpt: excerpt_id,
-                                            tooltip: match tooltip {
-                                                InlayHintTooltip::String(text) => HoverBlock {
-                                                    text,
-                                                    kind: HoverBlockKind::PlainText,
-                                                },
-                                                InlayHintTooltip::MarkupContent(content) => {
-                                                    HoverBlock {
-                                                        text: content.value,
-                                                        kind: content.kind,
-                                                    }
-                                                }
-                                            },
-                                            range: InlayHighlight {
-                                                inlay: hovered_hint.id,
-                                                inlay_position: hovered_hint.position,
-                                                range: extra_shift_left
-                                                    ..hovered_hint.text.len() + extra_shift_right,
-                                            },
-                                        },
-                                        cx,
-                                    );
-                                    hover_updated = true;
-                                }
-                            }
-                            project::InlayHintLabel::LabelParts(label_parts) => {
-                                let hint_start =
-                                    snapshot.anchor_to_inlay_offset(hovered_hint.position);
-                                if let Some((hovered_hint_part, part_range)) =
-                                    hover_popover::find_hovered_hint_part(
-                                        label_parts,
-                                        hint_start,
-                                        hovered_offset,
-                                    )
-                                {
-                                    let highlight_start =
-                                        (part_range.start - hint_start).0 + extra_shift_left;
-                                    let highlight_end =
-                                        (part_range.end - hint_start).0 + extra_shift_right;
-                                    let highlight = InlayHighlight {
-                                        inlay: hovered_hint.id,
-                                        inlay_position: hovered_hint.position,
-                                        range: highlight_start..highlight_end,
-                                    };
-                                    if let Some(tooltip) = hovered_hint_part.tooltip {
-                                        hover_popover::hover_at_inlay(
-                                            editor,
-                                            InlayHover {
-                                                excerpt: excerpt_id,
-                                                tooltip: match tooltip {
-                                                    InlayHintLabelPartTooltip::String(text) => {
-                                                        HoverBlock {
-                                                            text,
-                                                            kind: HoverBlockKind::PlainText,
-                                                        }
-                                                    }
-                                                    InlayHintLabelPartTooltip::MarkupContent(
-                                                        content,
-                                                    ) => HoverBlock {
-                                                        text: content.value,
-                                                        kind: content.kind,
-                                                    },
-                                                },
-                                                range: highlight.clone(),
-                                            },
-                                            cx,
-                                        );
-                                        hover_updated = true;
-                                    }
-                                    if let Some((language_server_id, location)) =
-                                        hovered_hint_part.location
-                                    {
-                                        go_to_definition_updated = true;
-                                        update_go_to_definition_link(
-                                            editor,
-                                            Some(GoToDefinitionTrigger::InlayHint(
-                                                highlight,
-                                                location,
-                                                language_server_id,
-                                            )),
-                                            cmd_held,
-                                            shift_held,
-                                            cx,
-                                        );
-                                    }
-                                }
-                            }
-                        };
-                    }
-                    ResolveState::Resolving => {}
-                }
-            }
-        }
-    }
-
-    if !go_to_definition_updated {
-        update_go_to_definition_link(editor, None, cmd_held, shift_held, cx);
-    }
-    if !hover_updated {
-        hover_popover::hover_at(editor, None, cx);
-    }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub enum LinkDefinitionKind {
-    Symbol,
-    Type,
-}
-
-pub fn show_link_definition(
-    definition_kind: LinkDefinitionKind,
-    editor: &mut Editor,
-    trigger_point: TriggerPoint,
-    snapshot: EditorSnapshot,
-    cx: &mut ViewContext<Editor>,
-) {
-    let same_kind = editor.link_go_to_definition_state.kind == Some(definition_kind);
-    if !same_kind {
-        hide_link_definition(editor, cx);
-    }
-
-    if editor.pending_rename.is_some() {
-        return;
-    }
-
-    let trigger_anchor = trigger_point.anchor();
-    let (buffer, buffer_position) = if let Some(output) = editor
-        .buffer
-        .read(cx)
-        .text_anchor_for_position(trigger_anchor.clone(), cx)
-    {
-        output
-    } else {
-        return;
-    };
-
-    let excerpt_id = if let Some((excerpt_id, _, _)) = editor
-        .buffer()
-        .read(cx)
-        .excerpt_containing(trigger_anchor.clone(), cx)
-    {
-        excerpt_id
-    } else {
-        return;
-    };
-
-    let project = if let Some(project) = editor.project.clone() {
-        project
-    } else {
-        return;
-    };
-
-    // Don't request again if the location is within the symbol region of a previous request with the same kind
-    if let Some(symbol_range) = &editor.link_go_to_definition_state.symbol_range {
-        if same_kind && symbol_range.point_within_range(&trigger_point, &snapshot) {
-            return;
-        }
-    }
-
-    let task = cx.spawn(|this, mut cx| {
-        async move {
-            let result = match &trigger_point {
-                TriggerPoint::Text(_) => {
-                    // query the LSP for definition info
-                    project
-                        .update(&mut cx, |project, cx| match definition_kind {
-                            LinkDefinitionKind::Symbol => {
-                                project.definition(&buffer, buffer_position, cx)
-                            }
-
-                            LinkDefinitionKind::Type => {
-                                project.type_definition(&buffer, buffer_position, cx)
-                            }
-                        })?
-                        .await
-                        .ok()
-                        .map(|definition_result| {
-                            (
-                                definition_result.iter().find_map(|link| {
-                                    link.origin.as_ref().map(|origin| {
-                                        let start = snapshot.buffer_snapshot.anchor_in_excerpt(
-                                            excerpt_id.clone(),
-                                            origin.range.start,
-                                        );
-                                        let end = snapshot.buffer_snapshot.anchor_in_excerpt(
-                                            excerpt_id.clone(),
-                                            origin.range.end,
-                                        );
-                                        RangeInEditor::Text(start..end)
-                                    })
-                                }),
-                                definition_result
-                                    .into_iter()
-                                    .map(GoToDefinitionLink::Text)
-                                    .collect(),
-                            )
-                        })
-                }
-                TriggerPoint::InlayHint(highlight, lsp_location, server_id) => Some((
-                    Some(RangeInEditor::Inlay(highlight.clone())),
-                    vec![GoToDefinitionLink::InlayHint(
-                        lsp_location.clone(),
-                        *server_id,
-                    )],
-                )),
-            };
-
-            this.update(&mut cx, |this, cx| {
-                // Clear any existing highlights
-                this.clear_highlights::<LinkGoToDefinitionState>(cx);
-                this.link_go_to_definition_state.kind = Some(definition_kind);
-                this.link_go_to_definition_state.symbol_range = result
-                    .as_ref()
-                    .and_then(|(symbol_range, _)| symbol_range.clone());
-
-                if let Some((symbol_range, definitions)) = result {
-                    this.link_go_to_definition_state.definitions = definitions.clone();
-
-                    let buffer_snapshot = buffer.read(cx).snapshot();
-
-                    // Only show highlight if there exists a definition to jump to that doesn't contain
-                    // the current location.
-                    let any_definition_does_not_contain_current_location =
-                        definitions.iter().any(|definition| {
-                            match &definition {
-                                GoToDefinitionLink::Text(link) => {
-                                    if link.target.buffer == buffer {
-                                        let range = &link.target.range;
-                                        // Expand range by one character as lsp definition ranges include positions adjacent
-                                        // but not contained by the symbol range
-                                        let start = buffer_snapshot.clip_offset(
-                                            range
-                                                .start
-                                                .to_offset(&buffer_snapshot)
-                                                .saturating_sub(1),
-                                            Bias::Left,
-                                        );
-                                        let end = buffer_snapshot.clip_offset(
-                                            range.end.to_offset(&buffer_snapshot) + 1,
-                                            Bias::Right,
-                                        );
-                                        let offset = buffer_position.to_offset(&buffer_snapshot);
-                                        !(start <= offset && end >= offset)
-                                    } else {
-                                        true
-                                    }
-                                }
-                                GoToDefinitionLink::InlayHint(_, _) => true,
-                            }
-                        });
-
-                    if any_definition_does_not_contain_current_location {
-                        let style = gpui::HighlightStyle {
-                            underline: Some(gpui::UnderlineStyle {
-                                thickness: px(1.),
-                                ..Default::default()
-                            }),
-                            color: Some(cx.theme().colors().link_text_hover),
-                            ..Default::default()
-                        };
-                        let highlight_range =
-                            symbol_range.unwrap_or_else(|| match &trigger_point {
-                                TriggerPoint::Text(trigger_anchor) => {
-                                    let snapshot = &snapshot.buffer_snapshot;
-                                    // If no symbol range returned from language server, use the surrounding word.
-                                    let (offset_range, _) =
-                                        snapshot.surrounding_word(*trigger_anchor);
-                                    RangeInEditor::Text(
-                                        snapshot.anchor_before(offset_range.start)
-                                            ..snapshot.anchor_after(offset_range.end),
-                                    )
-                                }
-                                TriggerPoint::InlayHint(highlight, _, _) => {
-                                    RangeInEditor::Inlay(highlight.clone())
-                                }
-                            });
-
-                        match highlight_range {
-                            RangeInEditor::Text(text_range) => this
-                                .highlight_text::<LinkGoToDefinitionState>(
-                                    vec![text_range],
-                                    style,
-                                    cx,
-                                ),
-                            RangeInEditor::Inlay(highlight) => this
-                                .highlight_inlays::<LinkGoToDefinitionState>(
-                                    vec![highlight],
-                                    style,
-                                    cx,
-                                ),
-                        }
-                    } else {
-                        hide_link_definition(this, cx);
-                    }
-                }
-            })?;
-
-            Ok::<_, anyhow::Error>(())
-        }
-        .log_err()
-    });
-
-    editor.link_go_to_definition_state.task = Some(task);
-}
-
-pub fn hide_link_definition(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
-    if editor.link_go_to_definition_state.symbol_range.is_some()
-        || !editor.link_go_to_definition_state.definitions.is_empty()
-    {
-        editor.link_go_to_definition_state.symbol_range.take();
-        editor.link_go_to_definition_state.definitions.clear();
-        cx.notify();
-    }
-
-    editor.link_go_to_definition_state.task = None;
-
-    editor.clear_highlights::<LinkGoToDefinitionState>(cx);
-}
-
-pub fn go_to_fetched_definition(
-    editor: &mut Editor,
-    point: PointForPosition,
-    split: bool,
-    cx: &mut ViewContext<Editor>,
-) {
-    go_to_fetched_definition_of_kind(LinkDefinitionKind::Symbol, editor, point, split, cx);
-}
-
-pub fn go_to_fetched_type_definition(
-    editor: &mut Editor,
-    point: PointForPosition,
-    split: bool,
-    cx: &mut ViewContext<Editor>,
-) {
-    go_to_fetched_definition_of_kind(LinkDefinitionKind::Type, editor, point, split, cx);
-}
-
-fn go_to_fetched_definition_of_kind(
-    kind: LinkDefinitionKind,
-    editor: &mut Editor,
-    point: PointForPosition,
-    split: bool,
-    cx: &mut ViewContext<Editor>,
-) {
-    let cached_definitions = editor.link_go_to_definition_state.definitions.clone();
-    hide_link_definition(editor, cx);
-    let cached_definitions_kind = editor.link_go_to_definition_state.kind;
-
-    let is_correct_kind = cached_definitions_kind == Some(kind);
-    if !cached_definitions.is_empty() && is_correct_kind {
-        if !editor.focus_handle.is_focused(cx) {
-            cx.focus(&editor.focus_handle);
-        }
-
-        editor.navigate_to_definitions(cached_definitions, split, cx);
-    } else {
-        editor.select(
-            SelectPhase::Begin {
-                position: point.next_valid,
-                add: false,
-                click_count: 1,
-            },
-            cx,
-        );
-
-        if point.as_valid().is_some() {
-            match kind {
-                LinkDefinitionKind::Symbol => editor.go_to_definition(&GoToDefinition, cx),
-                LinkDefinitionKind::Type => editor.go_to_type_definition(&GoToTypeDefinition, cx),
-            }
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{
-        display_map::ToDisplayPoint,
-        editor_tests::init_test,
-        inlay_hint_cache::tests::{cached_hint_labels, visible_hint_labels},
-        test::editor_lsp_test_context::EditorLspTestContext,
-    };
-    use futures::StreamExt;
-    use gpui::{Modifiers, ModifiersChangedEvent};
-    use indoc::indoc;
-    use language::language_settings::InlayHintSettings;
-    use lsp::request::{GotoDefinition, GotoTypeDefinition};
-    use util::assert_set_eq;
-
-    #[gpui::test]
-    async fn test_link_go_to_type_definition(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
-                type_definition_provider: Some(lsp::TypeDefinitionProviderCapability::Simple(true)),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-
-        cx.set_state(indoc! {"
-            struct A;
-            let vห‡ariable = A;
-        "});
-
-        // Basic hold cmd+shift, expect highlight in region if response contains type definition
-        let hover_point = cx.display_point(indoc! {"
-            struct A;
-            let vห‡ariable = A;
-        "});
-        let symbol_range = cx.lsp_range(indoc! {"
-            struct A;
-            let ยซvariableยป = A;
-        "});
-        let target_range = cx.lsp_range(indoc! {"
-            struct ยซAยป;
-            let variable = A;
-        "});
-
-        let mut requests =
-            cx.handle_request::<GotoTypeDefinition, _, _>(move |url, _, _| async move {
-                Ok(Some(lsp::GotoTypeDefinitionResponse::Link(vec![
-                    lsp::LocationLink {
-                        origin_selection_range: Some(symbol_range),
-                        target_uri: url.clone(),
-                        target_range,
-                        target_selection_range: target_range,
-                    },
-                ])))
-            });
-
-        // Press cmd+shift to trigger highlight
-        cx.update_editor(|editor, cx| {
-            update_go_to_definition_link(
-                editor,
-                Some(GoToDefinitionTrigger::Text(hover_point)),
-                true,
-                true,
-                cx,
-            );
-        });
-        requests.next().await;
-        cx.background_executor.run_until_parked();
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            struct A;
-            let ยซvariableยป = A;
-        "});
-
-        // Unpress shift causes highlight to go away (normal goto-definition is not valid here)
-        cx.update_editor(|editor, cx| {
-            crate::element::EditorElement::modifiers_changed(
-                editor,
-                &ModifiersChangedEvent {
-                    modifiers: Modifiers {
-                        command: true,
-                        ..Default::default()
-                    },
-                    ..Default::default()
-                },
-                cx,
-            );
-        });
-        // Assert no link highlights
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            struct A;
-            let variable = A;
-        "});
-
-        // Cmd+shift click without existing definition requests and jumps
-        let hover_point = cx.display_point(indoc! {"
-            struct A;
-            let vห‡ariable = A;
-        "});
-        let target_range = cx.lsp_range(indoc! {"
-            struct ยซAยป;
-            let variable = A;
-        "});
-
-        let mut requests =
-            cx.handle_request::<GotoTypeDefinition, _, _>(move |url, _, _| async move {
-                Ok(Some(lsp::GotoTypeDefinitionResponse::Link(vec![
-                    lsp::LocationLink {
-                        origin_selection_range: None,
-                        target_uri: url,
-                        target_range,
-                        target_selection_range: target_range,
-                    },
-                ])))
-            });
-
-        cx.update_editor(|editor, cx| {
-            go_to_fetched_type_definition(editor, PointForPosition::valid(hover_point), false, cx);
-        });
-        requests.next().await;
-        cx.background_executor.run_until_parked();
-
-        cx.assert_editor_state(indoc! {"
-            struct ยซAห‡ยป;
-            let variable = A;
-        "});
-    }
-
-    #[gpui::test]
-    async fn test_link_go_to_definition(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-
-        cx.set_state(indoc! {"
-                fn ห‡test() { do_work(); }
-                fn do_work() { test(); }
-            "});
-
-        // Basic hold cmd, expect highlight in region if response contains definition
-        let hover_point = cx.display_point(indoc! {"
-                fn test() { do_wห‡ork(); }
-                fn do_work() { test(); }
-            "});
-        let symbol_range = cx.lsp_range(indoc! {"
-                fn test() { ยซdo_workยป(); }
-                fn do_work() { test(); }
-            "});
-        let target_range = cx.lsp_range(indoc! {"
-                fn test() { do_work(); }
-                fn ยซdo_workยป() { test(); }
-            "});
-
-        let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
-            Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
-                lsp::LocationLink {
-                    origin_selection_range: Some(symbol_range),
-                    target_uri: url.clone(),
-                    target_range,
-                    target_selection_range: target_range,
-                },
-            ])))
-        });
-
-        cx.update_editor(|editor, cx| {
-            update_go_to_definition_link(
-                editor,
-                Some(GoToDefinitionTrigger::Text(hover_point)),
-                true,
-                false,
-                cx,
-            );
-        });
-        requests.next().await;
-        cx.background_executor.run_until_parked();
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-                fn test() { ยซdo_workยป(); }
-                fn do_work() { test(); }
-            "});
-
-        // Unpress cmd causes highlight to go away
-        cx.update_editor(|editor, cx| {
-            crate::element::EditorElement::modifiers_changed(editor, &Default::default(), cx);
-        });
-
-        // Assert no link highlights
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-                fn test() { do_work(); }
-                fn do_work() { test(); }
-            "});
-
-        // Response without source range still highlights word
-        cx.update_editor(|editor, _| editor.link_go_to_definition_state.last_trigger_point = None);
-        let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
-            Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
-                lsp::LocationLink {
-                    // No origin range
-                    origin_selection_range: None,
-                    target_uri: url.clone(),
-                    target_range,
-                    target_selection_range: target_range,
-                },
-            ])))
-        });
-        cx.update_editor(|editor, cx| {
-            update_go_to_definition_link(
-                editor,
-                Some(GoToDefinitionTrigger::Text(hover_point)),
-                true,
-                false,
-                cx,
-            );
-        });
-        requests.next().await;
-        cx.background_executor.run_until_parked();
-
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-                fn test() { ยซdo_workยป(); }
-                fn do_work() { test(); }
-            "});
-
-        // Moving mouse to location with no response dismisses highlight
-        let hover_point = cx.display_point(indoc! {"
-                fห‡n test() { do_work(); }
-                fn do_work() { test(); }
-            "});
-        let mut requests = cx
-            .lsp
-            .handle_request::<GotoDefinition, _, _>(move |_, _| async move {
-                // No definitions returned
-                Ok(Some(lsp::GotoDefinitionResponse::Link(vec![])))
-            });
-        cx.update_editor(|editor, cx| {
-            update_go_to_definition_link(
-                editor,
-                Some(GoToDefinitionTrigger::Text(hover_point)),
-                true,
-                false,
-                cx,
-            );
-        });
-        requests.next().await;
-        cx.background_executor.run_until_parked();
-
-        // Assert no link highlights
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-                fn test() { do_work(); }
-                fn do_work() { test(); }
-            "});
-
-        // Move mouse without cmd and then pressing cmd triggers highlight
-        let hover_point = cx.display_point(indoc! {"
-                fn test() { do_work(); }
-                fn do_work() { teห‡st(); }
-            "});
-        cx.update_editor(|editor, cx| {
-            update_go_to_definition_link(
-                editor,
-                Some(GoToDefinitionTrigger::Text(hover_point)),
-                false,
-                false,
-                cx,
-            );
-        });
-        cx.background_executor.run_until_parked();
-
-        // Assert no link highlights
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-                fn test() { do_work(); }
-                fn do_work() { test(); }
-            "});
-
-        let symbol_range = cx.lsp_range(indoc! {"
-                fn test() { do_work(); }
-                fn do_work() { ยซtestยป(); }
-            "});
-        let target_range = cx.lsp_range(indoc! {"
-                fn ยซtestยป() { do_work(); }
-                fn do_work() { test(); }
-            "});
-
-        let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
-            Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
-                lsp::LocationLink {
-                    origin_selection_range: Some(symbol_range),
-                    target_uri: url,
-                    target_range,
-                    target_selection_range: target_range,
-                },
-            ])))
-        });
-        cx.update_editor(|editor, cx| {
-            crate::element::EditorElement::modifiers_changed(
-                editor,
-                &ModifiersChangedEvent {
-                    modifiers: Modifiers {
-                        command: true,
-                        ..Default::default()
-                    },
-                },
-                cx,
-            );
-        });
-        requests.next().await;
-        cx.background_executor.run_until_parked();
-
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-                fn test() { do_work(); }
-                fn do_work() { ยซtestยป(); }
-            "});
-
-        // Deactivating the window dismisses the highlight
-        cx.update_workspace(|workspace, cx| {
-            workspace.on_window_activation_changed(cx);
-        });
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-                fn test() { do_work(); }
-                fn do_work() { test(); }
-            "});
-
-        // Moving the mouse restores the highlights.
-        cx.update_editor(|editor, cx| {
-            update_go_to_definition_link(
-                editor,
-                Some(GoToDefinitionTrigger::Text(hover_point)),
-                true,
-                false,
-                cx,
-            );
-        });
-        cx.background_executor.run_until_parked();
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-                fn test() { do_work(); }
-                fn do_work() { ยซtestยป(); }
-            "});
-
-        // Moving again within the same symbol range doesn't re-request
-        let hover_point = cx.display_point(indoc! {"
-                fn test() { do_work(); }
-                fn do_work() { tesห‡t(); }
-            "});
-        cx.update_editor(|editor, cx| {
-            update_go_to_definition_link(
-                editor,
-                Some(GoToDefinitionTrigger::Text(hover_point)),
-                true,
-                false,
-                cx,
-            );
-        });
-        cx.background_executor.run_until_parked();
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-                fn test() { do_work(); }
-                fn do_work() { ยซtestยป(); }
-            "});
-
-        // Cmd click with existing definition doesn't re-request and dismisses highlight
-        cx.update_editor(|editor, cx| {
-            go_to_fetched_definition(editor, PointForPosition::valid(hover_point), false, cx);
-        });
-        // Assert selection moved to to definition
-        cx.lsp
-            .handle_request::<GotoDefinition, _, _>(move |_, _| async move {
-                // Empty definition response to make sure we aren't hitting the lsp and using
-                // the cached location instead
-                Ok(Some(lsp::GotoDefinitionResponse::Link(vec![])))
-            });
-        cx.background_executor.run_until_parked();
-        cx.assert_editor_state(indoc! {"
-                fn ยซtestห‡ยป() { do_work(); }
-                fn do_work() { test(); }
-            "});
-
-        // Assert no link highlights after jump
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-                fn test() { do_work(); }
-                fn do_work() { test(); }
-            "});
-
-        // Cmd click without existing definition requests and jumps
-        let hover_point = cx.display_point(indoc! {"
-                fn test() { do_wห‡ork(); }
-                fn do_work() { test(); }
-            "});
-        let target_range = cx.lsp_range(indoc! {"
-                fn test() { do_work(); }
-                fn ยซdo_workยป() { test(); }
-            "});
-
-        let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
-            Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
-                lsp::LocationLink {
-                    origin_selection_range: None,
-                    target_uri: url,
-                    target_range,
-                    target_selection_range: target_range,
-                },
-            ])))
-        });
-        cx.update_editor(|editor, cx| {
-            go_to_fetched_definition(editor, PointForPosition::valid(hover_point), false, cx);
-        });
-        requests.next().await;
-        cx.background_executor.run_until_parked();
-        cx.assert_editor_state(indoc! {"
-                fn test() { do_work(); }
-                fn ยซdo_workห‡ยป() { test(); }
-            "});
-
-        // 1. We have a pending selection, mouse point is over a symbol that we have a response for, hitting cmd and nothing happens
-        // 2. Selection is completed, hovering
-        let hover_point = cx.display_point(indoc! {"
-                fn test() { do_wห‡ork(); }
-                fn do_work() { test(); }
-            "});
-        let target_range = cx.lsp_range(indoc! {"
-                fn test() { do_work(); }
-                fn ยซdo_workยป() { test(); }
-            "});
-        let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
-            Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
-                lsp::LocationLink {
-                    origin_selection_range: None,
-                    target_uri: url,
-                    target_range,
-                    target_selection_range: target_range,
-                },
-            ])))
-        });
-
-        // create a pending selection
-        let selection_range = cx.ranges(indoc! {"
-                fn ยซtest() { do_wยปork(); }
-                fn do_work() { test(); }
-            "})[0]
-            .clone();
-        cx.update_editor(|editor, cx| {
-            let snapshot = editor.buffer().read(cx).snapshot(cx);
-            let anchor_range = snapshot.anchor_before(selection_range.start)
-                ..snapshot.anchor_after(selection_range.end);
-            editor.change_selections(Some(crate::Autoscroll::fit()), cx, |s| {
-                s.set_pending_anchor_range(anchor_range, crate::SelectMode::Character)
-            });
-        });
-        cx.update_editor(|editor, cx| {
-            update_go_to_definition_link(
-                editor,
-                Some(GoToDefinitionTrigger::Text(hover_point)),
-                true,
-                false,
-                cx,
-            );
-        });
-        cx.background_executor.run_until_parked();
-        assert!(requests.try_next().is_err());
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-                fn test() { do_work(); }
-                fn do_work() { test(); }
-            "});
-        cx.background_executor.run_until_parked();
-    }
-
-    #[gpui::test]
-    async fn test_link_go_to_inlay(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-        cx.set_state(indoc! {"
-                struct TestStruct;
-
-                fn main() {
-                    let variableห‡ = TestStruct;
-                }
-            "});
-        let hint_start_offset = cx.ranges(indoc! {"
-                struct TestStruct;
-
-                fn main() {
-                    let variableห‡ = TestStruct;
-                }
-            "})[0]
-            .start;
-        let hint_position = cx.to_lsp(hint_start_offset);
-        let target_range = cx.lsp_range(indoc! {"
-                struct ยซTestStructยป;
-
-                fn main() {
-                    let variable = TestStruct;
-                }
-            "});
-
-        let expected_uri = cx.buffer_lsp_url.clone();
-        let hint_label = ": TestStruct";
-        cx.lsp
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let expected_uri = expected_uri.clone();
-                async move {
-                    assert_eq!(params.text_document.uri, expected_uri);
-                    Ok(Some(vec![lsp::InlayHint {
-                        position: hint_position,
-                        label: lsp::InlayHintLabel::LabelParts(vec![lsp::InlayHintLabelPart {
-                            value: hint_label.to_string(),
-                            location: Some(lsp::Location {
-                                uri: params.text_document.uri,
-                                range: target_range,
-                            }),
-                            ..Default::default()
-                        }]),
-                        kind: Some(lsp::InlayHintKind::TYPE),
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: Some(false),
-                        padding_right: Some(false),
-                        data: None,
-                    }]))
-                }
-            })
-            .next()
-            .await;
-        cx.background_executor.run_until_parked();
-        cx.update_editor(|editor, cx| {
-            let expected_layers = vec![hint_label.to_string()];
-            assert_eq!(expected_layers, cached_hint_labels(editor));
-            assert_eq!(expected_layers, visible_hint_labels(editor, cx));
-        });
-
-        let inlay_range = cx
-            .ranges(indoc! {"
-                struct TestStruct;
-
-                fn main() {
-                    let variableยซ ยป= TestStruct;
-                }
-            "})
-            .get(0)
-            .cloned()
-            .unwrap();
-        let hint_hover_position = cx.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
-            let previous_valid = inlay_range.start.to_display_point(&snapshot);
-            let next_valid = inlay_range.end.to_display_point(&snapshot);
-            assert_eq!(previous_valid.row(), next_valid.row());
-            assert!(previous_valid.column() < next_valid.column());
-            let exact_unclipped = DisplayPoint::new(
-                previous_valid.row(),
-                previous_valid.column() + (hint_label.len() / 2) as u32,
-            );
-            PointForPosition {
-                previous_valid,
-                next_valid,
-                exact_unclipped,
-                column_overshoot_after_line_end: 0,
-            }
-        });
-        // Press cmd to trigger highlight
-        cx.update_editor(|editor, cx| {
-            update_inlay_link_and_hover_points(
-                &editor.snapshot(cx),
-                hint_hover_position,
-                editor,
-                true,
-                false,
-                cx,
-            );
-        });
-        cx.background_executor.run_until_parked();
-        cx.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
-            let actual_highlights = snapshot
-                .inlay_highlights::<LinkGoToDefinitionState>()
-                .into_iter()
-                .flat_map(|highlights| highlights.values().map(|(_, highlight)| highlight))
-                .collect::<Vec<_>>();
-
-            let buffer_snapshot = editor.buffer().update(cx, |buffer, cx| buffer.snapshot(cx));
-            let expected_highlight = InlayHighlight {
-                inlay: InlayId::Hint(0),
-                inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right),
-                range: 0..hint_label.len(),
-            };
-            assert_set_eq!(actual_highlights, vec![&expected_highlight]);
-        });
-
-        // Unpress cmd causes highlight to go away
-        cx.update_editor(|editor, cx| {
-            crate::element::EditorElement::modifiers_changed(
-                editor,
-                &ModifiersChangedEvent {
-                    modifiers: Modifiers {
-                        command: false,
-                        ..Default::default()
-                    },
-                    ..Default::default()
-                },
-                cx,
-            );
-        });
-        // Assert no link highlights
-        cx.update_editor(|editor, cx| {
-                let snapshot = editor.snapshot(cx);
-                let actual_ranges = snapshot
-                    .text_highlight_ranges::<LinkGoToDefinitionState>()
-                    .map(|ranges| ranges.as_ref().clone().1)
-                    .unwrap_or_default();
-
-                assert!(actual_ranges.is_empty(), "When no cmd is pressed, should have no hint label selected, but got: {actual_ranges:?}");
-            });
-
-        // Cmd+click without existing definition requests and jumps
-        cx.update_editor(|editor, cx| {
-            crate::element::EditorElement::modifiers_changed(
-                editor,
-                &ModifiersChangedEvent {
-                    modifiers: Modifiers {
-                        command: true,
-                        ..Default::default()
-                    },
-                    ..Default::default()
-                },
-                cx,
-            );
-            update_inlay_link_and_hover_points(
-                &editor.snapshot(cx),
-                hint_hover_position,
-                editor,
-                true,
-                false,
-                cx,
-            );
-        });
-        cx.background_executor.run_until_parked();
-        cx.update_editor(|editor, cx| {
-            go_to_fetched_type_definition(editor, hint_hover_position, false, cx);
-        });
-        cx.background_executor.run_until_parked();
-        cx.assert_editor_state(indoc! {"
-                struct ยซTestStructห‡ยป;
-
-                fn main() {
-                    let variable = TestStruct;
-                }
-            "});
-    }
-}

crates/editor2/src/mouse_context_menu.rs ๐Ÿ”—

@@ -1,110 +0,0 @@
-use crate::{
-    DisplayPoint, Editor, EditorMode, FindAllReferences, GoToDefinition, GoToTypeDefinition,
-    Rename, RevealInFinder, SelectMode, ToggleCodeActions,
-};
-use gpui::{DismissEvent, Pixels, Point, Subscription, View, ViewContext};
-
-pub struct MouseContextMenu {
-    pub(crate) position: Point<Pixels>,
-    pub(crate) context_menu: View<ui::ContextMenu>,
-    _subscription: Subscription,
-}
-
-pub fn deploy_context_menu(
-    editor: &mut Editor,
-    position: Point<Pixels>,
-    point: DisplayPoint,
-    cx: &mut ViewContext<Editor>,
-) {
-    if !editor.is_focused(cx) {
-        editor.focus(cx);
-    }
-
-    // Don't show context menu for inline editors
-    if editor.mode() != EditorMode::Full {
-        return;
-    }
-
-    // Don't show the context menu if there isn't a project associated with this editor
-    if editor.project.is_none() {
-        return;
-    }
-
-    // Move the cursor to the clicked location so that dispatched actions make sense
-    editor.change_selections(None, cx, |s| {
-        s.clear_disjoint();
-        s.set_pending_display_range(point..point, SelectMode::Character);
-    });
-
-    let context_menu = ui::ContextMenu::build(cx, |menu, _cx| {
-        menu.action("Rename Symbol", Box::new(Rename))
-            .action("Go to Definition", Box::new(GoToDefinition))
-            .action("Go to Type Definition", Box::new(GoToTypeDefinition))
-            .action("Find All References", Box::new(FindAllReferences))
-            .action(
-                "Code Actions",
-                Box::new(ToggleCodeActions {
-                    deployed_from_indicator: false,
-                }),
-            )
-            .separator()
-            .action("Reveal in Finder", Box::new(RevealInFinder))
-    });
-    let context_menu_focus = context_menu.focus_handle(cx);
-    cx.focus(&context_menu_focus);
-
-    let _subscription = cx.subscribe(&context_menu, move |this, _, _event: &DismissEvent, cx| {
-        this.mouse_context_menu.take();
-        if context_menu_focus.contains_focused(cx) {
-            this.focus(cx);
-        }
-    });
-
-    editor.mouse_context_menu = Some(MouseContextMenu {
-        position,
-        context_menu,
-        _subscription,
-    });
-    cx.notify();
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
-    use indoc::indoc;
-
-    #[gpui::test]
-    async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-
-        cx.set_state(indoc! {"
-            fn teห‡st() {
-                do_work();
-            }
-        "});
-        let point = cx.display_point(indoc! {"
-            fn test() {
-                do_wห‡ork();
-            }
-        "});
-        cx.editor(|editor, _app| assert!(editor.mouse_context_menu.is_none()));
-        cx.update_editor(|editor, cx| deploy_context_menu(editor, Default::default(), point, cx));
-
-        cx.assert_editor_state(indoc! {"
-            fn test() {
-                do_wห‡ork();
-            }
-        "});
-        cx.editor(|editor, _app| assert!(editor.mouse_context_menu.is_some()));
-    }
-}

crates/editor2/src/movement.rs ๐Ÿ”—

@@ -1,926 +0,0 @@
-use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
-use crate::{char_kind, CharKind, EditorStyle, ToOffset, ToPoint};
-use gpui::{px, Pixels, TextSystem};
-use language::Point;
-
-use std::{ops::Range, sync::Arc};
-
-#[derive(Debug, PartialEq)]
-pub enum FindRange {
-    SingleLine,
-    MultiLine,
-}
-
-/// TextLayoutDetails encompasses everything we need to move vertically
-/// taking into account variable width characters.
-pub struct TextLayoutDetails {
-    pub text_system: Arc<TextSystem>,
-    pub editor_style: EditorStyle,
-    pub rem_size: Pixels,
-}
-
-pub fn left(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint {
-    if point.column() > 0 {
-        *point.column_mut() -= 1;
-    } else if point.row() > 0 {
-        *point.row_mut() -= 1;
-        *point.column_mut() = map.line_len(point.row());
-    }
-    map.clip_point(point, Bias::Left)
-}
-
-pub fn saturating_left(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint {
-    if point.column() > 0 {
-        *point.column_mut() -= 1;
-    }
-    map.clip_point(point, Bias::Left)
-}
-
-pub fn right(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint {
-    let max_column = map.line_len(point.row());
-    if point.column() < max_column {
-        *point.column_mut() += 1;
-    } else if point.row() < map.max_point().row() {
-        *point.row_mut() += 1;
-        *point.column_mut() = 0;
-    }
-    map.clip_point(point, Bias::Right)
-}
-
-pub fn saturating_right(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint {
-    *point.column_mut() += 1;
-    map.clip_point(point, Bias::Right)
-}
-
-pub fn up(
-    map: &DisplaySnapshot,
-    start: DisplayPoint,
-    goal: SelectionGoal,
-    preserve_column_at_start: bool,
-    text_layout_details: &TextLayoutDetails,
-) -> (DisplayPoint, SelectionGoal) {
-    up_by_rows(
-        map,
-        start,
-        1,
-        goal,
-        preserve_column_at_start,
-        text_layout_details,
-    )
-}
-
-pub fn down(
-    map: &DisplaySnapshot,
-    start: DisplayPoint,
-    goal: SelectionGoal,
-    preserve_column_at_end: bool,
-    text_layout_details: &TextLayoutDetails,
-) -> (DisplayPoint, SelectionGoal) {
-    down_by_rows(
-        map,
-        start,
-        1,
-        goal,
-        preserve_column_at_end,
-        text_layout_details,
-    )
-}
-
-pub fn up_by_rows(
-    map: &DisplaySnapshot,
-    start: DisplayPoint,
-    row_count: u32,
-    goal: SelectionGoal,
-    preserve_column_at_start: bool,
-    text_layout_details: &TextLayoutDetails,
-) -> (DisplayPoint, SelectionGoal) {
-    let mut goal_x = match goal {
-        SelectionGoal::HorizontalPosition(x) => x.into(), // todo!("Can the fields in SelectionGoal by Pixels? We should extract a geometry crate and depend on that.")
-        SelectionGoal::WrappedHorizontalPosition((_, x)) => x.into(),
-        SelectionGoal::HorizontalRange { end, .. } => end.into(),
-        _ => map.x_for_display_point(start, text_layout_details),
-    };
-
-    let prev_row = start.row().saturating_sub(row_count);
-    let mut point = map.clip_point(
-        DisplayPoint::new(prev_row, map.line_len(prev_row)),
-        Bias::Left,
-    );
-    if point.row() < start.row() {
-        *point.column_mut() = map.display_column_for_x(point.row(), goal_x, text_layout_details)
-    } else if preserve_column_at_start {
-        return (start, goal);
-    } else {
-        point = DisplayPoint::new(0, 0);
-        goal_x = px(0.);
-    }
-
-    let mut clipped_point = map.clip_point(point, Bias::Left);
-    if clipped_point.row() < point.row() {
-        clipped_point = map.clip_point(point, Bias::Right);
-    }
-    (
-        clipped_point,
-        SelectionGoal::HorizontalPosition(goal_x.into()),
-    )
-}
-
-pub fn down_by_rows(
-    map: &DisplaySnapshot,
-    start: DisplayPoint,
-    row_count: u32,
-    goal: SelectionGoal,
-    preserve_column_at_end: bool,
-    text_layout_details: &TextLayoutDetails,
-) -> (DisplayPoint, SelectionGoal) {
-    let mut goal_x = match goal {
-        SelectionGoal::HorizontalPosition(x) => x.into(),
-        SelectionGoal::WrappedHorizontalPosition((_, x)) => x.into(),
-        SelectionGoal::HorizontalRange { end, .. } => end.into(),
-        _ => map.x_for_display_point(start, text_layout_details),
-    };
-
-    let new_row = start.row() + row_count;
-    let mut point = map.clip_point(DisplayPoint::new(new_row, 0), Bias::Right);
-    if point.row() > start.row() {
-        *point.column_mut() = map.display_column_for_x(point.row(), goal_x, text_layout_details)
-    } else if preserve_column_at_end {
-        return (start, goal);
-    } else {
-        point = map.max_point();
-        goal_x = map.x_for_display_point(point, text_layout_details)
-    }
-
-    let mut clipped_point = map.clip_point(point, Bias::Right);
-    if clipped_point.row() > point.row() {
-        clipped_point = map.clip_point(point, Bias::Left);
-    }
-    (
-        clipped_point,
-        SelectionGoal::HorizontalPosition(goal_x.into()),
-    )
-}
-
-pub fn line_beginning(
-    map: &DisplaySnapshot,
-    display_point: DisplayPoint,
-    stop_at_soft_boundaries: bool,
-) -> DisplayPoint {
-    let point = display_point.to_point(map);
-    let soft_line_start = map.clip_point(DisplayPoint::new(display_point.row(), 0), Bias::Right);
-    let line_start = map.prev_line_boundary(point).1;
-
-    if stop_at_soft_boundaries && display_point != soft_line_start {
-        soft_line_start
-    } else {
-        line_start
-    }
-}
-
-pub fn indented_line_beginning(
-    map: &DisplaySnapshot,
-    display_point: DisplayPoint,
-    stop_at_soft_boundaries: bool,
-) -> DisplayPoint {
-    let point = display_point.to_point(map);
-    let soft_line_start = map.clip_point(DisplayPoint::new(display_point.row(), 0), Bias::Right);
-    let indent_start = Point::new(
-        point.row,
-        map.buffer_snapshot.indent_size_for_line(point.row).len,
-    )
-    .to_display_point(map);
-    let line_start = map.prev_line_boundary(point).1;
-
-    if stop_at_soft_boundaries && soft_line_start > indent_start && display_point != soft_line_start
-    {
-        soft_line_start
-    } else if stop_at_soft_boundaries && display_point != indent_start {
-        indent_start
-    } else {
-        line_start
-    }
-}
-
-pub fn line_end(
-    map: &DisplaySnapshot,
-    display_point: DisplayPoint,
-    stop_at_soft_boundaries: bool,
-) -> DisplayPoint {
-    let soft_line_end = map.clip_point(
-        DisplayPoint::new(display_point.row(), map.line_len(display_point.row())),
-        Bias::Left,
-    );
-    if stop_at_soft_boundaries && display_point != soft_line_end {
-        soft_line_end
-    } else {
-        map.next_line_boundary(display_point.to_point(map)).1
-    }
-}
-
-pub fn previous_word_start(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint {
-    let raw_point = point.to_point(map);
-    let scope = map.buffer_snapshot.language_scope_at(raw_point);
-
-    find_preceding_boundary(map, point, FindRange::MultiLine, |left, right| {
-        (char_kind(&scope, left) != char_kind(&scope, right) && !right.is_whitespace())
-            || left == '\n'
-    })
-}
-
-pub fn previous_subword_start(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint {
-    let raw_point = point.to_point(map);
-    let scope = map.buffer_snapshot.language_scope_at(raw_point);
-
-    find_preceding_boundary(map, point, FindRange::MultiLine, |left, right| {
-        let is_word_start =
-            char_kind(&scope, left) != char_kind(&scope, right) && !right.is_whitespace();
-        let is_subword_start =
-            left == '_' && right != '_' || left.is_lowercase() && right.is_uppercase();
-        is_word_start || is_subword_start || left == '\n'
-    })
-}
-
-pub fn next_word_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint {
-    let raw_point = point.to_point(map);
-    let scope = map.buffer_snapshot.language_scope_at(raw_point);
-
-    find_boundary(map, point, FindRange::MultiLine, |left, right| {
-        (char_kind(&scope, left) != char_kind(&scope, right) && !left.is_whitespace())
-            || right == '\n'
-    })
-}
-
-pub fn next_subword_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint {
-    let raw_point = point.to_point(map);
-    let scope = map.buffer_snapshot.language_scope_at(raw_point);
-
-    find_boundary(map, point, FindRange::MultiLine, |left, right| {
-        let is_word_end =
-            (char_kind(&scope, left) != char_kind(&scope, right)) && !left.is_whitespace();
-        let is_subword_end =
-            left != '_' && right == '_' || left.is_lowercase() && right.is_uppercase();
-        is_word_end || is_subword_end || right == '\n'
-    })
-}
-
-pub fn start_of_paragraph(
-    map: &DisplaySnapshot,
-    display_point: DisplayPoint,
-    mut count: usize,
-) -> DisplayPoint {
-    let point = display_point.to_point(map);
-    if point.row == 0 {
-        return DisplayPoint::zero();
-    }
-
-    let mut found_non_blank_line = false;
-    for row in (0..point.row + 1).rev() {
-        let blank = map.buffer_snapshot.is_line_blank(row);
-        if found_non_blank_line && blank {
-            if count <= 1 {
-                return Point::new(row, 0).to_display_point(map);
-            }
-            count -= 1;
-            found_non_blank_line = false;
-        }
-
-        found_non_blank_line |= !blank;
-    }
-
-    DisplayPoint::zero()
-}
-
-pub fn end_of_paragraph(
-    map: &DisplaySnapshot,
-    display_point: DisplayPoint,
-    mut count: usize,
-) -> DisplayPoint {
-    let point = display_point.to_point(map);
-    if point.row == map.max_buffer_row() {
-        return map.max_point();
-    }
-
-    let mut found_non_blank_line = false;
-    for row in point.row..map.max_buffer_row() + 1 {
-        let blank = map.buffer_snapshot.is_line_blank(row);
-        if found_non_blank_line && blank {
-            if count <= 1 {
-                return Point::new(row, 0).to_display_point(map);
-            }
-            count -= 1;
-            found_non_blank_line = false;
-        }
-
-        found_non_blank_line |= !blank;
-    }
-
-    map.max_point()
-}
-
-/// Scans for a boundary preceding the given start point `from` until a boundary is found,
-/// indicated by the given predicate returning true.
-/// The predicate is called with the character to the left and right of the candidate boundary location.
-/// If FindRange::SingleLine is specified and no boundary is found before the start of the current line, the start of the current line will be returned.
-pub fn find_preceding_boundary(
-    map: &DisplaySnapshot,
-    from: DisplayPoint,
-    find_range: FindRange,
-    mut is_boundary: impl FnMut(char, char) -> bool,
-) -> DisplayPoint {
-    let mut prev_ch = None;
-    let mut offset = from.to_point(map).to_offset(&map.buffer_snapshot);
-
-    for ch in map.buffer_snapshot.reversed_chars_at(offset) {
-        if find_range == FindRange::SingleLine && ch == '\n' {
-            break;
-        }
-        if let Some(prev_ch) = prev_ch {
-            if is_boundary(ch, prev_ch) {
-                break;
-            }
-        }
-
-        offset -= ch.len_utf8();
-        prev_ch = Some(ch);
-    }
-
-    map.clip_point(offset.to_display_point(map), Bias::Left)
-}
-
-/// Scans for a boundary following the given start point until a boundary is found, indicated by the
-/// given predicate returning true. The predicate is called with the character to the left and right
-/// of the candidate boundary location, and will be called with `\n` characters indicating the start
-/// or end of a line.
-pub fn find_boundary(
-    map: &DisplaySnapshot,
-    from: DisplayPoint,
-    find_range: FindRange,
-    mut is_boundary: impl FnMut(char, char) -> bool,
-) -> DisplayPoint {
-    let mut offset = from.to_offset(&map, Bias::Right);
-    let mut prev_ch = None;
-
-    for ch in map.buffer_snapshot.chars_at(offset) {
-        if find_range == FindRange::SingleLine && ch == '\n' {
-            break;
-        }
-        if let Some(prev_ch) = prev_ch {
-            if is_boundary(prev_ch, ch) {
-                break;
-            }
-        }
-
-        offset += ch.len_utf8();
-        prev_ch = Some(ch);
-    }
-    map.clip_point(offset.to_display_point(map), Bias::Right)
-}
-
-pub fn chars_after(
-    map: &DisplaySnapshot,
-    mut offset: usize,
-) -> impl Iterator<Item = (char, Range<usize>)> + '_ {
-    map.buffer_snapshot.chars_at(offset).map(move |ch| {
-        let before = offset;
-        offset = offset + ch.len_utf8();
-        (ch, before..offset)
-    })
-}
-
-pub fn chars_before(
-    map: &DisplaySnapshot,
-    mut offset: usize,
-) -> impl Iterator<Item = (char, Range<usize>)> + '_ {
-    map.buffer_snapshot
-        .reversed_chars_at(offset)
-        .map(move |ch| {
-            let after = offset;
-            offset = offset - ch.len_utf8();
-            (ch, offset..after)
-        })
-}
-
-pub fn is_inside_word(map: &DisplaySnapshot, point: DisplayPoint) -> bool {
-    let raw_point = point.to_point(map);
-    let scope = map.buffer_snapshot.language_scope_at(raw_point);
-    let ix = map.clip_point(point, Bias::Left).to_offset(map, Bias::Left);
-    let text = &map.buffer_snapshot;
-    let next_char_kind = text.chars_at(ix).next().map(|c| char_kind(&scope, c));
-    let prev_char_kind = text
-        .reversed_chars_at(ix)
-        .next()
-        .map(|c| char_kind(&scope, c));
-    prev_char_kind.zip(next_char_kind) == Some((CharKind::Word, CharKind::Word))
-}
-
-pub fn surrounding_word(map: &DisplaySnapshot, position: DisplayPoint) -> Range<DisplayPoint> {
-    let position = map
-        .clip_point(position, Bias::Left)
-        .to_offset(map, Bias::Left);
-    let (range, _) = map.buffer_snapshot.surrounding_word(position);
-    let start = range
-        .start
-        .to_point(&map.buffer_snapshot)
-        .to_display_point(map);
-    let end = range
-        .end
-        .to_point(&map.buffer_snapshot)
-        .to_display_point(map);
-    start..end
-}
-
-pub fn split_display_range_by_lines(
-    map: &DisplaySnapshot,
-    range: Range<DisplayPoint>,
-) -> Vec<Range<DisplayPoint>> {
-    let mut result = Vec::new();
-
-    let mut start = range.start;
-    // Loop over all the covered rows until the one containing the range end
-    for row in range.start.row()..range.end.row() {
-        let row_end_column = map.line_len(row);
-        let end = map.clip_point(DisplayPoint::new(row, row_end_column), Bias::Left);
-        if start != end {
-            result.push(start..end);
-        }
-        start = map.clip_point(DisplayPoint::new(row + 1, 0), Bias::Left);
-    }
-
-    // Add the final range from the start of the last end to the original range end.
-    result.push(start..range.end);
-
-    result
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{
-        display_map::Inlay,
-        test::{editor_test_context::EditorTestContext, marked_display_snapshot},
-        Buffer, DisplayMap, ExcerptRange, InlayId, MultiBuffer,
-    };
-    use gpui::{font, Context as _};
-    use project::Project;
-    use settings::SettingsStore;
-    use util::post_inc;
-
-    #[gpui::test]
-    fn test_previous_word_start(cx: &mut gpui::AppContext) {
-        init_test(cx);
-
-        fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
-            let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
-            assert_eq!(
-                previous_word_start(&snapshot, display_points[1]),
-                display_points[0]
-            );
-        }
-
-        assert("\nห‡   ห‡lorem", cx);
-        assert("ห‡\nห‡   lorem", cx);
-        assert("    ห‡loremห‡", cx);
-        assert("ห‡    ห‡lorem", cx);
-        assert("    ห‡lorห‡em", cx);
-        assert("\nlorem\nห‡   ห‡ipsum", cx);
-        assert("\n\nห‡\nห‡", cx);
-        assert("    ห‡lorem  ห‡ipsum", cx);
-        assert("loremห‡-ห‡ipsum", cx);
-        assert("loremห‡-#$@ห‡ipsum", cx);
-        assert("ห‡lorem_ห‡ipsum", cx);
-        assert(" ห‡defฮณห‡", cx);
-        assert(" ห‡bcฮ”ห‡", cx);
-        assert(" abห‡โ€”โ€”ห‡cd", cx);
-    }
-
-    #[gpui::test]
-    fn test_previous_subword_start(cx: &mut gpui::AppContext) {
-        init_test(cx);
-
-        fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
-            let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
-            assert_eq!(
-                previous_subword_start(&snapshot, display_points[1]),
-                display_points[0]
-            );
-        }
-
-        // Subword boundaries are respected
-        assert("lorem_ห‡ipห‡sum", cx);
-        assert("lorem_ห‡ipsumห‡", cx);
-        assert("ห‡lorem_ห‡ipsum", cx);
-        assert("lorem_ห‡ipsum_ห‡dolor", cx);
-        assert("loremห‡Ipห‡sum", cx);
-        assert("loremห‡Ipsumห‡", cx);
-
-        // Word boundaries are still respected
-        assert("\nห‡   ห‡lorem", cx);
-        assert("    ห‡loremห‡", cx);
-        assert("    ห‡lorห‡em", cx);
-        assert("\nlorem\nห‡   ห‡ipsum", cx);
-        assert("\n\nห‡\nห‡", cx);
-        assert("    ห‡lorem  ห‡ipsum", cx);
-        assert("loremห‡-ห‡ipsum", cx);
-        assert("loremห‡-#$@ห‡ipsum", cx);
-        assert(" ห‡defฮณห‡", cx);
-        assert(" bcห‡ฮ”ห‡", cx);
-        assert(" ห‡bcฮดห‡", cx);
-        assert(" abห‡โ€”โ€”ห‡cd", cx);
-    }
-
-    #[gpui::test]
-    fn test_find_preceding_boundary(cx: &mut gpui::AppContext) {
-        init_test(cx);
-
-        fn assert(
-            marked_text: &str,
-            cx: &mut gpui::AppContext,
-            is_boundary: impl FnMut(char, char) -> bool,
-        ) {
-            let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
-            assert_eq!(
-                find_preceding_boundary(
-                    &snapshot,
-                    display_points[1],
-                    FindRange::MultiLine,
-                    is_boundary
-                ),
-                display_points[0]
-            );
-        }
-
-        assert("abcห‡def\ngh\nijห‡k", cx, |left, right| {
-            left == 'c' && right == 'd'
-        });
-        assert("abcdef\nห‡gh\nijห‡k", cx, |left, right| {
-            left == '\n' && right == 'g'
-        });
-        let mut line_count = 0;
-        assert("abcdef\nห‡gh\nijห‡k", cx, |left, _| {
-            if left == '\n' {
-                line_count += 1;
-                line_count == 2
-            } else {
-                false
-            }
-        });
-    }
-
-    #[gpui::test]
-    fn test_find_preceding_boundary_with_inlays(cx: &mut gpui::AppContext) {
-        init_test(cx);
-
-        let input_text = "abcdefghijklmnopqrstuvwxys";
-        let font = font("Helvetica");
-        let font_size = px(14.0);
-        let buffer = MultiBuffer::build_simple(input_text, cx);
-        let buffer_snapshot = buffer.read(cx).snapshot(cx);
-        let display_map =
-            cx.new_model(|cx| DisplayMap::new(buffer, font, font_size, None, 1, 1, cx));
-
-        // add all kinds of inlays between two word boundaries: we should be able to cross them all, when looking for another boundary
-        let mut id = 0;
-        let inlays = (0..buffer_snapshot.len())
-            .map(|offset| {
-                [
-                    Inlay {
-                        id: InlayId::Suggestion(post_inc(&mut id)),
-                        position: buffer_snapshot.anchor_at(offset, Bias::Left),
-                        text: format!("test").into(),
-                    },
-                    Inlay {
-                        id: InlayId::Suggestion(post_inc(&mut id)),
-                        position: buffer_snapshot.anchor_at(offset, Bias::Right),
-                        text: format!("test").into(),
-                    },
-                    Inlay {
-                        id: InlayId::Hint(post_inc(&mut id)),
-                        position: buffer_snapshot.anchor_at(offset, Bias::Left),
-                        text: format!("test").into(),
-                    },
-                    Inlay {
-                        id: InlayId::Hint(post_inc(&mut id)),
-                        position: buffer_snapshot.anchor_at(offset, Bias::Right),
-                        text: format!("test").into(),
-                    },
-                ]
-            })
-            .flatten()
-            .collect();
-        let snapshot = display_map.update(cx, |map, cx| {
-            map.splice_inlays(Vec::new(), inlays, cx);
-            map.snapshot(cx)
-        });
-
-        assert_eq!(
-            find_preceding_boundary(
-                &snapshot,
-                buffer_snapshot.len().to_display_point(&snapshot),
-                FindRange::MultiLine,
-                |left, _| left == 'e',
-            ),
-            snapshot
-                .buffer_snapshot
-                .offset_to_point(5)
-                .to_display_point(&snapshot),
-            "Should not stop at inlays when looking for boundaries"
-        );
-    }
-
-    #[gpui::test]
-    fn test_next_word_end(cx: &mut gpui::AppContext) {
-        init_test(cx);
-
-        fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
-            let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
-            assert_eq!(
-                next_word_end(&snapshot, display_points[0]),
-                display_points[1]
-            );
-        }
-
-        assert("\nห‡   loremห‡", cx);
-        assert("    ห‡loremห‡", cx);
-        assert("    lorห‡emห‡", cx);
-        assert("    loremห‡    ห‡\nipsum\n", cx);
-        assert("\nห‡\nห‡\n\n", cx);
-        assert("loremห‡    ipsumห‡   ", cx);
-        assert("loremห‡-ห‡ipsum", cx);
-        assert("loremห‡#$@-ห‡ipsum", cx);
-        assert("loremห‡_ipsumห‡", cx);
-        assert(" ห‡bcฮ”ห‡", cx);
-        assert(" abห‡โ€”โ€”ห‡cd", cx);
-    }
-
-    #[gpui::test]
-    fn test_next_subword_end(cx: &mut gpui::AppContext) {
-        init_test(cx);
-
-        fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
-            let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
-            assert_eq!(
-                next_subword_end(&snapshot, display_points[0]),
-                display_points[1]
-            );
-        }
-
-        // Subword boundaries are respected
-        assert("loห‡remห‡_ipsum", cx);
-        assert("ห‡loremห‡_ipsum", cx);
-        assert("loremห‡_ipsumห‡", cx);
-        assert("loremห‡_ipsumห‡_dolor", cx);
-        assert("loห‡remห‡Ipsum", cx);
-        assert("loremห‡Ipsumห‡Dolor", cx);
-
-        // Word boundaries are still respected
-        assert("\nห‡   loremห‡", cx);
-        assert("    ห‡loremห‡", cx);
-        assert("    lorห‡emห‡", cx);
-        assert("    loremห‡    ห‡\nipsum\n", cx);
-        assert("\nห‡\nห‡\n\n", cx);
-        assert("loremห‡    ipsumห‡   ", cx);
-        assert("loremห‡-ห‡ipsum", cx);
-        assert("loremห‡#$@-ห‡ipsum", cx);
-        assert("loremห‡_ipsumห‡", cx);
-        assert(" ห‡bcห‡ฮ”", cx);
-        assert(" abห‡โ€”โ€”ห‡cd", cx);
-    }
-
-    #[gpui::test]
-    fn test_find_boundary(cx: &mut gpui::AppContext) {
-        init_test(cx);
-
-        fn assert(
-            marked_text: &str,
-            cx: &mut gpui::AppContext,
-            is_boundary: impl FnMut(char, char) -> bool,
-        ) {
-            let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
-            assert_eq!(
-                find_boundary(
-                    &snapshot,
-                    display_points[0],
-                    FindRange::MultiLine,
-                    is_boundary
-                ),
-                display_points[1]
-            );
-        }
-
-        assert("abcห‡def\ngh\nijห‡k", cx, |left, right| {
-            left == 'j' && right == 'k'
-        });
-        assert("abห‡cdef\ngh\nห‡ijk", cx, |left, right| {
-            left == '\n' && right == 'i'
-        });
-        let mut line_count = 0;
-        assert("abcห‡def\ngh\nห‡ijk", cx, |left, _| {
-            if left == '\n' {
-                line_count += 1;
-                line_count == 2
-            } else {
-                false
-            }
-        });
-    }
-
-    #[gpui::test]
-    fn test_surrounding_word(cx: &mut gpui::AppContext) {
-        init_test(cx);
-
-        fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
-            let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
-            assert_eq!(
-                surrounding_word(&snapshot, display_points[1]),
-                display_points[0]..display_points[2],
-                "{}",
-                marked_text.to_string()
-            );
-        }
-
-        assert("ห‡ห‡loremห‡  ipsum", cx);
-        assert("ห‡loห‡remห‡  ipsum", cx);
-        assert("ห‡loremห‡ห‡  ipsum", cx);
-        assert("loremห‡ ห‡  ห‡ipsum", cx);
-        assert("lorem\nห‡ห‡ห‡\nipsum", cx);
-        assert("lorem\nห‡ห‡ipsumห‡", cx);
-        assert("loremห‡,ห‡ห‡ ipsum", cx);
-        assert("ห‡loremห‡ห‡, ipsum", cx);
-    }
-
-    #[gpui::test]
-    async fn test_move_up_and_down_with_excerpts(cx: &mut gpui::TestAppContext) {
-        cx.update(|cx| {
-            init_test(cx);
-        });
-
-        let mut cx = EditorTestContext::new(cx).await;
-        let editor = cx.editor.clone();
-        let window = cx.window.clone();
-        _ = cx.update_window(window, |_, cx| {
-            let text_layout_details =
-                editor.update(cx, |editor, cx| editor.text_layout_details(cx));
-
-            let font = font("Helvetica");
-
-            let buffer =
-                cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abc\ndefg\nhijkl\nmn"));
-            let multibuffer = cx.new_model(|cx| {
-                let mut multibuffer = MultiBuffer::new(0);
-                multibuffer.push_excerpts(
-                    buffer.clone(),
-                    [
-                        ExcerptRange {
-                            context: Point::new(0, 0)..Point::new(1, 4),
-                            primary: None,
-                        },
-                        ExcerptRange {
-                            context: Point::new(2, 0)..Point::new(3, 2),
-                            primary: None,
-                        },
-                    ],
-                    cx,
-                );
-                multibuffer
-            });
-            let display_map =
-                cx.new_model(|cx| DisplayMap::new(multibuffer, font, px(14.0), None, 2, 2, cx));
-            let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
-
-            assert_eq!(snapshot.text(), "\n\nabc\ndefg\n\n\nhijkl\nmn");
-
-            let col_2_x =
-                snapshot.x_for_display_point(DisplayPoint::new(2, 2), &text_layout_details);
-
-            // Can't move up into the first excerpt's header
-            assert_eq!(
-                up(
-                    &snapshot,
-                    DisplayPoint::new(2, 2),
-                    SelectionGoal::HorizontalPosition(col_2_x.0),
-                    false,
-                    &text_layout_details
-                ),
-                (
-                    DisplayPoint::new(2, 0),
-                    SelectionGoal::HorizontalPosition(0.0)
-                ),
-            );
-            assert_eq!(
-                up(
-                    &snapshot,
-                    DisplayPoint::new(2, 0),
-                    SelectionGoal::None,
-                    false,
-                    &text_layout_details
-                ),
-                (
-                    DisplayPoint::new(2, 0),
-                    SelectionGoal::HorizontalPosition(0.0)
-                ),
-            );
-
-            let col_4_x =
-                snapshot.x_for_display_point(DisplayPoint::new(3, 4), &text_layout_details);
-
-            // Move up and down within first excerpt
-            assert_eq!(
-                up(
-                    &snapshot,
-                    DisplayPoint::new(3, 4),
-                    SelectionGoal::HorizontalPosition(col_4_x.0),
-                    false,
-                    &text_layout_details
-                ),
-                (
-                    DisplayPoint::new(2, 3),
-                    SelectionGoal::HorizontalPosition(col_4_x.0)
-                ),
-            );
-            assert_eq!(
-                down(
-                    &snapshot,
-                    DisplayPoint::new(2, 3),
-                    SelectionGoal::HorizontalPosition(col_4_x.0),
-                    false,
-                    &text_layout_details
-                ),
-                (
-                    DisplayPoint::new(3, 4),
-                    SelectionGoal::HorizontalPosition(col_4_x.0)
-                ),
-            );
-
-            let col_5_x =
-                snapshot.x_for_display_point(DisplayPoint::new(6, 5), &text_layout_details);
-
-            // Move up and down across second excerpt's header
-            assert_eq!(
-                up(
-                    &snapshot,
-                    DisplayPoint::new(6, 5),
-                    SelectionGoal::HorizontalPosition(col_5_x.0),
-                    false,
-                    &text_layout_details
-                ),
-                (
-                    DisplayPoint::new(3, 4),
-                    SelectionGoal::HorizontalPosition(col_5_x.0)
-                ),
-            );
-            assert_eq!(
-                down(
-                    &snapshot,
-                    DisplayPoint::new(3, 4),
-                    SelectionGoal::HorizontalPosition(col_5_x.0),
-                    false,
-                    &text_layout_details
-                ),
-                (
-                    DisplayPoint::new(6, 5),
-                    SelectionGoal::HorizontalPosition(col_5_x.0)
-                ),
-            );
-
-            let max_point_x =
-                snapshot.x_for_display_point(DisplayPoint::new(7, 2), &text_layout_details);
-
-            // Can't move down off the end
-            assert_eq!(
-                down(
-                    &snapshot,
-                    DisplayPoint::new(7, 0),
-                    SelectionGoal::HorizontalPosition(0.0),
-                    false,
-                    &text_layout_details
-                ),
-                (
-                    DisplayPoint::new(7, 2),
-                    SelectionGoal::HorizontalPosition(max_point_x.0)
-                ),
-            );
-            assert_eq!(
-                down(
-                    &snapshot,
-                    DisplayPoint::new(7, 2),
-                    SelectionGoal::HorizontalPosition(max_point_x.0),
-                    false,
-                    &text_layout_details
-                ),
-                (
-                    DisplayPoint::new(7, 2),
-                    SelectionGoal::HorizontalPosition(max_point_x.0)
-                ),
-            );
-        });
-    }
-
-    fn init_test(cx: &mut gpui::AppContext) {
-        let settings_store = SettingsStore::test(cx);
-        cx.set_global(settings_store);
-        theme::init(theme::LoadThemes::JustBase, cx);
-        language::init(cx);
-        crate::init(cx);
-        Project::init_settings(cx);
-    }
-}

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

@@ -1,83 +0,0 @@
-use std::path::PathBuf;
-
-use db::sqlez_macros::sql;
-use db::{define_connection, query};
-
-use workspace::{ItemId, WorkspaceDb, WorkspaceId};
-
-define_connection!(
-    // Current schema shape using pseudo-rust syntax:
-    // editors(
-    //   item_id: usize,
-    //   workspace_id: usize,
-    //   path: PathBuf,
-    //   scroll_top_row: usize,
-    //   scroll_vertical_offset: f32,
-    //   scroll_horizontal_offset: f32,
-    // )
-    pub static ref DB: EditorDb<WorkspaceDb> =
-        &[sql! (
-            CREATE TABLE editors(
-                item_id INTEGER NOT NULL,
-                workspace_id INTEGER NOT NULL,
-                path BLOB NOT NULL,
-                PRIMARY KEY(item_id, workspace_id),
-                FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)
-                ON DELETE CASCADE
-                ON UPDATE CASCADE
-            ) STRICT;
-        ),
-        sql! (
-            ALTER TABLE editors ADD COLUMN scroll_top_row INTEGER NOT NULL DEFAULT 0;
-            ALTER TABLE editors ADD COLUMN scroll_horizontal_offset REAL NOT NULL DEFAULT 0;
-            ALTER TABLE editors ADD COLUMN scroll_vertical_offset REAL NOT NULL DEFAULT 0;
-        )];
-);
-
-impl EditorDb {
-    query! {
-        pub fn get_path(item_id: ItemId, workspace_id: WorkspaceId) -> Result<Option<PathBuf>> {
-            SELECT path FROM editors
-            WHERE item_id = ? AND workspace_id = ?
-        }
-    }
-
-    query! {
-        pub async fn save_path(item_id: ItemId, workspace_id: WorkspaceId, path: PathBuf) -> Result<()> {
-            INSERT INTO editors
-                (item_id, workspace_id, path)
-            VALUES
-                (?1, ?2, ?3)
-            ON CONFLICT DO UPDATE SET
-                item_id = ?1,
-                workspace_id = ?2,
-                path = ?3
-        }
-    }
-
-    // Returns the scroll top row, and offset
-    query! {
-        pub fn get_scroll_position(item_id: ItemId, workspace_id: WorkspaceId) -> Result<Option<(u32, f32, f32)>> {
-            SELECT scroll_top_row, scroll_horizontal_offset, scroll_vertical_offset
-            FROM editors
-            WHERE item_id = ? AND workspace_id = ?
-        }
-    }
-
-    query! {
-        pub async fn save_scroll_position(
-            item_id: ItemId,
-            workspace_id: WorkspaceId,
-            top_row: u32,
-            vertical_offset: f32,
-            horizontal_offset: f32
-        ) -> Result<()> {
-            UPDATE OR IGNORE editors
-            SET
-                scroll_top_row = ?3,
-                scroll_horizontal_offset = ?4,
-                scroll_vertical_offset = ?5
-            WHERE item_id = ?1 AND workspace_id = ?2
-        }
-    }
-}

crates/editor2/src/rust_analyzer_ext.rs ๐Ÿ”—

@@ -1,119 +0,0 @@
-use std::sync::Arc;
-
-use anyhow::Context as _;
-use gpui::{Context, View, ViewContext, VisualContext, WindowContext};
-use language::Language;
-use multi_buffer::MultiBuffer;
-use project::lsp_ext_command::ExpandMacro;
-use text::ToPointUtf16;
-
-use crate::{element::register_action, Editor, ExpandMacroRecursively};
-
-pub fn apply_related_actions(editor: &View<Editor>, cx: &mut WindowContext) {
-    let is_rust_related = editor.update(cx, |editor, cx| {
-        editor
-            .buffer()
-            .read(cx)
-            .all_buffers()
-            .iter()
-            .any(|b| match b.read(cx).language() {
-                Some(l) => is_rust_language(l),
-                None => false,
-            })
-    });
-
-    if is_rust_related {
-        register_action(editor, cx, expand_macro_recursively);
-    }
-}
-
-pub fn expand_macro_recursively(
-    editor: &mut Editor,
-    _: &ExpandMacroRecursively,
-    cx: &mut ViewContext<'_, Editor>,
-) {
-    if editor.selections.count() == 0 {
-        return;
-    }
-    let Some(project) = &editor.project else {
-        return;
-    };
-    let Some(workspace) = editor.workspace() else {
-        return;
-    };
-
-    let multibuffer = editor.buffer().read(cx);
-
-    let Some((trigger_anchor, rust_language, server_to_query, buffer)) = editor
-        .selections
-        .disjoint_anchors()
-        .into_iter()
-        .filter(|selection| selection.start == selection.end)
-        .filter_map(|selection| Some((selection.start.buffer_id?, selection.start)))
-        .filter_map(|(buffer_id, trigger_anchor)| {
-            let buffer = multibuffer.buffer(buffer_id)?;
-            let rust_language = buffer.read(cx).language_at(trigger_anchor.text_anchor)?;
-            if !is_rust_language(&rust_language) {
-                return None;
-            }
-            Some((trigger_anchor, rust_language, buffer))
-        })
-        .find_map(|(trigger_anchor, rust_language, buffer)| {
-            project
-                .read(cx)
-                .language_servers_for_buffer(buffer.read(cx), cx)
-                .into_iter()
-                .find_map(|(adapter, server)| {
-                    if adapter.name.0.as_ref() == "rust-analyzer" {
-                        Some((
-                            trigger_anchor,
-                            Arc::clone(&rust_language),
-                            server.server_id(),
-                            buffer.clone(),
-                        ))
-                    } else {
-                        None
-                    }
-                })
-        })
-    else {
-        return;
-    };
-
-    let project = project.clone();
-    let buffer_snapshot = buffer.read(cx).snapshot();
-    let position = trigger_anchor.text_anchor.to_point_utf16(&buffer_snapshot);
-    let expand_macro_task = project.update(cx, |project, cx| {
-        project.request_lsp(
-            buffer,
-            project::LanguageServerToQuery::Other(server_to_query),
-            ExpandMacro { position },
-            cx,
-        )
-    });
-    cx.spawn(|_editor, mut cx| async move {
-        let macro_expansion = expand_macro_task.await.context("expand macro")?;
-        if macro_expansion.is_empty() {
-            log::info!("Empty macro expansion for position {position:?}");
-            return Ok(());
-        }
-
-        let buffer = project.update(&mut cx, |project, cx| {
-            project.create_buffer(&macro_expansion.expansion, Some(rust_language), cx)
-        })??;
-        workspace.update(&mut cx, |workspace, cx| {
-            let buffer = cx.new_model(|cx| {
-                MultiBuffer::singleton(buffer, cx).with_title(macro_expansion.name)
-            });
-            workspace.add_item(
-                Box::new(cx.new_view(|cx| Editor::for_multibuffer(buffer, Some(project), cx))),
-                cx,
-            );
-        })
-    })
-    .detach_and_log_err(cx);
-}
-
-fn is_rust_language(language: &Language) -> bool {
-    language.name().as_ref() == "Rust"
-}

crates/editor2/src/scroll.rs ๐Ÿ”—

@@ -1,460 +0,0 @@
-pub mod actions;
-pub mod autoscroll;
-pub mod scroll_amount;
-
-use crate::{
-    display_map::{DisplaySnapshot, ToDisplayPoint},
-    hover_popover::hide_hover,
-    persistence::DB,
-    Anchor, DisplayPoint, Editor, EditorEvent, EditorMode, InlayHintRefreshReason,
-    MultiBufferSnapshot, ToPoint,
-};
-use gpui::{point, px, AppContext, Entity, Pixels, Task, ViewContext};
-use language::{Bias, Point};
-use std::{
-    cmp::Ordering,
-    time::{Duration, Instant},
-};
-use util::ResultExt;
-use workspace::{ItemId, WorkspaceId};
-
-use self::{
-    autoscroll::{Autoscroll, AutoscrollStrategy},
-    scroll_amount::ScrollAmount,
-};
-
-pub const SCROLL_EVENT_SEPARATION: Duration = Duration::from_millis(28);
-pub const VERTICAL_SCROLL_MARGIN: f32 = 3.;
-const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1);
-
-#[derive(Default)]
-pub struct ScrollbarAutoHide(pub bool);
-
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub struct ScrollAnchor {
-    pub offset: gpui::Point<f32>,
-    pub anchor: Anchor,
-}
-
-impl ScrollAnchor {
-    fn new() -> Self {
-        Self {
-            offset: gpui::Point::default(),
-            anchor: Anchor::min(),
-        }
-    }
-
-    pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> gpui::Point<f32> {
-        let mut scroll_position = self.offset;
-        if self.anchor != Anchor::min() {
-            let scroll_top = self.anchor.to_display_point(snapshot).row() as f32;
-            scroll_position.y = scroll_top + scroll_position.y;
-        } else {
-            scroll_position.y = 0.;
-        }
-        scroll_position
-    }
-
-    pub fn top_row(&self, buffer: &MultiBufferSnapshot) -> u32 {
-        self.anchor.to_point(buffer).row
-    }
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub enum Axis {
-    Vertical,
-    Horizontal,
-}
-
-#[derive(Clone, Copy, Debug)]
-pub struct OngoingScroll {
-    last_event: Instant,
-    axis: Option<Axis>,
-}
-
-impl OngoingScroll {
-    fn new() -> Self {
-        Self {
-            last_event: Instant::now() - SCROLL_EVENT_SEPARATION,
-            axis: None,
-        }
-    }
-
-    pub fn filter(&self, delta: &mut gpui::Point<Pixels>) -> Option<Axis> {
-        const UNLOCK_PERCENT: f32 = 1.9;
-        const UNLOCK_LOWER_BOUND: Pixels = px(6.);
-        let mut axis = self.axis;
-
-        let x = delta.x.abs();
-        let y = delta.y.abs();
-        let duration = Instant::now().duration_since(self.last_event);
-        if duration > SCROLL_EVENT_SEPARATION {
-            //New ongoing scroll will start, determine axis
-            axis = if x <= y {
-                Some(Axis::Vertical)
-            } else {
-                Some(Axis::Horizontal)
-            };
-        } else if x.max(y) >= UNLOCK_LOWER_BOUND {
-            //Check if the current ongoing will need to unlock
-            match axis {
-                Some(Axis::Vertical) => {
-                    if x > y && x >= y * UNLOCK_PERCENT {
-                        axis = None;
-                    }
-                }
-
-                Some(Axis::Horizontal) => {
-                    if y > x && y >= x * UNLOCK_PERCENT {
-                        axis = None;
-                    }
-                }
-
-                None => {}
-            }
-        }
-
-        match axis {
-            Some(Axis::Vertical) => {
-                *delta = point(px(0.), delta.y);
-            }
-            Some(Axis::Horizontal) => {
-                *delta = point(delta.x, px(0.));
-            }
-            None => {}
-        }
-
-        axis
-    }
-}
-
-pub struct ScrollManager {
-    vertical_scroll_margin: f32,
-    anchor: ScrollAnchor,
-    ongoing: OngoingScroll,
-    autoscroll_request: Option<(Autoscroll, bool)>,
-    last_autoscroll: Option<(gpui::Point<f32>, f32, f32, AutoscrollStrategy)>,
-    show_scrollbars: bool,
-    hide_scrollbar_task: Option<Task<()>>,
-    dragging_scrollbar: bool,
-    visible_line_count: Option<f32>,
-}
-
-impl ScrollManager {
-    pub fn new() -> Self {
-        ScrollManager {
-            vertical_scroll_margin: VERTICAL_SCROLL_MARGIN,
-            anchor: ScrollAnchor::new(),
-            ongoing: OngoingScroll::new(),
-            autoscroll_request: None,
-            show_scrollbars: true,
-            hide_scrollbar_task: None,
-            dragging_scrollbar: false,
-            last_autoscroll: None,
-            visible_line_count: None,
-        }
-    }
-
-    pub fn clone_state(&mut self, other: &Self) {
-        self.anchor = other.anchor;
-        self.ongoing = other.ongoing;
-    }
-
-    pub fn anchor(&self) -> ScrollAnchor {
-        self.anchor
-    }
-
-    pub fn ongoing_scroll(&self) -> OngoingScroll {
-        self.ongoing
-    }
-
-    pub fn update_ongoing_scroll(&mut self, axis: Option<Axis>) {
-        self.ongoing.last_event = Instant::now();
-        self.ongoing.axis = axis;
-    }
-
-    pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> gpui::Point<f32> {
-        self.anchor.scroll_position(snapshot)
-    }
-
-    fn set_scroll_position(
-        &mut self,
-        scroll_position: gpui::Point<f32>,
-        map: &DisplaySnapshot,
-        local: bool,
-        autoscroll: bool,
-        workspace_id: Option<i64>,
-        cx: &mut ViewContext<Editor>,
-    ) {
-        let (new_anchor, top_row) = if scroll_position.y <= 0. {
-            (
-                ScrollAnchor {
-                    anchor: Anchor::min(),
-                    offset: scroll_position.max(&gpui::Point::default()),
-                },
-                0,
-            )
-        } else {
-            let scroll_top_buffer_point =
-                DisplayPoint::new(scroll_position.y as u32, 0).to_point(&map);
-            let top_anchor = map
-                .buffer_snapshot
-                .anchor_at(scroll_top_buffer_point, Bias::Right);
-
-            (
-                ScrollAnchor {
-                    anchor: top_anchor,
-                    offset: point(
-                        scroll_position.x,
-                        scroll_position.y - top_anchor.to_display_point(&map).row() as f32,
-                    ),
-                },
-                scroll_top_buffer_point.row,
-            )
-        };
-
-        self.set_anchor(new_anchor, top_row, local, autoscroll, workspace_id, cx);
-    }
-
-    fn set_anchor(
-        &mut self,
-        anchor: ScrollAnchor,
-        top_row: u32,
-        local: bool,
-        autoscroll: bool,
-        workspace_id: Option<i64>,
-        cx: &mut ViewContext<Editor>,
-    ) {
-        self.anchor = anchor;
-        cx.emit(EditorEvent::ScrollPositionChanged { local, autoscroll });
-        self.show_scrollbar(cx);
-        self.autoscroll_request.take();
-        if let Some(workspace_id) = workspace_id {
-            let item_id = cx.view().entity_id().as_u64() as ItemId;
-
-            cx.foreground_executor()
-                .spawn(async move {
-                    DB.save_scroll_position(
-                        item_id,
-                        workspace_id,
-                        top_row,
-                        anchor.offset.x,
-                        anchor.offset.y,
-                    )
-                    .await
-                    .log_err()
-                })
-                .detach()
-        }
-        cx.notify();
-    }
-
-    pub fn show_scrollbar(&mut self, cx: &mut ViewContext<Editor>) {
-        if !self.show_scrollbars {
-            self.show_scrollbars = true;
-            cx.notify();
-        }
-
-        if cx.default_global::<ScrollbarAutoHide>().0 {
-            self.hide_scrollbar_task = Some(cx.spawn(|editor, mut cx| async move {
-                cx.background_executor()
-                    .timer(SCROLLBAR_SHOW_INTERVAL)
-                    .await;
-                editor
-                    .update(&mut cx, |editor, cx| {
-                        editor.scroll_manager.show_scrollbars = false;
-                        cx.notify();
-                    })
-                    .log_err();
-            }));
-        } else {
-            self.hide_scrollbar_task = None;
-        }
-    }
-
-    pub fn scrollbars_visible(&self) -> bool {
-        self.show_scrollbars
-    }
-
-    pub fn has_autoscroll_request(&self) -> bool {
-        self.autoscroll_request.is_some()
-    }
-
-    pub fn is_dragging_scrollbar(&self) -> bool {
-        self.dragging_scrollbar
-    }
-
-    pub fn set_is_dragging_scrollbar(&mut self, dragging: bool, cx: &mut ViewContext<Editor>) {
-        if dragging != self.dragging_scrollbar {
-            self.dragging_scrollbar = dragging;
-            cx.notify();
-        }
-    }
-
-    pub fn clamp_scroll_left(&mut self, max: f32) -> bool {
-        if max < self.anchor.offset.x {
-            self.anchor.offset.x = max;
-            true
-        } else {
-            false
-        }
-    }
-}
-
-impl Editor {
-    pub fn vertical_scroll_margin(&mut self) -> usize {
-        self.scroll_manager.vertical_scroll_margin as usize
-    }
-
-    pub fn set_vertical_scroll_margin(&mut self, margin_rows: usize, cx: &mut ViewContext<Self>) {
-        self.scroll_manager.vertical_scroll_margin = margin_rows as f32;
-        cx.notify();
-    }
-
-    pub fn visible_line_count(&self) -> Option<f32> {
-        self.scroll_manager.visible_line_count
-    }
-
-    pub(crate) fn set_visible_line_count(&mut self, lines: f32, cx: &mut ViewContext<Self>) {
-        let opened_first_time = self.scroll_manager.visible_line_count.is_none();
-        self.scroll_manager.visible_line_count = Some(lines);
-        if opened_first_time {
-            cx.spawn(|editor, mut cx| async move {
-                editor
-                    .update(&mut cx, |editor, cx| {
-                        editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx)
-                    })
-                    .ok()
-            })
-            .detach()
-        }
-    }
-
-    pub fn set_scroll_position(
-        &mut self,
-        scroll_position: gpui::Point<f32>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.set_scroll_position_internal(scroll_position, true, false, cx);
-    }
-
-    pub(crate) fn set_scroll_position_internal(
-        &mut self,
-        scroll_position: gpui::Point<f32>,
-        local: bool,
-        autoscroll: bool,
-        cx: &mut ViewContext<Self>,
-    ) {
-        let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-
-        hide_hover(self, cx);
-        let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
-        self.scroll_manager.set_scroll_position(
-            scroll_position,
-            &map,
-            local,
-            autoscroll,
-            workspace_id,
-            cx,
-        );
-
-        self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
-    }
-
-    pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> gpui::Point<f32> {
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        self.scroll_manager.anchor.scroll_position(&display_map)
-    }
-
-    pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext<Self>) {
-        hide_hover(self, cx);
-        let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
-        let top_row = scroll_anchor
-            .anchor
-            .to_point(&self.buffer().read(cx).snapshot(cx))
-            .row;
-        self.scroll_manager
-            .set_anchor(scroll_anchor, top_row, true, false, workspace_id, cx);
-    }
-
-    pub(crate) fn set_scroll_anchor_remote(
-        &mut self,
-        scroll_anchor: ScrollAnchor,
-        cx: &mut ViewContext<Self>,
-    ) {
-        hide_hover(self, cx);
-        let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
-        let top_row = scroll_anchor
-            .anchor
-            .to_point(&self.buffer().read(cx).snapshot(cx))
-            .row;
-        self.scroll_manager
-            .set_anchor(scroll_anchor, top_row, false, false, workspace_id, cx);
-    }
-
-    pub fn scroll_screen(&mut self, amount: &ScrollAmount, cx: &mut ViewContext<Self>) {
-        if matches!(self.mode, EditorMode::SingleLine) {
-            cx.propagate();
-            return;
-        }
-
-        if self.take_rename(true, cx).is_some() {
-            return;
-        }
-
-        let cur_position = self.scroll_position(cx);
-        let new_pos = cur_position + point(0., amount.lines(self));
-        self.set_scroll_position(new_pos, cx);
-    }
-
-    /// Returns an ordering. The newest selection is:
-    ///     Ordering::Equal => on screen
-    ///     Ordering::Less => above the screen
-    ///     Ordering::Greater => below the screen
-    pub fn newest_selection_on_screen(&self, cx: &mut AppContext) -> Ordering {
-        let snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let newest_head = self
-            .selections
-            .newest_anchor()
-            .head()
-            .to_display_point(&snapshot);
-        let screen_top = self
-            .scroll_manager
-            .anchor
-            .anchor
-            .to_display_point(&snapshot);
-
-        if screen_top > newest_head {
-            return Ordering::Less;
-        }
-
-        if let Some(visible_lines) = self.visible_line_count() {
-            if newest_head.row() < screen_top.row() + visible_lines as u32 {
-                return Ordering::Equal;
-            }
-        }
-
-        Ordering::Greater
-    }
-
-    pub fn read_scroll_position_from_db(
-        &mut self,
-        item_id: u64,
-        workspace_id: WorkspaceId,
-        cx: &mut ViewContext<Editor>,
-    ) {
-        let scroll_position = DB.get_scroll_position(item_id, workspace_id);
-        if let Ok(Some((top_row, x, y))) = scroll_position {
-            let top_anchor = self
-                .buffer()
-                .read(cx)
-                .snapshot(cx)
-                .anchor_at(Point::new(top_row as u32, 0), Bias::Left);
-            let scroll_anchor = ScrollAnchor {
-                offset: gpui::Point::new(x, y),
-                anchor: top_anchor,
-            };
-            self.set_scroll_anchor(scroll_anchor, cx);
-        }
-    }
-}

crates/editor2/src/scroll/actions.rs ๐Ÿ”—

@@ -1,103 +0,0 @@
-use super::Axis;
-use crate::{
-    Autoscroll, Bias, Editor, EditorMode, NextScreen, ScrollAnchor, ScrollCursorBottom,
-    ScrollCursorCenter, ScrollCursorTop,
-};
-use gpui::{Point, ViewContext};
-
-impl Editor {
-    pub fn next_screen(&mut self, _: &NextScreen, cx: &mut ViewContext<Editor>) {
-        if self.take_rename(true, cx).is_some() {
-            return;
-        }
-
-        // todo!()
-        // if self.mouse_context_menu.read(cx).visible() {
-        //     return None;
-        // }
-
-        if matches!(self.mode, EditorMode::SingleLine) {
-            cx.propagate();
-            return;
-        }
-        self.request_autoscroll(Autoscroll::Next, cx);
-    }
-
-    pub fn scroll(
-        &mut self,
-        scroll_position: Point<f32>,
-        axis: Option<Axis>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.scroll_manager.update_ongoing_scroll(axis);
-        self.set_scroll_position(scroll_position, cx);
-    }
-
-    pub fn scroll_cursor_top(&mut self, _: &ScrollCursorTop, cx: &mut ViewContext<Editor>) {
-        let snapshot = self.snapshot(cx).display_snapshot;
-        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
-
-        let mut new_screen_top = self.selections.newest_display(cx).head();
-        *new_screen_top.row_mut() = new_screen_top.row().saturating_sub(scroll_margin_rows);
-        *new_screen_top.column_mut() = 0;
-        let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
-        let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
-
-        self.set_scroll_anchor(
-            ScrollAnchor {
-                anchor: new_anchor,
-                offset: Default::default(),
-            },
-            cx,
-        )
-    }
-
-    pub fn scroll_cursor_center(&mut self, _: &ScrollCursorCenter, cx: &mut ViewContext<Editor>) {
-        let snapshot = self.snapshot(cx).display_snapshot;
-        let visible_rows = if let Some(visible_rows) = self.visible_line_count() {
-            visible_rows as u32
-        } else {
-            return;
-        };
-
-        let mut new_screen_top = self.selections.newest_display(cx).head();
-        *new_screen_top.row_mut() = new_screen_top.row().saturating_sub(visible_rows / 2);
-        *new_screen_top.column_mut() = 0;
-        let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
-        let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
-
-        self.set_scroll_anchor(
-            ScrollAnchor {
-                anchor: new_anchor,
-                offset: Default::default(),
-            },
-            cx,
-        )
-    }
-
-    pub fn scroll_cursor_bottom(&mut self, _: &ScrollCursorBottom, cx: &mut ViewContext<Editor>) {
-        let snapshot = self.snapshot(cx).display_snapshot;
-        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
-        let visible_rows = if let Some(visible_rows) = self.visible_line_count() {
-            visible_rows as u32
-        } else {
-            return;
-        };
-
-        let mut new_screen_top = self.selections.newest_display(cx).head();
-        *new_screen_top.row_mut() = new_screen_top
-            .row()
-            .saturating_sub(visible_rows.saturating_sub(scroll_margin_rows));
-        *new_screen_top.column_mut() = 0;
-        let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
-        let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
-
-        self.set_scroll_anchor(
-            ScrollAnchor {
-                anchor: new_anchor,
-                offset: Default::default(),
-            },
-            cx,
-        )
-    }
-}

crates/editor2/src/scroll/autoscroll.rs ๐Ÿ”—

@@ -1,253 +0,0 @@
-use std::{cmp, f32};
-
-use gpui::{px, Pixels, ViewContext};
-use language::Point;
-
-use crate::{display_map::ToDisplayPoint, Editor, EditorMode, LineWithInvisibles};
-
-#[derive(PartialEq, Eq)]
-pub enum Autoscroll {
-    Next,
-    Strategy(AutoscrollStrategy),
-}
-
-impl Autoscroll {
-    pub fn fit() -> Self {
-        Self::Strategy(AutoscrollStrategy::Fit)
-    }
-
-    pub fn newest() -> Self {
-        Self::Strategy(AutoscrollStrategy::Newest)
-    }
-
-    pub fn center() -> Self {
-        Self::Strategy(AutoscrollStrategy::Center)
-    }
-}
-
-#[derive(PartialEq, Eq, Default)]
-pub enum AutoscrollStrategy {
-    Fit,
-    Newest,
-    #[default]
-    Center,
-    Top,
-    Bottom,
-}
-
-impl AutoscrollStrategy {
-    fn next(&self) -> Self {
-        match self {
-            AutoscrollStrategy::Center => AutoscrollStrategy::Top,
-            AutoscrollStrategy::Top => AutoscrollStrategy::Bottom,
-            _ => AutoscrollStrategy::Center,
-        }
-    }
-}
-
-impl Editor {
-    pub fn autoscroll_vertically(
-        &mut self,
-        viewport_height: Pixels,
-        line_height: Pixels,
-        cx: &mut ViewContext<Editor>,
-    ) -> bool {
-        let visible_lines = f32::from(viewport_height / line_height);
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut scroll_position = self.scroll_manager.scroll_position(&display_map);
-        let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
-            (display_map.max_point().row() as f32 - visible_lines + 1.).max(0.)
-        } else {
-            display_map.max_point().row() as f32
-        };
-        if scroll_position.y > max_scroll_top {
-            scroll_position.y = max_scroll_top;
-            self.set_scroll_position(scroll_position, cx);
-        }
-
-        let Some((autoscroll, local)) = self.scroll_manager.autoscroll_request.take() else {
-            return false;
-        };
-
-        let mut target_top;
-        let mut target_bottom;
-        if let Some(highlighted_rows) = &self.highlighted_rows {
-            target_top = highlighted_rows.start as f32;
-            target_bottom = target_top + 1.;
-        } else {
-            let selections = self.selections.all::<Point>(cx);
-            target_top = selections
-                .first()
-                .unwrap()
-                .head()
-                .to_display_point(&display_map)
-                .row() as f32;
-            target_bottom = selections
-                .last()
-                .unwrap()
-                .head()
-                .to_display_point(&display_map)
-                .row() as f32
-                + 1.0;
-
-            // If the selections can't all fit on screen, scroll to the newest.
-            if autoscroll == Autoscroll::newest()
-                || autoscroll == Autoscroll::fit() && target_bottom - target_top > visible_lines
-            {
-                let newest_selection_top = selections
-                    .iter()
-                    .max_by_key(|s| s.id)
-                    .unwrap()
-                    .head()
-                    .to_display_point(&display_map)
-                    .row() as f32;
-                target_top = newest_selection_top;
-                target_bottom = newest_selection_top + 1.;
-            }
-        }
-
-        let margin = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
-            0.
-        } else {
-            ((visible_lines - (target_bottom - target_top)) / 2.0).floor()
-        };
-
-        let strategy = match autoscroll {
-            Autoscroll::Strategy(strategy) => strategy,
-            Autoscroll::Next => {
-                let last_autoscroll = &self.scroll_manager.last_autoscroll;
-                if let Some(last_autoscroll) = last_autoscroll {
-                    if self.scroll_manager.anchor.offset == last_autoscroll.0
-                        && target_top == last_autoscroll.1
-                        && target_bottom == last_autoscroll.2
-                    {
-                        last_autoscroll.3.next()
-                    } else {
-                        AutoscrollStrategy::default()
-                    }
-                } else {
-                    AutoscrollStrategy::default()
-                }
-            }
-        };
-
-        match strategy {
-            AutoscrollStrategy::Fit | AutoscrollStrategy::Newest => {
-                let margin = margin.min(self.scroll_manager.vertical_scroll_margin);
-                let target_top = (target_top - margin).max(0.0);
-                let target_bottom = target_bottom + margin;
-                let start_row = scroll_position.y;
-                let end_row = start_row + visible_lines;
-
-                let needs_scroll_up = target_top < start_row;
-                let needs_scroll_down = target_bottom >= end_row;
-
-                if needs_scroll_up && !needs_scroll_down {
-                    scroll_position.y = target_top;
-                    self.set_scroll_position_internal(scroll_position, local, true, cx);
-                }
-                if !needs_scroll_up && needs_scroll_down {
-                    scroll_position.y = target_bottom - visible_lines;
-                    self.set_scroll_position_internal(scroll_position, local, true, cx);
-                }
-            }
-            AutoscrollStrategy::Center => {
-                scroll_position.y = (target_top - margin).max(0.0);
-                self.set_scroll_position_internal(scroll_position, local, true, cx);
-            }
-            AutoscrollStrategy::Top => {
-                scroll_position.y = (target_top).max(0.0);
-                self.set_scroll_position_internal(scroll_position, local, true, cx);
-            }
-            AutoscrollStrategy::Bottom => {
-                scroll_position.y = (target_bottom - visible_lines).max(0.0);
-                self.set_scroll_position_internal(scroll_position, local, true, cx);
-            }
-        }
-
-        self.scroll_manager.last_autoscroll = Some((
-            self.scroll_manager.anchor.offset,
-            target_top,
-            target_bottom,
-            strategy,
-        ));
-
-        true
-    }
-
-    pub fn autoscroll_horizontally(
-        &mut self,
-        start_row: u32,
-        viewport_width: Pixels,
-        scroll_width: Pixels,
-        max_glyph_width: Pixels,
-        layouts: &[LineWithInvisibles],
-        cx: &mut ViewContext<Self>,
-    ) -> bool {
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let selections = self.selections.all::<Point>(cx);
-
-        let mut target_left;
-        let mut target_right;
-
-        if self.highlighted_rows.is_some() {
-            target_left = px(0.);
-            target_right = px(0.);
-        } else {
-            target_left = px(f32::INFINITY);
-            target_right = px(0.);
-            for selection in selections {
-                let head = selection.head().to_display_point(&display_map);
-                if head.row() >= start_row && head.row() < start_row + layouts.len() as u32 {
-                    let start_column = head.column().saturating_sub(3);
-                    let end_column = cmp::min(display_map.line_len(head.row()), head.column() + 3);
-                    target_left = target_left.min(
-                        layouts[(head.row() - start_row) as usize]
-                            .line
-                            .x_for_index(start_column as usize),
-                    );
-                    target_right = target_right.max(
-                        layouts[(head.row() - start_row) as usize]
-                            .line
-                            .x_for_index(end_column as usize)
-                            + max_glyph_width,
-                    );
-                }
-            }
-        }
-
-        target_right = target_right.min(scroll_width);
-
-        if target_right - target_left > viewport_width {
-            return false;
-        }
-
-        let scroll_left = self.scroll_manager.anchor.offset.x * max_glyph_width;
-        let scroll_right = scroll_left + viewport_width;
-
-        if target_left < scroll_left {
-            self.scroll_manager.anchor.offset.x = (target_left / max_glyph_width).into();
-            true
-        } else if target_right > scroll_right {
-            self.scroll_manager.anchor.offset.x =
-                ((target_right - viewport_width) / max_glyph_width).into();
-            true
-        } else {
-            false
-        }
-    }
-
-    pub fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
-        self.scroll_manager.autoscroll_request = Some((autoscroll, true));
-        cx.notify();
-    }
-
-    pub(crate) fn request_autoscroll_remotely(
-        &mut self,
-        autoscroll: Autoscroll,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.scroll_manager.autoscroll_request = Some((autoscroll, false));
-        cx.notify();
-    }
-}

crates/editor2/src/scroll/scroll_amount.rs ๐Ÿ”—

@@ -1,28 +0,0 @@
-use crate::Editor;
-use serde::Deserialize;
-
-#[derive(Clone, PartialEq, Deserialize)]
-pub enum ScrollAmount {
-    // Scroll N lines (positive is towards the end of the document)
-    Line(f32),
-    // Scroll N pages (positive is towards the end of the document)
-    Page(f32),
-}
-
-impl ScrollAmount {
-    pub fn lines(&self, editor: &mut Editor) -> f32 {
-        match self {
-            Self::Line(count) => *count,
-            Self::Page(count) => editor
-                .visible_line_count()
-                .map(|mut l| {
-                    // for full pages subtract one to leave an anchor line
-                    if count.abs() == 1.0 {
-                        l -= 1.0
-                    }
-                    (l * count).trunc()
-                })
-                .unwrap_or(0.),
-        }
-    }
-}

crates/editor2/src/selections_collection.rs ๐Ÿ”—

@@ -1,888 +0,0 @@
-use std::{
-    cell::Ref,
-    iter, mem,
-    ops::{Deref, DerefMut, Range, Sub},
-    sync::Arc,
-};
-
-use collections::HashMap;
-use gpui::{AppContext, Model, Pixels};
-use itertools::Itertools;
-use language::{Bias, Point, Selection, SelectionGoal, TextDimension, ToPoint};
-use util::post_inc;
-
-use crate::{
-    display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
-    movement::TextLayoutDetails,
-    Anchor, DisplayPoint, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode, ToOffset,
-};
-
-#[derive(Debug, Clone)]
-pub struct PendingSelection {
-    pub selection: Selection<Anchor>,
-    pub mode: SelectMode,
-}
-
-#[derive(Debug, Clone)]
-pub struct SelectionsCollection {
-    display_map: Model<DisplayMap>,
-    buffer: Model<MultiBuffer>,
-    pub next_selection_id: usize,
-    pub line_mode: bool,
-    disjoint: Arc<[Selection<Anchor>]>,
-    pending: Option<PendingSelection>,
-}
-
-impl SelectionsCollection {
-    pub fn new(display_map: Model<DisplayMap>, buffer: Model<MultiBuffer>) -> Self {
-        Self {
-            display_map,
-            buffer,
-            next_selection_id: 1,
-            line_mode: false,
-            disjoint: Arc::from([]),
-            pending: Some(PendingSelection {
-                selection: Selection {
-                    id: 0,
-                    start: Anchor::min(),
-                    end: Anchor::min(),
-                    reversed: false,
-                    goal: SelectionGoal::None,
-                },
-                mode: SelectMode::Character,
-            }),
-        }
-    }
-
-    pub fn display_map(&self, cx: &mut AppContext) -> DisplaySnapshot {
-        self.display_map.update(cx, |map, cx| map.snapshot(cx))
-    }
-
-    fn buffer<'a>(&self, cx: &'a AppContext) -> Ref<'a, MultiBufferSnapshot> {
-        self.buffer.read(cx).read(cx)
-    }
-
-    pub fn clone_state(&mut self, other: &SelectionsCollection) {
-        self.next_selection_id = other.next_selection_id;
-        self.line_mode = other.line_mode;
-        self.disjoint = other.disjoint.clone();
-        self.pending = other.pending.clone();
-    }
-
-    pub fn count(&self) -> usize {
-        let mut count = self.disjoint.len();
-        if self.pending.is_some() {
-            count += 1;
-        }
-        count
-    }
-
-    /// The non-pending, non-overlapping selections. There could still be a pending
-    /// selection that overlaps these if the mouse is being dragged, etc. Returned as
-    /// selections over Anchors.
-    pub fn disjoint_anchors(&self) -> Arc<[Selection<Anchor>]> {
-        self.disjoint.clone()
-    }
-
-    pub fn pending_anchor(&self) -> Option<Selection<Anchor>> {
-        self.pending
-            .as_ref()
-            .map(|pending| pending.selection.clone())
-    }
-
-    pub fn pending<D: TextDimension + Ord + Sub<D, Output = D>>(
-        &self,
-        cx: &AppContext,
-    ) -> Option<Selection<D>> {
-        self.pending_anchor()
-            .as_ref()
-            .map(|pending| pending.map(|p| p.summary::<D>(&self.buffer(cx))))
-    }
-
-    pub fn pending_mode(&self) -> Option<SelectMode> {
-        self.pending.as_ref().map(|pending| pending.mode.clone())
-    }
-
-    pub fn all<'a, D>(&self, cx: &AppContext) -> Vec<Selection<D>>
-    where
-        D: 'a + TextDimension + Ord + Sub<D, Output = D> + std::fmt::Debug,
-    {
-        let disjoint_anchors = &self.disjoint;
-        let mut disjoint =
-            resolve_multiple::<D, _>(disjoint_anchors.iter(), &self.buffer(cx)).peekable();
-
-        let mut pending_opt = self.pending::<D>(cx);
-
-        iter::from_fn(move || {
-            if let Some(pending) = pending_opt.as_mut() {
-                while let Some(next_selection) = disjoint.peek() {
-                    if pending.start <= next_selection.end && pending.end >= next_selection.start {
-                        let next_selection = disjoint.next().unwrap();
-                        if next_selection.start < pending.start {
-                            pending.start = next_selection.start;
-                        }
-                        if next_selection.end > pending.end {
-                            pending.end = next_selection.end;
-                        }
-                    } else if next_selection.end < pending.start {
-                        return disjoint.next();
-                    } else {
-                        break;
-                    }
-                }
-
-                pending_opt.take()
-            } else {
-                disjoint.next()
-            }
-        })
-        .collect()
-    }
-
-    /// Returns all of the selections, adjusted to take into account the selection line_mode
-    pub fn all_adjusted(&self, cx: &mut AppContext) -> Vec<Selection<Point>> {
-        let mut selections = self.all::<Point>(cx);
-        if self.line_mode {
-            let map = self.display_map(cx);
-            for selection in &mut selections {
-                let new_range = map.expand_to_line(selection.range());
-                selection.start = new_range.start;
-                selection.end = new_range.end;
-            }
-        }
-        selections
-    }
-
-    pub fn all_adjusted_display(
-        &self,
-        cx: &mut AppContext,
-    ) -> (DisplaySnapshot, Vec<Selection<DisplayPoint>>) {
-        if self.line_mode {
-            let selections = self.all::<Point>(cx);
-            let map = self.display_map(cx);
-            let result = selections
-                .into_iter()
-                .map(|mut selection| {
-                    let new_range = map.expand_to_line(selection.range());
-                    selection.start = new_range.start;
-                    selection.end = new_range.end;
-                    selection.map(|point| point.to_display_point(&map))
-                })
-                .collect();
-            (map, result)
-        } else {
-            self.all_display(cx)
-        }
-    }
-
-    pub fn disjoint_in_range<'a, D>(
-        &self,
-        range: Range<Anchor>,
-        cx: &AppContext,
-    ) -> Vec<Selection<D>>
-    where
-        D: 'a + TextDimension + Ord + Sub<D, Output = D> + std::fmt::Debug,
-    {
-        let buffer = self.buffer(cx);
-        let start_ix = match self
-            .disjoint
-            .binary_search_by(|probe| probe.end.cmp(&range.start, &buffer))
-        {
-            Ok(ix) | Err(ix) => ix,
-        };
-        let end_ix = match self
-            .disjoint
-            .binary_search_by(|probe| probe.start.cmp(&range.end, &buffer))
-        {
-            Ok(ix) => ix + 1,
-            Err(ix) => ix,
-        };
-        resolve_multiple(&self.disjoint[start_ix..end_ix], &buffer).collect()
-    }
-
-    pub fn all_display(
-        &self,
-        cx: &mut AppContext,
-    ) -> (DisplaySnapshot, Vec<Selection<DisplayPoint>>) {
-        let display_map = self.display_map(cx);
-        let selections = self
-            .all::<Point>(cx)
-            .into_iter()
-            .map(|selection| selection.map(|point| point.to_display_point(&display_map)))
-            .collect();
-        (display_map, selections)
-    }
-
-    pub fn newest_anchor(&self) -> &Selection<Anchor> {
-        self.pending
-            .as_ref()
-            .map(|s| &s.selection)
-            .or_else(|| self.disjoint.iter().max_by_key(|s| s.id))
-            .unwrap()
-    }
-
-    pub fn newest<D: TextDimension + Ord + Sub<D, Output = D>>(
-        &self,
-        cx: &AppContext,
-    ) -> Selection<D> {
-        resolve(self.newest_anchor(), &self.buffer(cx))
-    }
-
-    pub fn newest_display(&self, cx: &mut AppContext) -> Selection<DisplayPoint> {
-        let display_map = self.display_map(cx);
-        let selection = self
-            .newest_anchor()
-            .map(|point| point.to_display_point(&display_map));
-        selection
-    }
-
-    pub fn oldest_anchor(&self) -> &Selection<Anchor> {
-        self.disjoint
-            .iter()
-            .min_by_key(|s| s.id)
-            .or_else(|| self.pending.as_ref().map(|p| &p.selection))
-            .unwrap()
-    }
-
-    pub fn oldest<D: TextDimension + Ord + Sub<D, Output = D>>(
-        &self,
-        cx: &AppContext,
-    ) -> Selection<D> {
-        resolve(self.oldest_anchor(), &self.buffer(cx))
-    }
-
-    pub fn first_anchor(&self) -> Selection<Anchor> {
-        self.disjoint[0].clone()
-    }
-
-    pub fn first<D: TextDimension + Ord + Sub<D, Output = D>>(
-        &self,
-        cx: &AppContext,
-    ) -> Selection<D> {
-        self.all(cx).first().unwrap().clone()
-    }
-
-    pub fn last<D: TextDimension + Ord + Sub<D, Output = D>>(
-        &self,
-        cx: &AppContext,
-    ) -> Selection<D> {
-        self.all(cx).last().unwrap().clone()
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn ranges<D: TextDimension + Ord + Sub<D, Output = D> + std::fmt::Debug>(
-        &self,
-        cx: &AppContext,
-    ) -> Vec<Range<D>> {
-        self.all::<D>(cx)
-            .iter()
-            .map(|s| {
-                if s.reversed {
-                    s.end.clone()..s.start.clone()
-                } else {
-                    s.start.clone()..s.end.clone()
-                }
-            })
-            .collect()
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn display_ranges(&self, cx: &mut AppContext) -> Vec<Range<DisplayPoint>> {
-        let display_map = self.display_map(cx);
-        self.disjoint_anchors()
-            .iter()
-            .chain(self.pending_anchor().as_ref())
-            .map(|s| {
-                if s.reversed {
-                    s.end.to_display_point(&display_map)..s.start.to_display_point(&display_map)
-                } else {
-                    s.start.to_display_point(&display_map)..s.end.to_display_point(&display_map)
-                }
-            })
-            .collect()
-    }
-
-    pub fn build_columnar_selection(
-        &mut self,
-        display_map: &DisplaySnapshot,
-        row: u32,
-        positions: &Range<Pixels>,
-        reversed: bool,
-        text_layout_details: &TextLayoutDetails,
-    ) -> Option<Selection<Point>> {
-        let is_empty = positions.start == positions.end;
-        let line_len = display_map.line_len(row);
-
-        let line = display_map.layout_row(row, &text_layout_details);
-
-        let start_col = line.closest_index_for_x(positions.start) as u32;
-        if start_col < line_len || (is_empty && positions.start == line.width) {
-            let start = DisplayPoint::new(row, start_col);
-            let end_col = line.closest_index_for_x(positions.end) as u32;
-            let end = DisplayPoint::new(row, end_col);
-
-            Some(Selection {
-                id: post_inc(&mut self.next_selection_id),
-                start: start.to_point(display_map),
-                end: end.to_point(display_map),
-                reversed,
-                goal: SelectionGoal::HorizontalRange {
-                    start: positions.start.into(),
-                    end: positions.end.into(),
-                },
-            })
-        } else {
-            None
-        }
-    }
-
-    pub(crate) fn change_with<R>(
-        &mut self,
-        cx: &mut AppContext,
-        change: impl FnOnce(&mut MutableSelectionsCollection) -> R,
-    ) -> (bool, R) {
-        let mut mutable_collection = MutableSelectionsCollection {
-            collection: self,
-            selections_changed: false,
-            cx,
-        };
-
-        let result = change(&mut mutable_collection);
-        assert!(
-            !mutable_collection.disjoint.is_empty() || mutable_collection.pending.is_some(),
-            "There must be at least one selection"
-        );
-        (mutable_collection.selections_changed, result)
-    }
-}
-
-pub struct MutableSelectionsCollection<'a> {
-    collection: &'a mut SelectionsCollection,
-    selections_changed: bool,
-    cx: &'a mut AppContext,
-}
-
-impl<'a> MutableSelectionsCollection<'a> {
-    pub fn display_map(&mut self) -> DisplaySnapshot {
-        self.collection.display_map(self.cx)
-    }
-
-    fn buffer(&self) -> Ref<MultiBufferSnapshot> {
-        self.collection.buffer(self.cx)
-    }
-
-    pub fn clear_disjoint(&mut self) {
-        self.collection.disjoint = Arc::from([]);
-    }
-
-    pub fn delete(&mut self, selection_id: usize) {
-        let mut changed = false;
-        self.collection.disjoint = self
-            .disjoint
-            .iter()
-            .filter(|selection| {
-                let found = selection.id == selection_id;
-                changed |= found;
-                !found
-            })
-            .cloned()
-            .collect();
-
-        self.selections_changed |= changed;
-    }
-
-    pub fn clear_pending(&mut self) {
-        if self.collection.pending.is_some() {
-            self.collection.pending = None;
-            self.selections_changed = true;
-        }
-    }
-
-    pub fn set_pending_anchor_range(&mut self, range: Range<Anchor>, mode: SelectMode) {
-        self.collection.pending = Some(PendingSelection {
-            selection: Selection {
-                id: post_inc(&mut self.collection.next_selection_id),
-                start: range.start,
-                end: range.end,
-                reversed: false,
-                goal: SelectionGoal::None,
-            },
-            mode,
-        });
-        self.selections_changed = true;
-    }
-
-    pub fn set_pending_display_range(&mut self, range: Range<DisplayPoint>, mode: SelectMode) {
-        let (start, end, reversed) = {
-            let display_map = self.display_map();
-            let buffer = self.buffer();
-            let mut start = range.start;
-            let mut end = range.end;
-            let reversed = if start > end {
-                mem::swap(&mut start, &mut end);
-                true
-            } else {
-                false
-            };
-
-            let end_bias = if end > start { Bias::Left } else { Bias::Right };
-            (
-                buffer.anchor_before(start.to_point(&display_map)),
-                buffer.anchor_at(end.to_point(&display_map), end_bias),
-                reversed,
-            )
-        };
-
-        let new_pending = PendingSelection {
-            selection: Selection {
-                id: post_inc(&mut self.collection.next_selection_id),
-                start,
-                end,
-                reversed,
-                goal: SelectionGoal::None,
-            },
-            mode,
-        };
-
-        self.collection.pending = Some(new_pending);
-        self.selections_changed = true;
-    }
-
-    pub fn set_pending(&mut self, selection: Selection<Anchor>, mode: SelectMode) {
-        self.collection.pending = Some(PendingSelection { selection, mode });
-        self.selections_changed = true;
-    }
-
-    pub fn try_cancel(&mut self) -> bool {
-        if let Some(pending) = self.collection.pending.take() {
-            if self.disjoint.is_empty() {
-                self.collection.disjoint = Arc::from([pending.selection]);
-            }
-            self.selections_changed = true;
-            return true;
-        }
-
-        let mut oldest = self.oldest_anchor().clone();
-        if self.count() > 1 {
-            self.collection.disjoint = Arc::from([oldest]);
-            self.selections_changed = true;
-            return true;
-        }
-
-        if !oldest.start.cmp(&oldest.end, &self.buffer()).is_eq() {
-            let head = oldest.head();
-            oldest.start = head.clone();
-            oldest.end = head;
-            self.collection.disjoint = Arc::from([oldest]);
-            self.selections_changed = true;
-            return true;
-        }
-
-        false
-    }
-
-    pub fn insert_range<T>(&mut self, range: Range<T>)
-    where
-        T: 'a + ToOffset + ToPoint + TextDimension + Ord + Sub<T, Output = T> + std::marker::Copy,
-    {
-        let mut selections = self.all(self.cx);
-        let mut start = range.start.to_offset(&self.buffer());
-        let mut end = range.end.to_offset(&self.buffer());
-        let reversed = if start > end {
-            mem::swap(&mut start, &mut end);
-            true
-        } else {
-            false
-        };
-        selections.push(Selection {
-            id: post_inc(&mut self.collection.next_selection_id),
-            start,
-            end,
-            reversed,
-            goal: SelectionGoal::None,
-        });
-        self.select(selections);
-    }
-
-    pub fn select<T>(&mut self, mut selections: Vec<Selection<T>>)
-    where
-        T: ToOffset + ToPoint + Ord + std::marker::Copy + std::fmt::Debug,
-    {
-        let buffer = self.buffer.read(self.cx).snapshot(self.cx);
-        selections.sort_unstable_by_key(|s| s.start);
-        // Merge overlapping selections.
-        let mut i = 1;
-        while i < selections.len() {
-            if selections[i - 1].end >= selections[i].start {
-                let removed = selections.remove(i);
-                if removed.start < selections[i - 1].start {
-                    selections[i - 1].start = removed.start;
-                }
-                if removed.end > selections[i - 1].end {
-                    selections[i - 1].end = removed.end;
-                }
-            } else {
-                i += 1;
-            }
-        }
-
-        self.collection.disjoint = Arc::from_iter(selections.into_iter().map(|selection| {
-            let end_bias = if selection.end > selection.start {
-                Bias::Left
-            } else {
-                Bias::Right
-            };
-            Selection {
-                id: selection.id,
-                start: buffer.anchor_after(selection.start),
-                end: buffer.anchor_at(selection.end, end_bias),
-                reversed: selection.reversed,
-                goal: selection.goal,
-            }
-        }));
-
-        self.collection.pending = None;
-        self.selections_changed = true;
-    }
-
-    pub fn select_anchors(&mut self, selections: Vec<Selection<Anchor>>) {
-        let buffer = self.buffer.read(self.cx).snapshot(self.cx);
-        let resolved_selections =
-            resolve_multiple::<usize, _>(&selections, &buffer).collect::<Vec<_>>();
-        self.select(resolved_selections);
-    }
-
-    pub fn select_ranges<I, T>(&mut self, ranges: I)
-    where
-        I: IntoIterator<Item = Range<T>>,
-        T: ToOffset,
-    {
-        let buffer = self.buffer.read(self.cx).snapshot(self.cx);
-        let ranges = ranges
-            .into_iter()
-            .map(|range| range.start.to_offset(&buffer)..range.end.to_offset(&buffer));
-        self.select_offset_ranges(ranges);
-    }
-
-    fn select_offset_ranges<I>(&mut self, ranges: I)
-    where
-        I: IntoIterator<Item = Range<usize>>,
-    {
-        let selections = ranges
-            .into_iter()
-            .map(|range| {
-                let mut start = range.start;
-                let mut end = range.end;
-                let reversed = if start > end {
-                    mem::swap(&mut start, &mut end);
-                    true
-                } else {
-                    false
-                };
-                Selection {
-                    id: post_inc(&mut self.collection.next_selection_id),
-                    start,
-                    end,
-                    reversed,
-                    goal: SelectionGoal::None,
-                }
-            })
-            .collect::<Vec<_>>();
-
-        self.select(selections)
-    }
-
-    pub fn select_anchor_ranges<I>(&mut self, ranges: I)
-    where
-        I: IntoIterator<Item = Range<Anchor>>,
-    {
-        let buffer = self.buffer.read(self.cx).snapshot(self.cx);
-        let selections = ranges
-            .into_iter()
-            .map(|range| {
-                let mut start = range.start;
-                let mut end = range.end;
-                let reversed = if start.cmp(&end, &buffer).is_gt() {
-                    mem::swap(&mut start, &mut end);
-                    true
-                } else {
-                    false
-                };
-                Selection {
-                    id: post_inc(&mut self.collection.next_selection_id),
-                    start,
-                    end,
-                    reversed,
-                    goal: SelectionGoal::None,
-                }
-            })
-            .collect::<Vec<_>>();
-        self.select_anchors(selections)
-    }
-
-    pub fn new_selection_id(&mut self) -> usize {
-        post_inc(&mut self.next_selection_id)
-    }
-
-    pub fn select_display_ranges<T>(&mut self, ranges: T)
-    where
-        T: IntoIterator<Item = Range<DisplayPoint>>,
-    {
-        let display_map = self.display_map();
-        let selections = ranges
-            .into_iter()
-            .map(|range| {
-                let mut start = range.start;
-                let mut end = range.end;
-                let reversed = if start > end {
-                    mem::swap(&mut start, &mut end);
-                    true
-                } else {
-                    false
-                };
-                Selection {
-                    id: post_inc(&mut self.collection.next_selection_id),
-                    start: start.to_point(&display_map),
-                    end: end.to_point(&display_map),
-                    reversed,
-                    goal: SelectionGoal::None,
-                }
-            })
-            .collect();
-        self.select(selections);
-    }
-
-    pub fn move_with(
-        &mut self,
-        mut move_selection: impl FnMut(&DisplaySnapshot, &mut Selection<DisplayPoint>),
-    ) {
-        let mut changed = false;
-        let display_map = self.display_map();
-        let selections = self
-            .all::<Point>(self.cx)
-            .into_iter()
-            .map(|selection| {
-                let mut moved_selection =
-                    selection.map(|point| point.to_display_point(&display_map));
-                move_selection(&display_map, &mut moved_selection);
-                let moved_selection =
-                    moved_selection.map(|display_point| display_point.to_point(&display_map));
-                if selection != moved_selection {
-                    changed = true;
-                }
-                moved_selection
-            })
-            .collect();
-
-        if changed {
-            self.select(selections)
-        }
-    }
-
-    pub fn move_offsets_with(
-        &mut self,
-        mut move_selection: impl FnMut(&MultiBufferSnapshot, &mut Selection<usize>),
-    ) {
-        let mut changed = false;
-        let snapshot = self.buffer().clone();
-        let selections = self
-            .all::<usize>(self.cx)
-            .into_iter()
-            .map(|selection| {
-                let mut moved_selection = selection.clone();
-                move_selection(&snapshot, &mut moved_selection);
-                if selection != moved_selection {
-                    changed = true;
-                }
-                moved_selection
-            })
-            .collect();
-        drop(snapshot);
-
-        if changed {
-            self.select(selections)
-        }
-    }
-
-    pub fn move_heads_with(
-        &mut self,
-        mut update_head: impl FnMut(
-            &DisplaySnapshot,
-            DisplayPoint,
-            SelectionGoal,
-        ) -> (DisplayPoint, SelectionGoal),
-    ) {
-        self.move_with(|map, selection| {
-            let (new_head, new_goal) = update_head(map, selection.head(), selection.goal);
-            selection.set_head(new_head, new_goal);
-        });
-    }
-
-    pub fn move_cursors_with(
-        &mut self,
-        mut update_cursor_position: impl FnMut(
-            &DisplaySnapshot,
-            DisplayPoint,
-            SelectionGoal,
-        ) -> (DisplayPoint, SelectionGoal),
-    ) {
-        self.move_with(|map, selection| {
-            let (cursor, new_goal) = update_cursor_position(map, selection.head(), selection.goal);
-            selection.collapse_to(cursor, new_goal)
-        });
-    }
-
-    pub fn maybe_move_cursors_with(
-        &mut self,
-        mut update_cursor_position: impl FnMut(
-            &DisplaySnapshot,
-            DisplayPoint,
-            SelectionGoal,
-        ) -> Option<(DisplayPoint, SelectionGoal)>,
-    ) {
-        self.move_cursors_with(|map, point, goal| {
-            update_cursor_position(map, point, goal).unwrap_or((point, goal))
-        })
-    }
-
-    pub fn replace_cursors_with(
-        &mut self,
-        mut find_replacement_cursors: impl FnMut(&DisplaySnapshot) -> Vec<DisplayPoint>,
-    ) {
-        let display_map = self.display_map();
-        let new_selections = find_replacement_cursors(&display_map)
-            .into_iter()
-            .map(|cursor| {
-                let cursor_point = cursor.to_point(&display_map);
-                Selection {
-                    id: post_inc(&mut self.collection.next_selection_id),
-                    start: cursor_point,
-                    end: cursor_point,
-                    reversed: false,
-                    goal: SelectionGoal::None,
-                }
-            })
-            .collect();
-        self.select(new_selections);
-    }
-
-    /// Compute new ranges for any selections that were located in excerpts that have
-    /// since been removed.
-    ///
-    /// Returns a `HashMap` indicating which selections whose former head position
-    /// was no longer present. The keys of the map are selection ids. The values are
-    /// the id of the new excerpt where the head of the selection has been moved.
-    pub fn refresh(&mut self) -> HashMap<usize, ExcerptId> {
-        let mut pending = self.collection.pending.take();
-        let mut selections_with_lost_position = HashMap::default();
-
-        let anchors_with_status = {
-            let buffer = self.buffer();
-            let disjoint_anchors = self
-                .disjoint
-                .iter()
-                .flat_map(|selection| [&selection.start, &selection.end]);
-            buffer.refresh_anchors(disjoint_anchors)
-        };
-        let adjusted_disjoint: Vec<_> = anchors_with_status
-            .chunks(2)
-            .map(|selection_anchors| {
-                let (anchor_ix, start, kept_start) = selection_anchors[0].clone();
-                let (_, end, kept_end) = selection_anchors[1].clone();
-                let selection = &self.disjoint[anchor_ix / 2];
-                let kept_head = if selection.reversed {
-                    kept_start
-                } else {
-                    kept_end
-                };
-                if !kept_head {
-                    selections_with_lost_position.insert(selection.id, selection.head().excerpt_id);
-                }
-
-                Selection {
-                    id: selection.id,
-                    start,
-                    end,
-                    reversed: selection.reversed,
-                    goal: selection.goal,
-                }
-            })
-            .collect();
-
-        if !adjusted_disjoint.is_empty() {
-            let resolved_selections =
-                resolve_multiple(adjusted_disjoint.iter(), &self.buffer()).collect();
-            self.select::<usize>(resolved_selections);
-        }
-
-        if let Some(pending) = pending.as_mut() {
-            let buffer = self.buffer();
-            let anchors =
-                buffer.refresh_anchors([&pending.selection.start, &pending.selection.end]);
-            let (_, start, kept_start) = anchors[0].clone();
-            let (_, end, kept_end) = anchors[1].clone();
-            let kept_head = if pending.selection.reversed {
-                kept_start
-            } else {
-                kept_end
-            };
-            if !kept_head {
-                selections_with_lost_position
-                    .insert(pending.selection.id, pending.selection.head().excerpt_id);
-            }
-
-            pending.selection.start = start;
-            pending.selection.end = end;
-        }
-        self.collection.pending = pending;
-        self.selections_changed = true;
-
-        selections_with_lost_position
-    }
-}
-
-impl<'a> Deref for MutableSelectionsCollection<'a> {
-    type Target = SelectionsCollection;
-    fn deref(&self) -> &Self::Target {
-        self.collection
-    }
-}
-
-impl<'a> DerefMut for MutableSelectionsCollection<'a> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        self.collection
-    }
-}
-
-// Panics if passed selections are not in order
-pub fn resolve_multiple<'a, D, I>(
-    selections: I,
-    snapshot: &MultiBufferSnapshot,
-) -> impl 'a + Iterator<Item = Selection<D>>
-where
-    D: TextDimension + Ord + Sub<D, Output = D> + std::fmt::Debug,
-    I: 'a + IntoIterator<Item = &'a Selection<Anchor>>,
-{
-    let (to_summarize, selections) = selections.into_iter().tee();
-    let mut summaries = snapshot
-        .summaries_for_anchors::<D, _>(
-            to_summarize
-                .flat_map(|s| [&s.start, &s.end])
-                .collect::<Vec<_>>(),
-        )
-        .into_iter();
-    selections.map(move |s| Selection {
-        id: s.id,
-        start: summaries.next().unwrap(),
-        end: summaries.next().unwrap(),
-        reversed: s.reversed,
-        goal: s.goal,
-    })
-}
-
-fn resolve<D: TextDimension + Ord + Sub<D, Output = D>>(
-    selection: &Selection<Anchor>,
-    buffer: &MultiBufferSnapshot,
-) -> Selection<D> {
-    selection.map(|p| p.summary::<D>(buffer))
-}

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

@@ -1,74 +0,0 @@
-pub mod editor_lsp_test_context;
-pub mod editor_test_context;
-
-use crate::{
-    display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
-    DisplayPoint, Editor, EditorMode, MultiBuffer,
-};
-
-use gpui::{Context, Model, Pixels, ViewContext};
-
-use project::Project;
-use util::test::{marked_text_offsets, marked_text_ranges};
-
-#[cfg(test)]
-#[ctor::ctor]
-fn init_logger() {
-    if std::env::var("RUST_LOG").is_ok() {
-        env_logger::init();
-    }
-}
-
-// Returns a snapshot from text containing '|' character markers with the markers removed, and DisplayPoints for each one.
-pub fn marked_display_snapshot(
-    text: &str,
-    cx: &mut gpui::AppContext,
-) -> (DisplaySnapshot, Vec<DisplayPoint>) {
-    let (unmarked_text, markers) = marked_text_offsets(text);
-
-    let font = cx.text_style().font();
-    let font_size: Pixels = 14usize.into();
-
-    let buffer = MultiBuffer::build_simple(&unmarked_text, cx);
-    let display_map = cx.new_model(|cx| DisplayMap::new(buffer, font, font_size, None, 1, 1, cx));
-    let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
-    let markers = markers
-        .into_iter()
-        .map(|offset| offset.to_display_point(&snapshot))
-        .collect();
-
-    (snapshot, markers)
-}
-
-pub fn select_ranges(editor: &mut Editor, marked_text: &str, cx: &mut ViewContext<Editor>) {
-    let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true);
-    assert_eq!(editor.text(cx), unmarked_text);
-    editor.change_selections(None, cx, |s| s.select_ranges(text_ranges));
-}
-
-pub fn assert_text_with_selections(
-    editor: &mut Editor,
-    marked_text: &str,
-    cx: &mut ViewContext<Editor>,
-) {
-    let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true);
-    assert_eq!(editor.text(cx), unmarked_text);
-    assert_eq!(editor.selections.ranges(cx), text_ranges);
-}
-
-// RA thinks this is dead code even though it is used in a whole lot of tests
-#[allow(dead_code)]
-#[cfg(any(test, feature = "test-support"))]
-pub(crate) fn build_editor(buffer: Model<MultiBuffer>, cx: &mut ViewContext<Editor>) -> Editor {
-    // todo!()
-    Editor::new(EditorMode::Full, buffer, None, /*None,*/ cx)
-}
-
-pub(crate) fn build_editor_with_project(
-    project: Model<Project>,
-    buffer: Model<MultiBuffer>,
-    cx: &mut ViewContext<Editor>,
-) -> Editor {
-    // todo!()
-    Editor::new(EditorMode::Full, buffer, Some(project), /*None,*/ cx)
-}

crates/editor2/src/test/editor_lsp_test_context.rs ๐Ÿ”—

@@ -1,298 +0,0 @@
-use std::{
-    borrow::Cow,
-    ops::{Deref, DerefMut, Range},
-    sync::Arc,
-};
-
-use anyhow::Result;
-use serde_json::json;
-
-use crate::{Editor, ToPoint};
-use collections::HashSet;
-use futures::Future;
-use gpui::{View, ViewContext, VisualTestContext};
-use indoc::indoc;
-use language::{point_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageQueries};
-use lsp::{notification, request};
-use multi_buffer::ToPointUtf16;
-use project::Project;
-use smol::stream::StreamExt;
-use workspace::{AppState, Workspace, WorkspaceHandle};
-
-use super::editor_test_context::{AssertionContextManager, EditorTestContext};
-
-pub struct EditorLspTestContext<'a> {
-    pub cx: EditorTestContext<'a>,
-    pub lsp: lsp::FakeLanguageServer,
-    pub workspace: View<Workspace>,
-    pub buffer_lsp_url: lsp::Url,
-}
-
-impl<'a> EditorLspTestContext<'a> {
-    pub async fn new(
-        mut language: Language,
-        capabilities: lsp::ServerCapabilities,
-        cx: &'a mut gpui::TestAppContext,
-    ) -> EditorLspTestContext<'a> {
-        let app_state = cx.update(AppState::test);
-
-        cx.update(|cx| {
-            language::init(cx);
-            crate::init(cx);
-            workspace::init(app_state.clone(), cx);
-            Project::init_settings(cx);
-        });
-
-        let file_name = format!(
-            "file.{}",
-            language
-                .path_suffixes()
-                .first()
-                .expect("language must have a path suffix for EditorLspTestContext")
-        );
-
-        let mut fake_servers = language
-            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-                capabilities,
-                ..Default::default()
-            }))
-            .await;
-
-        let project = Project::test(app_state.fs.clone(), [], cx).await;
-
-        project.update(cx, |project, _| project.languages().add(Arc::new(language)));
-
-        app_state
-            .fs
-            .as_fake()
-            .insert_tree("/root", json!({ "dir": { file_name.clone(): "" }}))
-            .await;
-
-        let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-
-        let workspace = window.root_view(cx).unwrap();
-
-        let mut cx = VisualTestContext::from_window(*window.deref(), cx);
-        project
-            .update(&mut cx, |project, cx| {
-                project.find_or_create_local_worktree("/root", true, cx)
-            })
-            .await
-            .unwrap();
-        cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
-            .await;
-        let file = cx.read(|cx| workspace.file_project_paths(cx)[0].clone());
-        let item = workspace
-            .update(&mut cx, |workspace, cx| {
-                workspace.open_path(file, None, true, cx)
-            })
-            .await
-            .expect("Could not open test file");
-        let editor = cx.update(|cx| {
-            item.act_as::<Editor>(cx)
-                .expect("Opened test file wasn't an editor")
-        });
-        editor.update(&mut cx, |editor, cx| editor.focus(cx));
-
-        let lsp = fake_servers.next().await.unwrap();
-        Self {
-            cx: EditorTestContext {
-                cx,
-                window: window.into(),
-                editor,
-                assertion_cx: AssertionContextManager::new(),
-            },
-            lsp,
-            workspace,
-            buffer_lsp_url: lsp::Url::from_file_path(format!("/root/dir/{file_name}")).unwrap(),
-        }
-    }
-
-    pub async fn new_rust(
-        capabilities: lsp::ServerCapabilities,
-        cx: &'a mut gpui::TestAppContext,
-    ) -> EditorLspTestContext<'a> {
-        let language = Language::new(
-            LanguageConfig {
-                name: "Rust".into(),
-                path_suffixes: vec!["rs".to_string()],
-                ..Default::default()
-            },
-            Some(tree_sitter_rust::language()),
-        )
-        .with_queries(LanguageQueries {
-            indents: Some(Cow::from(indoc! {r#"
-                [
-                    ((where_clause) _ @end)
-                    (field_expression)
-                    (call_expression)
-                    (assignment_expression)
-                    (let_declaration)
-                    (let_chain)
-                    (await_expression)
-                ] @indent
-
-                (_ "[" "]" @end) @indent
-                (_ "<" ">" @end) @indent
-                (_ "{" "}" @end) @indent
-                (_ "(" ")" @end) @indent"#})),
-            brackets: Some(Cow::from(indoc! {r#"
-                ("(" @open ")" @close)
-                ("[" @open "]" @close)
-                ("{" @open "}" @close)
-                ("<" @open ">" @close)
-                ("\"" @open "\"" @close)
-                (closure_parameters "|" @open "|" @close)"#})),
-            ..Default::default()
-        })
-        .expect("Could not parse queries");
-
-        Self::new(language, capabilities, cx).await
-    }
-
-    pub async fn new_typescript(
-        capabilities: lsp::ServerCapabilities,
-        cx: &'a mut gpui::TestAppContext,
-    ) -> EditorLspTestContext<'a> {
-        let mut word_characters: HashSet<char> = Default::default();
-        word_characters.insert('$');
-        word_characters.insert('#');
-        let language = Language::new(
-            LanguageConfig {
-                name: "Typescript".into(),
-                path_suffixes: vec!["ts".to_string()],
-                brackets: language::BracketPairConfig {
-                    pairs: vec![language::BracketPair {
-                        start: "{".to_string(),
-                        end: "}".to_string(),
-                        close: true,
-                        newline: true,
-                    }],
-                    disabled_scopes_by_bracket_ix: Default::default(),
-                },
-                word_characters,
-                ..Default::default()
-            },
-            Some(tree_sitter_typescript::language_typescript()),
-        )
-        .with_queries(LanguageQueries {
-            brackets: Some(Cow::from(indoc! {r#"
-                ("(" @open ")" @close)
-                ("[" @open "]" @close)
-                ("{" @open "}" @close)
-                ("<" @open ">" @close)
-                ("\"" @open "\"" @close)"#})),
-            indents: Some(Cow::from(indoc! {r#"
-                [
-                    (call_expression)
-                    (assignment_expression)
-                    (member_expression)
-                    (lexical_declaration)
-                    (variable_declaration)
-                    (assignment_expression)
-                    (if_statement)
-                    (for_statement)
-                ] @indent
-
-                (_ "[" "]" @end) @indent
-                (_ "<" ">" @end) @indent
-                (_ "{" "}" @end) @indent
-                (_ "(" ")" @end) @indent
-                "#})),
-            ..Default::default()
-        })
-        .expect("Could not parse queries");
-
-        Self::new(language, capabilities, cx).await
-    }
-
-    // Constructs lsp range using a marked string with '[', ']' range delimiters
-    pub fn lsp_range(&mut self, marked_text: &str) -> lsp::Range {
-        let ranges = self.ranges(marked_text);
-        self.to_lsp_range(ranges[0].clone())
-    }
-
-    pub fn to_lsp_range(&mut self, range: Range<usize>) -> lsp::Range {
-        let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
-        let start_point = range.start.to_point(&snapshot.buffer_snapshot);
-        let end_point = range.end.to_point(&snapshot.buffer_snapshot);
-
-        self.editor(|editor, cx| {
-            let buffer = editor.buffer().read(cx);
-            let start = point_to_lsp(
-                buffer
-                    .point_to_buffer_offset(start_point, cx)
-                    .unwrap()
-                    .1
-                    .to_point_utf16(&buffer.read(cx)),
-            );
-            let end = point_to_lsp(
-                buffer
-                    .point_to_buffer_offset(end_point, cx)
-                    .unwrap()
-                    .1
-                    .to_point_utf16(&buffer.read(cx)),
-            );
-
-            lsp::Range { start, end }
-        })
-    }
-
-    pub fn to_lsp(&mut self, offset: usize) -> lsp::Position {
-        let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
-        let point = offset.to_point(&snapshot.buffer_snapshot);
-
-        self.editor(|editor, cx| {
-            let buffer = editor.buffer().read(cx);
-            point_to_lsp(
-                buffer
-                    .point_to_buffer_offset(point, cx)
-                    .unwrap()
-                    .1
-                    .to_point_utf16(&buffer.read(cx)),
-            )
-        })
-    }
-
-    pub fn update_workspace<F, T>(&mut self, update: F) -> T
-    where
-        F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
-    {
-        self.workspace.update(&mut self.cx.cx, update)
-    }
-
-    pub fn handle_request<T, F, Fut>(
-        &self,
-        mut handler: F,
-    ) -> futures::channel::mpsc::UnboundedReceiver<()>
-    where
-        T: 'static + request::Request,
-        T::Params: 'static + Send,
-        F: 'static + Send + FnMut(lsp::Url, T::Params, gpui::AsyncAppContext) -> Fut,
-        Fut: 'static + Send + Future<Output = Result<T::Result>>,
-    {
-        let url = self.buffer_lsp_url.clone();
-        self.lsp.handle_request::<T, _, _>(move |params, cx| {
-            let url = url.clone();
-            handler(url, params, cx)
-        })
-    }
-
-    pub fn notify<T: notification::Notification>(&self, params: T::Params) {
-        self.lsp.notify::<T>(params);
-    }
-}
-
-impl<'a> Deref for EditorLspTestContext<'a> {
-    type Target = EditorTestContext<'a>;
-
-    fn deref(&self) -> &Self::Target {
-        &self.cx
-    }
-}
-
-impl<'a> DerefMut for EditorLspTestContext<'a> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.cx
-    }
-}

crates/editor2/src/test/editor_test_context.rs ๐Ÿ”—

@@ -1,404 +0,0 @@
-use crate::{
-    display_map::ToDisplayPoint, AnchorRangeExt, Autoscroll, DisplayPoint, Editor, MultiBuffer,
-};
-use collections::BTreeMap;
-use futures::Future;
-use gpui::{
-    AnyWindowHandle, AppContext, Keystroke, ModelContext, View, ViewContext, VisualTestContext,
-};
-use indoc::indoc;
-use itertools::Itertools;
-use language::{Buffer, BufferSnapshot};
-use parking_lot::RwLock;
-use project::{FakeFs, Project};
-use std::{
-    any::TypeId,
-    ops::{Deref, DerefMut, Range},
-    sync::{
-        atomic::{AtomicUsize, Ordering},
-        Arc,
-    },
-};
-use util::{
-    assert_set_eq,
-    test::{generate_marked_text, marked_text_ranges},
-};
-
-use super::build_editor_with_project;
-
-pub struct EditorTestContext<'a> {
-    pub cx: gpui::VisualTestContext<'a>,
-    pub window: AnyWindowHandle,
-    pub editor: View<Editor>,
-    pub assertion_cx: AssertionContextManager,
-}
-
-impl<'a> EditorTestContext<'a> {
-    pub async fn new(cx: &'a mut gpui::TestAppContext) -> EditorTestContext<'a> {
-        let fs = FakeFs::new(cx.executor());
-        // fs.insert_file("/file", "".to_owned()).await;
-        fs.insert_tree(
-            "/root",
-            gpui::serde_json::json!({
-                "file": "",
-            }),
-        )
-        .await;
-        let project = Project::test(fs, ["/root".as_ref()], cx).await;
-        let buffer = project
-            .update(cx, |project, cx| {
-                project.open_local_buffer("/root/file", cx)
-            })
-            .await
-            .unwrap();
-        let editor = cx.add_window(|cx| {
-            let editor =
-                build_editor_with_project(project, MultiBuffer::build_from_buffer(buffer, cx), cx);
-            editor.focus(cx);
-            editor
-        });
-        let editor_view = editor.root_view(cx).unwrap();
-        Self {
-            cx: VisualTestContext::from_window(*editor.deref(), cx),
-            window: editor.into(),
-            editor: editor_view,
-            assertion_cx: AssertionContextManager::new(),
-        }
-    }
-
-    pub fn condition(
-        &self,
-        predicate: impl FnMut(&Editor, &AppContext) -> bool,
-    ) -> impl Future<Output = ()> {
-        self.editor
-            .condition::<crate::EditorEvent>(&self.cx, predicate)
-    }
-
-    #[track_caller]
-    pub fn editor<F, T>(&mut self, read: F) -> T
-    where
-        F: FnOnce(&Editor, &ViewContext<Editor>) -> T,
-    {
-        self.editor
-            .update(&mut self.cx, |this, cx| read(&this, &cx))
-    }
-
-    #[track_caller]
-    pub fn update_editor<F, T>(&mut self, update: F) -> T
-    where
-        F: FnOnce(&mut Editor, &mut ViewContext<Editor>) -> T,
-    {
-        self.editor.update(&mut self.cx, update)
-    }
-
-    pub fn multibuffer<F, T>(&mut self, read: F) -> T
-    where
-        F: FnOnce(&MultiBuffer, &AppContext) -> T,
-    {
-        self.editor(|editor, cx| read(editor.buffer().read(cx), cx))
-    }
-
-    pub fn update_multibuffer<F, T>(&mut self, update: F) -> T
-    where
-        F: FnOnce(&mut MultiBuffer, &mut ModelContext<MultiBuffer>) -> T,
-    {
-        self.update_editor(|editor, cx| editor.buffer().update(cx, update))
-    }
-
-    pub fn buffer_text(&mut self) -> String {
-        self.multibuffer(|buffer, cx| buffer.snapshot(cx).text())
-    }
-
-    pub fn buffer<F, T>(&mut self, read: F) -> T
-    where
-        F: FnOnce(&Buffer, &AppContext) -> T,
-    {
-        self.multibuffer(|multibuffer, cx| {
-            let buffer = multibuffer.as_singleton().unwrap().read(cx);
-            read(buffer, cx)
-        })
-    }
-
-    pub fn update_buffer<F, T>(&mut self, update: F) -> T
-    where
-        F: FnOnce(&mut Buffer, &mut ModelContext<Buffer>) -> T,
-    {
-        self.update_multibuffer(|multibuffer, cx| {
-            let buffer = multibuffer.as_singleton().unwrap();
-            buffer.update(cx, update)
-        })
-    }
-
-    pub fn buffer_snapshot(&mut self) -> BufferSnapshot {
-        self.buffer(|buffer, _| buffer.snapshot())
-    }
-
-    pub fn add_assertion_context(&self, context: String) -> ContextHandle {
-        self.assertion_cx.add_context(context)
-    }
-
-    pub fn assertion_context(&self) -> String {
-        self.assertion_cx.context()
-    }
-
-    pub fn simulate_keystroke(&mut self, keystroke_text: &str) -> ContextHandle {
-        let keystroke_under_test_handle =
-            self.add_assertion_context(format!("Simulated Keystroke: {:?}", keystroke_text));
-        let keystroke = Keystroke::parse(keystroke_text).unwrap();
-
-        self.cx.dispatch_keystroke(self.window, keystroke, false);
-
-        keystroke_under_test_handle
-    }
-
-    pub fn simulate_keystrokes<const COUNT: usize>(
-        &mut self,
-        keystroke_texts: [&str; COUNT],
-    ) -> ContextHandle {
-        let keystrokes_under_test_handle =
-            self.add_assertion_context(format!("Simulated Keystrokes: {:?}", keystroke_texts));
-        for keystroke_text in keystroke_texts.into_iter() {
-            self.simulate_keystroke(keystroke_text);
-        }
-        // it is common for keyboard shortcuts to kick off async actions, so this ensures that they are complete
-        // before returning.
-        // NOTE: we don't do this in simulate_keystroke() because a possible cause of bugs is that typing too
-        // quickly races with async actions.
-        self.cx.background_executor.run_until_parked();
-
-        keystrokes_under_test_handle
-    }
-
-    pub fn run_until_parked(&mut self) {
-        self.cx.background_executor.run_until_parked();
-    }
-
-    pub fn ranges(&mut self, marked_text: &str) -> Vec<Range<usize>> {
-        let (unmarked_text, ranges) = marked_text_ranges(marked_text, false);
-        assert_eq!(self.buffer_text(), unmarked_text);
-        ranges
-    }
-
-    pub fn display_point(&mut self, marked_text: &str) -> DisplayPoint {
-        let ranges = self.ranges(marked_text);
-        let snapshot = self
-            .editor
-            .update(&mut self.cx, |editor, cx| editor.snapshot(cx));
-        ranges[0].start.to_display_point(&snapshot)
-    }
-
-    // Returns anchors for the current buffer using `ยซ` and `ยป`
-    pub fn text_anchor_range(&mut self, marked_text: &str) -> Range<language::Anchor> {
-        let ranges = self.ranges(marked_text);
-        let snapshot = self.buffer_snapshot();
-        snapshot.anchor_before(ranges[0].start)..snapshot.anchor_after(ranges[0].end)
-    }
-
-    pub fn set_diff_base(&mut self, diff_base: Option<&str>) {
-        let diff_base = diff_base.map(String::from);
-        self.update_buffer(|buffer, cx| buffer.set_diff_base(diff_base, cx));
-    }
-
-    /// Change the editor's text and selections using a string containing
-    /// embedded range markers that represent the ranges and directions of
-    /// each selection.
-    ///
-    /// Returns a context handle so that assertion failures can print what
-    /// editor state was needed to cause the failure.
-    ///
-    /// See the `util::test::marked_text_ranges` function for more information.
-    pub fn set_state(&mut self, marked_text: &str) -> ContextHandle {
-        let state_context = self.add_assertion_context(format!(
-            "Initial Editor State: \"{}\"",
-            marked_text.escape_debug().to_string()
-        ));
-        let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
-        self.editor.update(&mut self.cx, |editor, cx| {
-            editor.set_text(unmarked_text, cx);
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.select_ranges(selection_ranges)
-            })
-        });
-        state_context
-    }
-
-    /// Only change the editor's selections
-    pub fn set_selections_state(&mut self, marked_text: &str) -> ContextHandle {
-        let state_context = self.add_assertion_context(format!(
-            "Initial Editor State: \"{}\"",
-            marked_text.escape_debug().to_string()
-        ));
-        let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
-        self.editor.update(&mut self.cx, |editor, cx| {
-            assert_eq!(editor.text(cx), unmarked_text);
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.select_ranges(selection_ranges)
-            })
-        });
-        state_context
-    }
-
-    /// Make an assertion about the editor's text and the ranges and directions
-    /// of its selections using a string containing embedded range markers.
-    ///
-    /// See the `util::test::marked_text_ranges` function for more information.
-    #[track_caller]
-    pub fn assert_editor_state(&mut self, marked_text: &str) {
-        let (unmarked_text, expected_selections) = marked_text_ranges(marked_text, true);
-        let buffer_text = self.buffer_text();
-
-        if buffer_text != unmarked_text {
-            panic!("Unmarked text doesn't match buffer text\nBuffer text: {buffer_text:?}\nUnmarked text: {unmarked_text:?}\nRaw buffer text\n{buffer_text}Raw unmarked text\n{unmarked_text}");
-        }
-
-        self.assert_selections(expected_selections, marked_text.to_string())
-    }
-
-    pub fn editor_state(&mut self) -> String {
-        generate_marked_text(self.buffer_text().as_str(), &self.editor_selections(), true)
-    }
-
-    #[track_caller]
-    pub fn assert_editor_background_highlights<Tag: 'static>(&mut self, marked_text: &str) {
-        let expected_ranges = self.ranges(marked_text);
-        let actual_ranges: Vec<Range<usize>> = self.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
-            editor
-                .background_highlights
-                .get(&TypeId::of::<Tag>())
-                .map(|h| h.1.clone())
-                .unwrap_or_default()
-                .into_iter()
-                .map(|range| range.to_offset(&snapshot.buffer_snapshot))
-                .collect()
-        });
-        assert_set_eq!(actual_ranges, expected_ranges);
-    }
-
-    #[track_caller]
-    pub fn assert_editor_text_highlights<Tag: ?Sized + 'static>(&mut self, marked_text: &str) {
-        let expected_ranges = self.ranges(marked_text);
-        let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
-        let actual_ranges: Vec<Range<usize>> = snapshot
-            .text_highlight_ranges::<Tag>()
-            .map(|ranges| ranges.as_ref().clone().1)
-            .unwrap_or_default()
-            .into_iter()
-            .map(|range| range.to_offset(&snapshot.buffer_snapshot))
-            .collect();
-        assert_set_eq!(actual_ranges, expected_ranges);
-    }
-
-    #[track_caller]
-    pub fn assert_editor_selections(&mut self, expected_selections: Vec<Range<usize>>) {
-        let expected_marked_text =
-            generate_marked_text(&self.buffer_text(), &expected_selections, true);
-        self.assert_selections(expected_selections, expected_marked_text)
-    }
-
-    #[track_caller]
-    fn editor_selections(&mut self) -> Vec<Range<usize>> {
-        self.editor
-            .update(&mut self.cx, |editor, cx| {
-                editor.selections.all::<usize>(cx)
-            })
-            .into_iter()
-            .map(|s| {
-                if s.reversed {
-                    s.end..s.start
-                } else {
-                    s.start..s.end
-                }
-            })
-            .collect::<Vec<_>>()
-    }
-
-    #[track_caller]
-    fn assert_selections(
-        &mut self,
-        expected_selections: Vec<Range<usize>>,
-        expected_marked_text: String,
-    ) {
-        let actual_selections = self.editor_selections();
-        let actual_marked_text =
-            generate_marked_text(&self.buffer_text(), &actual_selections, true);
-        if expected_selections != actual_selections {
-            panic!(
-                indoc! {"
-
-                {}Editor has unexpected selections.
-
-                Expected selections:
-                {}
-
-                Actual selections:
-                {}
-            "},
-                self.assertion_context(),
-                expected_marked_text,
-                actual_marked_text,
-            );
-        }
-    }
-}
-
-impl<'a> Deref for EditorTestContext<'a> {
-    type Target = gpui::TestAppContext;
-
-    fn deref(&self) -> &Self::Target {
-        &self.cx
-    }
-}
-
-impl<'a> DerefMut for EditorTestContext<'a> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.cx
-    }
-}
-
-/// Tracks string context to be printed when assertions fail.
-/// Often this is done by storing a context string in the manager and returning the handle.
-#[derive(Clone)]
-pub struct AssertionContextManager {
-    id: Arc<AtomicUsize>,
-    contexts: Arc<RwLock<BTreeMap<usize, String>>>,
-}
-
-impl AssertionContextManager {
-    pub fn new() -> Self {
-        Self {
-            id: Arc::new(AtomicUsize::new(0)),
-            contexts: Arc::new(RwLock::new(BTreeMap::new())),
-        }
-    }
-
-    pub fn add_context(&self, context: String) -> ContextHandle {
-        let id = self.id.fetch_add(1, Ordering::Relaxed);
-        let mut contexts = self.contexts.write();
-        contexts.insert(id, context);
-        ContextHandle {
-            id,
-            manager: self.clone(),
-        }
-    }
-
-    pub fn context(&self) -> String {
-        let contexts = self.contexts.read();
-        format!("\n{}\n", contexts.values().join("\n"))
-    }
-}
-
-/// Used to track the lifetime of a piece of context so that it can be provided when an assertion fails.
-/// For example, in the EditorTestContext, `set_state` returns a context handle so that if an assertion fails,
-/// the state that was set initially for the failure can be printed in the error message
-pub struct ContextHandle {
-    id: usize,
-    manager: AssertionContextManager,
-}
-
-impl Drop for ContextHandle {
-    fn drop(&mut self) {
-        let mut contexts = self.manager.contexts.write();
-        contexts.remove(&self.id);
-    }
-}

crates/feedback/Cargo.toml ๐Ÿ”—

@@ -13,7 +13,7 @@ test-support = []
 [dependencies]
 client = { package = "client2", path = "../client2" }
 db = { package = "db2", path = "../db2" }
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 gpui = { package = "gpui2", path = "../gpui2" }
 language = { package = "language2", path = "../language2" }
 menu = { package = "menu2", path = "../menu2" }
@@ -44,4 +44,4 @@ tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown",
 urlencoding = "2.1.2"
 
 [dev-dependencies]
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }

crates/file_finder/Cargo.toml ๐Ÿ”—

@@ -9,7 +9,7 @@ path = "src/file_finder.rs"
 doctest = false
 
 [dependencies]
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 collections = { path = "../collections" }
 fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
 gpui = { package = "gpui2", path = "../gpui2" }
@@ -26,7 +26,7 @@ postage.workspace = true
 serde.workspace = true
 
 [dev-dependencies]
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }
 gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 language = { package = "language2", path = "../language2", features = ["test-support"] }
 workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }

crates/go_to_line/Cargo.toml ๐Ÿ”—

@@ -9,7 +9,7 @@ path = "src/go_to_line.rs"
 doctest = false
 
 [dependencies]
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 gpui = { package = "gpui2", path = "../gpui2" }
 menu = { package = "menu2", path = "../menu2" }
 serde.workspace = true
@@ -22,4 +22,4 @@ ui = { package = "ui2", path = "../ui2" }
 util = { path = "../util" }
 
 [dev-dependencies]
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }

crates/journal2/Cargo.toml ๐Ÿ”—

@@ -9,7 +9,7 @@ path = "src/journal2.rs"
 doctest = false
 
 [dependencies]
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 gpui = { package = "gpui2", path = "../gpui2" }
 util = { path = "../util" }
 workspace2 = { path = "../workspace2" }
@@ -24,4 +24,4 @@ log.workspace = true
 shellexpand = "2.1.0"
 
 [dev-dependencies]
-editor = { package="editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }

crates/language_selector/Cargo.toml ๐Ÿ”—

@@ -9,7 +9,7 @@ path = "src/language_selector.rs"
 doctest = false
 
 [dependencies]
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
 language = { package = "language2", path = "../language2" }
 gpui = { package = "gpui2", path = "../gpui2" }
@@ -23,4 +23,4 @@ workspace = { package = "workspace2", path = "../workspace2" }
 anyhow.workspace = true
 
 [dev-dependencies]
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }

crates/language_tools/Cargo.toml ๐Ÿ”—

@@ -10,7 +10,7 @@ doctest = false
 
 [dependencies]
 collections = { path = "../collections" }
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 settings = { package = "settings2", path = "../settings2" }
 theme = { package = "theme2", path = "../theme2" }
 language = { package = "language2", path = "../language2" }
@@ -27,7 +27,7 @@ tree-sitter.workspace = true
 
 [dev-dependencies]
 client = { package = "client2", path = "../client2", features = ["test-support"] }
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }
 gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }
 env_logger.workspace = true

crates/outline2/Cargo.toml ๐Ÿ”—

@@ -9,7 +9,7 @@ path = "src/outline.rs"
 doctest = false
 
 [dependencies]
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
 gpui = { package = "gpui2", path = "../gpui2" }
 ui = { package = "ui2", path = "../ui2" }
@@ -26,4 +26,4 @@ postage.workspace = true
 smol.workspace = true
 
 [dev-dependencies]
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }

crates/picker/Cargo.toml ๐Ÿ”—

@@ -9,7 +9,7 @@ path = "src/picker.rs"
 doctest = false
 
 [dependencies]
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 ui = { package = "ui2", path = "../ui2" }
 gpui = { package = "gpui2", path = "../gpui2" }
 menu = { package = "menu2", path = "../menu2" }
@@ -21,7 +21,7 @@ workspace = { package = "workspace2", path = "../workspace2"}
 parking_lot.workspace = true
 
 [dev-dependencies]
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }
 gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 serde_json.workspace = true
 ctor.workspace = true

crates/project_panel/Cargo.toml ๐Ÿ”—

@@ -11,7 +11,7 @@ doctest = false
 [dependencies]
 collections = { path = "../collections" }
 db = { path = "../db2", package = "db2" }
-editor = { path = "../editor2", package = "editor2" }
+editor = { path = "../editor" }
 gpui = { path = "../gpui2", package = "gpui2" }
 menu = { path = "../menu2", package = "menu2" }
 project = { path = "../project2", package = "project2" }
@@ -35,7 +35,7 @@ unicase = "2.6"
 [dev-dependencies]
 client = { path = "../client2", package = "client2", features = ["test-support"] }
 language = { path = "../language2", package = "language2", features = ["test-support"] }
-editor = { path = "../editor2", package = "editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }
 gpui = { path = "../gpui2", package = "gpui2", features = ["test-support"] }
 workspace = { path = "../workspace2", package = "workspace2", features = ["test-support"] }
 serde_json.workspace = true

crates/project_symbols/Cargo.toml ๐Ÿ”—

@@ -9,7 +9,7 @@ path = "src/project_symbols.rs"
 doctest = false
 
 [dependencies]
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 fuzzy = {package = "fuzzy2",  path = "../fuzzy2" }
 gpui = {package = "gpui2",  path = "../gpui2" }
 picker = {path = "../picker" }
@@ -27,7 +27,7 @@ smol.workspace = true
 
 [dev-dependencies]
 futures.workspace = true
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }
 settings = { package = "settings2", path = "../settings2", features = ["test-support"] }
 gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 language = { package = "language2", path = "../language2", features = ["test-support"] }

crates/quick_action_bar/Cargo.toml ๐Ÿ”—

@@ -10,13 +10,13 @@ doctest = false
 
 [dependencies]
 assistant = { package = "assistant2", path = "../assistant2" }
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 gpui = { package = "gpui2", path = "../gpui2" }
 search = { path = "../search" }
 workspace = { package = "workspace2", path = "../workspace2" }
 ui = { package = "ui2", path = "../ui2" }
 
 [dev-dependencies]
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }
 gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }

crates/recent_projects/Cargo.toml ๐Ÿ”—

@@ -9,7 +9,7 @@ path = "src/recent_projects.rs"
 doctest = false
 
 [dependencies]
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
 gpui = { package = "gpui2", path = "../gpui2" }
 language = { package = "language2", path = "../language2" }
@@ -27,4 +27,4 @@ postage.workspace = true
 smol.workspace = true
 
 [dev-dependencies]
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }

crates/search/Cargo.toml ๐Ÿ”—

@@ -11,7 +11,7 @@ doctest = false
 [dependencies]
 bitflags = "1"
 collections = { path = "../collections" }
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 gpui = { package = "gpui2", path = "../gpui2" }
 language = { package = "language2", path = "../language2" }
 menu = { package = "menu2", path = "../menu2" }
@@ -33,7 +33,7 @@ smol.workspace = true
 serde_json.workspace = true
 [dev-dependencies]
 client = { package = "client2", path = "../client2", features = ["test-support"] }
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }
 gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 
 workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }

crates/storybook2/Cargo.toml ๐Ÿ”—

@@ -15,7 +15,7 @@ backtrace-on-stack-overflow = "0.3.0"
 chrono = "0.4"
 clap = { version = "4.4", features = ["derive", "string"] }
 dialoguer = { version = "0.11.0", features = ["fuzzy-select"] }
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
 gpui = { package = "gpui2", path = "../gpui2" }
 indoc.workspace = true

crates/terminal_view/Cargo.toml ๐Ÿ”—

@@ -9,7 +9,7 @@ path = "src/terminal_view.rs"
 doctest = false
 
 [dependencies]
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 language = { package = "language2", path = "../language2" }
 gpui = { package = "gpui2", path = "../gpui2" }
 project = { package = "project2", path = "../project2" }
@@ -38,7 +38,7 @@ serde.workspace = true
 serde_derive.workspace = true
 
 [dev-dependencies]
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }
 gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 client = { package = "client2", path = "../client2", features = ["test-support"]}
 project = { package = "project2", path = "../project2", features = ["test-support"]}

crates/theme_selector/Cargo.toml ๐Ÿ”—

@@ -10,7 +10,7 @@ doctest = false
 
 [dependencies]
 client = { package = "client2", path = "../client2" }
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 feature_flags = { package = "feature_flags2", path = "../feature_flags2" }
 fs = { package = "fs2", path = "../fs2" }
 fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
@@ -27,4 +27,4 @@ postage.workspace = true
 smol.workspace = true
 
 [dev-dependencies]
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }

crates/vim/Cargo.toml ๐Ÿ”—

@@ -26,7 +26,7 @@ serde_json.workspace = true
 
 collections = { path = "../collections" }
 command_palette = { path = "../command_palette" }
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 gpui = { package = "gpui2", path = "../gpui2" }
 language = { package = "language2", path = "../language2" }
 search = { path = "../search" }
@@ -42,7 +42,7 @@ indoc.workspace = true
 parking_lot.workspace = true
 futures.workspace = true
 
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }
 gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 language = { package = "language2", path = "../language2", features = ["test-support"] }
 project = { package = "project2", path = "../project2", features = ["test-support"] }

crates/welcome/Cargo.toml ๐Ÿ”—

@@ -12,7 +12,7 @@ test-support = []
 
 [dependencies]
 client = { package = "client2", path = "../client2" }
-editor = { package = "editor2", path = "../editor2" }
+editor = { path = "../editor" }
 fs = { package = "fs2", path = "../fs2" }
 fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
 gpui = { package = "gpui2", path = "../gpui2" }
@@ -34,4 +34,4 @@ schemars.workspace = true
 serde.workspace = true
 
 [dev-dependencies]
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }

crates/zed/Cargo.toml ๐Ÿ”—

@@ -33,7 +33,7 @@ copilot = { package = "copilot2", path = "../copilot2" }
 copilot_button = { path = "../copilot_button" }
 diagnostics = { path = "../diagnostics" }
 db = { package = "db2", path = "../db2" }
-editor = { package="editor2", path = "../editor2" }
+editor = { path = "../editor" }
 feedback = { path = "../feedback" }
 file_finder = { path = "../file_finder" }
 search = { path = "../search" }